mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
Refactor asset handling and authentication logic (for node)
Updated asset path configuration and server-side logic to standardize asset serving. Introduced `shouldSkipAuth` to bypass authentication for asset requests. Added test coverage for the new asset path handling logic.
This commit is contained in:
9
app/__test__/auth/middleware.spec.ts
Normal file
9
app/__test__/auth/middleware.spec.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { describe, expect, it } from "bun:test";
|
||||||
|
import { shouldSkipAuth } from "../../src/auth/middlewares";
|
||||||
|
|
||||||
|
describe("auth middleware", () => {
|
||||||
|
it("should skip auth on asset paths", () => {
|
||||||
|
expect(shouldSkipAuth({ req: new Request("http://localhost/assets/test.js") })).toBe(true);
|
||||||
|
expect(shouldSkipAuth({ req: new Request("http://localhost/") })).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -58,7 +58,7 @@ const result = await esbuild.build({
|
|||||||
sourcemap,
|
sourcemap,
|
||||||
entryPoints: ["src/ui/main.tsx"],
|
entryPoints: ["src/ui/main.tsx"],
|
||||||
entryNames: "[dir]/[name]-[hash]",
|
entryNames: "[dir]/[name]-[hash]",
|
||||||
outdir: "dist/static",
|
outdir: "dist/static/assets",
|
||||||
platform: "browser",
|
platform: "browser",
|
||||||
bundle: true,
|
bundle: true,
|
||||||
splitting: true,
|
splitting: true,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { IncomingMessage } from "node:http";
|
import type { IncomingMessage } from "node:http";
|
||||||
import { App, type CreateAppConfig, registries } from "bknd";
|
import { App, type CreateAppConfig, registries } from "bknd";
|
||||||
|
import { config as $config } from "core";
|
||||||
import type { MiddlewareHandler } from "hono";
|
import type { MiddlewareHandler } from "hono";
|
||||||
import { StorageLocalAdapter } from "media/storage/adapters/StorageLocalAdapter";
|
import { StorageLocalAdapter } from "media/storage/adapters/StorageLocalAdapter";
|
||||||
import type { AdminControllerOptions } from "modules/server/AdminController";
|
import type { AdminControllerOptions } from "modules/server/AdminController";
|
||||||
@@ -106,12 +107,10 @@ export async function createRuntimeApp<Env = any>(
|
|||||||
App.Events.AppBuiltEvent,
|
App.Events.AppBuiltEvent,
|
||||||
async () => {
|
async () => {
|
||||||
if (serveStatic) {
|
if (serveStatic) {
|
||||||
if (Array.isArray(serveStatic)) {
|
const [path, handler] = Array.isArray(serveStatic)
|
||||||
const [path, handler] = serveStatic;
|
? serveStatic
|
||||||
|
: [$config.server.assets_path + "*", serveStatic];
|
||||||
app.modules.server.get(path, handler);
|
app.modules.server.get(path, handler);
|
||||||
} else {
|
|
||||||
app.modules.server.get("/*", serveStatic);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await config.onBuilt?.(app);
|
await config.onBuilt?.(app);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Permission } from "core";
|
import { type Permission, config } from "core";
|
||||||
import type { Context } from "hono";
|
import type { Context } from "hono";
|
||||||
import { createMiddleware } from "hono/factory";
|
import { createMiddleware } from "hono/factory";
|
||||||
import type { ServerEnv } from "modules/Module";
|
import type { ServerEnv } from "modules/Module";
|
||||||
@@ -21,18 +21,27 @@ async function resolveAuth(app: ServerEnv["Variables"]["app"], c: Context<Server
|
|||||||
authenticator.requestCookieRefresh(c);
|
authenticator.requestCookieRefresh(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function shouldSkipAuth(c: { req: Request }) {
|
||||||
|
return new URL(c.req.url).pathname.startsWith(config.server.assets_path);
|
||||||
|
}
|
||||||
|
|
||||||
export const auth = createMiddleware<ServerEnv>(async (c, next) => {
|
export const auth = createMiddleware<ServerEnv>(async (c, next) => {
|
||||||
|
if (!shouldSkipAuth) {
|
||||||
// make sure to only register once
|
// make sure to only register once
|
||||||
if (c.get("auth_registered")) {
|
if (c.get("auth_registered")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await resolveAuth(c.get("app"), c);
|
await resolveAuth(c.get("app"), c);
|
||||||
c.set("auth_registered", true);
|
c.set("auth_registered", true);
|
||||||
|
}
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|
||||||
export const permission = (...permissions: Permission[]) =>
|
export const permission = (...permissions: Permission[]) =>
|
||||||
createMiddleware<ServerEnv>(async (c, next) => {
|
createMiddleware<ServerEnv>(async (c, next) => {
|
||||||
|
if (!shouldSkipAuth) {
|
||||||
const app = c.get("app");
|
const app = c.get("app");
|
||||||
if (app) {
|
if (app) {
|
||||||
const p = Array.isArray(permissions) ? permissions : [permissions];
|
const p = Array.isArray(permissions) ? permissions : [permissions];
|
||||||
@@ -43,6 +52,7 @@ export const permission = (...permissions: Permission[]) =>
|
|||||||
} else {
|
} else {
|
||||||
console.warn("app not in context, skip permission check");
|
console.warn("app not in context, skip permission check");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ export interface DB {}
|
|||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
server: {
|
server: {
|
||||||
default_port: 1337
|
default_port: 1337,
|
||||||
|
assets_path: "/assets/"
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
default_primary_field: "id"
|
default_primary_field: "id"
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import type { Hono } from "hono";
|
|||||||
export type ServerEnv = {
|
export type ServerEnv = {
|
||||||
Variables: {
|
Variables: {
|
||||||
app: App;
|
app: App;
|
||||||
|
// to prevent resolving auth multiple times
|
||||||
auth_resolved: boolean;
|
auth_resolved: boolean;
|
||||||
|
// to only register once
|
||||||
auth_registered: boolean;
|
auth_registered: boolean;
|
||||||
html?: string;
|
html?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/** @jsxImportSource hono/jsx */
|
/** @jsxImportSource hono/jsx */
|
||||||
|
|
||||||
import type { App } from "App";
|
import type { App } from "App";
|
||||||
import { isDebug } from "core";
|
import { config, isDebug } from "core";
|
||||||
import { addFlashMessage } from "core/server/flash";
|
import { addFlashMessage } from "core/server/flash";
|
||||||
import { html } from "hono/html";
|
import { html } from "hono/html";
|
||||||
import { Fragment } from "hono/jsx";
|
import { Fragment } from "hono/jsx";
|
||||||
@@ -13,6 +13,7 @@ const htmlBkndContextReplace = "<!-- BKND_CONTEXT -->";
|
|||||||
// @todo: add migration to remove admin path from config
|
// @todo: add migration to remove admin path from config
|
||||||
export type AdminControllerOptions = {
|
export type AdminControllerOptions = {
|
||||||
basepath?: string;
|
basepath?: string;
|
||||||
|
assets_path?: string;
|
||||||
html?: string;
|
html?: string;
|
||||||
forceDev?: boolean | { mainPath: string };
|
forceDev?: boolean | { mainPath: string };
|
||||||
};
|
};
|
||||||
@@ -20,7 +21,7 @@ export type AdminControllerOptions = {
|
|||||||
export class AdminController extends Controller {
|
export class AdminController extends Controller {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly app: App,
|
private readonly app: App,
|
||||||
private options: AdminControllerOptions = {}
|
private _options: AdminControllerOptions = {}
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@@ -29,6 +30,14 @@ export class AdminController extends Controller {
|
|||||||
return this.app.modules.ctx();
|
return this.app.modules.ctx();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
...this._options,
|
||||||
|
basepath: this._options.basepath ?? "/",
|
||||||
|
assets_path: this._options.assets_path ?? config.server.assets_path
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
get basepath() {
|
get basepath() {
|
||||||
return this.options.basepath ?? "/";
|
return this.options.basepath ?? "/";
|
||||||
}
|
}
|
||||||
@@ -156,8 +165,16 @@ export class AdminController extends Controller {
|
|||||||
<title>BKND</title>
|
<title>BKND</title>
|
||||||
{isProd ? (
|
{isProd ? (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<script type="module" CrossOrigin src={"/" + assets?.js} />
|
<script
|
||||||
<link rel="stylesheet" crossOrigin href={"/" + assets?.css} />
|
type="module"
|
||||||
|
CrossOrigin
|
||||||
|
src={this.options.assets_path + assets?.js}
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
crossOrigin
|
||||||
|
href={this.options.assets_path + assets?.css}
|
||||||
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : (
|
) : (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
|||||||
Reference in New Issue
Block a user