feat: align LegacyHUB with TeamHUB platform contract (D2/D4, assets, security)
Close 12 audit-driven platform-compliance gaps on a single branch. - D4 dispatch: app/integrations/dispatch_client.py participant `legacyhub`, emits LegacyhubDocumentIndexed + AssetDerivativeReady after the indexing commit (idempotent uuid5), http_inbox route (reindex/tombstone) with audit-based dedupe; docs/dispatch-contract.md. Celery+Redis stays intra-module. - D2 SSO: app/integrations/identity.py validates X-TeamHub-* + role/scope mapper; security.py adds trusted-header enforcement (AUTH_REQUIRE_IDENTITY) and a scope check on /search; docker-compose.teamhub.yml (external teamhub_net + internal db net, api not host-published); RUNBOOK network/firewall section. - Asset standard: SearchHit/Citation carry asset_id/owner_module; buckets renamed teamhub-legacyhub-* (+quarantine/tmp/exports); purge-by-asset_id with legal-hold guard (app/indexing/projection.py); OCR-markdown derivative event. - audit_log model + Alembic 0003 + record_audit on writes (same transaction). - Secret masking: app/common/json_logger.py recursive mask wired into structlog (+ensure_ascii=False); event payloads redacted before persistence. - Service X-API-Key mandatory on ingest endpoints (defence-in-depth). - Port: host API 8000->8050 (collision with SalesHUB/MailHUB resolved), container still listens on 8000. - Config: no plaintext secret defaults; fail-loud in non-dev (no value leak). - Docs drift: README PG 5440, layered-auth note, 5173 removed from CORS; ingest/folder gated by ENABLE_FOLDER_INGEST (410 by default). - ADRs: layers mapping, shared-core extraction, UI locale (RU-first). Tests: 78 passing (ruff, compileall, pytest, tsc, vite build, compose config). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
55
docs/ADR-layers.md
Normal file
55
docs/ADR-layers.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# ADR: Layer mapping LegacyHUB ↔ platform CONVENTIONS
|
||||
|
||||
Status: accepted · 2026-06-15 · scope: `legacy-knowledge-indexer`
|
||||
|
||||
## Контекст
|
||||
|
||||
Платформенный `CONVENTIONS.md` описывает каноничную структуру pattern-B модуля:
|
||||
|
||||
```
|
||||
app/{main, config, db, api, models, repositories, services, integrations, workers}
|
||||
```
|
||||
|
||||
Исторический layout LegacyHUB сложился вокруг доменов обработки (`ingestion`,
|
||||
`indexing`, `storage`), а не вокруг технических слоёв. Имена папок не совпадают
|
||||
с конвенцией, но **доменные слои присутствуют**.
|
||||
|
||||
## Решение
|
||||
|
||||
Не переименовывать рабочие пакеты. Зафиксировать маппинг документально. Перекладка
|
||||
проверенного OCR/Markdown/Search-кода ради совпадения имён — риск регрессий без
|
||||
функциональной выгоды (governance: размер/имя изменения не повод для рефактора).
|
||||
Новые платформенные слои (`integrations/`, аудит) добавлены явно.
|
||||
|
||||
| Канон CONVENTIONS | Фактический пакет | Назначение |
|
||||
|---|---|---|
|
||||
| `main` | `app/main.py` | FastAPI composition shell, CORS, middleware, lifespan |
|
||||
| `config` | `app/config.py` | pydantic-settings, единственный читатель env |
|
||||
| `db` | `app/db/session.py` | engine + `session_scope` транзакция |
|
||||
| `models` | `app/db/models.py` | ORM (`documents`, `chunks`, `asset_ingest_jobs`, `audit_log`, …) |
|
||||
| `repositories` | `app/db/models.py` + `app/db/audit.py` + `app/storage/artifacts.py` | data-access; запись `audit_log` на write через `record_audit` |
|
||||
| `services` | `app/ingestion/*` + `app/indexing/*` | бизнес-пайплайн: OCR → извлечение → chunk → index → search |
|
||||
| `integrations` | `app/integrations/` (`identity`, `dispatch_client`) | seam к шлюзу/SSO и шине dispatch |
|
||||
| `workers` | `app/workers/` | Celery app + tasks (внутримодульный фон на Redis) |
|
||||
| (infra/helpers) | `app/storage/*`, `app/utils/*` | MinIO/S3, hashing, language, pdf, text |
|
||||
|
||||
## Аудит на write
|
||||
|
||||
`CONVENTIONS` и `03_INTEGRATION_STANDARDS §10` требуют запись в `audit_log` в той
|
||||
же транзакции, что и мутация. Реализация: `app/db/audit.py::record_audit(db, …)`
|
||||
вызывается внутри активного `session_scope`. Покрытые write-операции:
|
||||
|
||||
- `knowledge_ingest.register_verified_manifest` → `asset.ingest.registered`
|
||||
- `routes_ingestion.ingest_folder` → `ingest.folder.queued`
|
||||
- (P4) переиндексация и purge проекций → `asset.reindex` / `asset.projection.purged`
|
||||
|
||||
## Слой/границы
|
||||
|
||||
Соблюдается направление `shell → router → service → repository/infra`. Feature-код
|
||||
не импортирует composition shell. Кросс-доменные обращения идут через `integrations/`.
|
||||
|
||||
## Последствия
|
||||
|
||||
- Onboarding-ревьюер сверяет слои по этой таблице, а не по именам папок.
|
||||
- Будущий вынос `ingestion`/`indexing` в shared-core (см. `docs/ADR-shared-core.md`)
|
||||
не меняет этот маппинг — ядро становится зависимостью слоя `services`.
|
||||
Reference in New Issue
Block a user