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