From 6077f0e64f34ea1b730e0af7c37707045a4ac6c9 Mon Sep 17 00:00:00 2001 From: dswbx Date: Thu, 21 Nov 2024 16:24:33 +0100 Subject: [PATCH 01/13] added onBeforeUpdate listener + auto create a secret on auth enable --- app/__test__/core/object/SchemaObject.spec.ts | 54 +++++++++++++------ app/__test__/modules/AppAuth.spec.ts | 15 +++++- app/src/adapter/index.ts | 1 + app/src/auth/AppAuth.ts | 51 ++++++++++-------- app/src/auth/auth-schema.ts | 10 +--- app/src/auth/authenticate/Authenticator.ts | 10 ++-- app/src/core/object/SchemaObject.ts | 19 +++++-- app/src/core/utils/crypto.ts | 6 +++ app/src/modules/Module.ts | 12 +++-- app/src/modules/server/SystemController.ts | 10 +++- app/src/ui/client/schema/actions.ts | 31 ++++++++++- app/vite.dev.ts | 6 +-- 12 files changed, 158 insertions(+), 67 deletions(-) diff --git a/app/__test__/core/object/SchemaObject.spec.ts b/app/__test__/core/object/SchemaObject.spec.ts index f0d8434..01db1c7 100644 --- a/app/__test__/core/object/SchemaObject.spec.ts +++ b/app/__test__/core/object/SchemaObject.spec.ts @@ -65,11 +65,11 @@ describe("SchemaObject", async () => { expect(m.get()).toEqual({ methods: ["GET", "PATCH"] }); // array values are fully overwritten, whether accessed by index ... - m.patch("methods[0]", "POST"); - expect(m.get()).toEqual({ methods: ["POST"] }); + await m.patch("methods[0]", "POST"); + expect(m.get().methods[0]).toEqual("POST"); // or by path! - m.patch("methods", ["GET", "DELETE"]); + await m.patch("methods", ["GET", "DELETE"]); expect(m.get()).toEqual({ methods: ["GET", "DELETE"] }); }); @@ -93,15 +93,15 @@ describe("SchemaObject", async () => { expect(m.get()).toEqual({ s: { a: "b", b: { c: "d" } } }); // expect no change, because the default then applies - m.remove("s.a"); + await m.remove("s.a"); expect(m.get()).toEqual({ s: { a: "b", b: { c: "d" } } }); // adding another path, and then deleting it - m.patch("s.c", "d"); + await m.patch("s.c", "d"); expect(m.get()).toEqual({ s: { a: "b", b: { c: "d" }, c: "d" } } as any); // now it should be removed without applying again - m.remove("s.c"); + await m.remove("s.c"); expect(m.get()).toEqual({ s: { a: "b", b: { c: "d" } } }); }); @@ -113,14 +113,14 @@ describe("SchemaObject", async () => { ); expect(m.get()).toEqual({ methods: ["GET", "PATCH"] }); - m.set({ methods: ["GET", "POST"] }); + await m.set({ methods: ["GET", "POST"] }); expect(m.get()).toEqual({ methods: ["GET", "POST"] }); // wrong type expect(() => m.set({ methods: [1] as any })).toThrow(); }); - test("listener", async () => { + test("listener: onUpdate", async () => { let called = false; let result: any; const m = new SchemaObject( @@ -142,6 +142,30 @@ describe("SchemaObject", async () => { expect(result).toEqual({ methods: ["GET", "POST"] }); }); + test("listener: onBeforeUpdate", async () => { + let called = false; + const m = new SchemaObject( + Type.Object({ + methods: Type.Array(Type.String(), { default: ["GET", "PATCH"] }) + }), + undefined, + { + onBeforeUpdate: async (from, to) => { + await new Promise((r) => setTimeout(r, 10)); + called = true; + to.methods.push("OPTIONS"); + return to; + } + } + ); + + const result = await m.set({ methods: ["GET", "POST"] }); + expect(called).toBe(true); + expect(result).toEqual({ methods: ["GET", "POST", "OPTIONS"] }); + const [, result2] = await m.patch("methods", ["GET", "POST"]); + expect(result2).toEqual({ methods: ["GET", "POST", "OPTIONS"] }); + }); + test("throwIfRestricted", async () => { const m = new SchemaObject(Type.Object({}), undefined, { restrictPaths: ["a.b"] @@ -175,9 +199,9 @@ describe("SchemaObject", async () => { } ); - expect(() => m.patch("s.b.c", "e")).toThrow(); - expect(m.bypass().patch("s.b.c", "e")).toBeDefined(); - expect(() => m.patch("s.b.c", "f")).toThrow(); + expect(m.patch("s.b.c", "e")).rejects.toThrow(); + expect(m.bypass().patch("s.b.c", "e")).resolves.toBeDefined(); + expect(m.patch("s.b.c", "f")).rejects.toThrow(); expect(m.get()).toEqual({ s: { a: "b", b: { c: "e" } } }); }); @@ -222,7 +246,7 @@ describe("SchemaObject", async () => { overwritePaths: [/^entities\..*\.fields\..*\.config/] }); - m.patch("entities.some.fields.a", { type: "string", config: { another: "one" } }); + await m.patch("entities.some.fields.a", { type: "string", config: { another: "one" } }); expect(m.get()).toEqual({ entities: { @@ -251,7 +275,7 @@ describe("SchemaObject", async () => { overwritePaths: [/^entities\..*\.fields\..*\.config\.html_config$/] }); - m.patch("entities.test", { + await m.patch("entities.test", { fields: { content: { type: "text" @@ -296,7 +320,7 @@ describe("SchemaObject", async () => { expect(m.patch("desc", "entities.users.config.sort_dir")).rejects.toThrow(); - m.patch("entities.test", { + await m.patch("entities.test", { fields: { content: { type: "text" @@ -304,7 +328,7 @@ describe("SchemaObject", async () => { } }); - m.patch("entities.users.config", { + await m.patch("entities.users.config", { sort_dir: "desc" }); diff --git a/app/__test__/modules/AppAuth.spec.ts b/app/__test__/modules/AppAuth.spec.ts index 7861fc6..be1c7e1 100644 --- a/app/__test__/modules/AppAuth.spec.ts +++ b/app/__test__/modules/AppAuth.spec.ts @@ -19,10 +19,23 @@ describe("AppAuth", () => { await auth.build(); const config = auth.toJSON(); - expect(config.jwt.secret).toBeUndefined(); + expect(config.jwt).toBeUndefined(); expect(config.strategies.password.config).toBeUndefined(); }); + test("enabling auth: generate secret", async () => { + const auth = new AppAuth(undefined, ctx); + await auth.build(); + + const oldConfig = auth.toJSON(true); + //console.log(oldConfig); + await auth.schema().patch("enabled", true); + await auth.build(); + const newConfig = auth.toJSON(true); + //console.log(newConfig); + expect(newConfig.jwt.secret).not.toBe(oldConfig.jwt.secret); + }); + test("creates user on register", async () => { const auth = new AppAuth( { diff --git a/app/src/adapter/index.ts b/app/src/adapter/index.ts index 04a9bb8..820c3c2 100644 --- a/app/src/adapter/index.ts +++ b/app/src/adapter/index.ts @@ -16,6 +16,7 @@ export type CloudflareBkndConfig = { forceHttps?: boolean; }; +// @todo: move to App export type BkndConfig = { app: CreateAppConfig | ((env: Env) => CreateAppConfig); setAdminHtml?: boolean; diff --git a/app/src/auth/AppAuth.ts b/app/src/auth/AppAuth.ts index a829c8d..e0b53ce 100644 --- a/app/src/auth/AppAuth.ts +++ b/app/src/auth/AppAuth.ts @@ -1,16 +1,9 @@ import { type AuthAction, Authenticator, type ProfileExchange, Role, type Strategy } from "auth"; import { Exception } from "core"; -import { transformObject } from "core/utils"; -import { - type Entity, - EntityIndex, - type EntityManager, - EnumField, - type Field, - type Mutator -} from "data"; +import { type Static, secureRandomString, transformObject } from "core/utils"; +import { type Entity, EntityIndex, type EntityManager } from "data"; import { type FieldSchema, entity, enumm, make, text } from "data/prototype"; -import { cloneDeep, mergeWith, omit, pick } from "lodash-es"; +import { pick } from "lodash-es"; import { Module } from "modules/Module"; import { AuthController } from "./api/AuthController"; import { type AppAuthSchema, STRATEGIES, authConfigSchema } from "./auth-schema"; @@ -22,10 +15,25 @@ declare global { } } +type AuthSchema = Static; + export class AppAuth extends Module { private _authenticator?: Authenticator; cache: Record = {}; + override async onBeforeUpdate(from: AuthSchema, to: AuthSchema) { + const defaultSecret = authConfigSchema.properties.jwt.properties.secret.default; + + if (!from.enabled && to.enabled) { + if (to.jwt.secret === defaultSecret) { + console.warn("No JWT secret provided, generating a random one"); + to.jwt.secret = secureRandomString(64); + } + } + + return to; + } + override async build() { if (!this.config.enabled) { this.setBuilt(); @@ -46,14 +54,15 @@ export class AppAuth extends Module { return new STRATEGIES[strategy.type].cls(strategy.config as any); } catch (e) { throw new Error( - `Could not build strategy ${String(name)} with config ${JSON.stringify(strategy.config)}` + `Could not build strategy ${String( + name + )} with config ${JSON.stringify(strategy.config)}` ); } }); - const { fields, ...jwt } = this.config.jwt; this._authenticator = new Authenticator(strategies, this.resolveUser.bind(this), { - jwt + jwt: this.config.jwt }); this.registerEntities(); @@ -124,7 +133,11 @@ export class AppAuth extends Module { } private async login(strategy: Strategy, identifier: string, profile: ProfileExchange) { - console.log("--- trying to login", { strategy: strategy.getName(), identifier, profile }); + /*console.log("--- trying to login", { + strategy: strategy.getName(), + identifier, + profile + });*/ if (!("email" in profile)) { throw new Exception("Profile must have email"); } @@ -263,17 +276,9 @@ export class AppAuth extends Module { return this.configDefault; } - const obj = { + return { ...this.config, ...this.authenticator.toJSON(secrets) }; - - return { - ...obj, - jwt: { - ...obj.jwt, - fields: this.config.jwt.fields - } - }; } } diff --git a/app/src/auth/auth-schema.ts b/app/src/auth/auth-schema.ts index 19e5581..4118763 100644 --- a/app/src/auth/auth-schema.ts +++ b/app/src/auth/auth-schema.ts @@ -51,15 +51,7 @@ export const authConfigSchema = Type.Object( enabled: Type.Boolean({ default: false }), basepath: Type.String({ default: "/api/auth" }), entity_name: Type.String({ default: "users" }), - jwt: Type.Composite( - [ - jwtConfig, - Type.Object({ - fields: Type.Array(Type.String(), { default: ["id", "email", "role"] }) - }) - ], - { default: {}, additionalProperties: false } - ), + jwt: jwtConfig, strategies: Type.Optional( StringRecord(strategiesSchema, { title: "Strategies", diff --git a/app/src/auth/authenticate/Authenticator.ts b/app/src/auth/authenticate/Authenticator.ts index b335623..f6114a3 100644 --- a/app/src/auth/authenticate/Authenticator.ts +++ b/app/src/auth/authenticate/Authenticator.ts @@ -41,10 +41,11 @@ export interface UserPool { export const jwtConfig = Type.Object( { // @todo: autogenerate a secret if not present. But it must be persisted from AppAuth - secret: Type.String({ default: "secret" }), + secret: Type.String({ default: "" }), alg: Type.Optional(Type.String({ enum: ["HS256"], default: "HS256" })), expiresIn: Type.Optional(Type.String()), - issuer: Type.Optional(Type.String()) + issuer: Type.Optional(Type.String()), + fields: Type.Array(Type.String(), { default: ["id", "email", "role"] }) }, { default: {}, @@ -74,11 +75,6 @@ export class Authenticator = Record< this.userResolver = userResolver ?? (async (a, s, i, p) => p as any); this.strategies = strategies as Strategies; this.config = parse(authenticatorConfig, config ?? {}); - - /*const secret = String(this.config.jwt.secret); - if (secret === "secret" || secret.length === 0) { - this.config.jwt.secret = randomString(64, true); - }*/ } async resolve( diff --git a/app/src/core/object/SchemaObject.ts b/app/src/core/object/SchemaObject.ts index 8865c50..c70ef28 100644 --- a/app/src/core/object/SchemaObject.ts +++ b/app/src/core/object/SchemaObject.ts @@ -11,6 +11,10 @@ import { export type SchemaObjectOptions = { onUpdate?: (config: Static) => void | Promise; + onBeforeUpdate?: ( + from: Static, + to: Static + ) => Static | Promise>; restrictPaths?: string[]; overwritePaths?: (RegExp | string)[]; forceParse?: boolean; @@ -45,6 +49,13 @@ export class SchemaObject { return this._default; } + private async onBeforeUpdate(from: Static, to: Static): Promise> { + if (this.options?.onBeforeUpdate) { + return this.options.onBeforeUpdate(from, to); + } + return to; + } + get(options?: { stripMark?: boolean }): Static { if (options?.stripMark) { return stripMark(this._config); @@ -58,8 +69,10 @@ export class SchemaObject { forceParse: true, skipMark: this.isForceParse() }); - this._value = valid; - this._config = Object.freeze(valid); + const updatedConfig = noEmit ? valid : await this.onBeforeUpdate(this._config, valid); + + this._value = updatedConfig; + this._config = Object.freeze(updatedConfig); if (noEmit !== true) { await this.options?.onUpdate?.(this._config); @@ -134,7 +147,7 @@ export class SchemaObject { overwritePaths.length > 1 ? overwritePaths.filter((k) => overwritePaths.some((k2) => { - console.log("keep?", { k, k2 }, k2 !== k && k2.startsWith(k)); + //console.log("keep?", { k, k2 }, k2 !== k && k2.startsWith(k)); return k2 !== k && k2.startsWith(k); }) ) diff --git a/app/src/core/utils/crypto.ts b/app/src/core/utils/crypto.ts index 6996d1c..21a188a 100644 --- a/app/src/core/utils/crypto.ts +++ b/app/src/core/utils/crypto.ts @@ -27,3 +27,9 @@ export async function checksum(s: any) { const o = typeof s === "string" ? s : JSON.stringify(s); return await digest("SHA-1", o); } + +export function secureRandomString(length: number): string { + const array = new Uint8Array(length); + crypto.getRandomValues(array); + return Array.from(array, (byte) => String.fromCharCode(33 + (byte % 94))).join(""); +} diff --git a/app/src/modules/Module.ts b/app/src/modules/Module.ts index c3364c3..ecdf4ce 100644 --- a/app/src/modules/Module.ts +++ b/app/src/modules/Module.ts @@ -13,7 +13,7 @@ export type ModuleBuildContext = { guard: Guard; }; -export abstract class Module { +export abstract class Module> { private _built = false; private _schema: SchemaObject>; private _listener: any = () => null; @@ -28,10 +28,15 @@ export abstract class Module { await this._listener(c); }, restrictPaths: this.getRestrictedPaths(), - overwritePaths: this.getOverwritePaths() + overwritePaths: this.getOverwritePaths(), + onBeforeUpdate: this.onBeforeUpdate.bind(this) }); } + onBeforeUpdate(from: ConfigSchema, to: ConfigSchema): ConfigSchema | Promise { + return to; + } + setListener(listener: (c: ReturnType<(typeof this)["getSchema"]>) => void | Promise) { this._listener = listener; return this; @@ -92,7 +97,8 @@ export abstract class Module { }, forceParse: this.useForceParse(), restrictPaths: this.getRestrictedPaths(), - overwritePaths: this.getOverwritePaths() + overwritePaths: this.getOverwritePaths(), + onBeforeUpdate: this.onBeforeUpdate.bind(this) }); } diff --git a/app/src/modules/server/SystemController.ts b/app/src/modules/server/SystemController.ts index 5777338..d425a22 100644 --- a/app/src/modules/server/SystemController.ts +++ b/app/src/modules/server/SystemController.ts @@ -66,10 +66,16 @@ export class SystemController implements ClassController { console.error(e); if (e instanceof TypeInvalidError) { - return c.json({ success: false, errors: e.errors }, { status: 400 }); + return c.json( + { success: false, type: "type-invalid", errors: e.errors }, + { status: 400 } + ); + } + if (e instanceof Error) { + return c.json({ success: false, type: "error", error: e.message }, { status: 500 }); } - return c.json({ success: false }, { status: 500 }); + return c.json({ success: false, type: "unknown" }, { status: 500 }); } } diff --git a/app/src/ui/client/schema/actions.ts b/app/src/ui/client/schema/actions.ts index fc10000..8020d9b 100644 --- a/app/src/ui/client/schema/actions.ts +++ b/app/src/ui/client/schema/actions.ts @@ -1,4 +1,4 @@ -import { set } from "lodash-es"; +import { type NotificationData, notifications } from "@mantine/notifications"; import type { ModuleConfigs } from "../../../modules"; import type { AppQueryClient } from "../utils/AppQueryClient"; @@ -12,6 +12,25 @@ export type TSchemaActions = ReturnType; export function getSchemaActions({ client, setSchema }: SchemaActionsProps) { const baseUrl = client.baseUrl; const token = client.auth().state()?.token; + + async function displayError(action: string, module: string, res: Response, path?: string) { + const notification_data: NotificationData = { + id: "schema-error-" + [action, module, path].join("-"), + title: `Config update failed${path ? ": " + path : ""}`, + message: "Failed to complete config update", + color: "red", + position: "top-right", + withCloseButton: true, + autoClose: false + }; + try { + const { error } = (await res.json()) as any; + notifications.show({ ...notification_data, message: error }); + } catch (e) { + notifications.show(notification_data); + } + } + return { set: async ( module: keyof ModuleConfigs, @@ -46,6 +65,8 @@ export function getSchemaActions({ client, setSchema }: SchemaActionsProps) { } return data.success; + } else { + await displayError("set", module, res); } return false; @@ -80,6 +101,8 @@ export function getSchemaActions({ client, setSchema }: SchemaActionsProps) { } return data.success; + } else { + await displayError("patch", module, res, path); } return false; @@ -114,6 +137,8 @@ export function getSchemaActions({ client, setSchema }: SchemaActionsProps) { } return data.success; + } else { + await displayError("overwrite", module, res, path); } return false; @@ -149,6 +174,8 @@ export function getSchemaActions({ client, setSchema }: SchemaActionsProps) { } return data.success; + } else { + await displayError("add", module, res, path); } return false; @@ -182,6 +209,8 @@ export function getSchemaActions({ client, setSchema }: SchemaActionsProps) { } return data.success; + } else { + await displayError("remove", module, res, path); } return false; diff --git a/app/vite.dev.ts b/app/vite.dev.ts index 788406d..4186b6e 100644 --- a/app/vite.dev.ts +++ b/app/vite.dev.ts @@ -1,7 +1,7 @@ import { readFile } from "node:fs/promises"; import { serveStatic } from "@hono/node-server/serve-static"; import { createClient } from "@libsql/client/node"; -import { App, type BkndConfig } from "./src"; +import { App, type BkndConfig, type CreateAppConfig } from "./src"; import { LibsqlConnection } from "./src/data"; import { StorageLocalAdapter } from "./src/media/storage/adapters/StorageLocalAdapter"; import { registries } from "./src/modules/registries"; @@ -26,14 +26,14 @@ window.__vite_plugin_react_preamble_installed__ = true function createApp(config: BkndConfig, env: any) { const create_config = typeof config.app === "function" ? config.app(env) : config.app; - return App.create(create_config); + return App.create(create_config as CreateAppConfig); } function setAppBuildListener(app: App, config: BkndConfig, html: string) { app.emgr.on( "app-built", async () => { - await config.onBuilt?.(app); + await config.onBuilt?.(app as any); app.module.server.setAdminHtml(html); app.module.server.client.get("/assets/!*", serveStatic({ root: "./" })); }, From 2433833ad0187a1f217d3dc6deef2e689cb37c5b Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 23 Nov 2024 11:21:09 +0100 Subject: [PATCH 02/13] reworked html serving, added new permissions for api/auth, updated adapters --- app/index.html | 1 - app/package.json | 13 ++- app/src/App.ts | 12 +- app/src/adapter/bun/bun.adapter.ts | 34 +++++- .../cloudflare/cloudflare-workers.adapter.ts | 20 ++-- app/src/adapter/node/index.ts | 82 +++++++++++++ app/src/adapter/vite/vite.adapter.ts | 2 +- app/src/auth/api/AuthApi.ts | 2 +- app/src/auth/api/AuthController.ts | 25 ++-- app/src/auth/authorize/Guard.ts | 27 +++-- app/src/cli/commands/config.ts | 3 +- app/src/cli/commands/run/run.ts | 8 +- app/src/cli/commands/schema.ts | 3 +- app/src/data/api/DataController.ts | 10 +- app/src/data/connection/Connection.ts | 10 ++ app/src/data/entities/EntityManager.ts | 2 +- app/src/modules/ModuleManager.ts | 8 +- app/src/modules/permissions/index.ts | 2 + app/src/modules/server/AdminController.tsx | 104 +++++++++++++++++ app/src/modules/server/AppController.ts | 110 ------------------ app/src/modules/server/AppServer.ts | 38 +++--- app/src/modules/server/SystemController.ts | 9 +- app/src/ui/client/BkndProvider.tsx | 64 ++++++---- app/src/ui/client/schema/auth/use-auth.ts | 10 +- app/src/ui/client/utils/AppQueryClient.ts | 16 ++- app/src/ui/client/utils/AppReduced.ts | 3 +- app/src/ui/routes/auth/auth.login.tsx | 4 +- app/tsconfig.json | 2 +- app/tsup.adapters.ts | 6 + app/vite.dev.ts | 86 +++++--------- 30 files changed, 418 insertions(+), 298 deletions(-) create mode 100644 app/src/adapter/node/index.ts create mode 100644 app/src/modules/server/AdminController.tsx delete mode 100644 app/src/modules/server/AppController.ts diff --git a/app/index.html b/app/index.html index 4807db2..2efd6e2 100644 --- a/app/index.html +++ b/app/index.html @@ -6,7 +6,6 @@ BKND -
diff --git a/app/package.json b/app/package.json index a324e8d..39c65cc 100644 --- a/app/package.json +++ b/app/package.json @@ -92,7 +92,7 @@ "entry": ["src/index.ts", "src/ui/index.ts", "src/data/index.ts", "src/core/index.ts", "src/core/utils/index.ts"], "minify": true, "outDir": "dist", - "external": ["bun:test"], + "external": ["bun:test", "bknd/dist/manifest.json"], "sourcemap": true, "metafile": true, "platform": "browser", @@ -109,6 +109,9 @@ "react": ">=18", "react-dom": ">=18" }, + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", @@ -165,9 +168,15 @@ "import": "./dist/adapter/bun/index.js", "require": "./dist/adapter/bun/index.cjs" }, + "./adapter/node": { + "types": "./dist/adapter/node/index.d.ts", + "import": "./dist/adapter/node/index.js", + "require": "./dist/adapter/node/index.cjs" + }, "./dist/static/manifest.json": "./dist/static/.vite/manifest.json", "./dist/styles.css": "./dist/styles.css", - "./dist/index.html": "./dist/static/index.html" + "./dist/index.html": "./dist/static/index.html", + "./dist/manifest.json": "./dist/static/.vite/manifest.json" }, "files": [ "dist", diff --git a/app/src/App.ts b/app/src/App.ts index 4d7a432..a617a0a 100644 --- a/app/src/App.ts +++ b/app/src/App.ts @@ -7,6 +7,7 @@ import { type Modules } from "modules/ModuleManager"; import * as SystemPermissions from "modules/permissions"; +import { AdminController, type AdminControllerOptions } from "modules/server/AdminController"; import { SystemController } from "modules/server/SystemController"; export type AppPlugin = (app: App) => void; @@ -58,7 +59,7 @@ export class App { static create(config: CreateAppConfig) { let connection: Connection | undefined = undefined; - if (config.connection instanceof Connection) { + if (Connection.isConnection(config.connection)) { connection = config.connection; } else if (typeof config.connection === "object") { switch (config.connection.type) { @@ -66,6 +67,8 @@ export class App { connection = new LibsqlConnection(config.connection.config); break; } + } else { + throw new Error(`Unknown connection of type ${typeof config.connection} given.`); } if (!connection) { throw new Error("Invalid connection"); @@ -79,7 +82,6 @@ export class App { } async build(options?: { sync?: boolean; drop?: boolean; save?: boolean }) { - //console.log("building"); await this.modules.build(); if (options?.sync) { @@ -136,6 +138,12 @@ export class App { return this.modules.version(); } + registerAdminController(config?: AdminControllerOptions) { + // register admin + this.modules.server.route("/", new AdminController(this, config).getController()); + return this; + } + toJSON(secrets?: boolean) { return this.modules.toJSON(secrets); } diff --git a/app/src/adapter/bun/bun.adapter.ts b/app/src/adapter/bun/bun.adapter.ts index 9c276ce..fefe6ef 100644 --- a/app/src/adapter/bun/bun.adapter.ts +++ b/app/src/adapter/bun/bun.adapter.ts @@ -1,15 +1,37 @@ -import { readFile } from "node:fs/promises"; import path from "node:path"; import { App, type CreateAppConfig } from "bknd"; +import { LibsqlConnection } from "bknd/data"; import { serveStatic } from "hono/bun"; -let app: App; -export function serve(config: CreateAppConfig, distPath?: string) { +async function getConnection(conn?: CreateAppConfig["connection"]) { + if (conn) { + if (LibsqlConnection.isConnection(conn)) { + return conn; + } + + return new LibsqlConnection(conn.config); + } + + const createClient = await import("@libsql/client/node").then((m) => m.createClient); + if (!createClient) { + throw new Error('libsql client not found, you need to install "@libsql/client/node"'); + } + + console.log("Using in-memory database"); + return new LibsqlConnection(createClient({ url: ":memory:" })); +} + +export function serve(_config: Partial = {}, distPath?: string) { const root = path.resolve(distPath ?? "./node_modules/bknd/dist", "static"); + let app: App; return async (req: Request) => { if (!app) { - app = App.create(config); + const connection = await getConnection(_config.connection); + app = App.create({ + ..._config, + connection + }); app.emgr.on( "app-built", @@ -20,7 +42,7 @@ export function serve(config: CreateAppConfig, distPath?: string) { root }) ); - app.module?.server?.setAdminHtml(await readFile(root + "/index.html", "utf-8")); + app.registerAdminController(); }, "sync" ); @@ -28,6 +50,6 @@ export function serve(config: CreateAppConfig, distPath?: string) { await app.build(); } - return app.modules.server.fetch(req); + return app.fetch(req); }; } diff --git a/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts b/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts index 8798bbe..a7cb1a4 100644 --- a/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts +++ b/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts @@ -113,11 +113,10 @@ async function getFresh(config: BkndConfig, { env, html }: Context) { "sync" ); } - await app.build(); - if (config?.setAdminHtml !== false) { - app.module.server.setAdminHtml(html); + if (config.setAdminHtml) { + app.registerAdminController({ html }); } return app; @@ -147,6 +146,7 @@ async function getCached( await cache.delete(key); return c.json({ message: "Cache cleared" }); }); + app.registerAdminController({ html }); config.onBuilt!(app); }, @@ -163,13 +163,13 @@ async function getCached( ); await app.build(); - if (!cachedConfig) { - saveConfig(app.toJSON(true)); + + if (config.setAdminHtml) { + app.registerAdminController({ html }); } - //addAssetsRoute(app, manifest); - if (config?.setAdminHtml !== false) { - app.module.server.setAdminHtml(html); + if (!cachedConfig) { + saveConfig(app.toJSON(true)); } return app; @@ -212,10 +212,6 @@ export class DurableBkndApp extends DurableObject { colo: context.colo }); }); - - if (options?.setAdminHtml !== false) { - app.module.server.setAdminHtml(options.html); - } }, "sync" ); diff --git a/app/src/adapter/node/index.ts b/app/src/adapter/node/index.ts new file mode 100644 index 0000000..ef19c09 --- /dev/null +++ b/app/src/adapter/node/index.ts @@ -0,0 +1,82 @@ +import { readFile } from "node:fs/promises"; +import path from "node:path"; +import { serve as honoServe } from "@hono/node-server"; +import { serveStatic } from "@hono/node-server/serve-static"; +import { App, type CreateAppConfig } from "bknd"; +import { LibsqlConnection } from "bknd/data"; +import type { Manifest } from "vite"; + +async function getConnection(conn?: CreateAppConfig["connection"]) { + if (conn) { + if (LibsqlConnection.isConnection(conn)) { + return conn; + } + + return new LibsqlConnection(conn.config); + } + + const createClient = await import("@libsql/client/node").then((m) => m.createClient); + if (!createClient) { + throw new Error('libsql client not found, you need to install "@libsql/client/node"'); + } + + console.log("Using in-memory database"); + return new LibsqlConnection(createClient({ url: ":memory:" })); +} + +export type NodeAdapterOptions = { + relativeDistPath?: string; + viteManifest?: Manifest; + port?: number; + hostname?: string; + listener?: Parameters[1]; +}; + +export function serve(_config: Partial = {}, options: NodeAdapterOptions = {}) { + const root = path.relative( + process.cwd(), + path.resolve(options.relativeDistPath ?? "./node_modules/bknd/dist", "static") + ); + let app: App; + + honoServe( + { + port: options.port ?? 1337, + hostname: options.hostname, + fetch: async (req: Request) => { + if (!app) { + const connection = await getConnection(_config.connection); + app = App.create({ + ..._config, + connection + }); + + const viteManifest = + options.viteManifest ?? + JSON.parse(await readFile(path.resolve(root, ".vite/manifest.json"), "utf-8")); + + app.emgr.on( + "app-built", + async () => { + app.modules.server.get( + "/assets/*", + serveStatic({ + root + }) + ); + app.registerAdminController({ + viteManifest + }); + }, + "sync" + ); + + await app.build(); + } + + return app.fetch(req); + } + }, + options.listener + ); +} diff --git a/app/src/adapter/vite/vite.adapter.ts b/app/src/adapter/vite/vite.adapter.ts index 16b5393..6d7b3cf 100644 --- a/app/src/adapter/vite/vite.adapter.ts +++ b/app/src/adapter/vite/vite.adapter.ts @@ -31,7 +31,7 @@ function setAppBuildListener(app: App, config: BkndConfig, html: string) { "app-built", async () => { await config.onBuilt?.(app); - app.module.server.setAdminHtml(html); + app.registerAdminController(); app.module.server.client.get("/assets/!*", serveStatic({ root: "./" })); }, "sync" diff --git a/app/src/auth/api/AuthApi.ts b/app/src/auth/api/AuthApi.ts index 6b6ef0d..df7ffb0 100644 --- a/app/src/auth/api/AuthApi.ts +++ b/app/src/auth/api/AuthApi.ts @@ -34,7 +34,7 @@ export class AuthApi extends ModuleApi { } async strategies() { - return this.get<{ strategies: AppAuthSchema["strategies"] }>(["strategies"]); + return this.get>(["strategies"]); } async logout() {} diff --git a/app/src/auth/api/AuthController.ts b/app/src/auth/api/AuthController.ts index b9eb02f..a616bd0 100644 --- a/app/src/auth/api/AuthController.ts +++ b/app/src/auth/api/AuthController.ts @@ -1,31 +1,29 @@ import type { AppAuth } from "auth"; import type { ClassController } from "core"; import { Hono, type MiddlewareHandler } from "hono"; +import * as SystemPermissions from "modules/permissions"; export class AuthController implements ClassController { constructor(private auth: AppAuth) {} - getMiddleware: MiddlewareHandler = async (c, next) => { - // @todo: consider adding app name to the payload, because user is not refetched + get guard() { + return this.auth.ctx.guard; + } - //try { + getMiddleware: MiddlewareHandler = async (c, next) => { + let token: string | undefined; if (c.req.raw.headers.has("Authorization")) { const bearerHeader = String(c.req.header("Authorization")); - const token = bearerHeader.replace("Bearer ", ""); - const verified = await this.auth.authenticator.verify(token); + token = bearerHeader.replace("Bearer ", ""); + } + if (token) { // @todo: don't extract user from token, but from the database or cache + await this.auth.authenticator.verify(token); this.auth.ctx.guard.setUserContext(this.auth.authenticator.getUser()); - /*console.log("jwt verified?", { - verified, - auth: this.auth.authenticator.isUserLoggedIn() - });*/ } else { this.auth.authenticator.__setUserNull(); } - /* } catch (e) { - this.auth.authenticator.__setUserNull(); - }*/ await next(); }; @@ -49,7 +47,8 @@ export class AuthController implements ClassController { }); hono.get("/strategies", async (c) => { - return c.json({ strategies: this.auth.toJSON(false).strategies }); + const { strategies, basepath } = this.auth.toJSON(false); + return c.json({ strategies, basepath }); }); return hono; diff --git a/app/src/auth/authorize/Guard.ts b/app/src/auth/authorize/Guard.ts index caae555..f40348d 100644 --- a/app/src/auth/authorize/Guard.ts +++ b/app/src/auth/authorize/Guard.ts @@ -11,6 +11,8 @@ export type GuardConfig = { enabled?: boolean; }; +const debug = false; + export class Guard { permissions: Permission[]; user?: GuardUserContext; @@ -96,12 +98,12 @@ export class Guard { if (this.user && typeof this.user.role === "string") { const role = this.roles?.find((role) => role.name === this.user?.role); if (role) { - console.log("guard: role found", this.user.role); + debug && console.log("guard: role found", this.user.role); return role; } } - console.log("guard: role not found", this.user, this.user?.role); + debug && console.log("guard: role not found", this.user, this.user?.role); return this.getDefaultRole(); } @@ -109,10 +111,14 @@ export class Guard { return this.roles?.find((role) => role.is_default); } + isEnabled() { + return this.config?.enabled === true; + } + hasPermission(permission: Permission): boolean; hasPermission(name: string): boolean; hasPermission(permissionOrName: Permission | string): boolean { - if (this.config?.enabled !== true) { + if (!this.isEnabled()) { //console.log("guard not enabled, allowing"); return true; } @@ -126,10 +132,10 @@ export class Guard { const role = this.getUserRole(); if (!role) { - console.log("guard: role not found, denying"); + debug && console.log("guard: role not found, denying"); return false; } else if (role.implicit_allow === true) { - console.log("guard: role implicit allow, allowing"); + debug && console.log("guard: role implicit allow, allowing"); return true; } @@ -137,11 +143,12 @@ export class Guard { (rolePermission) => rolePermission.permission.name === name ); - console.log("guard: rolePermission, allowing?", { - permission: name, - role: role.name, - allowing: !!rolePermission - }); + debug && + console.log("guard: rolePermission, allowing?", { + permission: name, + role: role.name, + allowing: !!rolePermission + }); return !!rolePermission; } diff --git a/app/src/cli/commands/config.ts b/app/src/cli/commands/config.ts index 461ee78..3d853ab 100644 --- a/app/src/cli/commands/config.ts +++ b/app/src/cli/commands/config.ts @@ -7,6 +7,7 @@ export const config: CliCommand = (program) => { .description("get default config") .option("--pretty", "pretty print") .action((options) => { - console.log(getDefaultConfig(options.pretty)); + const config = getDefaultConfig(); + console.log(options.pretty ? JSON.stringify(config, null, 2) : JSON.stringify(config)); }); }; diff --git a/app/src/cli/commands/run/run.ts b/app/src/cli/commands/run/run.ts index 14730f0..dbb5cad 100644 --- a/app/src/cli/commands/run/run.ts +++ b/app/src/cli/commands/run/run.ts @@ -1,9 +1,9 @@ import type { Config } from "@libsql/client/node"; import { App } from "App"; import type { BkndConfig } from "adapter"; +import type { CliCommand } from "cli/types"; import { Option } from "commander"; import type { Connection } from "data"; -import type { CliCommand } from "../../types"; import { PLATFORMS, type Platform, @@ -48,14 +48,13 @@ type MakeAppConfig = { }; async function makeApp(config: MakeAppConfig) { - const html = await getHtml(); const app = new App(config.connection); app.emgr.on( "app-built", async () => { await attachServeStatic(app, config.server?.platform ?? "node"); - app.module.server.setAdminHtml(html); + app.registerAdminController({ html: await getHtml() }); if (config.onBuilt) { await config.onBuilt(app); @@ -70,14 +69,13 @@ async function makeApp(config: MakeAppConfig) { export async function makeConfigApp(config: BkndConfig, platform?: Platform) { const appConfig = typeof config.app === "function" ? config.app(process.env) : config.app; - const html = await getHtml(); const app = App.create(appConfig); app.emgr.on( "app-built", async () => { await attachServeStatic(app, platform ?? "node"); - app.module.server.setAdminHtml(html); + app.registerAdminController({ html: await getHtml() }); if (config.onBuilt) { await config.onBuilt(app); diff --git a/app/src/cli/commands/schema.ts b/app/src/cli/commands/schema.ts index 8c59d7e..13c1c1e 100644 --- a/app/src/cli/commands/schema.ts +++ b/app/src/cli/commands/schema.ts @@ -7,6 +7,7 @@ export const schema: CliCommand = (program) => { .description("get schema") .option("--pretty", "pretty print") .action((options) => { - console.log(getDefaultSchema(options.pretty)); + const schema = getDefaultSchema(); + console.log(options.pretty ? JSON.stringify(schema, null, 2) : JSON.stringify(schema)); }); }; diff --git a/app/src/data/api/DataController.ts b/app/src/data/api/DataController.ts index 418e8ae..3585b16 100644 --- a/app/src/data/api/DataController.ts +++ b/app/src/data/api/DataController.ts @@ -15,7 +15,7 @@ import { import { Hono } from "hono"; import type { Handler } from "hono/types"; import type { ModuleBuildContext } from "modules"; -import { AppData } from "../AppData"; +import * as SystemPermissions from "modules/permissions"; import { type AppDataConfig, FIELDS } from "../data-schema"; export class DataController implements ClassController { @@ -89,12 +89,10 @@ export class DataController implements ClassController { return func; } - // add timing - /*hono.use("*", async (c, next) => { - startTime(c, "data"); + hono.use("*", async (c, next) => { + this.ctx.guard.throwUnlessGranted(SystemPermissions.api); await next(); - endTime(c, "data"); - });*/ + }); // info hono.get( diff --git a/app/src/data/connection/Connection.ts b/app/src/data/connection/Connection.ts index b3f7e10..bc97ff0 100644 --- a/app/src/data/connection/Connection.ts +++ b/app/src/data/connection/Connection.ts @@ -42,6 +42,7 @@ export type DbFunctions = { }; export abstract class Connection { + cls = "bknd:connection"; kysely: Kysely; constructor( @@ -52,6 +53,15 @@ export abstract class Connection { this.kysely = kysely; } + /** + * This is a helper function to manage Connection classes + * coming from different places + * @param conn + */ + static isConnection(conn: any): conn is Connection { + return conn?.cls === "bknd:connection"; + } + getIntrospector(): ConnectionIntrospector { return this.kysely.introspection as ConnectionIntrospector; } diff --git a/app/src/data/entities/EntityManager.ts b/app/src/data/entities/EntityManager.ts index 353d3a9..674d7e2 100644 --- a/app/src/data/entities/EntityManager.ts +++ b/app/src/data/entities/EntityManager.ts @@ -36,7 +36,7 @@ export class EntityManager { relations.forEach((relation) => this.addRelation(relation)); indices.forEach((index) => this.addIndex(index)); - if (!(connection instanceof Connection)) { + if (!Connection.isConnection(connection)) { throw new UnableToConnectException(""); } diff --git a/app/src/modules/ModuleManager.ts b/app/src/modules/ModuleManager.ts index 85c6ea5..51a1768 100644 --- a/app/src/modules/ModuleManager.ts +++ b/app/src/modules/ModuleManager.ts @@ -425,19 +425,19 @@ export class ModuleManager { } } -export function getDefaultSchema(pretty = false) { +export function getDefaultSchema() { const schema = { type: "object", ...transformObject(MODULES, (module) => module.prototype.getSchema()) }; - return JSON.stringify(schema, null, pretty ? 2 : undefined); + return schema as any; } -export function getDefaultConfig(pretty = false): ModuleConfigs { +export function getDefaultConfig(): ModuleConfigs { const config = transformObject(MODULES, (module) => { return Default(module.prototype.getSchema(), {}); }); - return JSON.stringify(config, null, pretty ? 2 : undefined) as any; + return config as any; } diff --git a/app/src/modules/permissions/index.ts b/app/src/modules/permissions/index.ts index 8b9cb9b..820b3ec 100644 --- a/app/src/modules/permissions/index.ts +++ b/app/src/modules/permissions/index.ts @@ -1,5 +1,7 @@ import { Permission } from "core"; +export const admin = new Permission("system.admin"); +export const api = new Permission("system.api"); export const configRead = new Permission("system.config.read"); export const configReadSecrets = new Permission("system.config.read.secrets"); export const configWrite = new Permission("system.config.write"); diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx new file mode 100644 index 0000000..e600d9d --- /dev/null +++ b/app/src/modules/server/AdminController.tsx @@ -0,0 +1,104 @@ +/** @jsxImportSource hono/jsx */ + +import type { App } from "App"; +import { type ClassController, isDebug } from "core"; +import { Hono } from "hono"; +import { html, raw } from "hono/html"; +import { Fragment } from "hono/jsx"; +import * as SystemPermissions from "modules/permissions"; +import type { Manifest } from "vite"; + +const viteInject = ` +import RefreshRuntime from "/@react-refresh" +RefreshRuntime.injectIntoGlobalHook(window) +window.$RefreshReg$ = () => {} +window.$RefreshSig$ = () => (type) => type +window.__vite_plugin_react_preamble_installed__ = true +`; + +export type AdminControllerOptions = { + html?: string; + viteManifest?: Manifest; +}; + +export class AdminController implements ClassController { + constructor( + private readonly app: App, + private options: AdminControllerOptions = {} + ) {} + + get ctx() { + return this.app.modules.ctx(); + } + + getController(): Hono { + const hono = new Hono(); + const configs = this.app.modules.configs(); + const basepath = (String(configs.server.admin.basepath) + "/").replace(/\/+$/, "/"); + + this.ctx.server.get(basepath + "*", async (c) => { + if (this.options.html) { + return c.html(this.options.html); + } + + // @todo: implement guard redirect once cookie sessions arrive + + const isProd = !isDebug(); + let script: string | undefined; + let css: string[] = []; + + if (isProd) { + const manifest: Manifest = this.options.viteManifest + ? this.options.viteManifest + : isProd + ? // @ts-ignore cases issues when building types + await import("bknd/dist/manifest.json", { assert: { type: "json" } }).then( + (m) => m.default + ) + : {}; + //console.log("manifest", manifest, manifest["index.html"]); + const entry = Object.values(manifest).find((f: any) => f.isEntry === true); + if (!entry) { + // do something smart + return; + } + + script = "/" + entry.file; + css = entry.css?.map((c: string) => "/" + c) ?? []; + } + + return c.html( + + + + + BKND + {isProd ? ( + + - -` - ); -} - -function createApp(config: BkndConfig, env: any) { - const create_config = typeof config.app === "function" ? config.app(env) : config.app; - return App.create(create_config as CreateAppConfig); -} - -function setAppBuildListener(app: App, config: BkndConfig, html: string) { - app.emgr.on( - "app-built", - async () => { - await config.onBuilt?.(app as any); - app.module.server.setAdminHtml(html); - app.module.server.client.get("/assets/!*", serveStatic({ root: "./" })); - }, - "sync" - ); -} - -export async function serveFresh(config: BkndConfig, _html?: string) { - let html = _html; - if (!html) { - html = await getHtml(); - } - - html = addViteScripts(html); - - return { - async fetch(request: Request, env: any) { - const app = createApp(config, env); - - setAppBuildListener(app, config, html); - await app.build(); - - return app.fetch(request, env); - } - }; -} - registries.media.add("local", { cls: StorageLocalAdapter, schema: StorageLocalAdapter.prototype.getSchema() @@ -72,11 +16,35 @@ const connection = new LibsqlConnection( }) ); -const app = await serveFresh({ +function createApp(config: BkndConfig, env: any) { + const create_config = typeof config.app === "function" ? config.app(env) : config.app; + return App.create(create_config as CreateAppConfig); +} + +export async function serveFresh(config: BkndConfig) { + return { + async fetch(request: Request, env: any) { + const app = createApp(config, env); + + app.emgr.on( + "app-built", + async () => { + await config.onBuilt?.(app as any); + app.registerAdminController(); + app.module.server.client.get("/assets/*", serveStatic({ root: "./" })); + }, + "sync" + ); + await app.build(); + + return app.fetch(request, env); + } + }; +} + +export default await serveFresh({ app: { connection }, setAdminHtml: true }); - -export default app; From f70e2b2e10a760a260e574ac38b4ca63bbcde8c4 Mon Sep 17 00:00:00 2001 From: dswbx Date: Sat, 23 Nov 2024 11:21:25 +0100 Subject: [PATCH 03/13] updated examples --- bun.lockb | Bin 762496 -> 756088 bytes examples/bun/index.ts | 15 +++--- examples/bun/tsconfig.json | 2 +- examples/cloudflare-worker/src/index.ts | 8 ++-- examples/nextjs/package.json | 2 + examples/nextjs/src/pages/api/[...route].ts | 48 +------------------- examples/nextjs/test.db | Bin 0 -> 16384 bytes examples/node/index.js | 20 ++++++++ examples/node/package.json | 20 ++++++++ examples/remix/app/routes/admin.$.tsx | 2 +- examples/remix/package.json | 3 +- examples/remix/test.db | Bin 0 -> 16384 bytes examples/sw/index.html | 18 ++++++-- 13 files changed, 74 insertions(+), 64 deletions(-) create mode 100644 examples/nextjs/test.db create mode 100644 examples/node/index.js create mode 100644 examples/node/package.json create mode 100644 examples/remix/test.db diff --git a/bun.lockb b/bun.lockb index f97dc61985ebfdc342a59bcde1584f8b3ab76f04..ac24c2083b08593221888606f372f9170a4da9ac 100755 GIT binary patch delta 103389 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 delta 107270 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

a-I)o&|&_>UtF~y(qiOx~pc0+-+n6giigw15)7% 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 YFDFt>#4me--{F_%>BW?pY`5Zn0V#sC3;+NC diff --git a/examples/bun/index.ts b/examples/bun/index.ts index 7432622..57c4eae 100644 --- a/examples/bun/index.ts +++ b/examples/bun/index.ts @@ -1,10 +1,9 @@ -/* -// somehow causes types:build issues on app - +// @ts-ignore somehow causes types:build issues on app import type { CreateAppConfig } from "bknd"; +// @ts-ignore somehow causes types:build issues on app import { serve } from "bknd/adapter/bun"; -const root = "../../node_modules/bknd/dist"; +// this is optional, if omitted, it uses an in-memory database const config = { connection: { type: "libsql", @@ -16,8 +15,12 @@ const config = { Bun.serve({ port: 1337, - fetch: serve(config, root) + fetch: serve( + config, + // this is only required to run inside the same workspace + // leave blank if you're running this from a different project + "../../node_modules/bknd/dist" + ) }); console.log("Server running at http://localhost:1337"); -s*/ diff --git a/examples/bun/tsconfig.json b/examples/bun/tsconfig.json index 9e6a2e9..6f14404 100644 --- a/examples/bun/tsconfig.json +++ b/examples/bun/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "lib": ["ESNext", "DOM"], - "target": "ESNext", + "target": "ES2022", "module": "ESNext", "jsx": "react-jsx", "allowJs": true, diff --git a/examples/cloudflare-worker/src/index.ts b/examples/cloudflare-worker/src/index.ts index e6a9a9c..6a79894 100644 --- a/examples/cloudflare-worker/src/index.ts +++ b/examples/cloudflare-worker/src/index.ts @@ -8,15 +8,13 @@ export default serve( connection: { type: "libsql", config: { - url: env.DB_URL, - authToken: env.DB_TOKEN + url: "http://localhost:8080" } } }), onBuilt: async (app) => { - app.modules.server.get("/", (c) => c.json({ hello: "world" })); - }, - setAdminHtml: true + app.modules.server.get("/hello", (c) => c.json({ hello: "world" })); + } }, manifest ); diff --git a/examples/nextjs/package.json b/examples/nextjs/package.json index 40b4da3..fc734a4 100644 --- a/examples/nextjs/package.json +++ b/examples/nextjs/package.json @@ -4,6 +4,8 @@ "private": true, "scripts": { "dev": "next dev", + "db": "turso dev --db-file test.db", + "db:check": "sqlite3 test.db \"PRAGMA wal_checkpoint(FULL);\"", "build": "next build", "start": "next start", "lint": "next lint" diff --git a/examples/nextjs/src/pages/api/[...route].ts b/examples/nextjs/src/pages/api/[...route].ts index a9d64c5..17179a5 100644 --- a/examples/nextjs/src/pages/api/[...route].ts +++ b/examples/nextjs/src/pages/api/[...route].ts @@ -9,51 +9,7 @@ export default serve({ connection: { type: "libsql", config: { - url: process.env.DB_URL!, - authToken: process.env.DB_AUTH_TOKEN! + url: "http://localhost:8080" } } -}); /* -let app: App; - -async function getApp() { - if (!app) { - app = App.create({ - connection: { - type: "libsql", - config: { - url: process.env.DB_URL!, - authToken: process.env.DB_AUTH_TOKEN! - } - } - }); - await app.build(); - } - - return app; -} - -function getCleanRequest(req: Request) { - // clean search params from "route" attribute - const url = new URL(req.url); - url.searchParams.delete("route"); - return new Request(url.toString(), { - method: req.method, - headers: req.headers, - body: req.body - }); -} - -export default async (req: Request, requestContext: any) => { - //console.log("here"); - if (!app) { - app = await getApp(); - } - //const app = await getApp(); - const request = getCleanRequest(req); - //console.log("url", req.url); - //console.log("req", req); - return app.fetch(request, process.env); -}; -//export default handle(app.server.getInstance()) -*/ +}); diff --git a/examples/nextjs/test.db b/examples/nextjs/test.db new file mode 100644 index 0000000000000000000000000000000000000000..1c856e00ef6c9d5a33dd18db103a607b84feab72 GIT binary patch literal 16384 zcmeI3&2J+$6u>=6v{hNUVGpa23o^d2WmEc*T@blIwnQva7fPERP*siGjFWilj8l)N zr9?eI5nq@7AN(ua`d@J1#s!H3&z?*})2hpd5L)#`?acVue)jvl=ckuyy7}pvw1RfE z8E{MM9fQQcT#A8B5p+#a?5B0PgV{DjYOBH%F@-Xjg8jF?XdmP)7ECY`E(s7 zq(e9CZ%l}U64Mjr*9*5Q4;}=cc`(Vog0HP%BKA`ecf?e)cmdT^=lz%uL=9%nLs)(5 zV4v*#udqkOnM$P+e7WU!VRfVve<@wCf_d3L^0$>%jhzbKh;$5%U9FYi@%-G&zmeh$ zCFP%H=j-@JT49z1)zh7No!si^xGQ@jmIwni6l;taj|MV! z54$`OLvH)5!4@-NjdirrCQLegD7-+fWUp^ojVayJp%FGUahOkG$8NYuoGNW(&nabZ z!eFkHJ`4xK_H_ik+w8+u8^r6MZ2DWXz52v8w_NLk)>^CGVmtY)zTgp*dT!RLh5|9R z3vE~4)y|u1BJRPSYpj{V0!Dtptt$+n-RX#-Wjn{PFDTylB@>5o(aUQL)JiLb-+ES~ zfDOKkY%n8wsp19{cBN3xfRQU#Cj-W1Dh$Zjx~{HN$`!w?(d8-;HPEu3N+V$2SkRYI zB!G}_x-DiE&P1)NR4?efq++s&H7TrdNu@87CK|`}(!Wybee5F0S(#{KLr(=f4NPj8 zB$5zUj3*s->b^IQfFVzk zLv1ejI2Zld$$g&mW$e~T`rIv)nF~sD>jol{KJK1pUN&&rzFZ2mwBbFl*dy!0Y4TAx z%_!>tYsUj%J1a{o4;Pk~7gp+Y>D|Uk9e$=n4;#x%Cy$DdAnturb_pVq-R|~z^vf6+ zyEx2GxjQk7`FUB)4oi(4aFx3K;i0+eBtnEuQo~<4G&@%~=?W1cg)>O(KfH&Deflh1 zPVC?W%tX&mw0X|)NBlg~=Fn`=Ya_Q%l|dmiz%Yx!4;3 literal 0 HcmV?d00001 diff --git a/examples/node/index.js b/examples/node/index.js new file mode 100644 index 0000000..92ae1d2 --- /dev/null +++ b/examples/node/index.js @@ -0,0 +1,20 @@ +import { serve } from "bknd/adapter/node"; + +// this is optional, if omitted, it uses an in-memory database +/** @type {import("bknd").CreateAppConfig} */ +const config = { + connection: { + type: "libsql", + config: { + url: "http://localhost:8080" + } + } +}; + +serve(config, { + relativeDistPath: "../../node_modules/bknd/dist", + port: 1337, + listener: ({ port }) => { + console.log(`Server is running on http://localhost:${port}`); + } +}); diff --git a/examples/node/package.json b/examples/node/package.json new file mode 100644 index 0000000..630b647 --- /dev/null +++ b/examples/node/package.json @@ -0,0 +1,20 @@ +{ + "name": "node", + "module": "index.js", + "type": "module", + "private": true, + "scripts": { + "start": "node index.js", + "dev": "tsx --watch index.js" + }, + "dependencies": { + "bknd": "workspace:*", + "@hono/node-server": "^1.13.3" + }, + "devDependencies": { + "tsx": "^4.19.2" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } +} \ No newline at end of file diff --git a/examples/remix/app/routes/admin.$.tsx b/examples/remix/app/routes/admin.$.tsx index 76d0c75..d73a208 100644 --- a/examples/remix/app/routes/admin.$.tsx +++ b/examples/remix/app/routes/admin.$.tsx @@ -12,7 +12,7 @@ export default function AdminPage() { return ( - + ); } diff --git a/examples/remix/package.json b/examples/remix/package.json index 40b6617..f9c1602 100644 --- a/examples/remix/package.json +++ b/examples/remix/package.json @@ -6,7 +6,8 @@ "scripts": { "build": "remix vite:build", "dev": "remix vite:dev", - "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", + "db": "turso dev --db-file test.db", + "db:check": "sqlite3 test.db \"PRAGMA wal_checkpoint(FULL);\"", "start": "remix-serve ./build/server/index.js", "typecheck": "tsc" }, diff --git a/examples/remix/test.db b/examples/remix/test.db new file mode 100644 index 0000000000000000000000000000000000000000..1125cc29798df725cb41193d81107b7ed0b3a93f GIT binary patch literal 16384 zcmeI3Pj4eN6u>=6wN+WVfx{}>3$lD+%citVwkwefWJAOvb)mHB0aew=%{Ym-&N%gW zT1wRe6!GuU@4;8$#(oM8+_)fd;MtRDNLqFIBZO9cQ9Cn!wx9ie@A>Janr?lvF0G(l zZ3f)Z8hMQr3gkUX2_ZA1U%2^C5w1>_e}eqsn)?iS`|!K@JHM0S{5hFFzw<-s?3&To zg#?fQ5b}ckFTa9*$wi}Pv zTa;x&M(;BjF)Cv#dcx3HTNw8ZjR}`IHo+cTE-nC1|qf<$_|A|q>eAof16mrXG1W#5D1dT+Or^?di_U2~mqjuQ-@M&wS-FUhI z6Vjm@_BSR(LW$`KiyMVoq|10d1;%vEG4!+p-yRbUaiNDNDSi!vPANku#t42-*Z$vtV#-7$n@OW|l)!#^Q zmXgwsv-Ec3<{UJ%6bT>!B!C2vz;y(^eWNf(f`#+lT8-T5=(sC;CoB;LYA9AeVLTeh z*gfp=L=3s@vpQSOgca7&N}Dk0^r7$qxstuUWfi7$Plra>)Wl&vg`K+LCUL5?kv*rB zy#<50Qu-(y2;0{Y^zN_^T5S+-e7xmvjdt^iYi_&N`>pj>yTx|%S$)AHDD~W|riKDB zwo7eS-c>G|>mu&Ko-3@8!U9Hq&aEp9q21|-p=G~wlg1iH_0qpm>V51Y$XS_aV?$2`JPk}L znIw`BSBxhecIpf>LV1@sa?q4I9OcK29CaMJ@)wp! zA&3)L_1Tg2+YwOK!xIA$SFl6&WUIRN2wJ)4cCuZH0hbEI2I@zM57@RfP~G=N5isOQ za-_|Kk8{zVo!sY1U&d~oq|e<#nYo}ew{9RJ>ErH2=4At??aQT5OB>z;i#@U~oF*TI z(~PnXuy!;6wo_fHK3rN|U8>gT$~*OH4SuFX59_NdXKO`B5O=>Wxdai(Zg=M*`eh7^ zT^#18+?|-^{Jbn@ho!;}xk}ys@X%a!5+TCIso}32n%yg$bcKkJ!cUOczke4K`}A43 zoY=t$n2BDTX!D%okN8EV&87ckCUVKC7R-{l1yZYJ=VdD9uSj1`G!Mm3q4YB;{R$WS zAOR$R1dsp{Kmter2_OL^fCP{L5Params

- +
@@ -29,6 +26,19 @@ diff --git a/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts b/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts index 9dc071c..5224c10 100644 --- a/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts +++ b/app/src/adapter/cloudflare/cloudflare-workers.adapter.ts @@ -4,19 +4,15 @@ import { Hono } from "hono"; import { serveStatic } from "hono/cloudflare-workers"; import type { BkndConfig, CfBkndModeCache } from "../index"; -// @ts-ignore -import _html from "../../static/index.html"; - type Context = { request: Request; env: any; ctx: ExecutionContext; manifest: any; - html: string; + html?: string; }; -export function serve(_config: BkndConfig, manifest?: string, overrideHtml?: string) { - const html = overrideHtml ?? _html; +export function serve(_config: BkndConfig, manifest?: string, html?: string) { return { async fetch(request: Request, env: any, ctx: ExecutionContext) { const url = new URL(request.url); @@ -182,7 +178,7 @@ export class DurableBkndApp extends DurableObject { request: Request, options: { config: CreateAppConfig; - html: string; + html?: string; keepAliveSeconds?: number; setAdminHtml?: boolean; } diff --git a/app/src/auth/authenticate/Authenticator.ts b/app/src/auth/authenticate/Authenticator.ts index 32c7b42..082cd1c 100644 --- a/app/src/auth/authenticate/Authenticator.ts +++ b/app/src/auth/authenticate/Authenticator.ts @@ -55,12 +55,14 @@ export interface UserPool { const defaultCookieExpires = 60 * 60 * 24 * 7; // 1 week in seconds export const cookieConfig = Type.Partial( Type.Object({ - renew: Type.Boolean({ default: true }), path: Type.String({ default: "/" }), sameSite: StringEnum(["strict", "lax", "none"], { default: "lax" }), secure: Type.Boolean({ default: true }), httpOnly: Type.Boolean({ default: true }), - expires: Type.Number({ default: defaultCookieExpires }) // seconds + expires: Type.Number({ default: defaultCookieExpires }), // seconds + renew: Type.Boolean({ default: true }), + pathSuccess: Type.String({ default: "/" }), + pathLoggedOut: Type.String({ default: "/" }) }), { default: {}, additionalProperties: false } ); @@ -257,12 +259,11 @@ export class Authenticator = Record< return c.json(data); } - const successPath = "/"; + const successPath = this.config.cookie.pathSuccess ?? "/"; const successUrl = new URL(c.req.url).origin + successPath.replace(/\/+$/, "/"); const referer = new URL(redirect ?? c.req.header("Referer") ?? successUrl); if ("token" in data) { - // @todo: add config await this.setAuthCookie(c, data.token); // can't navigate to "/" – doesn't work on nextjs return c.redirect(successUrl); diff --git a/app/src/core/utils/strings.ts b/app/src/core/utils/strings.ts index adc68b7..a5a7c72 100644 --- a/app/src/core/utils/strings.ts +++ b/app/src/core/utils/strings.ts @@ -15,10 +15,6 @@ export function ucFirstAll(str: string, split: string = " "): string { .join(split); } -export function ucFirstAllSnakeToPascalWithSpaces(str: string, split: string = " "): string { - return ucFirstAll(snakeToPascalWithSpaces(str), split); -} - export function randomString(length: number, includeSpecial = false): string { const base = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const special = "!@#$%^&*()_+{}:\"<>?|[];',./`~"; @@ -49,6 +45,54 @@ export function pascalToKebab(pascalStr: string): string { return pascalStr.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(); } +type StringCaseType = + | "snake_case" + | "PascalCase" + | "camelCase" + | "kebab-case" + | "SCREAMING_SNAKE_CASE" + | "unknown"; +export function detectCase(input: string): StringCaseType { + if (/^[a-z]+(_[a-z]+)*$/.test(input)) { + return "snake_case"; + } else if (/^[A-Z][a-zA-Z]*$/.test(input)) { + return "PascalCase"; + } else if (/^[a-z][a-zA-Z]*$/.test(input)) { + return "camelCase"; + } else if (/^[a-z]+(-[a-z]+)*$/.test(input)) { + return "kebab-case"; + } else if (/^[A-Z]+(_[A-Z]+)*$/.test(input)) { + return "SCREAMING_SNAKE_CASE"; + } else { + return "unknown"; + } +} +export function identifierToHumanReadable(str: string) { + const _case = detectCase(str); + switch (_case) { + case "snake_case": + return snakeToPascalWithSpaces(str); + case "PascalCase": + return kebabToPascalWithSpaces(pascalToKebab(str)); + case "camelCase": + return ucFirst(kebabToPascalWithSpaces(pascalToKebab(str))); + case "kebab-case": + return kebabToPascalWithSpaces(str); + case "SCREAMING_SNAKE_CASE": + return snakeToPascalWithSpaces(str.toLowerCase()); + case "unknown": + return str; + } +} + +export function kebabToPascalWithSpaces(str: string): string { + return str.split("-").map(ucFirst).join(" "); +} + +export function ucFirstAllSnakeToPascalWithSpaces(str: string, split: string = " "): string { + return ucFirstAll(snakeToPascalWithSpaces(str), split); +} + /** * Replace simple mustache like {placeholders} in a string * diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx index 96af5c1..cd252a5 100644 --- a/app/src/modules/server/AdminController.tsx +++ b/app/src/modules/server/AdminController.tsx @@ -16,18 +16,13 @@ window.$RefreshReg$ = () => {} window.$RefreshSig$ = () => (type) => type window.__vite_plugin_react_preamble_installed__ = true `; +const htmlBkndContextReplace = ""; export type AdminControllerOptions = { html?: string; viteManifest?: Manifest; }; -const authRoutes = { - root: "/", - login: "/auth/login", - logout: "/auth/logout" -}; - export class AdminController implements ClassController { constructor( private readonly app: App, @@ -52,6 +47,13 @@ export class AdminController implements ClassController { html: string; }; }>().basePath(this.withBasePath()); + const authRoutes = { + root: "/", + success: configs.auth.cookie.pathSuccess ?? "/", + loggedOut: configs.auth.cookie.pathLoggedOut ?? "/", + login: "/auth/login", + logout: "/auth/logout" + }; hono.use("*", async (c, next) => { const obj = { @@ -77,7 +79,7 @@ export class AdminController implements ClassController { this.app.module.auth.authenticator?.isUserLoggedIn() && this.ctx.guard.granted(SystemPermissions.accessAdmin) ) { - return c.redirect(authRoutes.root); + return c.redirect(authRoutes.success); } const html = c.get("html"); @@ -86,7 +88,7 @@ export class AdminController implements ClassController { hono.get(authRoutes.logout, async (c) => { await auth.authenticator?.logout(c); - return c.redirect(authRoutes.login); + return c.redirect(authRoutes.loggedOut); }); } @@ -104,8 +106,16 @@ export class AdminController implements ClassController { } private async getHtml(obj: any = {}) { + const bknd_context = `window.__BKND__ = JSON.parse('${JSON.stringify(obj)}');`; + if (this.options.html) { - // @todo: add __BKND__ global + if (this.options.html.includes(htmlBkndContextReplace)) { + return this.options.html.replace(htmlBkndContextReplace, bknd_context); + } + + console.warn( + "Custom HTML needs to include '' to inject BKND context" + ); return this.options.html as string; } @@ -168,7 +178,7 @@ export class AdminController implements ClassController {
- - diff --git a/app/internal/esbuild.entry-output-meta.plugin.ts b/app/internal/esbuild.entry-output-meta.plugin.ts new file mode 100644 index 0000000..6bd3ab4 --- /dev/null +++ b/app/internal/esbuild.entry-output-meta.plugin.ts @@ -0,0 +1,33 @@ +import type { Metafile, Plugin } from "esbuild"; + +export const entryOutputMeta = ( + onComplete?: ( + outputs: { + output: string; + meta: Metafile["outputs"][string]; + }[] + ) => void | Promise +): Plugin => ({ + name: "report-entry-output-plugin", + setup(build) { + build.initialOptions.metafile = true; // Ensure metafile is enabled + + build.onEnd(async (result) => { + console.log("result", result); + if (result?.metafile?.outputs) { + const entries = build.initialOptions.entryPoints! as string[]; + + const outputs = Object.entries(result.metafile.outputs) + .filter(([, meta]) => { + return meta.entryPoint && entries.includes(meta.entryPoint); + }) + .map(([output, meta]) => ({ output, meta })); + if (outputs.length === 0) { + return; + } + + await onComplete?.(outputs); + } + }); + } +}); diff --git a/app/package.json b/app/package.json index 1c6b57a..a12d36f 100644 --- a/app/package.json +++ b/app/package.json @@ -5,18 +5,16 @@ "bin": "./dist/cli/index.js", "version": "0.0.13", "scripts": { - "build:all": "rm -rf dist && bun build:css && bun run build && bun build:vite && bun build:adapters && bun build:cli", + "build:all": "bun run build && bun run build:cli", "dev": "vite", "test": "ALL_TESTS=1 bun test --bail", - "build": "bun tsup && bun build:types", - "watch": "bun tsup --watch --onSuccess 'bun run build:types'", + "build": "bun run build.ts --minify --types", + "watch": "bun run build.ts --types --watch", "types": "bun tsc --noEmit", + "clean:types": "find ./dist -name '*.d.ts' -delete && rm -f ./dist/tsconfig.tsbuildinfo", "build:types": "tsc --emitDeclarationOnly", - "build:css": "bun tailwindcss -i ./src/ui/styles.css -o ./dist/styles.css", - "watch:css": "bun tailwindcss --watch -i ./src/ui/styles.css -o ./dist/styles.css", - "build:vite": "NODE_ENV=production vite build", - "build:adapters": "bun tsup.adapters.ts --minify", - "watch:adapters": "bun tsup.adapters.ts --watch", + "build:css": "bun tailwindcss -i src/ui/main.css -o ./dist/static/styles.css", + "watch:css": "bun tailwindcss --watch -i src/ui/main.css -o ./dist/styles.css", "updater": "bun x npm-check-updates -ui", "build:cli": "bun build src/cli/index.ts --target node --outdir dist/cli --minify", "cli": "LOCAL=1 bun src/cli/index.ts" @@ -77,6 +75,7 @@ "@types/react-dom": "^18.3.1", "@vitejs/plugin-react": "^4.3.3", "autoprefixer": "^10.4.20", + "esbuild-postcss": "^0.0.4", "node-fetch": "^3.3.2", "openapi-types": "^12.1.3", "postcss": "^8.4.47", @@ -88,20 +87,6 @@ "vite-plugin-static-copy": "^2.0.0", "vite-tsconfig-paths": "^5.0.1" }, - "tsup": { - "entry": ["src/index.ts", "src/ui/index.ts", "src/data/index.ts", "src/core/index.ts", "src/core/utils/index.ts"], - "minify": true, - "outDir": "dist", - "external": ["bun:test", "bknd/dist/manifest.json"], - "sourcemap": true, - "metafile": true, - "platform": "browser", - "format": ["esm", "cjs"], - "splitting": true, - "loader": { - ".svg": "dataurl" - } - }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" @@ -120,6 +105,11 @@ "import": "./dist/ui/index.js", "require": "./dist/ui/index.cjs" }, + "./client": { + "types": "./dist/ui/client/index.d.ts", + "import": "./dist/ui/client/index.js", + "require": "./dist/ui/client/index.cjs" + }, "./data": { "types": "./dist/data/index.d.ts", "import": "./dist/data/index.js", @@ -170,10 +160,8 @@ "import": "./dist/adapter/node/index.js", "require": "./dist/adapter/node/index.cjs" }, - "./dist/static/manifest.json": "./dist/static/.vite/manifest.json", - "./dist/styles.css": "./dist/styles.css", - "./dist/index.html": "./dist/static/index.html", - "./dist/manifest.json": "./dist/static/.vite/manifest.json" + "./dist/styles.css": "./dist/ui/main.css", + "./dist/manifest.json": "./dist/static/manifest.json" }, "files": [ "dist", diff --git a/app/src/adapter/vite/vite.adapter.ts b/app/src/adapter/vite/vite.adapter.ts index 6d7b3cf..6faaefe 100644 --- a/app/src/adapter/vite/vite.adapter.ts +++ b/app/src/adapter/vite/vite.adapter.ts @@ -1,59 +1,34 @@ -import { readFile } from "node:fs/promises"; import { serveStatic } from "@hono/node-server/serve-static"; import type { BkndConfig } from "bknd"; import { App } from "bknd"; -async function getHtml() { - return readFile("index.html", "utf8"); -} -function addViteScripts(html: string) { - return html.replace( - "", - ` - -` - ); -} - function createApp(config: BkndConfig, env: any) { const create_config = typeof config.app === "function" ? config.app(env) : config.app; return App.create(create_config); } -function setAppBuildListener(app: App, config: BkndConfig, html: string) { +function setAppBuildListener(app: App, config: BkndConfig, html?: string) { app.emgr.on( "app-built", async () => { await config.onBuilt?.(app); - app.registerAdminController(); - app.module.server.client.get("/assets/!*", serveStatic({ root: "./" })); + if (config.setAdminHtml) { + app.registerAdminController({ html, forceDev: true }); + app.module.server.client.get("/assets/*", serveStatic({ root: "./" })); + } }, "sync" ); } export async function serveFresh(config: BkndConfig, _html?: string) { - let html = _html; - if (!html) { - html = await getHtml(); - } - - html = addViteScripts(html); - return { async fetch(request: Request, env: any, ctx: ExecutionContext) { const app = createApp(config, env); - setAppBuildListener(app, config, html); + setAppBuildListener(app, config, _html); await app.build(); - //console.log("routes", app.module.server.client.routes); return app.fetch(request, env, ctx); } }; @@ -61,18 +36,11 @@ export async function serveFresh(config: BkndConfig, _html?: string) { let app: App; export async function serveCached(config: BkndConfig, _html?: string) { - let html = _html; - if (!html) { - html = await getHtml(); - } - - html = addViteScripts(html); - return { async fetch(request: Request, env: any, ctx: ExecutionContext) { if (!app) { app = createApp(config, env); - setAppBuildListener(app, config, html); + setAppBuildListener(app, config, _html); await app.build(); } diff --git a/app/src/cli/commands/run/platform.ts b/app/src/cli/commands/run/platform.ts index 76c033e..46a725b 100644 --- a/app/src/cli/commands/run/platform.ts +++ b/app/src/cli/commands/run/platform.ts @@ -28,7 +28,7 @@ export async function serveStatic(server: Platform): Promise } export async function attachServeStatic(app: any, platform: Platform) { - app.module.server.client.get("/assets/*", await serveStatic(platform)); + app.module.server.client.get("/*", await serveStatic(platform)); } export async function startServer(server: Platform, app: any, options: { port: number }) { diff --git a/app/src/cli/commands/run/run.ts b/app/src/cli/commands/run/run.ts index dbb5cad..0663f2f 100644 --- a/app/src/cli/commands/run/run.ts +++ b/app/src/cli/commands/run/run.ts @@ -54,7 +54,7 @@ async function makeApp(config: MakeAppConfig) { "app-built", async () => { await attachServeStatic(app, config.server?.platform ?? "node"); - app.registerAdminController({ html: await getHtml() }); + app.registerAdminController(); if (config.onBuilt) { await config.onBuilt(app); @@ -75,7 +75,7 @@ export async function makeConfigApp(config: BkndConfig, platform?: Platform) { "app-built", async () => { await attachServeStatic(app, platform ?? "node"); - app.registerAdminController({ html: await getHtml() }); + app.registerAdminController(); if (config.onBuilt) { await config.onBuilt(app); diff --git a/app/src/modules/server/AdminController.tsx b/app/src/modules/server/AdminController.tsx index cd252a5..522c55a 100644 --- a/app/src/modules/server/AdminController.tsx +++ b/app/src/modules/server/AdminController.tsx @@ -7,20 +7,12 @@ import { Hono } from "hono"; import { html } from "hono/html"; import { Fragment } from "hono/jsx"; import * as SystemPermissions from "modules/permissions"; -import type { Manifest } from "vite"; -const viteInject = ` -import RefreshRuntime from "/@react-refresh" -RefreshRuntime.injectIntoGlobalHook(window) -window.$RefreshReg$ = () => {} -window.$RefreshSig$ = () => (type) => type -window.__vite_plugin_react_preamble_installed__ = true -`; const htmlBkndContextReplace = ""; export type AdminControllerOptions = { html?: string; - viteManifest?: Manifest; + forceDev?: boolean; }; export class AdminController implements ClassController { @@ -114,44 +106,36 @@ export class AdminController implements ClassController { } console.warn( - "Custom HTML needs to include '' to inject BKND context" + `Custom HTML needs to include '${htmlBkndContextReplace}' to inject BKND context` ); return this.options.html as string; } const configs = this.app.modules.configs(); + const isProd = !isDebug() && !this.options.forceDev; - // @todo: implement guard redirect once cookie sessions arrive + const assets = { + js: "main.js", + css: "styles.css" + }; - const isProd = !isDebug(); - let script: string | undefined; - let css: string[] = []; - - // @todo: check why nextjs imports manifest, it's not required if (isProd) { - const manifest: Manifest = this.options.viteManifest - ? this.options.viteManifest - : isProd - ? // @ts-ignore cases issues when building types - await import("bknd/dist/manifest.json", { assert: { type: "json" } }).then( - (m) => m.default - ) - : {}; - //console.log("manifest", manifest, manifest["index.html"]); - const entry = Object.values(manifest).find((f: any) => f.isEntry === true); - if (!entry) { - // do something smart - return; + try { + // @ts-ignore + const manifest = await import("bknd/dist/manifest.json", { + assert: { type: "json" } + }).then((m) => m.default); + assets.js = manifest["src/ui/main.tsx"].name; + assets.css = manifest["src/ui/main.css"].name; + } catch (e) { + console.error("Error loading manifest", e); } - - script = "/" + entry.file; - css = entry.css?.map((c: string) => "/" + c) ?? []; } return ( {/* dnd complains otherwise */} - {html``} + {html``} @@ -162,14 +146,21 @@ export class AdminController implements ClassController { BKND {isProd ? ( -