docs(memory-bank): add creative phase CP-1 tenant settings UI

Решение: /tenant-settings с вкладками BIRD/Ревизии/Дополнительно, пункт mainNav «Параметры», /settings остаётся frontend-only.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-06-12 18:57:17 +07:00
co-authored by Cursor
parent de64374c91
commit ceb6f2f34f
2 changed files with 426 additions and 18 deletions
@@ -0,0 +1,77 @@
# Creative: Tenant Settings UI (CP-1)
📌 **CREATIVE PHASE START: Tenant Settings Module**
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
## 1️⃣ PROBLEM
**Description:** Tenant-настройки (`global_settings`, `/v1/settings`) разбросаны: вкладка «Система» в Operations и форма BIRD на `/network`. `/settings` зарезервирован под frontend (токен, тема). Нужен отдельный модуль без смешения с browser settings.
**Requirements:**
- Один route для всех tenant KV (BIRD, revision retention, custom)
- Роли: viewer — read; operator — PATCH
- Переиспользовать существующие схемы (`bird-settings.schema`, `revision-settings.schema`, `settings-api.ts`)
- Убрать дубли из Operations и Network
**Constraints:**
- Контракт API не менять на UI-фазе
- shadcn-svelte, Svelte 5 runes, superforms
- WEB-16: ConfirmDialog для удаления custom keys
## 2️⃣ OPTIONS
| Option | Описание |
|--------|----------|
| **A** | Одна страница `/tenant-settings` с Tabs: BIRD / Ревизии / Дополнительно |
| **B** | Три отдельных route: `/tenant-settings/bird`, `/revision`, `/extra` |
| **C** | Accordion на одной длинной странице без tabs |
| **D** | Оставить BIRD на Network, перенести только revision+KV |
## 3️⃣ ANALYSIS
| Criterion | A Tabs | B Multi-route | C Accordion | D Partial |
|-----------|--------|---------------|-------------|-----------|
| Discoverability | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐ |
| Соответствие плану | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐ |
| Меньше дублирования | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐ |
| Сложность реализации | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Консистентность с Monitoring/Ops tabs | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐ | ⭐⭐ |
**Key insights:**
- Monitoring уже использует `Tabs` (`system` / `postgres`) — паттерн знаком оператору.
- BIRD и revision логически связаны с pipeline/bundles — держать вместе усиливает «единый центр tenant config».
- Отдельные routes (B) дают deep links, но избыточны для ~10 полей.
## 4️⃣ DECISION
**Selected: Option A**`/tenant-settings` с тремя вкладками.
**Navigation:**
- **mainNav** (не bottom): новый пункт **`Параметры`** → `/tenant-settings`, icon `SlidersHorizontal`
- **bottomNav** `/settings` — без изменений семантики («Настройки интерфейса»)
- **Network:** Card «BIRD (кратко)» + кнопка «Изменить параметры» → `/tenant-settings?tab=bird`
- **Operations:** удалить tab `system`; в quick actions / empty state — ссылка на `/tenant-settings`
**Tab structure:**
| Tab value | Label | Компонент | Сохранение |
|-----------|-------|-----------|------------|
| `bird` | BIRD | `TenantBirdSettingsCard` (из `BirdSettingsForm`) | PATCH known bird_* |
| `revision` | Ревизии | `TenantRevisionSettingsCard` | PATCH `revision_retention_minutes` |
| `additional` | Дополнительно | `TenantAdditionalSettingsCard` | PATCH custom KV, operator |
**URL:** `?tab=bird|revision|additional` (как Operations `?tab=jobs`).
**Rationale:** минимальный diff, один mental model «параметры tenant», переиспользование tabs-паттерна Monitoring.
## 5️⃣ IMPLEMENTATION NOTES
- `web/src/routes/tenant-settings/+page.svelte` — PageHeader + Tabs
- Вынести карточки в `web/src/lib/components/tenant-settings/`
- Общий `loadSettings()` / `patchSettings()` из `settings-api.ts`
- PageHeader description: «Параметры control plane для текущего tenant (API /v1/settings)»
- Не показывать на этой странице token/theme
- После миграции: удалить `OperationsSystemSettingsTab`, упростить `network/+page.svelte`
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 **CREATIVE PHASE END: Tenant Settings UI**
+349 -18
View File
@@ -2,31 +2,362 @@
## Current Task
**[Не определена]** — пользователь вызвал `/van` без описания задачи.
**settings-ui-and-runtime-logs**
## Status
| Поле | Значение |
|------|----------|
| **Task ID** | `settings-ui-and-runtime-logs` |
| **Complexity** | **Level 4** |
| **Status** | **PLAN complete**`/creative` |
| **Дата VAN** | 2026-06-12 |
| **Дата PLAN** | 2026-06-12 |
- [x] VAN: platform detection
- [x] VAN: Memory Bank verification & creation
- [x] VAN: baseline project context
- [ ] Task definition (от пользователя)
- [ ] Complexity determination (Level 14)
- [ ] Implementation plan
- [ ] Execution
- [ ] Documentation / reflect
---
## Candidate Context (из git, не подтверждено)
## Resolved Decisions (от заказчика)
Возможное продолжение работы над **Maintenance Policies UI**:
| # | Вопрос | Решение |
|---|--------|---------|
| 1 | `/settings` vs tenant | **`/settings` — только frontend** (токен, тема, localStorage). **Tenant-настройки — отдельный модуль** (новый route + nav). |
| 2 | Очистка больших логов | **Синхронно** (HTTP 200/4xx/5xx), без `jobs.Registry` / 202. |
| 3 | Где FS API | **Только `evobgp-all`** с bind-mount volume на runtime-logs. На `evobgp-api``503` или отсутствие маршрута. |
| 4 | Audit очистки | **Да** — персистентный audit в PostgreSQL (и sqlite для паритета). |
- `MaintenancePoliciesTab.svelte` — вкладка политик в мониторинге
- `policy-schedule.ts` — редактор cron-расписания (5-field, UTC)
---
## Requirements
## Requirements Summary
_Ожидается формулировка задачи от пользователя._
### A. Web UI — два независимых модуля
Пример:
| Модуль | Route (план) | Данные | Роль |
|--------|--------------|--------|------|
| **Frontend settings** | `/settings` (существует) | localStorage, theme | любой пользователь UI |
| **Tenant settings** | `/tenant-settings` (новый) | `GET/PATCH /v1/settings``global_settings` | viewer read / operator write |
**Tenant module** объединяет сейчас разрозненное:
- `OperationsSystemSettingsTab` → revision + custom KV (убрать из Operations)
- `BirdSettingsForm` → BIRD keys (убрать с `/network` или оставить read-only summary + ссылка)
**`/settings` не трогать семантически** — только polish (заголовки, пояснения что это настройки браузера).
### B. Runtime logs — FS
- Корень: `EVOBGP_RUNTIME_LOGS_DIR` (prod default: `/opt/evobgp/runtime-logs`)
- Источник файлов: sidecar `stack-runtime-logs` (без изменений)
- API: list / stat / tail / cleanup (sync)
- Audit: каждая операция cleanup → запись в БД
---
## Technology Validation
| Технология | Версия / статус | Примечание |
|------------|-----------------|------------|
| Go stdlib `os`, `path/filepath` | 1.24 | FS read/truncate; без новых deps |
| PostgreSQL + sqlite миграции | 000026 | audit table |
| OpenAPI 3.1 | `docs/openapi.yaml` | новые paths под tag `RuntimeLogs` |
| SvelteKit 5 + shadcn | web/ | новые routes/components |
| Compose bind mount | `deploy/compose/*` | volume на `evobgp-all` |
**PoC не требуется** — паттерны audit и settings уже в репозитории.
---
## Architecture Overview
```mermaid
flowchart TB
subgraph web [Web UI]
FS["/settings<br>frontend only"]
TS["/tenant-settings<br>BIRD + revision + KV"]
MON["/monitoring?tab=runtime-logs"]
end
subgraph api [evobgp-all only]
H[httpapi handlers]
RL[internal/runtimelogs]
ST[store.Backend]
end
subgraph data [Data]
PG[(global_settings)]
AUD[(runtime_log_cleanup_audit)]
VOL["/opt/evobgp/runtime-logs/*.log"]
end
FS --> localStorage
TS --> H
MON --> H
H --> ST
H --> RL
ST --> PG
ST --> AUD
RL --> VOL
```
/van Доработать MaintenancePoliciesTab: валидация cron и тесты
---
## Phased Implementation Plan
### Phase 0 — Creative (обязательно перед BUILD)
Документы в `memory-bank/creative/`:
| ID | Тип | Тема | Вопросы |
|----|-----|------|---------|
| CP-1 | uiux | Tenant settings module | Структура вкладок: BIRD / Ревизии / Дополнительно; nav label |
| CP-2 | uiux | Runtime logs UI | Вкладка в Monitoring vs отдельный route |
| CP-3 | algorithm | Cleanup semantics | `truncate` (обнулить файл) vs `delete`; max tail bytes/lines |
| CP-4 | architecture | Path safety | Allowlist имён файлов `*.log`, запрет `..`, symlink policy |
**Уже решено (не обсуждать в creative):** sync cleanup, evobgp-all only, audit yes, settings split.
---
### Phase 1 — Contract & persistence (OpenAPI + migrations + store)
**Цель:** контракт и audit до FS-логики.
| # | Действие | Файлы |
|---|----------|-------|
| 1.1 | OpenAPI: `RuntimeLogs` tag | `docs/openapi.yaml` |
| 1.2 | Схемы: `RuntimeLogFile`, `RuntimeLogTail`, `RuntimeLogCleanupAudit` | `docs/openapi.yaml` |
| 1.3 | Paths (см. ниже) | `docs/openapi.yaml` |
| 1.4 | Миграция `000026_runtime_log_cleanup_audit` | `migrations/postgres/`, `migrations/sqlite/` |
| 1.5 | Типы + `store.Backend` методы | `internal/store/runtime_logs.go`, `backend.go` |
| 1.6 | Postgres + Memory реализации | `internal/repository/postgres_runtime_logs.go`, `internal/store/memory_runtime_logs.go` |
**OpenAPI paths (черновик):**
```
GET /v1/runtime-logs/files # list + size/mtime
GET /v1/runtime-logs/files/{filename} # tail (?lines= | ?bytes=, ?grep=)
DELETE /v1/runtime-logs/files/{filename} # cleanup (?mode=truncate|delete), operator+, sync 200
GET /v1/runtime-logs/cleanup-audit # cursor/limit, viewer+
```
**Audit table `runtime_log_cleanup_audit`:**
| Column | Type | Note |
|--------|------|------|
| id | TEXT PK | UUID |
| tenant_id | TEXT | из auth |
| actor_prefix | TEXT | API key prefix |
| filename | TEXT | basename only |
| action | TEXT | `truncate` \| `delete` |
| size_before | BIGINT | bytes |
| size_after | BIGINT | nullable |
| detail_json | JSONB | optional (grep stats, error) |
| created_at | TIMESTAMPTZ | |
**Checklist Phase 1:**
- [ ] `npx @redocly/cli lint docs/openapi.yaml`
- [ ] `scripts/check-migrations-pair.sh`
- [ ] store interface + memory tests
---
### Phase 2 — FS layer & config (evobgp-all only)
**Цель:** безопасное чтение/очистка файлов.
| # | Действие | Файлы |
|---|----------|-------|
| 2.1 | `EVOBGP_RUNTIME_LOGS_DIR` в config | `internal/config/config.go`, `docs/access.md` |
| 2.2 | Guard: FS enabled iff dir non-empty **and** `EVOBGP_SERVICE=evobgp-all` | `internal/runtimelogs/guard.go` |
| 2.3 | ListDir, Stat, Tail, Cleanup | `internal/runtimelogs/fs.go` |
| 2.4 | Path hardening: basename allowlist `[a-z0-9_.-]+\.log` | `internal/runtimelogs/safe.go` |
| 2.5 | Unit tests (temp dir) | `internal/runtimelogs/*_test.go` |
**Поведение при отключённом FS:**
- `GET``503` problem+json `runtime_logs_unavailable`
- `DELETE``503`
**Cleanup flow (sync):**
1. Stat file → `size_before`
2. Truncate or Remove
3. `AppendRuntimeLogCleanupAudit(...)`
4. Return `200` + audit id + sizes
**Checklist Phase 2:**
- [ ] `go test ./internal/runtimelogs/... -race`
- [ ] path traversal tests (`../`, symlinks)
---
### Phase 3 — HTTP handlers
| # | Действие | Файлы |
|---|----------|-------|
| 3.1 | Регистрация маршрутов | `internal/httpapi/routes.go` или `routes_runtime_logs.go` |
| 3.2 | Handlers | `internal/httpapi/handlers_runtime_logs.go` |
| 3.3 | `actorPrefix(a)` как в maintenance | reuse from `routes_maintenance.go` |
| 3.4 | Handler tests | `internal/httpapi/handlers_runtime_logs_test.go` |
**Роли:** list/tail/audit — `viewer+`; cleanup — `operator+`.
**Checklist Phase 3:**
- [ ] `go test ./internal/httpapi/... -race`
- [ ] `scripts/lint-httpapi.sh`
---
### Phase 4 — Deploy (compose)
| # | Действие | Файлы |
|---|----------|-------|
| 4.1 | Bind mount runtime-logs → evobgp-all | `deploy/compose/stack.microvps-full.yaml`, `docker-compose.microvps-full.yaml` |
| 4.2 | Env `EVOBGP_RUNTIME_LOGS_DIR=/opt/evobgp/runtime-logs` | compose env |
| 4.3 | `EVOBGP_SERVICE=evobgp-all` (если ещё не задан) | compose |
| 4.4 | Комментарий в quickstart / manual | `docs/quickstart.md` или `docs/manual.md` |
**Пример mount:**
```yaml
volumes:
- ${EVOBGP_RUNTIME_LOGS_HOST_DIR:-./runtime-logs}:/opt/evobgp/runtime-logs:rw
environment:
EVOBGP_RUNTIME_LOGS_DIR: /opt/evobgp/runtime-logs
```
**Checklist Phase 4:**
- [ ] dev: `./runtime-logs` рядом с compose
- [ ] prod: `/opt/evobgp/runtime-logs` на хосте
---
### Phase 5 — Web UI: Tenant settings module
**Цель:** отдельный модуль tenant-настроек; `/settings` остаётся frontend-only.
| # | Действие | Файлы |
|---|----------|-------|
| 5.1 | Новый route | `web/src/routes/tenant-settings/+page.svelte` |
| 5.2 | Компонент-обёртка с Tabs | `web/src/lib/components/tenant-settings/TenantSettingsPage.svelte` |
| 5.3 | Перенос логики из Operations | refactor `OperationsSystemSettingsTab``TenantRevisionSettingsCard.svelte` |
| 5.4 | Перенос BIRD | refactor `BirdSettingsForm``TenantBirdSettingsCard.svelte` |
| 5.5 | Custom KV card | `TenantAdditionalSettingsCard.svelte` |
| 5.6 | Nav: добавить пункт (main или bottom) | `web/src/lib/ui/app/layout/nav.ts` |
| 5.7 | Убрать tab `system` из Operations | `web/src/routes/operations/+page.svelte` |
| 5.8 | Network: заменить форму на Card-summary + link | `web/src/routes/network/+page.svelte` |
| 5.9 | Обновить ссылки в docs strings / empty states | grep `tab=system`, `BirdSettingsForm` |
**Структура tenant module (предложение для creative CP-1):**
```
/tenant-settings
├─ BIRD (bird_*)
├─ Ревизии (revision_retention_minutes)
└─ Дополнительно (custom KV, operator)
```
**Checklist Phase 5:**
- [ ] `npm run check && npm run lint`
- [ ] `/settings` без tenant-форм
---
### Phase 6 — Web UI: Runtime logs
| # | Действие | Файлы |
|---|----------|-------|
| 6.1 | API client | `web/src/lib/runtime-logs/runtime-logs-api.ts` |
| 6.2 | Tab в Monitoring | `web/src/lib/components/monitoring/RuntimeLogsTab.svelte` |
| 6.3 | Подключить tab | `web/src/routes/monitoring/+page.svelte` |
| 6.4 | Таблица файлов (size, mtime) | `AppDataTable` |
| 6.5 | Preview dialog | `ScrollPreBlock` + tail API |
| 6.6 | Cleanup | `ConfirmDialog` + DELETE sync |
| 6.7 | Sub-tab или section: Cleanup audit | таблица `GET /v1/runtime-logs/cleanup-audit` |
| 6.8 | 503 empty state | «Доступно только на evobgp-all с volume» |
**Checklist Phase 6:**
- [ ] `npm run check && npm run lint`
---
### Phase 7 — Integration, docs, QA
| # | Действие |
|---|----------|
| 7.1 | `docs/api.md` — новые endpoints |
| 7.2 | `docs/manual.md` — tenant-settings + runtime logs |
| 7.3 | `go test ./... -race -count=1` |
| 7.4 | E2E manual: list → tail → truncate → audit row |
---
## Dependency Graph
```mermaid
graph TD
P0[Phase 0 Creative] --> P1[Phase 1 OpenAPI+store]
P1 --> P2[Phase 2 FS layer]
P2 --> P3[Phase 3 HTTP]
P3 --> P4[Phase 4 Deploy]
P0 --> P5[Phase 5 Tenant UI]
P3 --> P6[Phase 6 Runtime logs UI]
P4 --> P6
P5 --> P7[Phase 7 QA]
P6 --> P7
```
**Параллелизация:** Phase 5 (tenant UI) можно начинать после Phase 0, не дожидаясь runtime logs backend.
---
## Risks & Mitigations
| Risk | Impact | Mitigation |
|------|--------|------------|
| Path traversal | Critical | basename only, allowlist, `filepath.Clean` + root check |
| Sync cleanup блокирует HTTP worker | Medium | лимит размера файла для DELETE; timeout context; документировать |
| evobgp-api без volume | Low | 503 + UI empty state |
| Дублирование settings forms | Medium | Phase 5 удаляет старые вхождения |
| reference compose (не all) | Low | FS API disabled; документировать |
---
## Creative Phases Required
- [x] **CP-1 uiux**`creative-tenant-settings-ui.md` ✅ Tabs `/tenant-settings`, nav «Параметры»
- [ ] **CP-2 uiux**`creative-runtime-logs-ui.md`
- [ ] **CP-3 algorithm**`creative-runtime-logs-cleanup.md` (truncate default vs delete)
- [ ] **CP-4 architecture**`creative-runtime-logs-path-safety.md`
---
## Acceptance Criteria
- [ ] `/settings` — только frontend (токен, тема)
- [ ] `/tenant-settings` — все tenant KV (BIRD + revision + custom)
- [ ] Operations без tab `system`; Network без полной BIRD-формы (summary + link)
- [ ] Runtime logs: list, tail, sync cleanup на evobgp-all
- [ ] Audit cleanup в БД + просмотр в UI
- [ ] `EVOBGP_RUNTIME_LOGS_DIR`, volume в compose
- [ ] redocly lint, go test -race, web check+lint
---
## Status Checklist
- [x] VAN
- [x] PLAN
- [ ] CREATIVE (4 docs)
- [ ] BUILD Phase 17
- [ ] REFLECT
- [ ] ARCHIVE
---
## Key Files (reference)
**Settings today:**
- `web/src/routes/settings/+page.svelte` — keep frontend-only
- `web/src/lib/components/operations/OperationsSystemSettingsTab.svelte` — migrate out
- `web/src/lib/components/network/BirdSettingsForm.svelte` — migrate out
- `internal/httpapi/routes_crud.go` — settings handlers (unchanged contract)
**Runtime logs today:**
- `deploy/compose/stack.microvps-full.yaml``stack-runtime-logs`, `./runtime-logs`
**Patterns:**
- `internal/httpapi/routes_maintenance.go` — audit list, actor_prefix
- `migrations/postgres/000025_*` — config audit table shape