Files
pwa-homelab-mon/AGENTS.md
T
alexey.bagno 28702bd029
Deploy / deploy (push) Successful in 8s
Monitor update
2026-08-31 22:42:40 +05:00

84 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md
PWA для менеджмента homelab: монорепозиторий Go-сервер + SPA-фронт, общаются через ConnectRPC.
Таргет — iPhone «На экран Домой» (standalone PWA).
## Стек
- **Сервер**: Go + echo + `connectrpc.com/connect`. Отдаёт RPC на `/homelab.v1.HomelabService/*` и статику из `dist/` (SPA-fallback включён). Порт из env `PORT` (дефолт `:8080`).
- **Фронт**: Preact + Vite + TypeScript, daisyUI v5 (tailwind v4), lucide-preact. Клиент — `@connectrpc/connect-web`, same-origin transport, в dev — прокси на :8080.
- **Контракт**: `proto/homelab/v1/homelab.proto` → кодоген в `gen/` (Go) и `web/src/gen/` (TS). Сгенерённый код **коммитится**, buf CLI не используется — только protoc.
- **Конфиг**: `config.yaml` (секции → items → bash-скрипты). Мониторы гоняются по таймеру на сервере, response-скрипты запускаются по кнопке с аргументами.
## Структура
```
proto/homelab/v1/ контракт (единственный источник правды)
gen/ сгенерённый Go-код (коммитим)
server/main.go весь сервер: конфиг, мониторы, RPC, статика, docs API, services API (systemd + docker)
web/src/App.tsx layout, топбар (desktop) + dock (mobile), 5 хардкод-табов, поллинг ListContent (3s)
web/src/components/ MonitorCard, ScriptCard, ServiceCard, Docs (ридер + textarea-редактор)
web/src/docsStore.ts localStorage доков (hub:doc:*), dirty-флаги, syncDocs: pull по хэшам + flush раз в 5s
web/src/gen/ сгенерированный TS-клиент
web/public/ PWA-обвязка: manifest, sw.js, иконки
scripts/ bash-скрипты, пути из config.yaml
docs/ markdown-доки, НЕ в репо — живут только на сервере рядом с бинарьём
```
## Категории
Хардкод в бандле (App.tsx CATEGORIES): Links (из config links), Monitor (config monitor), Exec (config exec), Docs (папка docs/, API ListDocs/GetDoc/SaveDoc), Services. Оффлайн: контент-стейт держит последний ответ, доки — localStorage; правки в оффлайне помечаются dirty и флашатся на сервер каждые 5s (client authority — последняя запись побеждает, без base-hash check).
## Services (systemd + docker)
Единый список карточек, едет в ответе `ListContent` (repeated Service, поле services) — своего RPC-поллинга нет.
- **systemd** (`kind: SYSTEMD`): юниты из `srvDir` — `*.service`, имя валидируется regex `^[a-z0-9_.-]+$`. Путь: env `SRV_DIR`, иначе `~/srv` (на боксе юниты НЕ рядом с WorkingDirectory). Статус: `systemctl --user show <unit> -p ActiveState,SubState,LoadState,UnitFileState` — парсить ТОЛЬКО по ключам `Key=Value` (вывод не в порядке -p!). `LoadState=not-found` = юнит выключен юзером намеренно, не ошибка.
- **docker** (`kind: DOCKER`): `docker ps -aq` + `docker inspect -f` (labels проекта, status, restart policy). Группировка по `com.docker.compose.project`, без лейбла — своя карточка по имени контейнера. Агрегат: все running → active, ни одного → inactive, иначе partial «up/total». One-shot хелперы (`policy=no` + не running: init/migrations) исключаются из агрегата и из кнопок.
- **Кнопки**: RPC `ServiceAction(name, kind, op)` — systemd → `systemctl --user start|stop|restart`; docker → `docker start|stop|restart` над нужными контейнерами проекта (start — только остановленные, stop/restart — только запущенные).
- **Status-вывод**: RPC `ServiceInfo(name, kind)` → `systemctl status --no-pager -n 0` (exit 3 у остановленного — валидный вывод!) или `docker ps -a` по label проекта (пусто → fallback по имени). Показывается в модалке по кнопке ⓘ (ServiceCard).
- Десктоп: секция Services рендерится шире остальных (`max-w-5xl`), карточки в grid 2–3 колонки.
## Команды (всё через make)
- `make dev` — сервер :8080 + vite :5173 (работать на http://localhost:5173)
- `make dev-srv` / `make dev-web` — по отдельности в два терминала
- `make proto` — кодоген Go+TS из proto
- `make web` — сборка фронта в `dist/` (tsc --noEmit + vite build; `tsc` — линтер, падение = ошибка типов)
- `make server` — Go-бинарь `bin/hub-api`
- `MOCK=1 make dev-srv` — mock-режим: секция Services отдает фикстуры (все виды статусов), кнопки и info-модалка работают по ним; реальных systemd/docker-проб нет
- `make test` — smoke-тест RPC (поднимает сервер на :8080, дергает curl'ом)
Никаких других способов сборки нет: CI делает ровно `make web server`.
## Деплой (CI)
`.gitea/workflows/deploy.yml`: push в `master` → checkout → `make web server` → cp на бокс → `systemctl --user restart hub-api`.
На боксе юнит должен иметь `WorkingDirectory=~/apps/hub` — сервер ищет `dist/`, `config.yaml`, `scripts/` относительно cwd, юниты сервисов — в `~/srv` (env `SRV_DIR` переопределяет). Локально всё то же самое из корня репо.
## Конвенции
- Один корень статики: `dist/`. Не вводить fallback-ов (`web/dist` не существует после `make web` — vite собирает в `web/dist`, make копирует в `./dist`).
- `int64` в proto → `bigint` в TS (не `number`!).
- Поля protobuf в TS — camelCase (`arg_types` → `argTypes`).
- Новый item-тип или поле в yaml = обновить proto + валидацию в `loadConfig` + компонент на фронте.
- Иконки: хардкод в компонентах из lucide-preact поимённо (не тянуть весь lucide через `icons` — раздувает бандл в 5 раз).
- Маркдаун: `marked` (~10KB gzip) + dangerouslySetInnerHTML без санитайзера (контент свой). Если редактор перестанет нравиться — **переход на MDXEditor** ( agreed upgrade path, изолирован в компоненте Docs).
- index.html и sw.js отдаются с `Cache-Control: no-cache` — иначе iOS не подхватит новый бандл.
- Логи сервера: monitors, RunScript и ServiceAction/ServiceInfo пишут в stdout; ошибки проб сервисов (`probe <key>: ...`) логируются один раз на изменение (появление/восстановление), не на каждый 3s тик; `ListContent` (поллинг-шум) исключён из HTTP-лога echo.
- SW: `web/public/sw.js`, версия кэша `homelab-shell-vN` — **бампить при изменении** офлайн-логики, иначе iOS не обновит.
- Тесты: `make test` (smoke RPC) + `tsc --noEmit` внутри `make web`. Не плодить фреймворки.
- После правок Go-кода — прогонять `gofmt -w server/` (агент делает это сам после любых правок).
## Подводные камни
- `systemctl show` выводит свойства в порядке внутренней таблицы systemd, а НЕ в порядке `-p` — позиционный парс ломает статики незаметно (все карточки «остановлен»). Только парсинг `Key=Value` по ключу.
- `systemctl status` возвращет exit 3 для неактивного юнита и 4 для отсутствующего — непустой вывод = валидный результат, err игнорировать.
- Docker-кнопки бьют по контейнерам выборочно (start → не-running, stop/restart → running), т.к. `docker stop` остановленного возвращает ошибку у некоторых версий, а рестарт one-shot init-контейнеров повторяет миграции.
- iOS PWA требует https (или localhost). Через Caddy-домен бокса — ок.
- SW на iOS у установленной PWA обновляется неохотно: правки SW → удалить иконку, открыть в Safari, добавить заново. Bump версии кэша в sw.js обязателен.
- Connect JSON: `int64` сериализуется строкой — в smoke-тестах grep'ать с кавычками.
- Джобы CI — host (не docker), user `homelab`; не добавлять docker-шаги.