# Phase 8: REST API Layer **Status:** ✅ Complete **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 - [x] Task 1: Set up chi router with middleware (logging, recovery, CORS, JSON content-type) - [x] Task 2: Implement project endpoints — GET/POST /api/projects, GET/PUT/DELETE /api/projects/:id - [x] Task 3: Implement stage endpoints — POST /api/projects/:id/stages, PUT/DELETE /api/projects/:id/stages/:stage - [x] Task 4: Implement instance endpoints — GET /api/projects/:id/stages/:stage/instances, POST (deploy), DELETE (remove) - [x] Task 5: Implement instance control endpoints — POST .../instances/:iid/stop, start, restart - [x] Task 6: Implement quick deploy endpoints — POST /api/deploy/inspect, POST /api/deploy/quick - [x] Task 7: Implement registry endpoints — GET/POST /api/registries, PUT/DELETE /api/registries/:id, POST .../test - [x] Task 8: Implement settings endpoints — GET/PUT /api/settings, GET /api/settings/webhook-url, POST .../regenerate - [x] Task 9: Implement deploy history endpoints — GET /api/deploys, GET /api/deploys/:id/logs (SSE stub) - [x] Task 10: Implement registry tags endpoint — GET /api/registries/:id/tags/:image - [x] Task 11: Wire webhook handler into router — POST /api/webhook/:secret-uuid - [x] 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 - [x] All tasks completed - [x] All API endpoints from PLAN.md are covered - [x] Consistent response format across all endpoints - [x] Input validation on all POST/PUT handlers - [x] No business logic in handlers (delegates to services) ## Handoff to Next Phase ### API Surface - `api.NewServer(store, docker, deployer, webhookHandler, encKey)` creates the server - `server.Router()` returns a `chi.Router` with all routes mounted under `/api` - Response envelope: `{"success": bool, "data": ..., "error": "..."}` ### Endpoints Implemented | Method | Path | Handler | |--------|------|---------| | GET | /api/projects | listProjects | | POST | /api/projects | createProject | | GET | /api/projects/{id} | getProject (includes stages) | | PUT | /api/projects/{id} | updateProject | | DELETE | /api/projects/{id} | deleteProject | | POST | /api/projects/{id}/stages | createStage | | PUT | /api/projects/{id}/stages/{stage} | updateStage | | DELETE | /api/projects/{id}/stages/{stage} | deleteStage | | GET | /api/projects/{id}/stages/{stage}/instances | listInstances | | POST | /api/projects/{id}/stages/{stage}/instances | deployInstance | | DELETE | /api/projects/{id}/stages/{stage}/instances/{iid} | removeInstance | | POST | .../instances/{iid}/stop | stopInstance | | POST | .../instances/{iid}/start | startInstance | | POST | .../instances/{iid}/restart | restartInstance | | GET | /api/deploys | listDeploys | | GET | /api/deploys/{id}/logs | getDeployLogs (JSON stub) | | POST | /api/deploy/inspect | inspectImage | | POST | /api/deploy/quick | quickDeploy | | GET | /api/registries | listRegistries | | POST | /api/registries | createRegistry | | PUT | /api/registries/{id} | updateRegistry | | DELETE | /api/registries/{id} | deleteRegistry | | POST | /api/registries/{id}/test | testRegistry | | GET | /api/registries/{id}/tags/* | listRegistryTags | | GET | /api/settings | getSettings | | PUT | /api/settings | updateSettings | | GET | /api/settings/webhook-url | getWebhookURL | | POST | /api/settings/regenerate | regenerateWebhookSecret | | POST | /api/webhook/{secret} | webhook handler (mounted from webhook package) | ### main.go Wiring - All services initialized: store, docker, npm, deployer, health, notifier, webhook, poller - HTTP server with graceful shutdown on SIGTERM/SIGINT - Environment variables: `DATA_DIR`, `SEED_FILE`, `ENCRYPTION_KEY`, `NPM_URL`, `POLLING_INTERVAL`, `LISTEN_ADDR` - Default listen address: `:8080` ### SSE Stub - `GET /api/deploys/{id}/logs` returns logs as JSON array (not SSE yet) - Real SSE streaming deferred to Phase 11 ### Security Notes - Registry tokens are encrypted before storage, decrypted on read for API calls - Settings response strips `npm_password` and `webhook_secret`, returns `has_npm_password` boolean - Registry list response strips tokens, returns `has_token` boolean - CORS allows all origins (dev mode) -- restrict in Phase 12