diff --git a/app/package.json b/app/package.json index 2db29e1e..cc8b5f83 100644 --- a/app/package.json +++ b/app/package.json @@ -250,7 +250,8 @@ }, "./dist/main.css": "./dist/ui/main.css", "./dist/styles.css": "./dist/ui/styles.css", - "./dist/manifest.json": "./dist/static/.vite/manifest.json" + "./dist/manifest.json": "./dist/static/.vite/manifest.json", + "./static/*": "./dist/static/*" }, "typesVersions": { "*": { diff --git a/app/src/adapter/bun/bun.adapter.ts b/app/src/adapter/bun/bun.adapter.ts index 5d7c1482..f6177eca 100644 --- a/app/src/adapter/bun/bun.adapter.ts +++ b/app/src/adapter/bun/bun.adapter.ts @@ -21,8 +21,8 @@ export async function createApp( return await createRuntimeApp( { - ...config, serveStatic: serveStatic({ root }), + ...config, }, args ?? (process.env as Env), opts, @@ -53,6 +53,7 @@ export function serve( onBuilt, buildConfig, adminOptions, + serveStatic, ...serveOptions }: BunBkndConfig = {}, args: Env = {} as Env, @@ -70,6 +71,7 @@ export function serve( buildConfig, adminOptions, distPath, + serveStatic, }, args, opts, diff --git a/app/src/adapter/index.ts b/app/src/adapter/index.ts index ebab187d..713fb0f6 100644 --- a/app/src/adapter/index.ts +++ b/app/src/adapter/index.ts @@ -1,9 +1,11 @@ import { App, type CreateAppConfig } from "bknd"; import { config as $config } from "bknd/core"; import { $console } from "bknd/utils"; -import type { MiddlewareHandler } from "hono"; +import type { Context, MiddlewareHandler, Next } from "hono"; import type { AdminControllerOptions } from "modules/server/AdminController"; import { Connection } from "bknd/data"; +import type { Manifest } from "vite"; +import { guessMimeType } from "media"; export { Connection } from "bknd/data"; @@ -140,3 +142,54 @@ export async function createRuntimeApp( return app; } + +/** + * Creates a middleware handler to serve static assets via dynamic imports. + * This is useful for environments where filesystem access is limited but bundled assets can be imported. + * + * @param manifest - Vite manifest object containing asset information + * @returns Hono middleware handler for serving static assets + * + * @example + * ```typescript + * import { serveStaticViaImport } from "bknd/adapter"; + * + * serve({ + * serveStatic: serveStaticViaImport(), + * }); + * ``` + */ +export function serveStaticViaImport(opts?: { manifest?: Manifest }) { + let files: string[] | undefined; + + // @ts-ignore + return async (c: Context, next: Next) => { + if (!files) { + const manifest = + opts?.manifest || ((await import("bknd/dist/manifest.json")).default as Manifest); + files = Object.values(manifest).flatMap((asset) => [asset.file, ...(asset.css || [])]); + } + + const path = c.req.path.substring(1); + if (files.includes(path)) { + try { + const content = await import(`bknd/static/${path}?raw`, { + assert: { type: "text" }, + }).then((m) => m.default); + + if (content) { + return c.body(content, { + headers: { + "Content-Type": guessMimeType(path), + "Cache-Control": "public, max-age=31536000, immutable", + }, + }); + } + } catch (e) { + console.error("Error serving static file:", e); + return c.text("File not found", 404); + } + } + await next(); + }; +} diff --git a/app/src/adapter/node/node.adapter.ts b/app/src/adapter/node/node.adapter.ts index 88b7d62e..66cc4698 100644 --- a/app/src/adapter/node/node.adapter.ts +++ b/app/src/adapter/node/node.adapter.ts @@ -32,8 +32,8 @@ export async function createApp( registerLocalMediaAdapter(); return await createRuntimeApp( { - ...config, serveStatic: serveStatic({ root }), + ...config, }, // @ts-ignore args ?? { env: process.env }, diff --git a/app/src/media/storage/mime-types-tiny.ts b/app/src/media/storage/mime-types-tiny.ts index 0718da2a..e7c42bbb 100644 --- a/app/src/media/storage/mime-types-tiny.ts +++ b/app/src/media/storage/mime-types-tiny.ts @@ -26,9 +26,11 @@ export const M = new Map([ ["eps", c.a("postscript")], ["epub", c.a("epub+zip")], ["ini", c.t()], + ["ico", c.i("vnd.microsoft.icon")], ["jar", c.a("java-archive")], ["jsonld", c.a("ld+json")], ["jpg", c.i("jpeg")], + ["js", c.t("javascript")], ["log", c.t()], ["m3u", c.t()], ["m3u8", c.a("vnd.apple.mpegurl")],