feat: Phase 4+5 — admin storage UI, backend proxy, CLAUDE.md enforcement

- backend/app/routers/storage_config.py: 5 admin-only endpoints proxying
  storage-service config + migration API (GET/PATCH/POST/DELETE)
- backend/app/main.py: register storage_config router
- frontend/src/api/client.ts: StorageStatus, MigrationStatus,
  StorageBackendConfig interfaces + 5 API functions
- frontend/src/pages/StorageAdminPage.tsx: full admin UI — backend health
  dot, driver selector (local/S3/WebDAV), conditional credential fields,
  Test & Migrate button, live 2s-poll migration progress bar, Cancel
- frontend/src/App.tsx: /admin/storage route (AdminRoute guard)
- CLAUDE.md: storage enforcement rule, updated Docker tables (6 services,
  3 volumes), §20 in merge checklist
- backend/CLAUDE.md, frontend/CLAUDE.md, doc-service/CLAUDE.md,
  ai-service/CLAUDE.md: updated to reflect storage-service integration
- tests/ALL_TESTS.md + tests/storage-service_tests.md: §20 (20 tests)
- backend/STATUS.md, frontend/STATUS.md: updated with new endpoints/routes
- changelog/2026-04-20_storage-service.md: full change log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-04-20 16:13:05 +02:00
parent 4c35d7a2a4
commit cfec3bb906
15 changed files with 746 additions and 22 deletions
+14 -2
View File
@@ -36,7 +36,8 @@ backend/
│ │ ├── config.py ← All settings via pydantic-settings (reads .env)
│ │ ├── security.py ← JWT sign/verify (RS256), bcrypt hash/verify
│ │ ├── sanitize.py ← Input sanitization helpers (see Security Standards)
│ │ ── app_config.py ← Per-service config load/save to /config volume; theme files in /config/themes/
│ │ ── app_config.py ← Per-service config load/save via storage-service; theme files in config/themes/{id}.json
│ │ └── config_storage.py ← Thin async HTTP helpers: read_json/write_json/delete_key/list_keys → storage-service config bucket
│ ├── models/
│ │ ├── __init__.py ← Imports all models (required for Alembic autogenerate)
│ │ ├── user.py ← User model
@@ -56,7 +57,8 @@ backend/
│ │ ├── services.py ← GET /services (health status)
│ │ ├── plugins.py ← Generic plugin proxy (GET/PATCH /api/plugins/*)
│ │ ├── categories_proxy.py ← Transparent proxy → doc-service /categories/*
│ │ ── documents_proxy.py ← Transparent proxy → doc-service /documents/*
│ │ ── documents_proxy.py ← Transparent proxy → doc-service /documents/*
│ │ └── storage_config.py ← Admin proxy → storage-service config + migration endpoints
│ └── services/
│ ├── service_health.py ← Background 30s health-check loop; caches /plugin/manifest per service
│ └── group_bootstrap.py ← Ensures {service-id}-admin group exists for every registered service at startup
@@ -216,6 +218,16 @@ Unique constraint: `(group_id, user_id)`
Auth: is_superuser OR member of group listed in manifest `required_groups`. Returns 404 (not 403) to hide existence.
### Admin — Storage (`/api/admin`) — admin-only
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/admin/storage-config` | Current backend driver + health → proxied from storage-service `/health` |
| PATCH | `/api/admin/storage-config` | Reconfigure backend without data migration (same-backend credential update) |
| POST | `/api/admin/storage-config/migrate` | Start async migration to a new backend (copy → verify → switch → cleanup) |
| GET | `/api/admin/storage-config/migrate/status` | Poll migration progress: `{state, total, done, failed, errors[]}` |
| DELETE | `/api/admin/storage-config/migrate` | Cancel a running migration; old backend remains active |
### Documents and Categories — proxied
`/api/documents/*` and `/api/documents/categories/*` are transparently proxied to `doc-service:8001`. The backend injects `x-user-id`, `x-user-groups`, and `x-user-is-admin` headers. See `features/doc-service/CLAUDE.md` for the internal endpoint list.