Files
deploy-felhom-compose/CLAUDE.md
T

138 lines
7.0 KiB
Markdown

# CLAUDE.md — Project Instructions for Claude Code
> This file is read automatically by Claude Code at the start of every session.
> It replaces the "Instructions" panel from the claude.ai Project.
> Keep it updated as the project evolves.
## Project overview
Creating a business (Felhom) for home-server deployment for Hungarian customers. This repository
(`deploy-felhom-compose`) contains the felhom-controller — a Go application that manages Docker
Compose stacks on customer hardware via a Hungarian-language web dashboard.
See `controller/README.md` for full architecture and status.
See `CONTEXT.md` for current project state, recent work, and decisions (update after each session).
Claude in Chrome extension is available — can be used to test web UI on demo-felhom.eu or verify dashboard deployments in browser.
## Code quality rules
- Always double-check generated code for bugs, logic issues, syntax errors
- Handle edge cases without overcomplicating the script/program
- Add debug capabilities (logging, verbose output) for easier troubleshooting
- If you need more input or troubleshooting command output, ask first — don't guess
## Repository structure
This repo has two main parts:
```
deploy-felhom-compose/
├── controller/ # Go application (this is the main codebase)
│ ├── cmd/controller/ # Entry point (main.go)
│ ├── internal/
│ │ ├── config/ # YAML config loading
│ │ ├── stacks/ # Docker Compose operations, deploy flow
│ │ ├── sync/ # Git sync — periodic pull of app catalog repo
│ │ ├── api/ # REST API endpoints
│ │ ├── system/ # System info (memory, disk)
│ │ └── web/ # Dashboard UI (embedded HTML/CSS templates)
│ ├── Dockerfile
│ ├── Makefile
│ └── go.mod
├── scripts/ # Setup scripts for customer nodes
├── CLAUDE.md # This file
└── CONTEXT.md # Project memory / state
```
## Related repositories (not in this repo)
- **app-catalog-felhom.eu** — Docker Compose templates + .felhom.yml metadata per app
- **felhom.eu** — Website (htmls) + k3s manifests for the web server
- **homelab-manifests** — Viktor's k3s cluster manifests (dooplex.hu services)
- **misc-scripts** — Helper scripts for daily tasks
All hosted at `gitea.dooplex.hu/admin/`
## Test environments
| Node | Hardware | Domain | IP | Notes |
|------|----------|--------|----|-------|
| demo-felhom | Acemagic N100, 16G RAM, 512G SSD + 1TB HDD | demo-felhom.eu | 192.168.0.162 | Primary test node, Cloudflare Tunnel |
| pi-customer-1 | Raspberry Pi 3B+, 1G RAM, 32G SD | pi-customer-1.local | 192.168.0.161 | Secondary test, not yet active |
- Pi-hole DNS on local network forwards `*.demo-felhom.eu` → 192.168.0.162
- External access via Cloudflare Tunnel → Traefik reverse proxy
## Build & deploy workflow
Code is edited locally on Windows. Build happens on the server via SSH.
```bash
# Push changes
git add -A && git commit -m "..." && git push
# Build + push image on server
ssh kisfenyo@192.168.0.180 "cd ~/build/felhom-controller && git -C ~/git/deploy-felhom-compose pull && ./build.sh --push"
# Deploy on demo node
ssh kisfenyo@192.168.0.162 "cd /opt/docker/felhom-controller && docker pull gitea.dooplex.hu/admin/felhom-controller: && docker compose up -d"
```
## Tech stack
- **Language:** Go 1.22+
- **Web framework:** stdlib `net/http` + `html/template` (no frameworks)
- **Templates:** Embedded as Go string constants in `templates.go` (Hungarian UI)
- **CSS:** Single embedded const in `templates.go` (no external CSS files)
- **Auth:** bcrypt password hash + session cookies
- **Container orchestration:** Docker Compose via CLI (`docker compose up -d`)
- **Reverse proxy:** Traefik (separate stack, managed by controller)
- **Tunnel:** Cloudflare Tunnel (cloudflared, separate stack)
## Key patterns
- All UI text is in Hungarian (Budapest timezone, Hungarian locale)
- Templates use Go template functions: `stateColor`, `stateLabel`, `stateIcon`, `stateStr`, `isOperational`, `logoURL`, `logoPNGURL`, `appPageURL`
- Container states: `running`, `starting`, `unhealthy`, `stopped`, `exited`, `restarting`, `paused`, `not_deployed`
- Docker `.State` field is combined with `.Status` field to detect health substatus
- Stacks are sorted alphabetically by DisplayName
- Protected stacks (traefik, cloudflared, felhom-controller) can't be stopped from UI
- `app.yaml` persists deploy config; `deployed: true` flag controls UI state
- Password fields require explicit user input or generation (no silent auto-fill)
## Git sync module (internal/sync)
- Uses `os/exec` to call `git` CLI — no Go git library dependency
- On startup: clones repo to `{data_dir}/catalog-cache/` (shallow clone, `--depth 1`)
- Periodically: `git fetch --depth 1` + `git reset --hard origin/{branch}`
- Copies only `docker-compose.yml` and `.felhom.yml` to stacks dir
- **Never overwrites** `app.yaml` or `.env` — these contain deployed secrets
- Content-hash comparison (SHA-256) — only writes if file actually changed
- After sync, triggers `ScanStacks()` rescan for dashboard update
- `POST /api/sync` triggers immediate sync (30s debounce)
- "Sablonok frissítése" button on Alkalmazások page
- Sync status exposed in `/api/system/info` response
## Debug logging
The controller has two-tier logging controlled by `logging.level` in `controller.yaml` (or `FELHOM_LOGGING_LEVEL` env var):
- **`info`** (default): Operation success/failure with elapsed time, post-start container states, scan counts
- **`debug`**: All of above plus env var keys per compose command, local image availability checks, compose command completion times, log fetch byte counts
Key patterns used in `internal/stacks/`:
- `time.Since(start)` for operation timing — always logged at INFO level
- `m.isDebug()` gates verbose output (env var keys, image checks)
- `truncateStr(s, 500)` caps stdout/stderr in error logs
- `logPostStartStatus()` runs async (goroutine + 3s sleep) after start/restart/update/deploy — never blocks or fails the operation
- `checkLocalImages()` parses compose YAML for `image:` lines, runs `docker image inspect` per image
- Env var **keys** are logged, never values (secrets safety)
## Important lessons learned
1. `PAPERLESS_OCR_LANGUAGES` (plural, with S) **installs** tesseract packs; `PAPERLESS_OCR_LANGUAGE` (singular) **selects** which to use
2. `docker compose restart` does NOT pick up new images — always use `docker compose up -d`
3. Go map iteration order is random — always sort before displaying in UI
4. Docker's `.State` field says "running" even for unhealthy containers — must parse `.Status` for health info
5. After `DeployStack()` succeeds, update in-memory `Deployed` flag immediately — `RefreshStatus()` only reads docker ps, not app.yaml
6. `docker compose up -d` returns exit 0 even when containers crash-loop — post-start status check is essential for detecting failures