diff --git a/BASELINE.md b/BASELINE.md index 5280f8a..e276119 100644 --- a/BASELINE.md +++ b/BASELINE.md @@ -60,3 +60,41 @@ - `Dockerfile.spa` — Multi-stage: Bun build → Caddy serve - `Caddyfile` — Serves dist on :80, reverse-proxies /api/* and /mcp to api:3000 - `docker-compose.yml` — 4 services: db, api, spa, worker (old web service removed) + +## Phase 7 — Deploy (completed) + +**Deploy time:** 2026-08-01T02:40Z +**Commit:** `fc59431140329bd93223dfae3c72406d9e02646e` +**Branch:** `redesign/ui-v2` + +### Image SHAs +| Image | SHA256 | +|-------|--------| +| `projecte-api` | `fc46878d1618bb3a0d6a43d645a21cad14caa1bb6eaf773e4e32579a268adc10` | +| `projecte-spa` | `1cced4e61fb35d31df53225b3c8f5863a3d581b82ed961c13286bd5e3f940f48` | +| `projecte-worker` | `7fc57170100f6cf6b12439835f5700383c7c4570eecf3c4ab3f06fa739d10d07` | + +### Compose file SHA +`bb21417829c866048509f81881304e81b5e29430270a278e0b3a77caa68f8b1b` + +### Verification results +| Check | Result | +|-------|--------| +| `docker compose ps` — all 4 services running, db healthy | ✅ | +| `curl :3000/api/health` — 200 + `{"status":"ok"}` | ✅ | +| `curl :3000/` — 200 + HTML | ✅ | +| Login (user@example.com) — token received | ✅ | +| `/api/domains` — 2 domains returned | ✅ | +| `/mcp` — endpoint reachable (requires API key) | ✅ | +| All 13 page routes return 200 | ✅ (13/13) | + +### Container state +| Container | Status | Ports | +|-----------|--------|-------| +| `project-e-db` | Running (healthy) | :5432 | +| `project-e-api` | Running | :3001 | +| `project-e-spa` | Running | :3000 | +| `project-e-worker` | Running | — | + +### URL +- http://10.0.0.204:3000 — **LIVE** (SPA served by Caddy, API reverse-proxied) diff --git a/Caddyfile b/Caddyfile index 5768a9a..ff86146 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,11 +1,17 @@ :80 { root * /usr/share/caddy - file_server + encode gzip - @api path /api/* /mcp* - reverse_proxy @api api:3000 + route /api/* { + reverse_proxy api:3000 + } - handle_errors { - respond 404 "Not Found" + route /mcp* { + reverse_proxy api:3000 + } + + route { + try_files {path} /index.html + file_server } } diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..64ff9e2 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,219 @@ +# Project E — Deploy Guide + +## How to redeploy + +```bash +cd ~/ProjectE + +# Pull latest +git pull origin redesign/ui-v2 + +# Rebuild images +docker compose build + +# Restart stack +docker compose up -d + +# Check status +docker compose ps +``` + +## How to roll back + +If the new stack fails: + +```bash +cd ~/ProjectE + +# Stop the new stack +docker compose down + +# Restart the old worker (Node) from the legacy compose +# (The old compose file is preserved in git history) +# docker compose -f docker-compose.legacy.yml up -d worker +``` + +## How to view logs + +```bash +# All services +docker compose logs --tail=50 -f + +# Specific service +docker compose logs --tail=50 -f api +docker compose logs --tail=50 -f spa +docker compose logs --tail=50 -f worker +docker compose logs --tail=50 -f db +``` + +## How to debug + +### API health check +```bash +curl http://localhost:3001/api/health +``` + +### SPA health check +```bash +curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/ +``` + +### API through reverse proxy +```bash +curl http://localhost:3000/api/health +``` + +### Login test +```bash +TOKEN=$(curl -s -X POST http://localhost:3000/api/auth/credentials \ + -H "Content-Type: application/json" \ + -d email:user@example.com | \ + python3 -c "import sys,json; print(json.load(sys.stdin).get(token,))") +echo "Token: $TOKEN" +``` + +### Check container health +```bash +docker inspect project-e-db --format {{.State.Health.Status}} +``` + +### Restart a single service +```bash +docker compose restart api +docker compose restart spa +``` + +### Rebuild a single service +```bash +docker compose build spa +docker compose up -d --force-recreate spa +``` + +## Architecture + +``` +Internet → :3000 → Caddy (SPA container) + ├── /api/* → api:3000 (Hono/Bun) + ├── /mcp* → api:3000 (Hono/Bun) + └── /* → index.html (SPA fallback) + +API (:3001, direct) → PostgreSQL (:5432) +Worker → PostgreSQL +``` + +## Important notes + +- The `project-e-pg-data` Docker volume contains the live database. **Do not delete it.** +- The `apps/web-legacy/` directory contains the old Next.js app for reference. **Do not delete it.** +- Port 3000 is the SPA (Caddy), port 3001 is the API directly (for debugging). +- The MCP endpoint requires a valid API key (separate from JWT auth). +EOF cd ~/ProjectE && cat > DEPLOY.md << 'EOF' +# Project E — Deploy Guide + +## How to redeploy + +```bash +cd ~/ProjectE + +# Pull latest +git pull origin redesign/ui-v2 + +# Rebuild images +docker compose build + +# Restart stack +docker compose up -d + +# Check status +docker compose ps +``` + +## How to roll back + +If the new stack fails: + +```bash +cd ~/ProjectE + +# Stop the new stack +docker compose down + +# Restart the old worker (Node) from the legacy compose +# (The old compose file is preserved in git history) +# docker compose -f docker-compose.legacy.yml up -d worker +``` + +## How to view logs + +```bash +# All services +docker compose logs --tail=50 -f + +# Specific service +docker compose logs --tail=50 -f api +docker compose logs --tail=50 -f spa +docker compose logs --tail=50 -f worker +docker compose logs --tail=50 -f db +``` + +## How to debug + +### API health check +```bash +curl http://localhost:3001/api/health +``` + +### SPA health check +```bash +curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/ +``` + +### API through reverse proxy +```bash +curl http://localhost:3000/api/health +``` + +### Login test +```bash +TOKEN=$(curl -s -X POST http://localhost:3000/api/auth/credentials \ + -H "Content-Type: application/json" \ + -d password: | \ + python3 -c "import sys,json; print(json.load(sys.stdin).get(token,))") +echo "Token: $TOKEN" +``` + +### Check container health +```bash +docker inspect project-e-db --format {{.State.Health.Status}} +``` + +### Restart a single service +```bash +docker compose restart api +docker compose restart spa +``` + +### Rebuild a single service +```bash +docker compose build spa +docker compose up -d --force-recreate spa +``` + +## Architecture + +``` +Internet → :3000 → Caddy (SPA container) + ├── /api/* → api:3000 (Hono/Bun) + ├── /mcp* → api:3000 (Hono/Bun) + └── /* → index.html (SPA fallback) + +API (:3001, direct) → PostgreSQL (:5432) +Worker → PostgreSQL +``` + +## Important notes + +- The `project-e-pg-data` Docker volume contains the live database. **Do not delete it.** +- The `apps/web-legacy/` directory contains the old Next.js app for reference. **Do not delete it.** +- Port 3000 is the SPA (Caddy), port 3001 is the API directly (for debugging). +- The MCP endpoint requires a valid API key (separate from JWT auth). diff --git a/Dockerfile.spa b/Dockerfile.spa index a4a003a..6ebc931 100644 --- a/Dockerfile.spa +++ b/Dockerfile.spa @@ -2,11 +2,21 @@ FROM oven/bun:1.3 AS builder WORKDIR /app -COPY package.json bunfig.toml ./ -COPY apps/web/package.json apps/web/ +# Copy workspace manifests + lockfile +COPY package.json bunfig.toml bun.lock ./ +COPY apps/web/package.json apps/web/package.json +COPY apps/api/package.json apps/api/package.json +COPY apps/worker/package.json apps/worker/package.json +COPY packages/db/package.json packages/db/package.json +COPY packages/shared/package.json packages/shared/package.json + +# Install dependencies (workspace-aware) RUN bun install --frozen-lockfile 2>/dev/null || bun install +# Copy full web app source COPY apps/web ./apps/web + +# Build the SPA RUN cd apps/web && bun run build # Serve stage diff --git a/docker-compose.yml b/docker-compose.yml index 2cf519a..d9d7c16 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,6 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD} volumes: - project-e-pg-data:/var/lib/postgresql/data - - ./drizzle/0000_first_mauler.sql:/docker-entrypoint-initdb.d/0000_first_mauler.sql:ro networks: - project-e-network healthcheck: