chore: add feature planner setup for docker-watcher-core
Create structured plan files with 12 phases covering the full implementation: scaffold, store, crypto, Docker/NPM clients, registry poller, webhook, deployer, API layer, SvelteKit frontend, embedding, and hardening.
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
# Feature Context: Docker Watcher Core
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
- **Development mode:** Automated
|
||||||
|
- **Execution mode:** Orchestrator
|
||||||
|
- **Strategy:** Big Bang (with per-phase code quality reviews)
|
||||||
|
- **Build (Go):** `go build ./cmd/server/`
|
||||||
|
- **Test (Go):** `go test ./...`
|
||||||
|
- **Lint (Go):** `golangci-lint run`
|
||||||
|
- **Build (Frontend):** `cd web && npm run build`
|
||||||
|
- **Test (Frontend):** `cd web && npm test`
|
||||||
|
- **Dev server:** `go run ./cmd/server/`
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
Greenfield project. Only PLAN.md exists with the architecture document.
|
||||||
|
|
||||||
|
## Temporary Workarounds
|
||||||
|
None yet.
|
||||||
|
|
||||||
|
## Cross-Phase Dependencies
|
||||||
|
- Phase 2 depends on Phase 1 (store CRUD for seed import)
|
||||||
|
- Phases 3 and 4 are independent of each other (can run in parallel)
|
||||||
|
- Phase 5 depends on Phase 1 (store for poll state)
|
||||||
|
- Phase 6 depends on Phase 3 (Docker inspect for auto-creation) and Phase 1 (store)
|
||||||
|
- Phase 7 depends on Phases 3, 4, 5 (Docker, NPM, registry clients)
|
||||||
|
- Phase 8 depends on Phases 1-7 (wires everything to HTTP)
|
||||||
|
- Phases 9 and 10 are independent of each other (can run in parallel)
|
||||||
|
- Phase 11 depends on Phases 8, 9, 10 (embeds frontend, SSE wires to API)
|
||||||
|
- Phase 12 depends on all prior phases
|
||||||
|
|
||||||
|
## Deferred Work
|
||||||
|
None yet.
|
||||||
|
|
||||||
|
## Failed Approaches
|
||||||
|
None yet.
|
||||||
|
|
||||||
|
## Review Findings Log
|
||||||
|
None yet.
|
||||||
|
|
||||||
|
## Phase Execution Log
|
||||||
|
| Phase | Agent Used | Test Writer | Parallel | Notes |
|
||||||
|
|-------|-----------|-------------|----------|-------|
|
||||||
|
| — | — | — | — | No phases executed yet |
|
||||||
|
|
||||||
|
## Environment & Runtime Notes
|
||||||
|
- Platform: Windows 10 (development), Linux (deployment target)
|
||||||
|
- Docker socket: `/var/run/docker.sock` (Linux) — development may need Docker Desktop
|
||||||
|
- Go version: TBD (will be determined in Phase 1)
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
- Big Bang strategy: intermediate phases skip build/tests, code quality reviews after every phase
|
||||||
|
- Final phase (12) is the only phase where build + full test suite must pass
|
||||||
|
- Phases 3+4 and 9+10 identified for parallel execution
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Feature: Docker Watcher Core
|
||||||
|
|
||||||
|
**Branch:** `feature/docker-watcher-core`
|
||||||
|
**Base branch:** `main`
|
||||||
|
**Created:** 2026-03-27
|
||||||
|
**Status:** 🟡 In Progress
|
||||||
|
**Strategy:** Big Bang (with per-phase code quality reviews)
|
||||||
|
**Mode:** Automated
|
||||||
|
**Execution:** Orchestrator
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
A self-hosted tool that automates Docker container deployment with Nginx Proxy Manager integration. Detects new images from Gitea/GitHub registries, deploys containers, and configures reverse proxy routing — all from a web dashboard. Supports multiple simultaneous versions of the same project.
|
||||||
|
|
||||||
|
## Build & Test Commands
|
||||||
|
- **Build (Go):** `go build ./cmd/server/`
|
||||||
|
- **Test (Go):** `go test ./...`
|
||||||
|
- **Lint (Go):** `golangci-lint run`
|
||||||
|
- **Build (Frontend):** `cd web && npm run build`
|
||||||
|
- **Test (Frontend):** `cd web && npm test`
|
||||||
|
- **Dev server:** `go run ./cmd/server/`
|
||||||
|
|
||||||
|
## Phases
|
||||||
|
|
||||||
|
- [ ] Phase 1: Project Scaffold & SQLite Store [domain: backend] → [subplan](./phase-1-scaffold-store.md)
|
||||||
|
- [ ] Phase 2: Crypto & Config Seed Loader [domain: backend] → [subplan](./phase-2-crypto-config.md)
|
||||||
|
- [ ] Phase 3: Docker Client [domain: backend] → [subplan](./phase-3-docker-client.md)
|
||||||
|
- [ ] Phase 4: NPM Client [domain: backend] → [subplan](./phase-4-npm-client.md)
|
||||||
|
- [ ] Phase 5: Registry Client & Poller [domain: backend] → [subplan](./phase-5-registry-poller.md)
|
||||||
|
- [ ] Phase 6: Webhook Handler [domain: backend] → [subplan](./phase-6-webhook-handler.md)
|
||||||
|
- [ ] Phase 7: Deployer & Health Checker [domain: backend] → [subplan](./phase-7-deployer.md)
|
||||||
|
- [ ] Phase 8: REST API Layer [domain: backend] → [subplan](./phase-8-api-layer.md)
|
||||||
|
- [ ] Phase 9: SvelteKit Dashboard & Project Views [domain: frontend] → [subplan](./phase-9-dashboard.md)
|
||||||
|
- [ ] Phase 10: Quick Deploy & Settings Pages [domain: frontend] → [subplan](./phase-10-settings-deploy.md)
|
||||||
|
- [ ] Phase 11: Frontend Embed & Real-Time Updates [domain: fullstack] → [subplan](./phase-11-embed-sse.md)
|
||||||
|
- [ ] Phase 12: Hardening [domain: backend] → [subplan](./phase-12-hardening.md)
|
||||||
|
|
||||||
|
### Parallel Execution Notes
|
||||||
|
- Phases 3 and 4 are independent (Docker client vs NPM client) — can run in parallel
|
||||||
|
- Phases 9 and 10 are independent (dashboard vs settings pages) — can run in parallel
|
||||||
|
|
||||||
|
## Phase Progress Log
|
||||||
|
|
||||||
|
| Phase | Domain | Status | Review | Build | Committed |
|
||||||
|
|-------|--------|--------|--------|-------|-----------|
|
||||||
|
| Phase 1: Scaffold & Store | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 2: Crypto & Config | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 3: Docker Client | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 4: NPM Client | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 5: Registry & Poller | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 6: Webhook Handler | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 7: Deployer & Health | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 8: API Layer | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 9: Dashboard | frontend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 10: Settings & Deploy | frontend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 11: Embed & SSE | fullstack | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
|
||||||
|
| Phase 12: Hardening | backend | ⬜ Not Started | ⬜ | ✅ Required (Final) | ⬜ |
|
||||||
|
|
||||||
|
## Final Review
|
||||||
|
- [ ] Comprehensive code review
|
||||||
|
- [ ] Full build passes
|
||||||
|
- [ ] Full test suite passes
|
||||||
|
- [ ] Security review
|
||||||
|
- [ ] Merged to `main`
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Phase 1: Project Scaffold & SQLite Store
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Initialize the Go project, establish the directory structure, and implement the SQLite store with schema, migrations, and CRUD operations for all entities.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Initialize Go module (`go mod init`), create directory structure per PLAN.md
|
||||||
|
- [ ] Task 2: Add core dependencies to go.mod (sqlite, chi, yaml, uuid, cron)
|
||||||
|
- [ ] Task 3: Define SQLite schema — tables for projects, stages, registries, settings, instances, deploys, deploy_logs
|
||||||
|
- [ ] Task 4: Implement store initialization with auto-migration (create tables if not exist)
|
||||||
|
- [ ] Task 5: Implement projects CRUD (Create, GetByID, GetAll, Update, Delete)
|
||||||
|
- [ ] Task 6: Implement stages CRUD (Create, GetByProjectID, Update, Delete)
|
||||||
|
- [ ] Task 7: Implement registries CRUD (Create, GetByID, GetAll, Update, Delete)
|
||||||
|
- [ ] Task 8: Implement settings Get/Update (single-row config pattern)
|
||||||
|
- [ ] Task 9: Implement instances CRUD (Create, GetByStageID, GetByID, Update, Delete, UpdateStatus)
|
||||||
|
- [ ] Task 10: Implement deploys CRUD (Create, GetByProjectID, GetRecent, GetByID) + deploy_logs append
|
||||||
|
- [ ] Task 11: Create `cmd/server/main.go` entry point (minimal — just opens DB, defers close)
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `go.mod` — module definition and dependencies
|
||||||
|
- `go.sum` — dependency checksums
|
||||||
|
- `cmd/server/main.go` — entry point
|
||||||
|
- `internal/store/store.go` — DB connection, schema, migrations
|
||||||
|
- `internal/store/projects.go` — project queries
|
||||||
|
- `internal/store/stages.go` — stage queries
|
||||||
|
- `internal/store/registries.go` — registry queries
|
||||||
|
- `internal/store/settings.go` — settings queries
|
||||||
|
- `internal/store/instances.go` — instance queries
|
||||||
|
- `internal/store/deploys.go` — deploy history queries
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- `go mod tidy` succeeds
|
||||||
|
- All store CRUD functions are implemented with proper error handling
|
||||||
|
- Schema covers all entities from the architecture plan
|
||||||
|
- Entry point compiles (may not fully run until later phases wire everything)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Use `modernc.org/sqlite` for CGo-free SQLite
|
||||||
|
- Use `go-chi/chi/v5` for routing (will be wired in Phase 8)
|
||||||
|
- Settings table uses a single-row pattern (one row, upsert on update)
|
||||||
|
- Instance status should be an enum-like string: "running", "stopped", "failed", "removing"
|
||||||
|
- Deploy status: "pending", "pulling", "starting", "configuring_proxy", "health_checking", "success", "failed", "rolled_back"
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Code follows Go conventions (gofmt, proper error returns)
|
||||||
|
- [ ] No unintended side effects
|
||||||
|
- [ ] Schema is normalized and covers all planned entities
|
||||||
|
- [ ] CRUD functions handle not-found cases properly
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# Phase 10: Quick Deploy & Settings Pages
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** frontend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Build the Quick Deploy page (paste image, auto-inspect, one-click deploy) and all Settings pages (registries, credentials, global settings, webhook URL).
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Quick Deploy page (`routes/deploy/+page.svelte`) — image URL input, inspect button
|
||||||
|
- [ ] Task 2: Quick Deploy inspect flow — call /api/deploy/inspect, display auto-filled form (project name, port, stage, subdomain)
|
||||||
|
- [ ] Task 3: Quick Deploy submit — user reviews defaults, clicks Deploy, calls /api/deploy/quick
|
||||||
|
- [ ] Task 4: Settings layout (`routes/settings/+layout.svelte`) — sub-navigation for settings sections
|
||||||
|
- [ ] Task 5: Global settings page (`routes/settings/+page.svelte`) — domain, server IP, network, subdomain pattern, polling interval
|
||||||
|
- [ ] Task 6: Registries page (`routes/settings/registries/+page.svelte`) — list, add, edit, delete, test connection
|
||||||
|
- [ ] Task 7: Credentials page (`routes/settings/credentials/+page.svelte`) — NPM credentials, registry tokens (masked display)
|
||||||
|
- [ ] Task 8: Webhook URL display and regenerate button in settings
|
||||||
|
- [ ] Task 9: Projects config page (`routes/projects/config/+page.svelte` or integrated into project detail) — add/edit/delete projects, configure stages
|
||||||
|
- [ ] Task 10: Stage configuration form — tag patterns, auto_deploy toggle, max_instances, subdomain override
|
||||||
|
- [ ] Task 11: Form validation on all input pages — required fields, URL format, port range
|
||||||
|
- [ ] Task 12: Success/error toast notifications for all form submissions
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `web/src/routes/deploy/+page.svelte` — quick deploy
|
||||||
|
- `web/src/routes/settings/+layout.svelte` — settings layout
|
||||||
|
- `web/src/routes/settings/+page.svelte` — global settings
|
||||||
|
- `web/src/routes/settings/registries/+page.svelte` — registry management
|
||||||
|
- `web/src/routes/settings/credentials/+page.svelte` — credential management
|
||||||
|
- `web/src/lib/components/Toast.svelte` — toast notifications
|
||||||
|
- `web/src/lib/components/FormField.svelte` — reusable form field with validation
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Quick Deploy: paste image URL → inspect → review defaults → deploy works end-to-end
|
||||||
|
- All settings are editable and saved via API
|
||||||
|
- Registry test connection shows success/failure
|
||||||
|
- Credentials are masked in display (`••••••••`)
|
||||||
|
- Webhook URL is shown with copy button and regenerate option
|
||||||
|
- Form validation prevents bad submissions
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Quick Deploy is the zero-config entry point — should be dead simple UX
|
||||||
|
- Credential fields: show mask, edit replaces entirely (no partial edit)
|
||||||
|
- Registry test: calls POST /api/registries/:id/test, shows connection result
|
||||||
|
- Toast component: appears top-right, auto-dismiss after 5s, color-coded (green/red)
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Quick deploy flow is intuitive (minimal clicks)
|
||||||
|
- [ ] Credentials never shown in plaintext in UI
|
||||||
|
- [ ] Form validation covers required fields and formats
|
||||||
|
- [ ] Error states are handled with user-friendly messages
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Phase 11: Frontend Embed & Real-Time Updates
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** fullstack
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Build SvelteKit to static files, embed into the Go binary with `go:embed`, serve from Go, and implement SSE for real-time deploy progress and instance status updates.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Configure SvelteKit static adapter to output to `web/build/`
|
||||||
|
- [ ] Task 2: Add `//go:embed web/build` directive in Go — serve static files
|
||||||
|
- [ ] Task 3: Create Go handler for serving embedded SPA — serve index.html for all non-API routes (SPA fallback)
|
||||||
|
- [ ] Task 4: Implement SSE endpoint for deploy logs — `GET /api/deploys/:id/logs` streams deploy progress in real-time
|
||||||
|
- [ ] Task 5: Implement SSE endpoint for instance status — `GET /api/events` streams instance status changes
|
||||||
|
- [ ] Task 6: Create event bus/broadcaster in Go — publish events from deployer, subscribe from SSE handlers
|
||||||
|
- [ ] Task 7: Frontend: connect to SSE for deploy progress — update deploy log view in real-time
|
||||||
|
- [ ] Task 8: Frontend: connect to SSE for instance status — update dashboard/project views without refresh
|
||||||
|
- [ ] Task 9: Handle SSE reconnection in frontend — auto-reconnect with backoff on disconnect
|
||||||
|
- [ ] Task 10: Build script/Makefile — `make build` builds frontend then Go binary
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `web/svelte.config.js` — ensure static adapter outputs to `web/build/`
|
||||||
|
- `internal/api/static.go` — embedded static file server with SPA fallback
|
||||||
|
- `internal/api/sse.go` — SSE endpoints for deploy logs and instance events
|
||||||
|
- `internal/events/bus.go` — event bus for publishing/subscribing to events
|
||||||
|
- `web/src/lib/sse.ts` — SSE client helper with auto-reconnect
|
||||||
|
- `web/src/routes/+layout.svelte` — wire up global SSE connection for instance status
|
||||||
|
- `Makefile` — build frontend + backend
|
||||||
|
- `cmd/server/main.go` — wire embedded static serving and event bus
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- `make build` produces a single Go binary with embedded frontend
|
||||||
|
- Go binary serves the SvelteKit SPA on all non-API routes
|
||||||
|
- Deploy progress streams in real-time via SSE
|
||||||
|
- Instance status updates appear without page refresh
|
||||||
|
- SSE reconnects automatically after network hiccups
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- `go:embed` requires the embedded directory to be relative to the Go source file
|
||||||
|
- SPA fallback: any request that doesn't match `/api/*` gets `index.html`
|
||||||
|
- Event bus: simple pub/sub with channels — no external dependency needed
|
||||||
|
- SSE format: `data: {"type": "deploy_log", "payload": {...}}\n\n`
|
||||||
|
- Keep SSE connections lightweight — use context cancellation for cleanup
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Single binary serves both API and frontend
|
||||||
|
- [ ] SSE handles multiple concurrent clients
|
||||||
|
- [ ] No goroutine leaks on SSE disconnect
|
||||||
|
- [ ] Build process is reproducible (Makefile)
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Phase 12: Hardening
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Production hardening — blue-green deploys, promote flow, dashboard auth, graceful shutdown, structured logging, and config export.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Blue-green deploys — start new container, health check, swap NPM proxy, then stop old container (zero downtime)
|
||||||
|
- [ ] Task 2: Promote flow — enforce `promote_from` for production deploys (only tags running in source stage are eligible)
|
||||||
|
- [ ] Task 3: Dashboard auth — basic auth or token-based authentication for the web UI
|
||||||
|
- [ ] Task 4: Auth middleware — protect all /api/* routes except webhook
|
||||||
|
- [ ] Task 5: Graceful shutdown — handle SIGTERM/SIGINT, drain in-progress deploys, close DB, stop poller
|
||||||
|
- [ ] Task 6: Structured logging — JSON logs with deploy context (project, stage, tag, instance ID)
|
||||||
|
- [ ] Task 7: Config export — download current SQLite state as YAML (reverse of seed import)
|
||||||
|
- [ ] Task 8: Dockerfile — multi-stage build (build frontend + Go, copy to minimal image)
|
||||||
|
- [ ] Task 9: docker-compose.yml — production-ready compose file with volumes, network, env
|
||||||
|
- [ ] Task 10: Final wiring review — ensure all services are properly initialized and shut down
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/deployer/bluegreen.go` — blue-green deploy strategy
|
||||||
|
- `internal/deployer/promote.go` — promote flow logic
|
||||||
|
- `internal/api/auth.go` — authentication middleware
|
||||||
|
- `internal/config/export.go` — config export to YAML
|
||||||
|
- `internal/logging/logger.go` — structured JSON logger
|
||||||
|
- `cmd/server/main.go` — graceful shutdown, structured logging init
|
||||||
|
- `Dockerfile` — multi-stage build
|
||||||
|
- `docker-compose.yml` — production compose file
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Blue-green: zero downtime during deploy (old container serves until new one is healthy)
|
||||||
|
- Promote: production deploy only accepts tags from the specified source stage
|
||||||
|
- Auth: unauthenticated requests to /api/* (except webhook) return 401
|
||||||
|
- Graceful shutdown: in-progress deploys complete before exit
|
||||||
|
- Logs are JSON-formatted with contextual fields
|
||||||
|
- Config export produces valid YAML that could be re-imported
|
||||||
|
- Docker image builds and runs correctly
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Blue-green: keep old container running until new one passes health check, then swap NPM proxy and stop old
|
||||||
|
- Auth: start simple (basic auth via env var), can be enhanced later (JWT, OIDC)
|
||||||
|
- SIGTERM handling: use Go's `os/signal` + `context.WithCancel`
|
||||||
|
- Structured logging: use `log/slog` (Go stdlib since 1.21)
|
||||||
|
- Dockerfile: build stage with Node.js + Go, runtime stage with scratch/alpine
|
||||||
|
- This is the FINAL phase — build and full test suite MUST pass here
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Blue-green deploy handles rollback if new container fails
|
||||||
|
- [ ] Auth doesn't block webhook endpoint
|
||||||
|
- [ ] Graceful shutdown tested with concurrent deploys
|
||||||
|
- [ ] Dockerfile produces a minimal image
|
||||||
|
- [ ] docker-compose.yml matches the example in PLAN.md
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- This is the final phase — no handoff needed. -->
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Phase 2: Crypto & Config Seed Loader
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Implement AES-256 encryption for credential storage and the YAML seed config parser that imports into SQLite on first launch.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Implement AES-256-GCM encrypt/decrypt functions using Go stdlib `crypto/aes` + `crypto/cipher`
|
||||||
|
- [ ] Task 2: Key derivation from ENCRYPTION_KEY env var (SHA-256 hash to get 32 bytes)
|
||||||
|
- [ ] Task 3: Define YAML config structs matching the seed format from PLAN.md
|
||||||
|
- [ ] Task 4: Implement YAML parser — read and validate seed file
|
||||||
|
- [ ] Task 5: Implement seed importer — checks if DB is empty, if so imports YAML into SQLite via store CRUD
|
||||||
|
- [ ] Task 6: Encrypt credential fields (registry tokens, NPM password) during import
|
||||||
|
- [ ] Task 7: Create `docker-watcher.example.yaml` with documented example config
|
||||||
|
- [ ] Task 8: Wire seed import into `cmd/server/main.go` startup sequence
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/crypto/crypto.go` — AES-256-GCM encrypt/decrypt
|
||||||
|
- `internal/config/config.go` — YAML structs and parser
|
||||||
|
- `internal/config/seed.go` — seed import logic (YAML → SQLite)
|
||||||
|
- `docker-watcher.example.yaml` — example seed config
|
||||||
|
- `cmd/server/main.go` — add seed import to startup
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Encrypt then decrypt round-trips correctly
|
||||||
|
- Different plaintexts produce different ciphertexts (random nonce)
|
||||||
|
- YAML parsing handles all fields from the seed format
|
||||||
|
- Seed import creates projects, stages, registries, and settings in SQLite
|
||||||
|
- Credentials are encrypted before storage
|
||||||
|
- Import is idempotent — skipped if DB already has data
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- ENCRYPTION_KEY is the only secret env var — everything else is encrypted in SQLite
|
||||||
|
- Use GCM mode for authenticated encryption (integrity + confidentiality)
|
||||||
|
- Seed import should be transactional — all or nothing
|
||||||
|
- The example YAML should have placeholder values, not real credentials
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Crypto uses secure practices (random nonce, GCM, no ECB)
|
||||||
|
- [ ] No hardcoded keys or secrets
|
||||||
|
- [ ] YAML parsing validates required fields
|
||||||
|
- [ ] Import is transactional
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Phase 3: Docker Client
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Implement the Docker Engine API wrapper for container lifecycle management — pull images, inspect, create/start/stop/remove containers, and manage networks.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Create Docker client wrapper with socket connection (`/var/run/docker.sock`)
|
||||||
|
- [ ] Task 2: Implement `PullImage(ctx, image, tag, authConfig)` — pull with optional registry auth
|
||||||
|
- [ ] Task 3: Implement `InspectImage(ctx, image)` — extract EXPOSE ports, HEALTHCHECK, labels
|
||||||
|
- [ ] Task 4: Implement `CreateContainer(ctx, config)` — create with name, image, env, ports, network, labels
|
||||||
|
- [ ] Task 5: Implement `StartContainer(ctx, containerID)`, `StopContainer(ctx, containerID, timeout)`, `RemoveContainer(ctx, containerID, force)`
|
||||||
|
- [ ] Task 6: Implement `RestartContainer(ctx, containerID, timeout)`
|
||||||
|
- [ ] Task 7: Implement `ListContainers(ctx, filters)` — filter by labels to find managed containers
|
||||||
|
- [ ] Task 8: Implement `EnsureNetwork(ctx, networkName)` — create network if not exists
|
||||||
|
- [ ] Task 9: Implement `ConnectNetwork(ctx, networkID, containerID)` — attach container to network
|
||||||
|
- [ ] Task 10: Add docker-watcher labels to all managed containers (`docker-watcher.project`, `docker-watcher.stage`, `docker-watcher.instance-id`)
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/docker/client.go` — Docker client wrapper, connection setup
|
||||||
|
- `internal/docker/container.go` — container lifecycle operations
|
||||||
|
- `internal/docker/image.go` — pull and inspect operations
|
||||||
|
- `internal/docker/network.go` — network management
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Client connects to Docker socket
|
||||||
|
- Pull handles both public and authenticated registries
|
||||||
|
- Image inspection extracts port, healthcheck, and label metadata
|
||||||
|
- Container creation applies all config (env, ports, network, labels)
|
||||||
|
- All operations return meaningful errors
|
||||||
|
- Managed containers are identifiable via labels
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Use `github.com/docker/docker/client` SDK
|
||||||
|
- Container names should be deterministic: `dw-{project}-{stage}-{tag-sanitized}`
|
||||||
|
- All containers should be on the shared network (e.g., `staging-net`)
|
||||||
|
- Port mapping: container's EXPOSE port → random host port (Docker auto-assigns)
|
||||||
|
- Auth config for private registries will come from the store (encrypted tokens)
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Proper context propagation for cancellation
|
||||||
|
- [ ] Resource cleanup (close client, remove failed containers)
|
||||||
|
- [ ] No hardcoded values
|
||||||
|
- [ ] Error messages include container/image identifiers
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Phase 4: NPM Client
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Implement the Nginx Proxy Manager API client — JWT authentication, CRUD for proxy hosts, and host lookup.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Create NPM client struct with base URL, cached JWT token, and auto-refresh
|
||||||
|
- [ ] Task 2: Implement `Authenticate(ctx, email, password)` — POST /api/tokens, store JWT
|
||||||
|
- [ ] Task 3: Implement `CreateProxyHost(ctx, config)` — POST /api/nginx/proxy-hosts
|
||||||
|
- [ ] Task 4: Implement `UpdateProxyHost(ctx, id, config)` — PUT /api/nginx/proxy-hosts/{id}
|
||||||
|
- [ ] Task 5: Implement `DeleteProxyHost(ctx, id)` — DELETE /api/nginx/proxy-hosts/{id}
|
||||||
|
- [ ] Task 6: Implement `ListProxyHosts(ctx)` — GET /api/nginx/proxy-hosts
|
||||||
|
- [ ] Task 7: Implement `FindProxyHostByDomain(ctx, domain)` — search existing hosts by domain name
|
||||||
|
- [ ] Task 8: Define proxy host config struct (domain, forward host/port, SSL settings, etc.)
|
||||||
|
- [ ] Task 9: Handle JWT token expiry — re-authenticate automatically on 401
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/npm/client.go` — NPM API client, auth, HTTP helpers
|
||||||
|
- `internal/npm/types.go` — request/response types for proxy hosts
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Client authenticates and caches JWT
|
||||||
|
- CRUD operations work for proxy hosts
|
||||||
|
- Token refresh happens transparently on expiry
|
||||||
|
- Proxy host config supports: domain, forward host, forward port, SSL (Let's Encrypt optional)
|
||||||
|
- FindByDomain enables checking if a proxy already exists before creating
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- NPM API base: typically `http://npm:81/api`
|
||||||
|
- Forward host for containers: use container name on the shared Docker network
|
||||||
|
- Forward port: the container's internal port (from EXPOSE)
|
||||||
|
- SSL: for staging, can be disabled; production may want Let's Encrypt
|
||||||
|
- NPM credentials come from settings (encrypted in SQLite, decrypted at runtime)
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] JWT caching and refresh work correctly
|
||||||
|
- [ ] HTTP errors are properly handled (not just status code, but response body)
|
||||||
|
- [ ] No credentials logged or leaked in errors
|
||||||
|
- [ ] Struct types match NPM API contract
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Phase 5: Registry Client & Poller
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Implement the registry client interface with Gitea implementation, and the periodic tag polling scheduler.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Define `Registry` interface — `ListTags(ctx, image)`, `GetLatestTag(ctx, image, pattern)`
|
||||||
|
- [ ] Task 2: Implement Gitea registry client — uses Gitea API to list container image tags
|
||||||
|
- [ ] Task 3: Implement tag pattern matching — match tags against glob patterns (e.g., `dev-*`, `v*`)
|
||||||
|
- [ ] Task 4: Implement tag comparison — detect new tags since last poll (store last-seen tag per project/stage)
|
||||||
|
- [ ] Task 5: Create poller service — periodic scheduler using `robfig/cron`
|
||||||
|
- [ ] Task 6: Poller logic — for each project/stage with polling enabled, check for new tags, trigger deploy if auto_deploy
|
||||||
|
- [ ] Task 7: Add `last_polled_tag` field to instances or a new `poll_state` table in store
|
||||||
|
- [ ] Task 8: Implement registry factory — create client based on registry type (gitea, future: github, dockerhub)
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/registry/registry.go` — interface definition + factory
|
||||||
|
- `internal/registry/gitea.go` — Gitea registry implementation
|
||||||
|
- `internal/registry/poller.go` — polling scheduler service
|
||||||
|
- `internal/store/poll_state.go` — poll state persistence (optional, or extend existing tables)
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Gitea client can list tags for a given image
|
||||||
|
- Tag pattern matching correctly filters tags (glob-style)
|
||||||
|
- Poller runs on configurable interval
|
||||||
|
- New tags are detected by comparing against stored state
|
||||||
|
- Registry factory returns correct client based on type
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Gitea API: `GET /api/v1/packages/{owner}/container/{image}/tags` (or similar, verify against Gitea docs)
|
||||||
|
- Auth: Bearer token from registry config
|
||||||
|
- Polling interval comes from global settings
|
||||||
|
- The poller is a fallback — webhooks are the primary detection mechanism (Phase 6)
|
||||||
|
- GitHub Container Registry support is future work — just define the interface now
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Interface is clean and minimal
|
||||||
|
- [ ] Pattern matching handles edge cases (empty pattern, no tags)
|
||||||
|
- [ ] Poller doesn't leak goroutines
|
||||||
|
- [ ] Registry auth tokens handled securely
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Phase 6: Webhook Handler
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Implement the secret UUID-based webhook endpoint that receives image push notifications from CI systems, with auto-creation of unknown projects.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Implement webhook HTTP handler — `POST /api/webhook/:secret-uuid`
|
||||||
|
- [ ] Task 2: Validate incoming payload — extract image name and tag
|
||||||
|
- [ ] Task 3: Look up project by image name in store — match against configured project images
|
||||||
|
- [ ] Task 4: If known project: match tag to stage via tag patterns, determine if auto_deploy
|
||||||
|
- [ ] Task 5: If unknown project: auto-create project with defaults from image inspection (EXPOSE port, labels)
|
||||||
|
- [ ] Task 6: Generate and store webhook secret UUID in settings (on first launch)
|
||||||
|
- [ ] Task 7: Implement webhook URL regeneration (new UUID, invalidates old one)
|
||||||
|
- [ ] Task 8: Define webhook payload struct (`{"image": "registry/org/app:tag"}`)
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/webhook/handler.go` — webhook HTTP handler + payload parsing
|
||||||
|
- `internal/webhook/matcher.go` — project/stage matching logic
|
||||||
|
- `internal/webhook/autocreate.go` — auto-create project from unknown image
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Valid webhook URL with correct UUID triggers processing
|
||||||
|
- Invalid/missing UUID returns 404 (no information leak)
|
||||||
|
- Known images are matched to projects and stages
|
||||||
|
- Unknown images trigger auto-creation with sensible defaults
|
||||||
|
- Webhook URL can be regenerated
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Webhook URL format: `POST /api/webhook/d8f2a1e9-...`
|
||||||
|
- No authentication needed beyond the secret UUID
|
||||||
|
- Auto-created projects use: image EXPOSE port, "dev" as default stage, auto_deploy: true
|
||||||
|
- The webhook handler calls into the deployer (Phase 7) — for now, define the interface/callback
|
||||||
|
- Keep the handler thin — it matches and delegates
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] No information leak on invalid UUIDs
|
||||||
|
- [ ] Payload validation rejects malformed input
|
||||||
|
- [ ] Auto-creation uses safe defaults
|
||||||
|
- [ ] Handler is stateless (delegates to store/deployer)
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Phase 7: Deployer & Health Checker
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Implement the core deployment orchestrator: pull → start container → configure NPM proxy → health check → success/rollback. Plus multi-instance support and notifications.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Define deployer service struct — depends on Docker client, NPM client, store, notifier
|
||||||
|
- [ ] Task 2: Implement deploy flow: pull image → create container → start → connect to network → configure proxy → health check
|
||||||
|
- [ ] Task 3: Implement subdomain generation per convention: `stage-{stage}-{project}` for default, `stage-{stage}-{project}-{tag}` for specific
|
||||||
|
- [ ] Task 4: Sanitize tags for DNS (dots → dashes, lowercase, truncate)
|
||||||
|
- [ ] Task 5: Implement health checker — HTTP GET to `http://container:{port}{healthcheck_path}` with retries and timeout
|
||||||
|
- [ ] Task 6: Implement rollback on health check failure — remove new container, delete NPM proxy host, update instance status
|
||||||
|
- [ ] Task 7: Implement multi-instance support — multiple tags of same project/stage can run simultaneously
|
||||||
|
- [ ] Task 8: Implement max_instances enforcement — remove oldest instance when limit reached
|
||||||
|
- [ ] Task 9: Implement notification webhook — POST to configured URL on deploy success/failure
|
||||||
|
- [ ] Task 10: Create deploy history records in store (status, timestamps, logs)
|
||||||
|
- [ ] Task 11: Implement deploy log streaming — append log entries during deploy for real-time visibility
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/deployer/deployer.go` — main deploy orchestrator
|
||||||
|
- `internal/deployer/subdomain.go` — subdomain generation and DNS sanitization
|
||||||
|
- `internal/deployer/rollback.go` — rollback logic
|
||||||
|
- `internal/health/checker.go` — HTTP health checker with retries
|
||||||
|
- `internal/notify/notifier.go` — webhook notification sender
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- Full deploy flow works end-to-end (pull → proxy → health check)
|
||||||
|
- Failed health checks trigger automatic rollback
|
||||||
|
- Multi-instance: deploying a new tag doesn't stop existing instances
|
||||||
|
- max_instances removes oldest instance when exceeded
|
||||||
|
- Notifications fire on success and failure
|
||||||
|
- Deploy history is recorded with status and timestamps
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Health check: 3 retries, 5s between retries, 10s timeout per attempt (configurable later)
|
||||||
|
- Subdomain pattern comes from global settings
|
||||||
|
- Notifications are fire-and-forget (don't block deploy on notification failure)
|
||||||
|
- Deploy logs should be structured entries (timestamp + message) for SSE streaming later
|
||||||
|
- The deployer is the central orchestrator — webhook handler and poller both call into it
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] Rollback cleans up ALL resources (container, proxy, instance record)
|
||||||
|
- [ ] No goroutine leaks
|
||||||
|
- [ ] Error handling at every step of the deploy flow
|
||||||
|
- [ ] Subdomain generation produces valid DNS names
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Phase 8: REST API Layer
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** backend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Wire up all REST API endpoints using chi router, connecting the store, deployer, and other services to HTTP handlers.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Set up chi router with middleware (logging, recovery, CORS, JSON content-type)
|
||||||
|
- [ ] Task 2: Implement project endpoints — GET/POST /api/projects, GET/PUT/DELETE /api/projects/:id
|
||||||
|
- [ ] Task 3: Implement stage endpoints — POST /api/projects/:id/stages, PUT/DELETE /api/projects/:id/stages/:stage
|
||||||
|
- [ ] Task 4: Implement instance endpoints — GET /api/projects/:id/stages/:stage/instances, POST (deploy), DELETE (remove)
|
||||||
|
- [ ] Task 5: Implement instance control endpoints — POST .../instances/:iid/stop, start, restart
|
||||||
|
- [ ] Task 6: Implement quick deploy endpoints — POST /api/deploy/inspect, POST /api/deploy/quick
|
||||||
|
- [ ] Task 7: Implement registry endpoints — GET/POST /api/registries, PUT/DELETE /api/registries/:id, POST .../test
|
||||||
|
- [ ] Task 8: Implement settings endpoints — GET/PUT /api/settings, GET /api/settings/webhook-url, POST .../regenerate
|
||||||
|
- [ ] Task 9: Implement deploy history endpoints — GET /api/deploys, GET /api/deploys/:id/logs (SSE stub)
|
||||||
|
- [ ] Task 10: Implement registry tags endpoint — GET /api/registries/:id/tags/:image
|
||||||
|
- [ ] Task 11: Wire webhook handler into router — POST /api/webhook/:secret-uuid
|
||||||
|
- [ ] Task 12: Wire everything in main.go — initialize all services, start HTTP server
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `internal/api/router.go` — chi router setup, middleware
|
||||||
|
- `internal/api/projects.go` — project CRUD handlers
|
||||||
|
- `internal/api/stages.go` — stage CRUD handlers
|
||||||
|
- `internal/api/instances.go` — instance lifecycle handlers
|
||||||
|
- `internal/api/deploys.go` — deploy + quick deploy handlers
|
||||||
|
- `internal/api/registries.go` — registry CRUD + test + tags handlers
|
||||||
|
- `internal/api/settings.go` — settings handlers
|
||||||
|
- `internal/api/middleware.go` — middleware (logging, CORS, recovery)
|
||||||
|
- `internal/api/response.go` — consistent API response helpers (envelope format)
|
||||||
|
- `cmd/server/main.go` — full service wiring and HTTP server start
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- All endpoints from the API spec in PLAN.md are implemented
|
||||||
|
- Consistent JSON envelope response format (success, data, error, metadata)
|
||||||
|
- CORS configured for frontend dev (localhost origins)
|
||||||
|
- Proper HTTP status codes (200, 201, 400, 404, 500)
|
||||||
|
- main.go starts a fully wired HTTP server
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Response envelope: `{"success": bool, "data": any, "error": string|null, "meta": {pagination}}`
|
||||||
|
- CORS: allow all origins in dev, restrict in production (configurable later)
|
||||||
|
- SSE for deploy logs is a stub in this phase — real implementation in Phase 11
|
||||||
|
- Quick deploy: /inspect pulls and inspects image, returns defaults; /quick creates project + deploys
|
||||||
|
- All handlers should validate input and return 400 for bad requests
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] All API endpoints from PLAN.md are covered
|
||||||
|
- [ ] Consistent response format across all endpoints
|
||||||
|
- [ ] Input validation on all POST/PUT handlers
|
||||||
|
- [ ] No business logic in handlers (delegates to services)
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Phase 9: SvelteKit Dashboard & Project Views
|
||||||
|
|
||||||
|
**Status:** ⬜ Not Started
|
||||||
|
**Parent plan:** [PLAN.md](./PLAN.md)
|
||||||
|
**Domain:** frontend
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
Build the SvelteKit frontend with the dashboard overview and project detail views — project list, instance status, controls, and deploy history.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
- [ ] Task 1: Initialize SvelteKit project in `web/` directory with TypeScript, static adapter
|
||||||
|
- [ ] Task 2: Set up Tailwind CSS (or project's preferred styling)
|
||||||
|
- [ ] Task 3: Create shared API client (`lib/api.ts`) — typed fetch wrapper for all backend endpoints
|
||||||
|
- [ ] Task 4: Define TypeScript types (`lib/types.ts`) — Project, Stage, Instance, Deploy, Registry, Settings
|
||||||
|
- [ ] Task 5: Create layout with navigation — sidebar or top nav with Dashboard, Projects, Deploy, Settings links
|
||||||
|
- [ ] Task 6: Dashboard page (`routes/+page.svelte`) — project overview cards with instance counts, status indicators, latest activity
|
||||||
|
- [ ] Task 7: Projects list page (`routes/projects/+page.svelte`) — all projects with quick stats, "Add Project" button
|
||||||
|
- [ ] Task 8: Project detail page (`routes/projects/[id]/+page.svelte`) — stages, instances per stage, controls
|
||||||
|
- [ ] Task 9: Instance controls — Stop, Start, Restart, Remove buttons with confirmation dialogs
|
||||||
|
- [ ] Task 10: Deploy history section in project detail — recent deploys with status, timestamp, tag
|
||||||
|
- [ ] Task 11: "Deploy new version" dropdown — list available tags from registry, trigger deploy
|
||||||
|
- [ ] Task 12: Create reusable components: StatusBadge, InstanceCard, ProjectCard, ConfirmDialog
|
||||||
|
|
||||||
|
## Files to Modify/Create
|
||||||
|
- `web/package.json` — SvelteKit project config
|
||||||
|
- `web/svelte.config.js` — SvelteKit config with static adapter
|
||||||
|
- `web/vite.config.ts` — Vite config with API proxy for dev
|
||||||
|
- `web/src/app.html` — base HTML
|
||||||
|
- `web/src/lib/api.ts` — API client
|
||||||
|
- `web/src/lib/types.ts` — shared TypeScript types
|
||||||
|
- `web/src/routes/+layout.svelte` — app layout with navigation
|
||||||
|
- `web/src/routes/+page.svelte` — dashboard
|
||||||
|
- `web/src/routes/projects/+page.svelte` — project list
|
||||||
|
- `web/src/routes/projects/[id]/+page.svelte` — project detail
|
||||||
|
- `web/src/lib/components/StatusBadge.svelte` — status indicator
|
||||||
|
- `web/src/lib/components/InstanceCard.svelte` — instance display
|
||||||
|
- `web/src/lib/components/ProjectCard.svelte` — project summary card
|
||||||
|
- `web/src/lib/components/ConfirmDialog.svelte` — confirmation modal
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
- SvelteKit project builds to static output
|
||||||
|
- Dashboard shows all projects with live status
|
||||||
|
- Project detail shows stages, instances, and controls
|
||||||
|
- Instance controls trigger correct API calls
|
||||||
|
- Deploy dropdown fetches and displays available tags
|
||||||
|
- UI is responsive and clean
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- SvelteKit static adapter for embedding in Go binary
|
||||||
|
- API proxy in vite.config.ts for dev: proxy `/api` to `http://localhost:8080`
|
||||||
|
- Use SvelteKit's `fetch` for SSR-compatible data loading
|
||||||
|
- Status colors: green=running, yellow=starting, red=failed, gray=stopped
|
||||||
|
- Keep components small and reusable
|
||||||
|
|
||||||
|
## Review Checklist
|
||||||
|
- [ ] All tasks completed
|
||||||
|
- [ ] TypeScript types match backend API response format
|
||||||
|
- [ ] API client handles errors gracefully with user feedback
|
||||||
|
- [ ] No hardcoded API URLs (use relative paths)
|
||||||
|
- [ ] Components are reusable and well-structured
|
||||||
|
|
||||||
|
## Handoff to Next Phase
|
||||||
|
<!-- Filled in by the implementation agent after completing this phase. -->
|
||||||
Reference in New Issue
Block a user