feat(deploy): serve SPA from server, Dockerfile, compose, deploy docs

- Add serveStatic middleware to server/src/app.ts to serve web/dist
- Include SPA fallback route for client-side routing deep links
- Create Dockerfile with multi-stage build (Node deps + build, slim runtime)
- Add docker-compose.yml for single-command deployment
- Add docs/deploy.md with deployment instructions and Caddy reverse proxy example
- Add bun run build:web step to CI pipeline after typecheck

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
marcuspaico
2026-08-17 14:23:29 -07:00
parent 1c97b8e33b
commit 24bc8917f1
5 changed files with 45 additions and 0 deletions

View File

@@ -14,4 +14,5 @@ jobs:
git init -q . && git fetch -q --depth 1 "https://git.rehbock.xyz/${{ github.repository }}.git" "${{ github.sha }}" && git checkout -q FETCH_HEAD git init -q . && git fetch -q --depth 1 "https://git.rehbock.xyz/${{ github.repository }}.git" "${{ github.sha }}" && git checkout -q FETCH_HEAD
- run: bun install --frozen-lockfile - run: bun install --frozen-lockfile
- run: bun run typecheck - run: bun run typecheck
- run: bun run build:web
- run: bun test server - run: bun test server

16
Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
FROM oven/bun:1 AS build
WORKDIR /app
COPY package.json bun.lock* ./
COPY server/package.json server/
COPY web/package.json web/
COPY shared/package.json shared/
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build:web
FROM oven/bun:1-slim
WORKDIR /app
COPY --from=build /app /app
ENV DATA_DIR=/data
EXPOSE 3000
CMD ["bun", "server/src/index.ts"]

8
docker-compose.yml Normal file
View File

@@ -0,0 +1,8 @@
services:
helios:
build: .
ports:
- "3000:3000"
volumes:
- ./data:/data
restart: unless-stopped

17
docs/deploy.md Normal file
View File

@@ -0,0 +1,17 @@
# Deploying Helios
git clone https://git.rehbock.xyz/marcus/helios.git && cd helios
docker compose up -d
Open http://your-host:3000 and create the instance password.
Back up the `./data` directory — it holds the SQLite DB, uploads, and
`secret.key`. Without `secret.key` your stored API keys are unrecoverable.
Run the host disk encrypted; Helios encrypts credentials column-level but
not the whole DB file.
## Reverse proxy (Caddy)
helios.example.com {
reverse_proxy localhost:3000
}

View File

@@ -1,5 +1,6 @@
import { Hono } from "hono"; import { Hono } from "hono";
import { getCookie } from "hono/cookie"; import { getCookie } from "hono/cookie";
import { serveStatic } from "hono/bun";
import type { Db } from "./db"; import type { Db } from "./db";
import { authRoutes, isAuthenticated } from "./routes/auth"; import { authRoutes, isAuthenticated } from "./routes/auth";
import { settingsRoutes } from "./routes/settings"; import { settingsRoutes } from "./routes/settings";
@@ -20,5 +21,7 @@ export function createApp(deps: Deps) {
app.route("/api", authRoutes({ db: deps.db })); app.route("/api", authRoutes({ db: deps.db }));
app.route("/api", settingsRoutes(deps)); app.route("/api", settingsRoutes(deps));
// Later route groups (connectors, chat) mount here. // Later route groups (connectors, chat) mount here.
app.use("/*", serveStatic({ root: "./web/dist" }));
app.get("/*", serveStatic({ path: "./web/dist/index.html" }));
return app; return app;
} }