From a109cfb3c16e0a8cf8e62c7511dfb7cc381913f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20F=C3=B6rtsch?= Date: Wed, 11 Mar 2026 13:01:59 +0100 Subject: [PATCH] add deploy script, fix production paths, use port 3006 Co-Authored-By: Claude Opus 4.6 --- .env.example | 2 +- deploy.sh | 113 +++++++++++++++++++++ packages/client/src/hooks/use-websocket.ts | 5 +- packages/client/src/routes/index.tsx | 2 +- packages/client/vite.config.ts | 4 +- packages/server/src/env.ts | 2 +- 6 files changed, 122 insertions(+), 6 deletions(-) create mode 100755 deploy.sh diff --git a/.env.example b/.env.example index bc93bce..35a3627 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,2 @@ DATABASE_URL=postgresql://localhost:5433/celebrate_esc -PORT=3001 +PORT=3006 diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..35cb0e1 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +set -euo pipefail + +# celebrate-esc deploy script — idempotent, can be re-run safely +# Target: Uberspace 8 (serve.uber.space) + +HOST="serve" +SERVICE_DIR="services/celebrate-esc" +STATIC_DIR="/var/www/virtual/serve/html/celebrate-esc" +DB_NAME="celebrate_esc" +PORT=3006 + +echo "=== celebrate-esc deploy ===" + +# ── 1. Create database if needed ───────────────────────────────────── +echo "→ ensuring database exists..." +ssh "$HOST" "createdb -h localhost -p 5433 $DB_NAME 2>/dev/null || true" + +# ── 2. Build client locally ────────────────────────────────────────── +echo "→ building client..." +cd packages/client +VITE_BASE="/celebrate-esc/" bun run build +cd ../.. + +# ── 3. Sync server code ───────────────────────────────────────────── +echo "→ syncing server to $HOST:~/$SERVICE_DIR/..." +ssh "$HOST" "mkdir -p ~/$SERVICE_DIR" +rsync -az --delete \ + --exclude='node_modules' \ + --exclude='.env' \ + packages/server/ "$HOST:~/$SERVICE_DIR/server/" +rsync -az --delete \ + --exclude='node_modules' \ + packages/shared/ "$HOST:~/$SERVICE_DIR/shared/" + +# Copy root workspace files needed for bun install +scp package.json "$HOST:~/$SERVICE_DIR/" +scp tsconfig.json "$HOST:~/$SERVICE_DIR/" +scp bun.lock "$HOST:~/$SERVICE_DIR/" + +# ── 4. Install server dependencies on remote ───────────────────────── +echo "→ installing server dependencies..." +ssh "$HOST" "cd ~/$SERVICE_DIR && cat > package.json << 'PKGJSON' +{ + \"name\": \"celebrate-esc\", + \"private\": true, + \"workspaces\": [ + \"shared\", + \"server\" + ] +} +PKGJSON +bun install --frozen-lockfile 2>/dev/null || bun install" + +# ── 5. Create .env on remote if missing ────────────────────────────── +echo "→ ensuring .env exists..." +ssh "$HOST" "test -f ~/$SERVICE_DIR/.env || cat > ~/$SERVICE_DIR/.env << 'ENVFILE' +DATABASE_URL=postgresql://localhost:5433/$DB_NAME +PORT=$PORT +ENVFILE" + +# ── 6. Run migrations ──────────────────────────────────────────────── +echo "→ running database migrations..." +ssh "$HOST" "cd ~/$SERVICE_DIR/server && DATABASE_URL=postgresql://localhost:5433/$DB_NAME bun drizzle-kit migrate" + +# ── 7. Deploy static client files ──────────────────────────────────── +echo "→ deploying client static files..." +ssh "$HOST" "mkdir -p $STATIC_DIR" +rsync -az --delete packages/client/dist/ "$HOST:$STATIC_DIR/" + +# Create .htaccess for SPA routing +ssh "$HOST" "cat > $STATIC_DIR/.htaccess << 'HTACCESS' +RewriteEngine On +RewriteBase /celebrate-esc/ +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . index.html [L] +HTACCESS" + +# ── 8. Create systemd service ──────────────────────────────────────── +echo "→ setting up systemd service..." +ssh "$HOST" "cat > ~/.config/systemd/user/celebrate-esc.service << 'UNIT' +[Unit] +Description=celebrate-esc API server +After=postgresql.service + +[Service] +Type=simple +WorkingDirectory=%h/services/celebrate-esc/server +ExecStart=/home/serve/.local/share/mise/installs/bun/1.3.0/bin/bun run --env-file=../.env src/index.ts +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=default.target +UNIT + +systemctl --user daemon-reload +systemctl --user enable celebrate-esc.service +systemctl --user restart celebrate-esc.service" + +# ── 9. Set up web backend routing ──────────────────────────────────── +echo "→ configuring web backend routing..." +ssh "$HOST" "uberspace web backend set /celebrate-esc/api --http --port $PORT --remove-prefix 2>/dev/null || true" + +# ── 10. Verify ──────────────────────────────────────────────────────── +echo "→ verifying deployment..." +sleep 2 +ssh "$HOST" "systemctl --user status celebrate-esc.service --no-pager | head -5" +echo "" +echo "=== deploy complete ===" +echo "Frontend: https://serve.uber.space/celebrate-esc/" +echo "API: https://serve.uber.space/celebrate-esc/api/health" diff --git a/packages/client/src/hooks/use-websocket.ts b/packages/client/src/hooks/use-websocket.ts index e96a7a6..1e90316 100644 --- a/packages/client/src/hooks/use-websocket.ts +++ b/packages/client/src/hooks/use-websocket.ts @@ -41,9 +41,10 @@ export function useWebSocket(roomCode: string) { const sessionId = stored?.roomCode === roomCode ? stored.sessionId : null const protocol = window.location.protocol === "https:" ? "wss:" : "ws:" + const base = import.meta.env.BASE_URL.replace(/\/$/, "") const wsUrl = sessionId - ? `${protocol}//${window.location.host}/api/ws/${roomCode}?sessionId=${sessionId}` - : `${protocol}//${window.location.host}/api/ws/${roomCode}` + ? `${protocol}//${window.location.host}${base}/api/ws/${roomCode}?sessionId=${sessionId}` + : `${protocol}//${window.location.host}${base}/api/ws/${roomCode}` setConnectionStatus("connecting") const ws = new WebSocket(wsUrl) diff --git a/packages/client/src/routes/index.tsx b/packages/client/src/routes/index.tsx index 28c1c94..0f47fce 100644 --- a/packages/client/src/routes/index.tsx +++ b/packages/client/src/routes/index.tsx @@ -35,7 +35,7 @@ function CreateRoomCard() { setError("") try { - const res = await fetch("/api/rooms", { + const res = await fetch(`${import.meta.env.BASE_URL}api/rooms`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ displayName: displayName.trim() }), diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts index c6c7ef0..b4d6a6a 100644 --- a/packages/client/vite.config.ts +++ b/packages/client/vite.config.ts @@ -5,6 +5,7 @@ import react from "@vitejs/plugin-react" import { defineConfig } from "vite" export default defineConfig({ + base: process.env.VITE_BASE ?? "/", plugins: [TanStackRouterVite(), react(), tailwindcss()], resolve: { alias: { @@ -14,8 +15,9 @@ export default defineConfig({ server: { proxy: { "/api": { - target: "http://localhost:3001", + target: "http://localhost:3006", changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, ""), ws: true, }, }, diff --git a/packages/server/src/env.ts b/packages/server/src/env.ts index 931b8d6..7f3e88d 100644 --- a/packages/server/src/env.ts +++ b/packages/server/src/env.ts @@ -2,7 +2,7 @@ import { z } from "zod" const envSchema = z.object({ DATABASE_URL: z.string().url(), - PORT: z.coerce.number().default(3001), + PORT: z.coerce.number().default(3006), }) export const env = envSchema.parse(process.env)