# TASK.md — Controller Refactoring: Template Split, Server Decomposition, Domain Rename > **Goal:** Improve code organization for maintainability and Claude Code efficiency. > No new features — purely structural refactoring + one config rename. > > **Version bump:** v0.3.0 (structural refactor milestone) --- ## Task 1: Split templates.go → go:embed (HIGH PRIORITY) The `templates.go` file contains ALL HTML templates and CSS as Go string constants. The file itself says: *"As the UI grows, switch to go:embed for easier editing."* With 7 templates + full CSS, it's time. ### 1.1 — Create template files directory Create `controller/internal/web/templates/` with individual files: ``` internal/web/templates/ ├── layout.html ← from layoutTmpl const ├── dashboard.html ← from dashboardTmpl const ├── stacks.html ← from stacksTmpl const (the stacks list page, NOT the old dashboard list) ├── login.html ← from loginTmpl const ├── logs.html ← from logsTmpl const ├── deploy.html ← from deployTmpl const ├── app_info.html ← from appInfoTmpl const └── style.css ← from cssTemplate const ``` Each `.html` file should contain ONLY the template content (the `{{define "name"}}...{{end}}` block). Keep the existing template names (`layout_start`, `layout_end`, `dashboard`, `stacks`, `login`, etc.). ### 1.2 — Create embed.go Create `controller/internal/web/embed.go`: ```go package web import "embed" //go:embed templates/*.html templates/*.css var templateFS embed.FS ``` ### 1.3 — Update template loading in server.go (or the new funcmap.go, see Task 2) Replace the current `loadTemplates()` method that parses the `allTemplates` const: ```go func (s *Server) loadTemplates() { funcMap := template.FuncMap{ /* ... existing funcs ... */ } s.tmpl = template.Must( template.New("").Funcs(funcMap).ParseFS(templateFS, "templates/*.html"), ) } ``` CSS serving: Instead of the `cssTemplate` const, read from `templateFS`: ```go func (s *Server) serveCSSHandler(w http.ResponseWriter, r *http.Request) { data, err := templateFS.ReadFile("templates/style.css") if err != nil { http.Error(w, "CSS not found", 500) return } w.Header().Set("Content-Type", "text/css; charset=utf-8") w.Header().Set("Cache-Control", "public, max-age=3600") w.Write(data) } ``` Register this handler for `/static/style.css` in `ServeHTTP` (replace the current inline CSS serving). ### 1.4 — Delete old string constants Remove from `templates.go`: - `const allTemplates = ...` - `const layoutTmpl = ...` - `const dashboardTmpl = ...` - `const stacksTmpl = ...` - `const loginTmpl = ...` - `const logsTmpl = ...` - `const deployTmpl = ...` - `const appInfoTmpl = ...` - `const cssTemplate = ...` After this, `templates.go` should either be empty (delete it) or contain only the felhom logo SVG constant if that's still embedded as a string (keep that one — it's small). ### 1.5 — Verify the build - `go build ./cmd/controller/` must succeed - `go:embed` requires Go 1.16+ (we're on 1.22, fine) - Templates are still compiled into the binary — zero runtime file dependencies (same as before) - Verify that the HTML files actually include the `{{define "name"}}...{{end}}` wrappers (ParseFS needs them to register template names) ### Important notes - The `` in layout.html already exists, so CSS loading via the `/static/style.css` route should already work — just make sure the handler reads from embed.FS instead of serving the const. - The felhom logo SVG can stay as a Go const (it's small) or move to `templates/felhom-logo.svg` and be served from embed.FS too. Either approach is fine. --- ## Task 2: Split server.go into focused files (MEDIUM PRIORITY) Currently `server.go` handles: Server struct, auth/sessions, page handlers, template FuncMap, asset serving, and HTTP routing. Split into: ### 2.1 — Create `auth.go` Move from `server.go` to `internal/web/auth.go`: - `type session struct` - `const sessionCookieName`, `const sessionMaxAge` - `RequireAuth()` middleware method - `loginHandler()`, `loginPostHandler()`, `logoutHandler()` - `createSession()`, `isValidSession()`, `cleanupSessions()` - `renderLogin()` helper ### 2.2 — Create `handlers.go` Move from `server.go` to `internal/web/handlers.go`: - `baseData()` helper - `dashboardHandler()` - `stacksHandler()` - `deployPageHandler()` - `deployPagePostHandler()` (if it exists as separate handler) - `appDetailHandler()` - `logsPageHandler()` ### 2.3 — Create `funcmap.go` Move from `server.go` to `internal/web/funcmap.go`: - The entire `template.FuncMap` definition from `loadTemplates()` - Extract it as a standalone function: `func (s *Server) templateFuncMap() template.FuncMap` - Then `loadTemplates()` becomes a clean 3-liner calling `templateFuncMap()` + `ParseFS` ### 2.4 — Keep in server.go After the split, `server.go` should contain only: - `type Server struct` - `func NewServer()` - `func (s *Server) loadTemplates()` (now a 3-liner) - `func (s *Server) ServeHTTP()` (HTTP routing dispatch) - `func (s *Server) render()` helper - Static file/asset serving handlers (`serveStaticFile`, `serveCSSHandler`, `serveLogoHandler`) ### 2.5 — Verify the split All files are in `package web` — no import changes needed within the package. The `Server` struct and all its methods are accessible across files in the same package. Run `go build ./cmd/controller/` to verify everything compiles. --- ## Task 3: Rename controller domain from `dashboard.*` to `felhom.*` (LOW PRIORITY) ### 3.1 — Update controller's docker-compose.yml In `controller/docker-compose.yml`, change the Traefik label: ```yaml # OLD: - "traefik.http.routers.controller.rule=Host(`dashboard.${DOMAIN}`)" # NEW: - "traefik.http.routers.controller.rule=Host(`felhom.${DOMAIN}`)" ``` ### 3.2 — Update docker-setup.sh In `controller/scripts/docker-setup.sh`, update `print_summary()` output: - Any reference to `dashboard.${BASE_DOMAIN}` → `felhom.${BASE_DOMAIN}` Also check install_controller() if it generates compose files or prints URLs. ### 3.3 — Update controller.yaml.example If there's any reference to `dashboard.*` in the example config or comments, update to `felhom.*`. ### 3.4 — Update documentation In CLAUDE.md build/deploy workflow sections, update any `dashboard.` references to `felhom.`. ### 3.5 — Cloudflare Tunnel public hostname (MANUAL — not code) **Reminder for Viktor:** After deploying, manually update the Cloudflare Tunnel public hostname in the Zero Trust dashboard: - Old: `dashboard.demo-felhom.eu` → Traefik - New: `felhom.demo-felhom.eu` → Traefik ### 3.6 — Pi-hole DNS (MANUAL — not code) **Reminder for Viktor:** If there's a pi-hole local DNS record for `dashboard.demo-felhom.eu`, update it to `felhom.demo-felhom.eu` (or rely on the wildcard `*.demo-felhom.eu` record). --- ## Task 4: Update documentation ### 4.1 — README.md - Update directory structure to show `internal/web/templates/` directory - Update "Tech stack" section: "Templates: go:embed HTML files" instead of "Go string constants" - Mention the `felhom.*` subdomain for the controller - Update file tree showing the new files (embed.go, auth.go, handlers.go, funcmap.go) ### 4.2 — CLAUDE.md - Update workspace layout to reflect the new `internal/web/` file structure - Update "Tech stack" section - Update any `dashboard.*` references to `felhom.*` - Note the go:embed pattern for future template additions ### 4.3 — CONTEXT.md - Add session entry documenting the refactoring - Note: templates moved from Go string constants to go:embed HTML files - Note: server.go split into auth.go, handlers.go, funcmap.go - Note: controller domain changed from dashboard.* to felhom.* - Update version to v0.3.0 ### 4.4 — BUILDING.md - Update the structure check in build.sh verification section if needed (the `internal/web/templates/` directory should exist now) --- ## Implementation order 1. **Task 1** (templates.go → go:embed) — do this first, biggest impact 2. **Task 2** (server.go split) — do this second, leverages the cleaner templates 3. **Task 3** (domain rename) — small, do last 4. **Task 4** (docs) — update after all code changes ## Verification checklist - [ ] `go build ./cmd/controller/` compiles successfully - [ ] All 7 HTML templates render correctly (login, dashboard, stacks, deploy, app_info, logs, layout) - [ ] CSS loads at `/static/style.css` - [ ] Felhom logo SVG loads at `/static/felhom-logo.svg` - [ ] App logos/screenshots still serve from `/assets/` - [ ] Auth (login/logout/session) works unchanged - [ ] Stack operations (start/stop/deploy) work unchanged - [ ] Controller accessible at `felhom.demo-felhom.eu` (after CF tunnel update) - [ ] No broken links or template errors in browser console - [ ] Build + push via build.sh works - [ ] Deploy on demo-felhom works