145 lines
4.3 KiB
Markdown
145 lines
4.3 KiB
Markdown
# Implementation Plan: Audit Overview Dashboard (009)
|
|
|
|
**Branch**: `009-ops-audit-overview` | **Date**: 2026-06-08 | **Spec**: [spec.md](./spec.md)
|
|
|
|
## Summary
|
|
|
|
Implementar Track A MVP: collectors read-only, persistência SQLite, API overview/scorecard, worker periódico, UI grid estilo Cloudflare. Primeiro tenant: VM112; domínios auto-descobertos via eventos onboarding.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: Python 3.11+ (API + worker VM122); JS vanilla (frontend)
|
|
|
|
**Primary Dependencies**: FastAPI, httpx, dnspython (ou subprocess dig), sqlite3, redis (worker queue existente)
|
|
|
|
**Storage**: SQLite novas tabelas `audit_domains`, `audit_checks`
|
|
|
|
**Testing**: `scripts/verify-audit-overview.sh`; mock tenant offline
|
|
|
|
**Target Platform**: VM122 worker → VM112 API `:8090` + DNS público + HTTPS webmail
|
|
|
|
**Performance Goals**: Ciclo completo 1 domínio < 30s; overview API < 500ms
|
|
|
|
**Constraints**: Read-only; LAN para VM112; sem novos containers (worker existente)
|
|
|
|
## Constitution Check
|
|
|
|
| Princípio | Status |
|
|
|-----------|--------|
|
|
| IV. Mail vs Ops | ✅ PASS — collectors read-only, Ops separado |
|
|
| VII. Spec-Driven | ✅ PASS |
|
|
| IX. YAGNI | ✅ PASS — SQLite, 8 checks fixos |
|
|
|
|
## Project Structure
|
|
|
|
```text
|
|
specs/009-ops-audit-overview/
|
|
├── spec.md
|
|
├── plan.md
|
|
├── research.md
|
|
├── contracts/audit-api.md
|
|
├── checklists/requirements.md
|
|
└── tasks.md
|
|
|
|
api/app/
|
|
├── main.py # routes /audit/*
|
|
├── collectors/
|
|
│ ├── __init__.py
|
|
│ ├── base.py
|
|
│ ├── vm112.py # carbonio, nginx, cert via portal API
|
|
│ ├── dns.py # mx, spf, dkim, dmarc
|
|
│ └── webmail.py # HTTP check
|
|
└── audit_store.py # SQLite CRUD
|
|
|
|
worker/
|
|
└── audit_runner.py # loop ou job redis
|
|
|
|
frontend/assets/
|
|
├── app.js # view overview + scorecard drill-down
|
|
└── styles.css # cards health grid
|
|
```
|
|
|
|
## Phase 0: Research
|
|
|
|
Ver [research.md](./research.md).
|
|
|
|
## Phase 1: Data Model
|
|
|
|
```sql
|
|
CREATE TABLE audit_domains (
|
|
id INTEGER PRIMARY KEY,
|
|
tenant_id INTEGER NOT NULL,
|
|
domain TEXT NOT NULL,
|
|
source TEXT NOT NULL DEFAULT 'onboarding',
|
|
created_at TEXT NOT NULL,
|
|
UNIQUE(tenant_id, domain)
|
|
);
|
|
|
|
CREATE TABLE audit_checks (
|
|
id INTEGER PRIMARY KEY,
|
|
tenant_id INTEGER NOT NULL,
|
|
domain TEXT NOT NULL,
|
|
check_id TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
message TEXT,
|
|
evidence TEXT,
|
|
checked_at TEXT NOT NULL,
|
|
UNIQUE(tenant_id, domain, check_id)
|
|
);
|
|
```
|
|
|
|
## Phase 2: Collectors
|
|
|
|
| Module | Checks |
|
|
|--------|--------|
|
|
| `vm112.py` | carbonio, nginx_vhost, cert_le |
|
|
| `dns.py` | dns_mx, dns_spf, dns_dkim, dns_dmarc |
|
|
| `webmail.py` | webmail_http |
|
|
|
|
Runner: `run_audit(tenant_id, domain) -> dict[check_id, result]`
|
|
|
|
## Phase 3: API
|
|
|
|
- `GET /api/v1/audit/overview`
|
|
- `GET /api/v1/audit/tenants/{id}/scorecard?domain=`
|
|
- `POST /api/v1/audit/run/{tenant_id}?domain=` (manual trigger, ops use)
|
|
|
|
## Phase 4: Worker
|
|
|
|
- Env `AUDIT_INTERVAL_SEC=600`
|
|
- A cada ciclo: list domains → run_audit → upsert audit_checks
|
|
- Auto-register domains from `webhook_events` where event in (`account.created`, `onboarding.completed`)
|
|
|
|
## Phase 5: UI
|
|
|
|
- Nova tab **Overview** ou substituir Infra básica
|
|
- Grid cards: tenant name, score X/8, status badge, last audit
|
|
- Click → scorecard modal/panel com 8 rows
|
|
|
|
## Implementation Phases (time estimate)
|
|
|
|
| Phase | Tasks | ~Time |
|
|
|-------|-------|-------|
|
|
| A Schema + store | T001-T003 | 1h |
|
|
| B Collectors | T004-T008 | 3h |
|
|
| C API | T009-T011 | 1.5h |
|
|
| D Worker | T012-T014 | 1.5h |
|
|
| E UI | T015-T018 | 2h |
|
|
| F Test + deploy | T019-T021 | 1h |
|
|
|
|
**Total ~10h** — pode paralelizar com 004 após API base pronta.
|
|
|
|
## Risk & Mitigation
|
|
|
|
| Risco | Mitigação |
|
|
|-------|-----------|
|
|
| Portal API lenta | Timeout 10s por check; partial results |
|
|
| DNS rate limit | Cache 10 min; sequential checks |
|
|
| Falso negativo DKIM | evidence field com TXT encontrado |
|
|
| Worker sobrecarga | 1 tenant MVP; queue single-thread |
|
|
|
|
## Sequencing with 004
|
|
|
|
- **Paralelo possível**: equipas diferentes (004 portal+funil, 009 worker+UI)
|
|
- **Dependência soft**: 009 domain auto-discovery beneficia de 004 `onboarding.completed` mas funciona com `account.created` existente
|
|
- **Recomendação**: implementar 004 Phase A primeiro; 009 Phase A-B em paralelo; UI 004+009 na mesma sprint UI
|