mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 12:37:20 +00:00
fix: updated cloudflare adapter to use runtime config, aligned vite
This commit is contained in:
@@ -180,7 +180,10 @@ export class App {
|
||||
registerAdminController(config?: AdminControllerOptions) {
|
||||
// register admin
|
||||
this.adminController = new AdminController(this, config);
|
||||
this.modules.server.route(config?.basepath ?? "/", this.adminController.getController());
|
||||
this.modules.server.route(
|
||||
this.adminController.basepath,
|
||||
this.adminController.getController(),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export async function createApp<Env extends AwsLambdaEnv = AwsLambdaEnv>(
|
||||
case "url":
|
||||
additional.adminOptions = {
|
||||
...(typeof adminOptions === "object" ? adminOptions : {}),
|
||||
assets_path: assets.url,
|
||||
assetsPath: assets.url,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/// <reference types="@cloudflare/workers-types" />
|
||||
|
||||
import type { FrameworkBkndConfig } from "bknd/adapter";
|
||||
import type { RuntimeBkndConfig } from "bknd/adapter";
|
||||
import { Hono } from "hono";
|
||||
import { serveStatic } from "hono/cloudflare-workers";
|
||||
import { getFresh } from "./modes/fresh";
|
||||
import { getCached } from "./modes/cached";
|
||||
import { getDurable } from "./modes/durable";
|
||||
import { getFresh, getWarm } from "./modes/fresh";
|
||||
import type { App } from "bknd";
|
||||
|
||||
export type CloudflareEnv = object;
|
||||
export type CloudflareBkndConfig<Env = CloudflareEnv> = FrameworkBkndConfig<Env> & {
|
||||
export type CloudflareBkndConfig<Env = CloudflareEnv> = RuntimeBkndConfig<Env> & {
|
||||
mode?: "warm" | "fresh" | "cache" | "durable";
|
||||
bindings?: (args: Env) => {
|
||||
kv?: KVNamespace;
|
||||
@@ -20,8 +21,6 @@ export type CloudflareBkndConfig<Env = CloudflareEnv> = FrameworkBkndConfig<Env>
|
||||
keepAliveSeconds?: number;
|
||||
forceHttps?: boolean;
|
||||
manifest?: string;
|
||||
setAdminHtml?: boolean;
|
||||
html?: string;
|
||||
};
|
||||
|
||||
export type Context<Env = CloudflareEnv> = {
|
||||
@@ -43,7 +42,7 @@ export function serve<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
throw new Error("manifest is required with static 'kv'");
|
||||
}
|
||||
|
||||
if (config.manifest && config.static !== "assets") {
|
||||
if (config.manifest && config.static === "kv") {
|
||||
const pathname = url.pathname.slice(1);
|
||||
const assetManifest = JSON.parse(config.manifest);
|
||||
if (pathname && pathname in assetManifest) {
|
||||
@@ -70,18 +69,24 @@ export function serve<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
const context = { request, env, ctx } as Context<Env>;
|
||||
const mode = config.mode ?? "warm";
|
||||
|
||||
let app: App;
|
||||
switch (mode) {
|
||||
case "fresh":
|
||||
return await getFresh(config, context);
|
||||
app = await getFresh(config, context, { force: true });
|
||||
break;
|
||||
case "warm":
|
||||
return await getWarm(config, context);
|
||||
app = await getFresh(config, context);
|
||||
break;
|
||||
case "cache":
|
||||
return await getCached(config, context);
|
||||
app = await getCached(config, context);
|
||||
break;
|
||||
case "durable":
|
||||
return await getDurable(config, context);
|
||||
default:
|
||||
throw new Error(`Unknown mode ${mode}`);
|
||||
}
|
||||
|
||||
return app.fetch(request, env, ctx);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { D1Connection, type D1ConnectionConfig } from "./D1Connection";
|
||||
|
||||
export * from "./cloudflare-workers.adapter";
|
||||
export { makeApp, getFresh, getWarm } from "./modes/fresh";
|
||||
export { makeApp, getFresh } from "./modes/fresh";
|
||||
export { getCached } from "./modes/cached";
|
||||
export { DurableBkndApp, getDurable } from "./modes/durable";
|
||||
export { D1Connection, type D1ConnectionConfig };
|
||||
|
||||
@@ -40,7 +40,6 @@ export async function getCached<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
);
|
||||
await config.beforeBuild?.(app);
|
||||
},
|
||||
adminOptions: { html: config.html },
|
||||
},
|
||||
{ env, ctx, ...args },
|
||||
);
|
||||
|
||||
@@ -25,9 +25,7 @@ export async function getDurable<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
|
||||
const res = await stub.fire(ctx.request, {
|
||||
config: create_config,
|
||||
html: config.html,
|
||||
keepAliveSeconds: config.keepAliveSeconds,
|
||||
setAdminHtml: config.setAdminHtml,
|
||||
});
|
||||
|
||||
const headers = new Headers(res.headers);
|
||||
@@ -110,6 +108,7 @@ export class DurableBkndApp extends DurableObject {
|
||||
}
|
||||
|
||||
async onBuilt(app: App) {}
|
||||
|
||||
async beforeBuild(app: App) {}
|
||||
|
||||
protected keepAlive(seconds: number) {
|
||||
|
||||
@@ -7,22 +7,15 @@ export async function makeApp<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
args: Env = {} as Env,
|
||||
opts?: RuntimeOptions,
|
||||
) {
|
||||
return await createRuntimeApp<Env>(
|
||||
{
|
||||
...makeConfig(config, args),
|
||||
adminOptions: config.html ? { html: config.html } : undefined,
|
||||
},
|
||||
args,
|
||||
opts,
|
||||
);
|
||||
return await createRuntimeApp<Env>(makeConfig(config, args), args, opts);
|
||||
}
|
||||
|
||||
export async function getWarm<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
config: CloudflareBkndConfig<Env>,
|
||||
ctx: Context<Env>,
|
||||
opts: RuntimeOptions = {},
|
||||
) {
|
||||
const app = await makeApp(
|
||||
return await makeApp(
|
||||
{
|
||||
...config,
|
||||
onBuilt: async (app) => {
|
||||
@@ -33,16 +26,4 @@ export async function getWarm<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
ctx.env,
|
||||
opts,
|
||||
);
|
||||
return app.fetch(ctx.request);
|
||||
}
|
||||
|
||||
export async function getFresh<Env extends CloudflareEnv = CloudflareEnv>(
|
||||
config: CloudflareBkndConfig<Env>,
|
||||
ctx: Context<Env>,
|
||||
opts: RuntimeOptions = {},
|
||||
) {
|
||||
return await getWarm(config, ctx, {
|
||||
...opts,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import { serveStatic } from "@hono/node-server/serve-static";
|
||||
import { type DevServerOptions, default as honoViteDevServer } from "@hono/vite-dev-server";
|
||||
import {
|
||||
type DevServerOptions,
|
||||
default as honoViteDevServer,
|
||||
} from "@hono/vite-dev-server";
|
||||
import type { App } from "bknd";
|
||||
import { type RuntimeBkndConfig, createRuntimeApp } from "bknd/adapter";
|
||||
import {
|
||||
type RuntimeBkndConfig,
|
||||
createRuntimeApp,
|
||||
type FrameworkOptions,
|
||||
} from "bknd/adapter";
|
||||
import { registerLocalMediaAdapter } from "bknd/adapter/node";
|
||||
import { devServerConfig } from "./dev-server-config";
|
||||
|
||||
export type ViteBkndConfig<Env = any> = RuntimeBkndConfig<Env> & {
|
||||
mode?: "cached" | "fresh";
|
||||
setAdminHtml?: boolean;
|
||||
forceDev?: boolean | { mainPath: string };
|
||||
html?: string;
|
||||
};
|
||||
export type ViteEnv = NodeJS.ProcessEnv;
|
||||
export type ViteBkndConfig<Env = ViteEnv> = RuntimeBkndConfig<Env> & {};
|
||||
|
||||
export function addViteScript(html: string, addBkndContext: boolean = true) {
|
||||
export function addViteScript(
|
||||
html: string,
|
||||
addBkndContext: boolean = true,
|
||||
) {
|
||||
return html.replace(
|
||||
"</head>",
|
||||
`<script type="module">
|
||||
@@ -28,52 +34,40 @@ ${addBkndContext ? "<!-- BKND_CONTEXT -->" : ""}
|
||||
);
|
||||
}
|
||||
|
||||
async function createApp(config: ViteBkndConfig = {}, env?: any) {
|
||||
async function createApp<ViteEnv>(
|
||||
config: ViteBkndConfig<ViteEnv> = {},
|
||||
env: ViteEnv = {} as ViteEnv,
|
||||
opts: FrameworkOptions = {},
|
||||
): Promise<App> {
|
||||
registerLocalMediaAdapter();
|
||||
return await createRuntimeApp(
|
||||
{
|
||||
...config,
|
||||
adminOptions:
|
||||
config.setAdminHtml === false
|
||||
? undefined
|
||||
: {
|
||||
html: config.html,
|
||||
forceDev: config.forceDev ?? {
|
||||
mainPath: "/src/main.tsx",
|
||||
},
|
||||
},
|
||||
adminOptions: config.adminOptions ?? {
|
||||
forceDev: {
|
||||
mainPath: "/src/main.tsx",
|
||||
},
|
||||
},
|
||||
serveStatic: ["/assets/*", serveStatic({ root: config.distPath ?? "./" })],
|
||||
},
|
||||
env,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
export function serveFresh(config: Omit<ViteBkndConfig, "mode"> = {}) {
|
||||
export function serve<ViteEnv>(
|
||||
config: ViteBkndConfig<ViteEnv> = {},
|
||||
args?: ViteEnv,
|
||||
opts?: FrameworkOptions,
|
||||
) {
|
||||
return {
|
||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
||||
const app = await createApp(config, env);
|
||||
const app = await createApp(config, env, opts);
|
||||
return app.fetch(request, env, ctx);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
let app: App;
|
||||
export function serveCached(config: Omit<ViteBkndConfig, "mode"> = {}) {
|
||||
return {
|
||||
async fetch(request: Request, env: any, ctx: ExecutionContext) {
|
||||
if (!app) {
|
||||
app = await createApp(config, env);
|
||||
}
|
||||
|
||||
return app.fetch(request, env, ctx);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function serve({ mode, ...config }: ViteBkndConfig = {}) {
|
||||
return mode === "fresh" ? serveFresh(config) : serveCached(config);
|
||||
}
|
||||
|
||||
export function devServer(options: DevServerOptions) {
|
||||
return honoViteDevServer({
|
||||
...devServerConfig,
|
||||
|
||||
@@ -14,10 +14,11 @@ const htmlBkndContextReplace = "<!-- BKND_CONTEXT -->";
|
||||
// @todo: add migration to remove admin path from config
|
||||
export type AdminControllerOptions = {
|
||||
basepath?: string;
|
||||
assets_path?: string;
|
||||
adminBasepath?: string;
|
||||
assetsPath?: string;
|
||||
html?: string;
|
||||
forceDev?: boolean | { mainPath: string };
|
||||
debug_rerenders?: boolean;
|
||||
debugRerenders?: boolean;
|
||||
};
|
||||
|
||||
export class AdminController extends Controller {
|
||||
@@ -36,7 +37,8 @@ export class AdminController extends Controller {
|
||||
return {
|
||||
...this._options,
|
||||
basepath: this._options.basepath ?? "/",
|
||||
assets_path: this._options.assets_path ?? config.server.assets_path,
|
||||
adminBasepath: this._options.adminBasepath ?? "",
|
||||
assetsPath: this._options.assetsPath ?? config.server.assets_path,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,6 +50,10 @@ export class AdminController extends Controller {
|
||||
return (this.basepath + route).replace(/(?<!:)\/+/g, "/");
|
||||
}
|
||||
|
||||
private withAdminBasePath(route: string = "") {
|
||||
return this.withBasePath(this.options.adminBasepath + route);
|
||||
}
|
||||
|
||||
override getController() {
|
||||
const { auth: authMiddleware, permission } = this.middlewares;
|
||||
const hono = this.create().use(
|
||||
@@ -63,16 +69,16 @@ export class AdminController extends Controller {
|
||||
|
||||
const authRoutes = {
|
||||
root: "/",
|
||||
success: configs.auth.cookie.pathSuccess ?? "/",
|
||||
loggedOut: configs.auth.cookie.pathLoggedOut ?? "/",
|
||||
login: "/auth/login",
|
||||
logout: "/auth/logout",
|
||||
success: configs.auth.cookie.pathSuccess ?? this.withAdminBasePath("/"),
|
||||
loggedOut: configs.auth.cookie.pathLoggedOut ?? this.withAdminBasePath("/"),
|
||||
login: this.withAdminBasePath("/auth/login"),
|
||||
logout: this.withAdminBasePath("/auth/logout"),
|
||||
};
|
||||
|
||||
hono.use("*", async (c, next) => {
|
||||
const obj = {
|
||||
user: c.get("auth")?.user,
|
||||
logout_route: this.withBasePath(authRoutes.logout),
|
||||
logout_route: this.withAdminBasePath(authRoutes.logout),
|
||||
};
|
||||
const html = await this.getHtml(obj);
|
||||
if (!html) {
|
||||
@@ -164,8 +170,8 @@ export class AdminController extends Controller {
|
||||
|
||||
if (isProd) {
|
||||
let manifest: any;
|
||||
if (this.options.assets_path.startsWith("http")) {
|
||||
manifest = await fetch(this.options.assets_path + "manifest.json", {
|
||||
if (this.options.assetsPath.startsWith("http")) {
|
||||
manifest = await fetch(this.options.assetsPath + "manifest.json", {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
@@ -182,7 +188,7 @@ export class AdminController extends Controller {
|
||||
assets.css = manifest["src/ui/main.tsx"].css[0] as any;
|
||||
}
|
||||
|
||||
const favicon = isProd ? this.options.assets_path + "favicon.ico" : "/favicon.ico";
|
||||
const favicon = isProd ? this.options.assetsPath + "favicon.ico" : "/favicon.ico";
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
@@ -197,7 +203,7 @@ export class AdminController extends Controller {
|
||||
/>
|
||||
<link rel="icon" href={favicon} type="image/x-icon" />
|
||||
<title>BKND</title>
|
||||
{this.options.debug_rerenders && (
|
||||
{this.options.debugRerenders && (
|
||||
<script
|
||||
crossOrigin="anonymous"
|
||||
src="//unpkg.com/react-scan/dist/auto.global.js"
|
||||
@@ -205,8 +211,8 @@ export class AdminController extends Controller {
|
||||
)}
|
||||
{isProd ? (
|
||||
<Fragment>
|
||||
<script type="module" src={this.options.assets_path + assets?.js} />
|
||||
<link rel="stylesheet" href={this.options.assets_path + assets?.css} />
|
||||
<script type="module" src={this.options.assetsPath + assets?.js} />
|
||||
<link rel="stylesheet" href={this.options.assetsPath + assets?.css} />
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment>
|
||||
|
||||
Reference in New Issue
Block a user