feat(docker-watcher): phase 2 - crypto & config seed loader

AES-256-GCM encryption for credential storage, YAML seed config
parser with validation, and transactional import into SQLite.
Credentials (registry tokens, NPM password) encrypted before storage.
This commit is contained in:
2026-03-27 21:01:16 +03:00
parent d63c831d15
commit cdf21682d6
10 changed files with 602 additions and 19 deletions
+24 -3
View File
@@ -13,6 +13,7 @@
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`
@@ -34,16 +35,18 @@ A self-hosted tool that automates Docker container deployment with Nginx Proxy M
- [ ] 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)
- [ ] Phase 13: Frontend Polish & Modern UI [domain: frontend] → [subplan](./phase-13-ui-polish.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 | ✅ Complete | | ⏭️ Skip (Big Bang) | |
| ----- | ------ | ------ | ------ | ----- | --------- |
| Phase 1: Scaffold & Store | backend | ✅ Complete | ✅ Pass w/ fixes | ⏭️ 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) | ⬜ |
@@ -54,9 +57,27 @@ A self-hosted tool that automates Docker container deployment with Nginx Proxy M
| 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) | ⬜ |
| Phase 12: Hardening | backend | ⬜ Not Started | ⬜ | ⏭️ Skip (Big Bang) | ⬜ |
| Phase 13: UI Polish | frontend | ⬜ Not Started | ⬜ | ✅ Required (Final) | ⬜ |
## Amendment Log
### Amendment 1 — 2026-03-27
**Type:** Added phase
**What changed:** Added Phase 13: Frontend Polish & Modern UI after Phase 12
**Why:** User wants modern look & feel with SVG icons and polished frontend
**Impact on existing phases:** None — Phase 13 runs after all functionality is complete. Build/tests now required on Phase 13 (final) instead of Phase 12.
### Amendment 2 — 2026-03-27
**Type:** Modified phase
**What changed:** Added Task 13 (EN/RU localization) to Phase 13: Frontend Polish & Modern UI
**Why:** User wants bilingual support (English and Russian) in the dashboard
**Impact on existing phases:** None — contained within Phase 13
## Final Review
- [ ] Comprehensive code review
- [ ] Full build passes
- [ ] Full test suite passes
@@ -0,0 +1,59 @@
# Phase 13: Frontend Polish & Modern UI
**Status:** ⬜ Not Started
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** frontend
## Objective
Enhance the web UI with a modern, polished look and feel — custom SVG icons, refined typography, consistent color palette, smooth transitions, and overall professional frontend quality.
## Tasks
- [ ] Task 1: Design system foundations — define color palette (dark/light), spacing scale, typography scale, border radius tokens as CSS custom properties
- [ ] Task 2: SVG icon set — create or integrate a consistent icon library (Lucide, Heroicons, or custom SVGs) for all UI actions (deploy, stop, start, restart, remove, settings, registry, etc.)
- [ ] Task 3: Refine layout — polished sidebar/topnav with active state indicators, smooth transitions, responsive breakpoints
- [ ] Task 4: Dashboard cards — redesign project cards with status indicators, instance count badges, sparkline activity, hover effects
- [ ] Task 5: Project detail view — clean table/card layout for instances, inline status badges with pulse animation for "running", deploy history timeline
- [ ] Task 6: Form styling — consistent input fields, select dropdowns, toggle switches (replace checkboxes), button hierarchy (primary/secondary/danger)
- [ ] Task 7: Toast/notification system — slide-in toasts with icons, auto-dismiss, stacking
- [ ] Task 8: Loading states — skeleton loaders for data fetching, spinner for actions, progress indicator for deploys
- [ ] Task 9: Empty states — illustrated empty states with call-to-action for "no projects", "no instances", "no deploys"
- [ ] Task 10: Responsive design — mobile-friendly layout, collapsible sidebar, touch-friendly controls
- [ ] Task 11: Micro-interactions — button press feedback, status transition animations, deploy progress animation
- [ ] Task 12: Dark mode support (optional) — toggle in settings, respect system preference
- [ ] Task 13: Localization (EN/RU) — i18n setup with locale switcher, translate all UI strings to English and Russian, persist language preference
## Files to Modify/Create
- `web/src/lib/styles/` — design tokens, global styles
- `web/src/lib/components/icons/` — SVG icon components
- `web/src/lib/components/` — enhanced existing components
- `web/src/lib/i18n/` — locale files (en.json, ru.json), i18n helper, locale switcher component
- All route files — refined layouts and styling, replace hardcoded strings with i18n keys
## Acceptance Criteria
- UI looks modern and professional — not "default framework" appearance
- Consistent icon language throughout the app
- Smooth transitions and meaningful animations (not gratuitous)
- Responsive down to mobile viewport
- Loading and empty states provide good UX
- Color palette works well in both light and dark contexts
- All UI strings available in English and Russian, switchable via locale picker
## Notes
- This phase runs AFTER all functionality is complete — pure visual/UX enhancement
- Do not change any functionality or API contracts
- Prefer CSS custom properties for theming over hardcoded values
- Keep bundle size reasonable — inline SVGs preferred over icon font libraries
- Animations should be tasteful and serve UX, not decoration
- For i18n, use a lightweight approach (JSON locale files + Svelte store) — no heavy i18n framework needed
- Default language: English. Russian as secondary. Locale persisted to localStorage
## Review Checklist
- [ ] All tasks completed
- [ ] Visual consistency across all pages
- [ ] No functionality regressions
- [ ] Responsive on mobile/tablet/desktop
- [ ] Accessible (proper contrast ratios, focus states, aria labels on icons)
## Handoff to Next Phase
<!-- This is the final UI phase — no handoff needed. -->
@@ -1,6 +1,6 @@
# Phase 2: Crypto & Config Seed Loader
**Status:** ⬜ Not Started
**Status:** ✅ Complete
**Parent plan:** [PLAN.md](./PLAN.md)
**Domain:** backend
@@ -9,14 +9,14 @@ Implement AES-256 encryption for credential storage and the YAML seed config par
## 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
- [x] Task 1: Implement AES-256-GCM encrypt/decrypt functions using Go stdlib `crypto/aes` + `crypto/cipher`
- [x] Task 2: Key derivation from ENCRYPTION_KEY env var (SHA-256 hash to get 32 bytes)
- [x] Task 3: Define YAML config structs matching the seed format from PLAN.md
- [x] Task 4: Implement YAML parser — read and validate seed file
- [x] Task 5: Implement seed importer — checks if DB is empty, if so imports YAML into SQLite via store CRUD
- [x] Task 6: Encrypt credential fields (registry tokens, NPM password) during import
- [x] Task 7: Create `docker-watcher.example.yaml` with documented example config
- [x] 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
@@ -40,11 +40,22 @@ Implement AES-256 encryption for credential storage and the YAML seed config par
- 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
- [x] All tasks completed
- [x] Crypto uses secure practices (random nonce, GCM, no ECB)
- [x] No hardcoded keys or secrets
- [x] YAML parsing validates required fields
- [x] Import is transactional
## Handoff to Next Phase
<!-- Filled in by the implementation agent after completing this phase. -->
- `crypto.Encrypt(key, plaintext)` and `crypto.Decrypt(key, ciphertextHex)` handle AES-256-GCM encryption; ciphertext is hex-encoded with prepended nonce
- `crypto.KeyFromEnv()` derives a `[32]byte` key from the `ENCRYPTION_KEY` env var via SHA-256
- `crypto.EncryptIfNotEmpty(key, value)` is a convenience wrapper that passes through empty strings unchanged
- `config.ImportSeed(db, seedPath)` is the single entry point for seed import — called from `main.go` at startup
- Import is idempotent: skipped if the DB already has projects or registries
- Import is transactional: all inserts happen within a single SQLite transaction (rollback on any failure)
- Registry `token` and settings `npm_password` are now stored encrypted in SQLite — later phases that read these fields must decrypt with `crypto.Decrypt(key, value)`
- `store.DB()` method was added to expose the underlying `*sql.DB` for transaction use
- Seed file path is configurable via `SEED_FILE` env var (default: `./docker-watcher.yaml`)
- YAML validation ensures: `global.domain` is required, every project needs `image`, project registry references must exist, stages need `tag_pattern`
- `go.sum` still does not exist — run `go mod tidy` when Go toolchain is available