feat(M5): docker-compose full stack (caddy + node backend + sqlite)
docker-compose.yml: two profiles.
- backend: backend (node+ws+better-sqlite3, /data volume) + frontend
(Caddy static build, STORAGE=ws, same-origin proxy)
- firebase: existing Dockerfile + nginx (upstream path, untouched)
Run: docker compose --profile backend up --build. OrbStack local now,
remote docker context later.
server/Dockerfile: node:18-alpine, workspaces (shared dep), rebuild
better-sqlite3 for musl, DB at /data/tracker.sqlite.
Dockerfile.ws: CRA build STORAGE=ws → caddy:2-alpine serves /srv.
No backend URL baked (same-origin).
Caddyfile: handle /api/* + handle /ws → backend:4001 (path preserved,
mutually-exclusive handles so try_files SPA fallback never shadows proxy).
handle { static try_files } last. HTTP basic auth block optional.
src/storage/ws.js: same-origin defaults. Empty baseUrl = relative fetch
(Caddy proxy). wsUrl derives from window.location (http→ws/https→wss).
Fallback localhost for bare npm start dev.
.dockerignore: add data/ scratch/ tmp/ (never bake into image). Keep
Caddyfile in context (frontend build COPYs it).
Smoke verified via OrbStack:
- GET / → 200 (static SPA)
- PUT/GET /api/doc roundtrip → JSON persists
- WS /ws subscribe + change push → both work through proxy
Firebase profile: pre-existing Dockerfile requires .env.local (hardcoded
COPY on main, not changed here). User must create file. Not a regression.
This commit is contained in:
+13
-2
@@ -13,8 +13,19 @@ if (typeof WebSocket !== 'undefined') {
|
||||
}
|
||||
|
||||
function createWsStorage({ baseUrl, wsUrl } = {}) {
|
||||
const API = (baseUrl || 'http://127.0.0.1:4001').replace(/\/$/, '');
|
||||
const WS = wsUrl || (API.replace(/^http/, 'ws') + '/ws');
|
||||
// Same-origin by default: empty baseUrl = relative fetch (Caddy/proxy).
|
||||
// Fallback to localhost for bare `npm start` dev without proxy.
|
||||
const API = (baseUrl || (typeof window !== 'undefined' && window.location ? '' : 'http://127.0.0.1:4001')).replace(/\/$/, '');
|
||||
let WS;
|
||||
if (wsUrl) {
|
||||
WS = wsUrl;
|
||||
} else if (typeof window !== 'undefined' && window.location) {
|
||||
// derive from current origin (http→ws, https→wss), same host/port.
|
||||
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
WS = `${proto}//${window.location.host}/ws`;
|
||||
} else {
|
||||
WS = 'ws://127.0.0.1:4001/ws';
|
||||
}
|
||||
|
||||
// App passes firebase-prefixed paths: artifacts/{APP_ID}/public/data/campaigns/...
|
||||
// Backend uses canonical paths. Strip prefix.
|
||||
|
||||
Reference in New Issue
Block a user