summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-27 23:17:18 +0900
committerAdam Malczewski <[email protected]>2026-05-27 23:17:18 +0900
commit60f56367dfc1e3c5095d034bed4fb4f572e32b55 (patch)
treeaa4bc7be095e7030ce0b1a67e33b1abe4cc8c36a /packages
parent4f0ed4ed9456e30344228f0106f6bb104417da3d (diff)
downloaddispatch-60f56367dfc1e3c5095d034bed4fb4f572e32b55.tar.gz
dispatch-60f56367dfc1e3c5095d034bed4fb4f572e32b55.zip
refactor(packaging): split into dispatch/dispatch-systemd/dispatch-s6, separate dispatch-electron, add bun-based frontend serve
PKGBUILD is now a split package producing three .pkg.tar.zst files in one makepkg run: - dispatch base application files (/opt/dispatch), CLI wrappers (dispatch-api, dispatch-frontend), env configs (/etc/dispatch/dispatch-api.conf, /etc/dispatch/dispatch-frontend.conf) - dispatch-systemd systemd user units for dispatch-api + dispatch-frontend (conflicts with dispatch-s6) - dispatch-s6 s6-rc service pipelines (-srv + -log) for both services (conflicts with dispatch-systemd) The Electron desktop wrapper moved to its own self-contained PKGBUILD at packaging/electron/, producing dispatch-electron. It bundles its own copy of the frontend dist + electron entry points under /opt/dispatch-electron and does not depend on the base dispatch package, so users can install it standalone and point VITE_API_URL at any backend at build time. Files under packaging/ are now read directly from ${_projectdir}/packaging/ rather than via source=(); the two s6 service dirs share basenames (run, type) which would collide inside ${srcdir}. New frontend static server: packages/frontend/serve.ts uses Bun's built-in HTTP server (no extra runtime deps) with SPA fallback to index.html and path-traversal protection. PORT/HOST/DIST_DIR overridable via env. Exposed as 'bun run --cwd packages/frontend serve' and via /usr/bin/dispatch-frontend. Build scripts: - bin/build-pkg now prints the freshest built packages - bin/install-pkg installs dispatch + dispatch-systemd by default; accepts package names or --all; searches both packaging/ and packaging/electron/ - bin/build-pkg-electron new, builds the electron split - bin/build-pkg-windows replaces bin/windows-pkg; drops the hard-coded WSL copy step, just prints the win-unpacked path .gitignore extended to cover packaging/*.tar.zst and packaging/electron/{src,pkg,*.pkg.tar.zst,*.tar.zst}.
Diffstat (limited to 'packages')
-rw-r--r--packages/frontend/package.json1
-rw-r--r--packages/frontend/serve.ts66
2 files changed, 67 insertions, 0 deletions
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 453df07..1d11524 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -8,6 +8,7 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
+ "serve": "bun serve.ts",
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "svelte-check --tsconfig ./tsconfig.json",
diff --git a/packages/frontend/serve.ts b/packages/frontend/serve.ts
new file mode 100644
index 0000000..75b0a9f
--- /dev/null
+++ b/packages/frontend/serve.ts
@@ -0,0 +1,66 @@
+// Static file server for the built frontend (packages/frontend/dist).
+// Uses Bun's built-in HTTP server — no extra runtime dependencies.
+//
+// Environment variables:
+// PORT — port to listen on (default: 18391)
+// HOST — interface to bind to (default: 0.0.0.0)
+// DIST_DIR — absolute path to the static assets directory
+// (default: <this-file's-dir>/dist)
+//
+// SPA fallback: requests that don't match a file on disk fall back to
+// index.html so client-side routing works.
+
+import { existsSync, statSync } from "node:fs";
+import { join, normalize, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+
+const moduleDir = (() => {
+ try {
+ // @ts-expect-error — Bun provides import.meta.dir
+ return import.meta.dir as string;
+ } catch {
+ return resolve(fileURLToPath(import.meta.url), "..");
+ }
+})();
+
+const port = Number(process.env.PORT) || 18391;
+const host = process.env.HOST || "0.0.0.0";
+const distDir = resolve(process.env.DIST_DIR || join(moduleDir, "dist"));
+
+if (!existsSync(distDir) || !statSync(distDir).isDirectory()) {
+ console.error(`[dispatch-frontend] dist directory not found: ${distDir}`);
+ console.error("[dispatch-frontend] Build the frontend first (bun run --cwd packages/frontend build)");
+ process.exit(1);
+}
+
+const indexPath = join(distDir, "index.html");
+
+function safeResolve(urlPath: string): string | null {
+ // Normalize and prevent path-traversal outside distDir.
+ const decoded = decodeURIComponent(urlPath);
+ const candidate = resolve(distDir, "." + normalize(decoded));
+ if (!candidate.startsWith(distDir)) return null;
+ return candidate;
+}
+
+Bun.serve({
+ port,
+ hostname: host,
+ async fetch(req) {
+ const url = new URL(req.url);
+ let pathname = url.pathname;
+ if (pathname === "/" || pathname === "") pathname = "/index.html";
+
+ const resolved = safeResolve(pathname);
+ if (resolved && existsSync(resolved) && statSync(resolved).isFile()) {
+ return new Response(Bun.file(resolved));
+ }
+
+ // SPA fallback — serve index.html for unknown routes
+ return new Response(Bun.file(indexPath), {
+ headers: { "content-type": "text/html; charset=utf-8" },
+ });
+ },
+});
+
+console.log(`[dispatch-frontend] serving ${distDir} on http://${host}:${port}`);