chore: Delete all outdated plan files (Sanierungsplan, FIX-PLAN, UMBAU_PLAN, etc.)
Deleted 34 outdated/obsolete planning documents: - SANIERUNGS_FORTSCHRITT.md, UMBAU_PLAN.md, FIX-PLAN.md, FIX-PLAN-V2.md - MASTER-PLAN.md, PLUGIN-SYSTEM-UMBAUPLAN.md, PROGRESS.md - ENTERPRISE_RBAC_PLAN.md, RBAC_PROGRESS.md - docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md, docs/RECOVERY_SCOPE.md - docs/phase0_phase1_acceptance_report.md, docs/phase0_error_list.md - quality-gate-phase1/2/2-r2/2-r3.md, security-review-phase2.md - requirements.md, requirements-review.md, test_report.md - frontend-gap-analysis.md, codebase-vs-requirements.md - architecture-feasibility-review.md, extracted-architecture-details.md - docs/migration_history_audit.md, docs/infrastructure_audit_report.md - docs/RECOVERY_ACCEPTANCE_REPORT.md, AGENTS.md.bak Also: Removed Sanierungsplan reference from alembic migration comment
This commit is contained in:
-571
@@ -1,571 +0,0 @@
|
||||
# LeoCRM — AGENTS.md
|
||||
|
||||
**Projekt:** leocrm
|
||||
**Erstellt:** 2026-06-28
|
||||
**Status:** Draft — ready for implementation
|
||||
|
||||
---
|
||||
|
||||
## 1. Build & Test Commands
|
||||
|
||||
### Backend (Python / FastAPI)
|
||||
|
||||
#### Setup
|
||||
```bash
|
||||
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
#### Run Dev Server
|
||||
```bash
|
||||
|
||||
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
#### Database Migrations (Alembic)
|
||||
```bash
|
||||
|
||||
# Generate migration after model changes
|
||||
alembic revision --autogenerate -m "description"
|
||||
# Apply migrations
|
||||
alembic upgrade head
|
||||
# Rollback one migration
|
||||
alembic downgrade -1
|
||||
```
|
||||
|
||||
#### Run All Backend Tests
|
||||
```bash
|
||||
|
||||
python -m pytest -v --tb=short
|
||||
```
|
||||
|
||||
#### Run Specific Test File
|
||||
```bash
|
||||
|
||||
python -m pytest tests/test_auth.py -v --tb=short
|
||||
```
|
||||
|
||||
#### Run Tests with Coverage
|
||||
```bash
|
||||
|
||||
python -m pytest --cov=app --cov-report=term-missing --cov-report=html
|
||||
```
|
||||
|
||||
#### Run Tests with Grep Filter
|
||||
```bash
|
||||
|
||||
python -m pytest -k 'tenant or auth' -v
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
```bash
|
||||
|
||||
mypy app/ --ignore-missing-imports
|
||||
```
|
||||
|
||||
#### Linting
|
||||
```bash
|
||||
|
||||
ruff check app/
|
||||
ruff format app/
|
||||
```
|
||||
|
||||
### Frontend (React / Vite / TypeScript)
|
||||
|
||||
#### Setup
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
```
|
||||
|
||||
#### Run Dev Server
|
||||
```bash
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
#### Build Production
|
||||
```bash
|
||||
cd frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
#### Run All Frontend Tests
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest run --reporter=verbose
|
||||
```
|
||||
|
||||
#### Run Tests with Coverage
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest run --coverage
|
||||
```
|
||||
|
||||
#### Run Tests in Watch Mode (dev)
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest watch
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
```bash
|
||||
cd frontend
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
#### Linting
|
||||
```bash
|
||||
cd frontend
|
||||
npx eslint src/ --ext .ts,.tsx
|
||||
```
|
||||
|
||||
### Docker Compose (Full Stack)
|
||||
|
||||
#### Build All Services
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
#### Start All Services
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
#### View Logs
|
||||
```bash
|
||||
docker compose logs -f backend
|
||||
```
|
||||
|
||||
#### Stop All Services
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
#### Validate Compose Config
|
||||
```bash
|
||||
docker compose config --quiet
|
||||
```
|
||||
|
||||
### E2E Tests (Playwright)
|
||||
|
||||
```bash
|
||||
cd e2e
|
||||
npx playwright install
|
||||
npx playwright test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Test Rules
|
||||
|
||||
### TDD (Test-Driven Development)
|
||||
|
||||
- **Red-Green-Refactor:** Write failing test first → implement minimum code to pass → refactor.
|
||||
- **Every new endpoint gets a test BEFORE implementation.**
|
||||
- **Every bug fix starts with a reproduction test.**
|
||||
|
||||
### Coverage Targets
|
||||
|
||||
| Layer | Coverage Target | Measured By |
|
||||
|-------|----------------|-------------|
|
||||
| Backend Core (app/core/) | 85% | pytest-cov |
|
||||
| Backend Models+Services | 85% | pytest-cov |
|
||||
| Backend Routes | 85% | pytest-cov |
|
||||
| Backend Plugins | 80% | pytest-cov |
|
||||
| Frontend Components | 75% | vitest coverage |
|
||||
| Frontend Plugin UI | 70% | vitest coverage |
|
||||
| E2E (critical paths) | 100% of defined specs | Playwright |
|
||||
|
||||
### Test File Structure
|
||||
|
||||
#### Backend
|
||||
```
|
||||
backend/tests/
|
||||
├── conftest.py — Fixtures: test client, test DB, auth helpers, seed data
|
||||
├── test_auth.py — Auth endpoints, RBAC, password reset
|
||||
├── test_tenant.py — Tenant isolation, cross-tenant access
|
||||
├── test_companies.py — Company CRUD, search, filter, pagination, soft-delete
|
||||
├── test_contacts.py — Contact CRUD, N:M links, GDPR delete
|
||||
├── test_import_export.py — CSV import/export, XLSX export, dry-run preview
|
||||
├── test_plugins.py — Plugin lifecycle, event bus, migrations
|
||||
├── test_dms.py — DMS folders, files, upload, shares, permissions
|
||||
├── test_calendar.py — Entries, recurrence, kanban, ICS, resources
|
||||
├── test_mail.py — Accounts, IMAP sync, send, threading, rules, PGP
|
||||
├── test_tags.py — Tag CRUD, assignment, bulk
|
||||
├── test_notifications.py — Notification CRUD, unread count
|
||||
├── test_health.py — Health endpoint
|
||||
├── test_ai_copilot.py — KI-Copilot API, RBAC enforcement, history
|
||||
├── test_workflows.py — Workflow CRUD, instances, approval/rejection, event triggers
|
||||
├── test_monitoring.py — Extended health, Prometheus metrics, alerting
|
||||
└── test_performance.py — 200k seed, list <500ms, FTS <500ms, streaming export
|
||||
```
|
||||
|
||||
#### Frontend
|
||||
```
|
||||
frontend/src/__tests__/
|
||||
├── components/ — UI component unit tests (Button, Input, Modal, Table, etc.)
|
||||
├── features/ — Feature integration tests (CompanyList, ContactForm, etc.)
|
||||
├── hooks/ — Custom hook tests (useDebounce, usePagination, etc.)
|
||||
├── plugins/ — Plugin UI tests (DMS, Calendar, Mail, Tags)
|
||||
└── search/ — Global search tests
|
||||
```
|
||||
|
||||
#### E2E
|
||||
```
|
||||
e2e/
|
||||
├── auth.spec.ts — Login → logout flow
|
||||
├── company-crud.spec.ts — Create → edit → delete company
|
||||
├── contact-crud.spec.ts — Create → link to company → delete
|
||||
├── search.spec.ts — Global search
|
||||
└── plugin-toggle.spec.ts — Activate/deactivate plugin
|
||||
```
|
||||
|
||||
### Test Conventions
|
||||
|
||||
- **Test names:** `test_<action>_<condition>_<expected_result>` (e.g., `test_login_with_invalid_credentials_returns_401`)
|
||||
- **Test structure:** Arrange → Act → Assert (AAA pattern)
|
||||
- **Fixtures:** Use `conftest.py` for shared fixtures. No fixture duplication across files.
|
||||
- **Test DB:** Use in-memory or ephemeral PostgreSQL (via testcontainers or pytest-postgresql). NEVER test against production DB.
|
||||
- **Mocking:** Mock external services (SMTP, IMAP, OnlyOffice) in tests. Use `unittest.mock.AsyncMock` for async mocks.
|
||||
- **Assertions:** Use pytest's native `assert` for backend, `expect()` from `@testing-library/jest-dom` for frontend.
|
||||
- **No flaky tests:** Tests must be deterministic. Use explicit waits, not sleeps.
|
||||
- **Test isolation:** Each test must be independent. No test depends on another test's side effects.
|
||||
|
||||
### Don't Modify Tests Rule
|
||||
|
||||
- **NEVER modify existing tests to make them pass.** If a test fails, fix the code, not the test.
|
||||
- **Exception:** If the test itself is wrong (testing incorrect behavior), document why and get approval before changing.
|
||||
- **Test files are owned by the QA process, not the implementer.**
|
||||
|
||||
---
|
||||
|
||||
## 3. Conventions
|
||||
|
||||
### Backend Structure
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── main.py — FastAPI app entry point, lifespan, middleware registration
|
||||
├── config.py — Pydantic Settings (reads from env vars)
|
||||
├── deps.py — FastAPI dependency injection (auth, db, tenant, permissions)
|
||||
├── core/ — Core infrastructure (cross-cutting concerns)
|
||||
│ ├── db/ — SQLAlchemy engine, session factory, base model
|
||||
│ ├── tenant.py — TenantMixin, ORM auto-filter, tenant context
|
||||
│ ├── auth.py — Session auth, password hashing (bcrypt), RBAC
|
||||
│ ├── event_bus.py — Async in-process event bus
|
||||
│ ├── service_container.py — DI container
|
||||
│ ├── storage.py — File storage (local/S3)
|
||||
│ ├── cache.py — Redis cache wrapper
|
||||
│ ├── jobs.py — ARQ job queue integration
|
||||
│ ├── notifications.py — Notification service
|
||||
│ └── audit.py — Audit log middleware
|
||||
├── models/ — SQLAlchemy ORM models (one file per domain)
|
||||
├── schemas/ — Pydantic schemas (request/response, one file per domain)
|
||||
├── services/ — Business logic (one file per domain)
|
||||
├── routes/ — FastAPI routers (one file per domain)
|
||||
├── plugins/ — Plugin system
|
||||
│ ├── registry.py — Plugin discovery, registration
|
||||
│ ├── manifest.py — Plugin manifest Pydantic schema
|
||||
│ ├── lifecycle.py — Install/activate/deactivate/uninstall
|
||||
│ ├── migrations.py — Plugin DB migration runner
|
||||
│ ├── ui_registry.py — Plugin UI component registration
|
||||
│ └── builtins/ — Built-in plugins
|
||||
│ ├── dms/ — DMS plugin
|
||||
│ ├── calendar/ — Calendar plugin
|
||||
│ ├── mail/ — Mail plugin
|
||||
│ └── tags/ — Tags plugin
|
||||
└── utils/ — Shared utilities (validation, export, import)
|
||||
```
|
||||
|
||||
### Backend Naming Conventions
|
||||
|
||||
- **Files:** `snake_case.py` (e.g., `company_service.py`)
|
||||
- **Classes:** `PascalCase` (e.g., `CompanyService`, `CompanyModel`)
|
||||
- **Functions/Methods:** `snake_case` (e.g., `get_company_by_id`)
|
||||
- **Constants:** `UPPER_SNAKE_CASE` (e.g., `SESSION_TIMEOUT_HOURS`)
|
||||
- **Models:** `<Entity>Model` suffix or just `<Entity>` (e.g., `Company`, `Contact`)
|
||||
- **Schemas:** `<Entity>Create`, `<Entity>Update`, `<Entity>Read`, `<Entity>List` (Pydantic)
|
||||
- **Services:** `<Entity>Service` (e.g., `CompanyService`)
|
||||
- **Routers:** `<entity>_router` variable, file name `<entity>_router.py`
|
||||
- **Tests:** `test_<domain>.py` (e.g., `test_companies.py`)
|
||||
|
||||
### Backend Code Conventions
|
||||
|
||||
- **Async first:** All route handlers and service methods are `async def`.
|
||||
- **Type hints:** All function signatures have type hints (Python 3.12+ syntax).
|
||||
- **Docstrings:** All public functions/classes have docstrings (Google style).
|
||||
- **Error handling:** Use FastAPI `HTTPException` with proper status codes. Never raise generic `Exception`.
|
||||
- **Validation:** Pydantic schemas validate input. Never validate in routes directly.
|
||||
- **Tenant scoping:** Never query without tenant filter (ORM auto-filter handles this, but be aware).
|
||||
- **UUID:** All IDs are UUID. Never use integer auto-increment.
|
||||
- **Timestamps:** All datetime fields are `TIMESTAMPTZ`. Never use naive datetime.
|
||||
- **Soft-delete:** Use `deleted_at IS NULL` filter. Never hard-delete without explicit `gdpr=true` flag.
|
||||
- **Audit:** All mutations must create audit log entries. Use the audit middleware/decorator.
|
||||
|
||||
### Frontend Structure
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── main.tsx — React entry point
|
||||
├── App.tsx — Root component, router, providers
|
||||
├── api/ — API client (axios), interceptors, endpoint definitions
|
||||
├── components/ — Shared UI components
|
||||
│ ├── layout/ — Shell, Sidebar, TopBar, ContentArea
|
||||
│ ├── ui/ — Button, Input, Select, Modal, Toast, Table, Card, Badge, Avatar
|
||||
│ └── shared/ — EmptyState, LoadingState, ConfirmDialog, Pagination, Skeleton
|
||||
├── features/ — Feature modules (one folder per feature)
|
||||
│ ├── auth/ — Login, PasswordReset
|
||||
│ ├── companies/ — CompanyList, CompanyDetail, CompanyForm
|
||||
│ ├── contacts/ — ContactList, ContactDetail, ContactForm
|
||||
│ ├── settings/ — SettingsTree, ProfileSettings, RoleEditor
|
||||
│ ├── audit/ — AuditLog
|
||||
│ ├── dashboard/ — Dashboard
|
||||
│ └── search/ — GlobalSearch
|
||||
├── plugins/ — Plugin UI loading framework
|
||||
│ ├── PluginRegistry.tsx — Fetch manifests, register components
|
||||
│ └── PluginLoader.tsx — Dynamic lazy-loading of plugin components
|
||||
├── hooks/ — Custom React hooks (useDebounce, usePagination, useAuth, etc.)
|
||||
├── store/ — Zustand stores (useAuthStore, useUIStore, useTenantStore)
|
||||
├── i18n/ — react-i18next setup + locale files (de.json, en.json)
|
||||
├── styles/ — Global CSS, design tokens (Tailwind config), accessibility
|
||||
└── utils/ — Utilities (format, validation, export, constants)
|
||||
```
|
||||
|
||||
### Frontend Naming Conventions
|
||||
|
||||
- **Files:** `PascalCase.tsx` for components (e.g., `CompanyList.tsx`), `camelCase.ts` for utilities (e.g., `apiClient.ts`)
|
||||
- **Components:** `PascalCase` (e.g., `CompanyList`, `ContactForm`)
|
||||
- **Hooks:** `use<Feature>` (e.g., `useDebounce`, `useAuth`)
|
||||
- **Stores:** `use<Domain>Store` (e.g., `useAuthStore`, `useUIStore`)
|
||||
- **Types/Interfaces:** `PascalCase` (e.g., `CompanyData`, `ContactFormValues`)
|
||||
- **API functions:** `camelCase` (e.g., `getCompanies`, `createContact`)
|
||||
- **Test files:** `<Component>.test.tsx` next to component or in `__tests__/` mirror
|
||||
|
||||
### Frontend Code Conventions
|
||||
|
||||
- **TypeScript strict:** `strict: true` in tsconfig.json. No `any` types.
|
||||
- **Functional components:** Only function components, no class components.
|
||||
- **Hooks:** Custom hooks for reusable logic. No inline hooks in JSX.
|
||||
- **TanStack Query:** Server state via `useQuery` / `useMutation`. No manual fetch in components.
|
||||
- **Zustand:** Client state only (UI toggles, theme, active tenant). No server data in Zustand.
|
||||
- **React Hook Form + Zod:** All forms use `react-hook-form` with `zodResolver`.
|
||||
- **Tailwind CSS:** No custom CSS files (except global + accessibility). Use Tailwind utility classes.
|
||||
- **i18n:** All user-visible strings go through `t()` from `react-i18next`. No hardcoded strings.
|
||||
- **Accessibility:** ARIA attributes on all interactive elements. 44px touch targets. Keyboard navigation.
|
||||
- **Lazy loading:** Plugin components use `React.lazy()` with `Suspense` boundaries.
|
||||
|
||||
### Git Conventions
|
||||
|
||||
- **Branch naming:** `feature/T01-core-infrastructure`, `fix/auth-tenant-isolation`, `hotfix/critical-bug`
|
||||
- **Commit messages:** Conventional Commits format:
|
||||
- `feat(core): implement auth system with session-based login`
|
||||
- `fix(dms): resolve folder permission bypass on move`
|
||||
- `test(mail): add IMAP sync integration tests`
|
||||
- `refactor(calendar): extract recurrence engine to separate module`
|
||||
- `docs(architecture): update ADR-03 with plugin lifecycle details`
|
||||
- **PR titles:** `[T01] Core Infrastructure + Multi-Tenant + Auth System`
|
||||
- **Branch from:** `main` (or feature branch for sub-features)
|
||||
- **Merge strategy:** Squash merge to `main` after review + CI passes
|
||||
|
||||
---
|
||||
|
||||
## 4. Task-Zuweisung (Subagenten pro Task)
|
||||
|
||||
### Phasen-Plan
|
||||
|
||||
#### v1 Core Phases (Phase 3 — Implementation)
|
||||
|
||||
| Phase | Tasks | Parallel | Subagent Profile | Description |
|
||||
|-------|-------|----------|-------------------|-------------|
|
||||
| 1 | T01 | No | implementation_engineer | Foundation: Core, Auth, Multi-Tenant, RLS, Rate Limiting |
|
||||
| 2 | T02, T03 | Yes (2 agents) | implementation_engineer ×2 | Core entities + Plugin framework parallel |
|
||||
| 3 | T07a, T09 | Yes (2 agents) | implementation_engineer ×2 | Frontend Shell+Auth+UI Library + KI-Copilot/Workflow parallel |
|
||||
| 4 | T07b | No | implementation_engineer | Frontend Feature Pages (Companies, Contacts, Settings, Dashboard, Search) |
|
||||
| 5 | T10 | No | implementation_engineer | Monitoring, Performance, Doku, Environment Config |
|
||||
|
||||
#### v2 Plugin Phases (nach v1 Deployment)
|
||||
|
||||
| Phase | Tasks | Parallel | Subagent Profile | Description |
|
||||
|-------|-------|----------|-------------------|-------------|
|
||||
| 6 | T04, T05, T06, T11 | Yes (4 agents) | implementation_engineer ×4 | DMS, Calendar, Mail, Tags+Permissions backends parallel |
|
||||
| 7 | T08a, T08b, T08c | Yes (3 agents) | implementation_engineer ×3 | Frontend DMS+Tags, Calendar, Mail+Search parallel |
|
||||
|
||||
### Task-to-Subagent Mapping
|
||||
|
||||
| Task ID | Title | Subagent | Dependencies | Phase | Scope |
|
||||
|---------|-------|----------|--------------|-------|-------|
|
||||
| T01 | Core Infrastructure + Multi-Tenant + Auth | implementation_engineer | — | 1 | v1 |
|
||||
| T02 | Company + Contact + Import/Export | implementation_engineer | T01 | 2 | v1 |
|
||||
| T03 | Plugin System Framework | implementation_engineer | T01 | 2 | v1 |
|
||||
| T07a | Frontend SPA — Shell, Auth, Routing, i18n, UI Library | implementation_engineer | T01 | 3 | v1 |
|
||||
| T07b | Frontend SPA — Companies, Contacts, Settings, Dashboard, Search | implementation_engineer | T01, T02, T07a | 4 | v1 |
|
||||
| T09 | KI-Copilot + Workflow Engine | implementation_engineer | T01, T02 | 3 | v1 |
|
||||
| T10 | Monitoring + Performance + Doku + Env Config | implementation_engineer | T01, T02 | 5 | v1 |
|
||||
| T04 | DMS Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T05 | Calendar Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T06 | Mail Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T11 | Tags + Permissions + Entity Links Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T08a | Frontend DMS + Tags + Permissions UI | implementation_engineer | T04, T07b | 7 | v2 |
|
||||
| T08b | Frontend Calendar UI | implementation_engineer | T05, T07b | 7 | v2 |
|
||||
| T08c | Frontend Mail + Global Search UI | implementation_engineer | T06, T07b | 7 | v2 |
|
||||
|
||||
### Parallelization Notes
|
||||
|
||||
**v1 Phases:**
|
||||
- **Phase 2:** T02 (Company/Contact) and T03 (Plugin Framework) are independent after T01 — safe to run in parallel.
|
||||
- **Phase 3:** T07a (Frontend Shell+Auth+UI Library) depends only on T01. T09 (KI/Workflow) depends on T01+T02. Both can run in parallel if API contracts are frozen.
|
||||
- **Phase 4:** T07b (Frontend Feature Pages) depends on T07a (UI library, routing, auth) + T02 (company/contact API). Must run after T07a.
|
||||
- **Phase 5:** T10 (Monitoring+Doku) depends on T01+T02. Can run parallel with T07b.
|
||||
|
||||
**v2 Phases (after v1 deployment):**
|
||||
- **Phase 6:** T04 (DMS), T05 (Calendar), T06 (Mail), T11 (Tags+Perm) all depend on T01+T03 — safe to run in parallel.
|
||||
- **Phase 7:** T08a/T08b/T08c depend on T07b + respective backend (T04/T05/T06) — safe to run in parallel.
|
||||
|
||||
### Block Rules
|
||||
|
||||
- Block = max 3 Tasks per implementation block.
|
||||
- After each block: quality_reviewer review → block_compactor → context_compactor → User checkpoint.
|
||||
- quality_reviewer and release_auditor do NOT count toward the 3-task limit.
|
||||
- After 3 blocks (9 tasks): release_auditor runs full audit.
|
||||
- Token budget: ~3000 tokens per task. If tool result >5000 tokens: context_compactor.
|
||||
|
||||
---
|
||||
|
||||
## 5. Forbidden Patterns
|
||||
|
||||
### Backend Forbidden
|
||||
|
||||
- ❌ **SQLite:** No SQLite as database. PostgreSQL 16 only (ADR-01).
|
||||
- ❌ **Jinja2:** No server-side HTML rendering. API-only backend (ADR-03).
|
||||
- ❌ **Cross-Tenant Data Access:** No query without tenant_id filter. ORM auto-filter must not be bypassed.
|
||||
- ❌ **Plaintext Passwords:** Passwords must be bcrypt-hashed (cost=12). Never store or log plaintext.
|
||||
- ❌ **JWT Tokens:** No JWT auth in v1. Session-based auth with HttpOnly cookies only (ADR-05).
|
||||
- ❌ **Naive Datetime:** All datetime fields must be timezone-aware (TIMESTAMPTZ). Never use `datetime.now()` without tz.
|
||||
- ❌ **Integer IDs:** All primary keys are UUID. Never use auto-increment integer IDs.
|
||||
- ❌ **Hard-Delete without GDPR flag:** Companies/Contacts use soft-delete. Hard-delete only with explicit `?gdpr=true`.
|
||||
- ❌ **Manual Tenant Filter:** Never manually add `.filter(Tenant.id == x)` in services. The ORM auto-filter handles this.
|
||||
- ❌ **Sync I/O in Routes:** All route handlers are `async def`. Never use blocking I/O (use `asyncpg`, `aiofiles`, etc.).
|
||||
- ❌ **Raw SQL without Tenant Check:** Any raw SQL query must explicitly include `tenant_id` filter.
|
||||
- ❌ **Secrets in Code:** No hardcoded secrets. All secrets via environment variables.
|
||||
- ❌ **Unvalidated Input:** All request bodies validated by Pydantic schemas. Never trust raw request data.
|
||||
- ❌ **Missing Audit Log:** All create/update/delete operations must create audit log entries.
|
||||
- ❌ **Plugin Tables without tenant_id:** All plugin-created tables must include `tenant_id` column. The migration validator enforces this.
|
||||
|
||||
### Frontend Forbidden
|
||||
|
||||
- ❌ **Class Components:** No class components. Functional components with hooks only.
|
||||
- ❌ **Inline Styles:** No `style={{}}` props. Use Tailwind utility classes.
|
||||
- ❌ **Hardcoded Strings:** No user-visible hardcoded strings. Use `t()` from i18n.
|
||||
- ❌ **Manual Fetch in Components:** No `fetch()` or `axios` calls in components. Use TanStack Query hooks.
|
||||
- ❌ **Server Data in Zustand:** Zustand is for client state only. Server data goes in TanStack Query.
|
||||
- ❌ **`any` Types:** No `any` type. Use proper TypeScript types.
|
||||
- ❌ **Missing ARIA Attributes:** All interactive elements must have ARIA labels.
|
||||
- ❌ **Touch Targets < 44px:** All buttons/links must have minimum 44px touch target.
|
||||
- ❌ **Direct DOM Manipulation:** No `document.getElementById()` or `querySelector()` in components. Use React refs.
|
||||
- ❌ **Unsafe HTML Rendering:** No `dangerouslySetInnerHTML` without sanitization. Mail bodies must be sanitized (DOMPurify equivalent).
|
||||
|
||||
### Deployment Forbidden
|
||||
|
||||
- ❌ **Running as Root in Container:** Containers run as non-root user (app:app).
|
||||
- ❌ **Exposed DB Port in Production:** PostgreSQL port (5432) must not be exposed externally in production.
|
||||
- ❌ **No Health Check:** All services must have Docker health checks configured.
|
||||
- ❌ **No Volume for Storage:** File storage must use a named volume, not ephemeral container storage.
|
||||
- ❌ **Secrets in docker-compose.yml:** No secrets in compose file. Use `.env` file or Docker secrets.
|
||||
|
||||
---
|
||||
|
||||
## 6. Quality Gates
|
||||
|
||||
### Per-Task Quality Gate
|
||||
|
||||
Before a task is marked complete:
|
||||
1. All test_spec commands must pass.
|
||||
2. Coverage target must be met (measured by pytest-cov / vitest coverage).
|
||||
3. TypeScript compiles without errors (`tsc --noEmit`).
|
||||
4. Linting passes (ruff for backend, eslint for frontend).
|
||||
5. Build succeeds (Vite build for frontend, no build step for backend).
|
||||
6. No forbidden patterns detected.
|
||||
7. All acceptance criteria verified as testable.
|
||||
|
||||
### Phase Gate (after each phase)
|
||||
|
||||
1. All tasks in the phase pass their quality gates.
|
||||
2. quality_reviewer subagent reviews the phase output.
|
||||
3. No critical issues from quality_reviewer.
|
||||
4. Block compactor saves progress.
|
||||
5. User checkpoint before next phase.
|
||||
|
||||
### Release Gate (before v1 deployment)
|
||||
|
||||
1. All 7 v1 tasks complete (T01, T02, T03, T07a, T07b, T09, T10).
|
||||
2. release_auditor runs full audit.
|
||||
3. Docker Compose builds and starts successfully.
|
||||
4. Health endpoint returns 200.
|
||||
5. E2E tests (Playwright) pass.
|
||||
6. All forbidden patterns checked.
|
||||
|
||||
### v2 Release Gate (before v2 plugin deployment)
|
||||
|
||||
1. All 7 v2 tasks complete (T04, T05, T06, T11, T08a, T08b, T08c).
|
||||
2. release_auditor runs full audit.
|
||||
3. All plugin backends + frontends pass quality gates.
|
||||
4. Plugin install/activate/deactivate lifecycle tested.
|
||||
5. All forbidden patterns checked.
|
||||
|
||||
---
|
||||
|
||||
## 7. Environment Setup
|
||||
|
||||
### Development Environment
|
||||
|
||||
| Variable | Value | Purpose |
|
||||
|----------|-------|---------|
|
||||
| `POSTGRES_HOST` | `localhost` (dev) / `postgres` (docker) | Database host |
|
||||
| `POSTGRES_PORT` | `5432` | Database port |
|
||||
| `POSTGRES_DB` | `leocrm` | Database name |
|
||||
| `POSTGRES_USER` | `leocrm` | Database user |
|
||||
| `POSTGRES_PASSWORD` | (from .env) | Database password |
|
||||
| `REDIS_URL` | `redis://localhost:6379/0` | Redis for cache + sessions + jobs |
|
||||
| `LEOCRM_SECRET_KEY` | (min 32 chars) | Session signing secret |
|
||||
| `SESSION_TIMEOUT_HOURS` | `8` | Session expiry |
|
||||
| `MAIL_ENCRYPTION_KEY` | (32-byte hex) | AES-256 key for mail credentials |
|
||||
| `STORAGE_BACKEND` | `local` (dev) / `s3` (prod) | File storage backend |
|
||||
| `STORAGE_PATH` | `/data/leocrm/storage` | Local storage path |
|
||||
| `ONLYOFFICE_URL` | `http://onlyoffice:80` | OnlyOffice document server |
|
||||
| `LOG_LEVEL` | `INFO` | Logging level |
|
||||
|
||||
### Test Environment
|
||||
|
||||
- Test DB: Ephemeral PostgreSQL (pytest-postgresql or testcontainers).
|
||||
- Test Redis: Ephemeral or fakeredis.
|
||||
- External services (IMAP, SMTP, OnlyOffice): Mocked via `unittest.mock.AsyncMock`.
|
||||
- Test fixtures in `conftest.py` provide: test client, authenticated client (per role), seeded data.
|
||||
|
||||
---
|
||||
|
||||
## 8. Architecture Reference
|
||||
|
||||
Full architecture details: `architecture.md`
|
||||
|
||||
Full task graph with test specs: `task_graph.json`
|
||||
|
||||
Key ADRs:
|
||||
- ADR-01: PostgreSQL 16 (not SQLite)
|
||||
- ADR-02: ARQ (not Celery)
|
||||
- ADR-03: Built-in plugins with manifest (not dynamic pip-install)
|
||||
- ADR-04: TanStack Query (not Redux)
|
||||
- ADR-05: Session-based auth (not JWT)
|
||||
- ADR-06: Soft-delete with `deleted_at` column
|
||||
|
||||
---
|
||||
|
||||
## Handoff
|
||||
|
||||
- **AGENTS.md status:** COMPLETE
|
||||
- **task_graph.json status:** COMPLETE (14 tasks: 7 v1 + 7 v2, all with test_spec, 143 features covered, v1/v2 separated, v2.1.0)
|
||||
- **architecture.md status:** COMPLETE (73/73 v1 features referenced, v2 sections marked)
|
||||
- **Ready for v1 implementation:** YES (pending quality_reviewer review + plan_mode transition to implementation_allowed)
|
||||
- **v2 implementation:** After v1 deployment, separate phase
|
||||
@@ -1,210 +0,0 @@
|
||||
# Enterprise RBAC Plan — LeoCRM
|
||||
|
||||
## Gesamt: 23 Sprints, 74 Features, 230h
|
||||
|
||||
### Sprint 1 — Fundament (14h)
|
||||
- [ ] entity_permissions Tabelle + expires_at + Migration 0049
|
||||
- [ ] OwnedMixin + owner_id auf allen Models + Migration 0050
|
||||
- [ ] Universeller Permission Service (CRUD + get_effective_access + get_visible_ids)
|
||||
- [ ] Universelle Permission API (5 Endpoints)
|
||||
- [ ] Redis-Cache für Entity-Permissions (Bitmap)
|
||||
- [ ] PostgreSQL RLS Policies + set_user_context()
|
||||
- [ ] Rate Limiting auf Permission-Änderungen
|
||||
- [ ] Folder ACLs in entity_permissions migrieren (Migration 0051)
|
||||
|
||||
### Sprint 2 — Row-Level Security (16h)
|
||||
- [ ] apply_visibility_filter() Helper
|
||||
- [ ] Query-Filter in alle 28 Routes
|
||||
- [ ] Child-Entity-Vererbung
|
||||
- [ ] Batch-Resolution
|
||||
- [ ] BaseSearchProvider mit Visibility-Filter
|
||||
- [ ] ContactDetail/ContactsList Permission-Checks
|
||||
- [ ] Copy/Duplicate Permission
|
||||
- [ ] EXISTS-Optimization für RLS
|
||||
|
||||
### Sprint 3 — Search/Dashboard/Export (13h)
|
||||
- [ ] GlobalSearch Visibility-Filter
|
||||
- [ ] Two-Phase Search
|
||||
- [ ] Search-Index Pre-Filter
|
||||
- [ ] Dashboard-Counts pro User
|
||||
- [ ] Export-Filter
|
||||
- [ ] Reports-Filter
|
||||
- [ ] Frontend-Filter für alle 4
|
||||
|
||||
### Sprint 4 — Field-Level komplett (10h)
|
||||
- [ ] Custom Field Sensitivity
|
||||
- [ ] Field Definitions für alle Entities + Plugin-Registration
|
||||
- [ ] filter_fields_by_permission() in alle Responses
|
||||
- [ ] Field-Level Permission Editor UI
|
||||
- [ ] Frontend: readonly/hidden in ContactDetail + ContactsList + DMS + Mail + AI
|
||||
|
||||
### Sprint 5 — Sharing UI (8h)
|
||||
- [ ] Universeller ShareDialog Komponente
|
||||
- [ ] Share-Button in 8 Detail-Ansichten
|
||||
- [ ] Owner-Spalte in 8 Listen
|
||||
- [ ] Permission-UI (Buttons ausblenden)
|
||||
- [ ] Permission-Expiration UI
|
||||
|
||||
### Sprint 6 — Notifications + Audit + Real-time (10h)
|
||||
- [ ] Permission-Change-Notifications
|
||||
- [ ] Audit-Trail für Permission-Änderungen
|
||||
- [ ] Notification-Entity-Filter
|
||||
- [ ] Real-time WebSocket Sync
|
||||
- [ ] Redis Pub/Sub für WebSocket Fan-Out
|
||||
|
||||
### Sprint 7 — E-Mail Postfächer (8h)
|
||||
- [ ] Mailbox owner_id + Migration
|
||||
- [ ] Mailbox Permissions (entity_permissions)
|
||||
- [ ] Mail Permission Migration
|
||||
- [ ] Mail-Query-Filter
|
||||
- [ ] Mail-Field-Level
|
||||
- [ ] Frontend: Mailbox-Liste + Mail-Liste + Mail-Detail
|
||||
|
||||
### Sprint 8 — Plugin Entities (14h)
|
||||
- [ ] DMS owner_id + Permissions + Migration
|
||||
- [ ] Calendar owner_id + Permissions + Migration
|
||||
- [ ] Tasks owner_id + Permissions + Migration
|
||||
- [ ] Kommunikation RBAC Migration
|
||||
- [ ] Entity Links Permission
|
||||
- [ ] Tags Permission
|
||||
- [ ] 15 Plugin Entity Registration
|
||||
- [ ] DMS Permission Migration
|
||||
- [ ] Folder-Path-Materialization
|
||||
- [ ] Frontend Permission-Checks für DMS + Calendar + Tasks
|
||||
|
||||
### Sprint 9 — App-Sichtbarkeit (7h)
|
||||
- [ ] Plugin Manifest permission Feld
|
||||
- [ ] tenant_plugin_activation Tabelle + API
|
||||
- [ ] Sidebar Permission-Filter
|
||||
- [ ] TopBar Permission-Filter
|
||||
- [ ] Settings-Navigation Permission-Filter
|
||||
- [ ] Route-Guards (ProtectedRoute)
|
||||
|
||||
### Sprint 10 — Advanced Security + AI + WebSocket (18h)
|
||||
- [ ] API-Token Scopes
|
||||
- [ ] Webhook Scope Filter
|
||||
- [ ] Workflow Scope Filter
|
||||
- [ ] Contact Merge Permission-Check
|
||||
- [ ] AI Copilot Permission-Aware (process_query + execute_action)
|
||||
- [ ] AI Tool Registry
|
||||
- [ ] AI System Prompt mit Permission-Context
|
||||
- [ ] AI Proactive Permission-Aware
|
||||
- [ ] AI UI Control Permission-Checks
|
||||
- [ ] MCP Permission-Scopes
|
||||
- [ ] Automation Permission-Checks
|
||||
- [ ] WebSocket Permission-Checks
|
||||
- [ ] Event Bus Permission-Filter
|
||||
- [ ] Frontend: AI + Notifications + Workflows + DedupMerge
|
||||
|
||||
### Sprint 11 — Owner Management (5h)
|
||||
- [ ] Owner-Transfer (Bulk) API
|
||||
- [ ] Auto-Transfer bei User-Deaktivierung
|
||||
- [ ] Backup/Restore Permissions
|
||||
- [ ] Frontend Owner-Transfer-UI
|
||||
|
||||
### Sprint 12 — Zentrale Einstellungsseite (9h)
|
||||
- [ ] Rechte-Settings-Page mit Tabs
|
||||
- [ ] Freigaben-Übersicht (Admin-Dashboard)
|
||||
- [ ] Audit-View für Permission-Changes
|
||||
- [ ] CustomFields Sensitivity UI
|
||||
- [ ] App-Sichtbarkeit-Tab
|
||||
|
||||
### Sprint 13 — ABAC Engine (18h)
|
||||
- [ ] entity_policies Tabelle + Migration
|
||||
- [ ] Policy-Engine: JSONB → SQLAlchemy Übersetzer
|
||||
- [ ] apply_policy_filter() + Integration mit RBAC-Filter
|
||||
- [ ] Policy-Cache (Redis) + Invalidation
|
||||
- [ ] Policy Service (CRUD)
|
||||
- [ ] Policy API (5 Endpoints)
|
||||
- [ ] GIN-Indexes für ABAC
|
||||
- [ ] Pre-compiled SQL Fragments
|
||||
- [ ] Policy-Intersection-Optimization
|
||||
- [ ] Materialized Policy Result
|
||||
|
||||
### Sprint 14 — ABAC UI (10h)
|
||||
- [ ] ABAC Rule-Editor mit AND/OR Gruppen
|
||||
- [ ] Feld-Auswahl (Core + Custom Fields)
|
||||
- [ ] Vorschau + Test-Tool
|
||||
- [ ] Custom Field ABAC Support (JSONB-Path)
|
||||
|
||||
### Sprint 15 — Templates & Automation (5h)
|
||||
- [ ] permission_templates Tabelle + Migration
|
||||
- [ ] Default-Policies für neue Entities
|
||||
- [ ] Auto-Share bei Erstellung
|
||||
- [ ] Frontend Template-Editor UI
|
||||
|
||||
### Sprint 16 — Mass & Bulk (4h)
|
||||
- [ ] Bulk-Share API
|
||||
- [ ] Mass-Operations
|
||||
- [ ] Frontend Bulk-Share-UI
|
||||
|
||||
### Sprint 17 — Analytics & Konflikte (5h)
|
||||
- [ ] Permission-Analytics API
|
||||
- [ ] Konflikt-Erkennung
|
||||
- [ ] Orphaned-Permissions-Cleanup
|
||||
- [ ] Frontend Analytics-Dashboard
|
||||
|
||||
### Sprint 18 — Delegation (4h)
|
||||
- [ ] permission_delegations Tabelle + Migration
|
||||
- [ ] Delegation Service + API
|
||||
- [ ] Abwesenheits-UI
|
||||
- [ ] Auto-Expiry
|
||||
|
||||
### Sprint 19 — Resolution-Strategien (3h)
|
||||
- [ ] Konfigurierbare Override-Regeln
|
||||
- [ ] Tenant-Einstellung
|
||||
- [ ] Frontend UI
|
||||
|
||||
### Sprint 20 — Tests (12h)
|
||||
- [ ] Backend: Entity Permissions Tests
|
||||
- [ ] Backend: ABAC Tests
|
||||
- [ ] Backend: Performance Tests (100K Datensätze)
|
||||
- [ ] Backend: Search Permission Tests
|
||||
- [ ] Backend: WebSocket Permission Tests
|
||||
- [ ] Frontend: ProtectedRoute Tests
|
||||
- [ ] Frontend: Permission-UI Tests
|
||||
- [ ] Frontend: ShareDialog Tests
|
||||
|
||||
### Sprint 21 — Dokumentation (3h)
|
||||
- [ ] docs/permissions.md
|
||||
- [ ] docs/permissions_plugin_dev.md
|
||||
- [ ] Plugin Template mit Permission-Beispielen
|
||||
- [ ] API-Docs
|
||||
|
||||
### Sprint 22 — Guest Access (28h)
|
||||
- [ ] guest_users Tabelle + Migration
|
||||
- [ ] Guest Auth (Login, Session, Logout)
|
||||
- [ ] Guest Permission Resolution (Service + RLS)
|
||||
- [ ] Guest Invitation Flow (Backend + E-Mail)
|
||||
- [ ] Guest API (limited endpoints)
|
||||
- [ ] Guest Frontend (vereinfachtes Layout + Views)
|
||||
- [ ] Guest Permission Management UI (Settings)
|
||||
- [ ] Guest Expiration & Auto-Cleanup
|
||||
- [ ] Guest Audit Trail
|
||||
- [ ] Guest Security (IP-Whitelist, Rate Limit, Watermarking)
|
||||
- [ ] Guest Tests
|
||||
|
||||
### Sprint 23 — Infrastructure (4h)
|
||||
- [ ] PgBouncer Setup
|
||||
- [ ] Audit Log Partitioning
|
||||
- [ ] Connection Pool Config
|
||||
|
||||
## Permission Levels
|
||||
| Level | Sichtbar? | Bearbeiten? | Löschen? | Teilen? |
|
||||
|-------|:---:|:---:|:---:|:---:|
|
||||
| Owner | ✅ | ✅ | ✅ | ✅ |
|
||||
| Admin | ✅ | ✅ | ✅ | ✅ |
|
||||
| Write | ✅ | ✅ | ❌ | ❌ |
|
||||
| Read | ✅ | ❌ | ❌ | ❌ |
|
||||
| None | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
## Architecture
|
||||
- PostgreSQL RLS (Safety Net)
|
||||
- Materialized View (user_entity_visibility)
|
||||
- Redis Bitmap Cache
|
||||
- Batch-Resolution
|
||||
- GIN-Indexes (ABAC + JSONB)
|
||||
- Folder-Path-Materialization (GiST)
|
||||
- PgBouncer Connection Pool
|
||||
- Redis Pub/Sub WebSocket Fan-Out
|
||||
- Audit Log Partitioning
|
||||
-323
@@ -1,323 +0,0 @@
|
||||
# LeoCRM Fix-Plan V2 — Gründliche Analyse & Maßnahmen
|
||||
|
||||
*Erstellt: 2026-07-26 — basierend auf externem Audit + eigener Code-Verifikation*
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Von 16 zentralen Punkten des externen Audits wurden **alle 16 durch Code-Inspektion verifiziert**. Zusätzlich wurden **5 neue Probleme** gefunden (UploadFile-Bug, Redis-Default-Passwort, exponierte Ports, unauthentifizierter Error-Endpoint, fehlende Security-Headers).
|
||||
|
||||
**Gesamtstatus:** Alle Phasen implementiert (Stand 2026-07-27). M5 (Frontend-Integration) als letzte Phase abgeschlossen.
|
||||
|
||||
---
|
||||
|
||||
## Implementierungs-Status (Stand 2026-07-27)
|
||||
|
||||
Die folgenden Phasen wurden gemäß Git-Historie implementiert:
|
||||
|
||||
| Phase | Commit | Maßnahmen | Status |
|
||||
|-------|--------|-----------|--------|
|
||||
| **Phase 1** (B1-B10) | `5ec1fc9` | Kritische Release-Blocker: Redis-Singleton (B1), Plugin-Routen (B2), UploadFile response_model (B3), DMS-Streaming (B4), Outbox-Worker (B5), Passwort-Reset-Mail (B6), Webhook-SSRF (B7), RLS-DB-Role (B8), .env-Korrektur (B9), Redis-Ports (B10) | ✅ Implementiert |
|
||||
| **Phase 2** (H1-H7) | `604a2b7` | Error-Endpoint (H1), Rate-Limiter (H2), CSRF-Redis (H3), WebSocket-Auth (H4), File-Upload (H5), Security-Headers (H6), Migration-Repair (H7) | ✅ Implementiert |
|
||||
| **Phase 3** (M1-M4, M6) | `825d638` | Passwort-Komplexität (M1), Login-Response (M2), Permission-Cache (M3), ENVIRONMENT (M4), weitere (M6) | ✅ Implementiert |
|
||||
| **Phase 4** | `b6e3afd` | Webhooks, Backup/Restore UI, Onboarding/Tutorial | ✅ Implementiert |
|
||||
| **Plugin-System-Umbau** | `98eb1d0` | Plugin-Routen nur in create_app(), require_active_plugin() Dependency, WebSocket-Skip | ✅ Implementiert |
|
||||
|
||||
### Verifizierte P0-Behebungen
|
||||
|
||||
| P0 | Problem | Status | Beweis |
|
||||
|----|---------|--------|--------|
|
||||
| P0-1 | Auth-Bypass via X-Internal-Call | ✅ Behoben | `app/deps.py` hat keinen X-Internal-Call Code mehr. Auth nur via Session-Cookie. |
|
||||
| P0-2 | Destruktive Migrationen | ✅ Behoben | Migration 0021 benennt Tabellen um (`*_old`). Migration 0044 repariert RLS. |
|
||||
| P0-3 | Plugin-Upload RCE | ✅ Neutralisiert | Alle Upload-Endpoints deaktiviert (403). `_extract_plugin_from_zip()` ist Dead Code. |
|
||||
| P0-4 | RLS nicht erzwungen | ✅ Behoben | Migration 0028 setzt FORCE RLS. Migration 0044 erstellt `crm_runtime` (NOSUPERUSER, NOBYPASSRLS). |
|
||||
| P0-5 | Plugin-Doppelregistrierung | ✅ Behoben | Routen nur in create_app(). require_active_plugin() prüft Aktivierungsstatus. |
|
||||
| P0-6 | Kein persistentes Volume | ✅ Behoben | docker-compose.yml hat volumes für PostgreSQL, Redis, App-Uploads, Worker. |
|
||||
| P0-7 | Öffentliche Domain | ✅ Behoben | Keine crm.media-on.de Referenz mehr in docker-compose.yml. |
|
||||
|
||||
### Weitere verifizierte Behebungen
|
||||
- **B1** (doppelte get_redis()): ✅ Nur eine Definition in `app/core/auth.py` Zeile 53
|
||||
- **B3** (UploadFile response_model): ✅ `response_model=None` in dms, calendar, mail routes
|
||||
- **B7** (Webhook SSRF): ✅ Private IP-Check, `follow_redirects=False`, Protokoll-Check
|
||||
- **B9** (AUTH_SECRET vs SECRET_KEY): ✅ `.env.docker.example` verwendet `SECRET_KEY`
|
||||
- **B10** (Redis-Default-Passwort + Ports): ✅ Ports auskommentiert, Redis-Passwort required
|
||||
- **WebSocket Auth**: ✅ Beide WS-Endpunkte haben `verify_ws_origin()`, Session-Cookie-Validierung, `user_id` aus Session
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Kritische Release-Blocker (vor Produktivbetrieb)
|
||||
|
||||
### B1. Doppelte `get_redis()` entfernen
|
||||
- **Datei:** `app/core/auth.py` Zeilen 53 + 94
|
||||
- **Problem:** Zweite Definition überschreibt Singleton, erzeugt pro Aufruf neue Verbindung → Connection Leak
|
||||
- **Fix:** Zweite `def get_redis()` (Zeile 94) löschen. Erste Definition (Zeile 53) beibehalten.
|
||||
- **Aufwand:** 5 Min
|
||||
- **Risiko:** Keines — erste Definition ist korrekt
|
||||
|
||||
### B2. Plugin-Routen-Registrierung reparieren
|
||||
- **Datei:** `app/main.py` Zeilen 375-416
|
||||
- **Problem:** Alle Plugin-Routen werden statisch in `create_app()` registriert, unabhängig vom Aktivierungsstatus. Deaktivierte Plugins bleiben erreichbar. Kommentar in Zeile 416 sagt das Gegenteil.
|
||||
- **Fix:**
|
||||
1. Statische Registrierung aus `create_app()` entfernen
|
||||
2. In `lifespan()` nur Routen für `active=True` Plugins registrieren
|
||||
3. `Depends(require_active_plugin("name"))` als zentrale Prüfung ergänzen
|
||||
4. Bei Deaktivierung: Router entfernen oder 403-Dependency ergänzen
|
||||
- **Aufwand:** 2-3 Std
|
||||
- **Risiko:** Mittel — muss sicherstellen dass keine Route doppelt registriert wird
|
||||
|
||||
### B3. UploadFile Route-Registration Bug
|
||||
- **Dateien:** `app/plugins/builtins/dms/routes.py`, `calendar/routes.py`, `mail/routes.py`, `kommunikation/routes.py`, `ai_assistant/routes.py`
|
||||
- **Problem:** FastAPI kann `UploadFile` nicht als Response-Model auflösen → 5 Plugins failen beim Registrieren mit `Invalid args for response field`
|
||||
- **Fix:** `response_model=None` zu allen Endpoints mit `UploadFile`-Rückgabe hinzufügen, oder Return-Type auf `Response`/`dict` ändern
|
||||
- **Aufwand:** 30 Min
|
||||
- **Risiko:** Keines — Routen sind aktuell gar nicht registriert
|
||||
|
||||
### B4. DMS-Upload auf echtes Streaming umstellen
|
||||
- **Datei:** `app/plugins/builtins/dms/routes.py` Zeilen 444-472
|
||||
- **Problem:** Chunks werden in `list[bytes]` gesammelt, dann `b"".join()` → 100MB Datei = 200MB+ RAM. `save_stream()` existiert aber wird nicht benutzt.
|
||||
- **Fix:**
|
||||
```python
|
||||
async def chunk_generator():
|
||||
while chunk := await file.read(CHUNK_SIZE):
|
||||
yield chunk
|
||||
await storage.save_stream(storage_path, chunk_generator())
|
||||
```
|
||||
Hash und Größe während des Streams berechnen.
|
||||
- **Aufwand:** 1 Std
|
||||
- **Risiko:** Gering — save_stream() ist bereits implementiert
|
||||
|
||||
### B5. Outbox-Worker: Event-Handler registrieren
|
||||
- **Datei:** `app/core/worker.py` `on_startup()`
|
||||
- **Problem:** Worker liest Events aus Outbox, published an lokalen EventBus, aber es sind keine Handler registriert → Events werden als `published` markiert ohne Verarbeitung
|
||||
- **Fix:**
|
||||
1. In `on_startup()`: Plugin-Event-Handler registrieren (wie in `lifespan()` der API)
|
||||
2. `webhook_dispatcher._dispatch_event` an EventBus subscriben
|
||||
3. Plugin-Participant-Handler registrieren
|
||||
- **Aufwand:** 2 Std
|
||||
- **Risiko:** Mittel — muss gleiche Handler wie API-Container registrieren
|
||||
|
||||
### B6. Passwort-Reset-Mailjob implementieren
|
||||
- **Dateien:** `app/services/auth_service.py`, `app/core/jobs.py`, `app/core/job_registry.py`
|
||||
- **Problem:** `send_password_reset_email` Job wird gequeued aber nie registriert → Mail wird nicht versendet. Token wird in Logs geschrieben (Zeile 240-241).
|
||||
- **Fix:**
|
||||
1. `send_password_reset_email` Worker-Funktion implementieren (SMTP/IMAP)
|
||||
2. Mit `register_job()` registrieren
|
||||
3. `logger.warning("raw_token for development: %s", raw_token)` entfernen
|
||||
4. Token nur im Development-Mode loggen, nie in Production
|
||||
- **Aufwand:** 2 Std
|
||||
- **Risiko:** Gering
|
||||
|
||||
### B7. Webhook SSRF-Schutz + Secret-Behandlung
|
||||
- **Dateien:** `app/services/webhook_service.py`, `app/schemas/webhook.py`
|
||||
- **Problem:** Kein SSRF-Schutz — User können interne Dienste ansprechen (redis:6379, postgres:5432, 169.254.169.254). Webhook-Secret wird im Response zurückgegeben.
|
||||
- **Fix:**
|
||||
1. SSRF-Prüfung: DNS auflösen, private IPs blocken (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x, ::1)
|
||||
2. Redirects deaktivieren oder prüfen
|
||||
3. Protokoll-Allowlist (nur https)
|
||||
4. `secret` aus `WebhookResponse` entfernen
|
||||
5. Secret gehasht in DB speichern
|
||||
- **Aufwand:** 3 Std
|
||||
- **Risiko:** Gering
|
||||
|
||||
### B8. RLS: Separater DB-Runtime-User
|
||||
- **Dateien:** `docker-compose.yml`, `alembic/versions/0044_db_roles.py` (neu)
|
||||
- **Problem:** `POSTGRES_USER` (crm_user) ist Superuser → umgeht RLS auch mit FORCE. Spätere Tabellen (user_preferences, saved_filters, etc.) haben keine RLS-Policy.
|
||||
- **Fix:**
|
||||
1. Neue Migration `0044_db_roles.py`: erstellt `crm_runtime` (NOSUPERUSER, NOBYPASSRLS)
|
||||
2. `crm_runtime` bekommt nur SELECT/INSERT/UPDATE/DELETE Rechte
|
||||
3. `docker-compose.yml`: API und Worker nutzen `crm_runtime`, Migrationen nutzen `crm_owner`
|
||||
4. Neue Migration `0045_rls_new_tables.py`: RLS für alle Tabellen mit `tenant_id` die nach 0028 hinzukamen
|
||||
- **Aufwand:** 4 Std
|
||||
- **Risiko:** Hoch — muss bestehende Datenbanken migrieren ohne Datenverlust
|
||||
|
||||
### B9. .env.docker.example korrigieren
|
||||
- **Datei:** `.env.docker.example`
|
||||
- **Problem:** Verwendet `AUTH_SECRET` statt `SECRET_KEY` (config.py erwartet `SECRET_KEY`)
|
||||
- **Fix:** `AUTH_SECRET` → `SECRET_KEY` umbenennen
|
||||
- **Aufwand:** 5 Min
|
||||
- **Risiko:** Keines
|
||||
|
||||
### B10. Redis-Default-Passwort + exponierte Ports
|
||||
- **Datei:** `docker-compose.yml`
|
||||
- **Problem:** Redis-Passwort default `changeme`, PostgreSQL (5432) und Redis (6379) Ports exponiert
|
||||
- **Fix:**
|
||||
1. Redis-Passwort als Required-Env ohne Default
|
||||
2. `ports:` Sektion für DB und Redis entfernen (nur internes Docker-Netzwerk)
|
||||
3. Falls Debug-Zugriff nötig: nur an 127.0.0.1 binden
|
||||
- **Aufwand:** 15 Min
|
||||
- **Risiko:** Gering — bestehende Setups müssen .env anpassen
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Hohe Priorität (kurz nach Release)
|
||||
|
||||
### H1. Unauthentifizierter Error-Endpoint absichern
|
||||
- **Datei:** `app/routes/errors.py`
|
||||
- **Problem:** `POST /api/v1/errors` ohne Auth, sendet Daten an Forgejo als öffentliches Issue. Context-Dict kann sensible Daten enthalten.
|
||||
- **Fix:**
|
||||
1. Context-Felder filtern (keine Tokens, Passwörter, Headers)
|
||||
2. Forgejo-Issues nur in non-production erstellen
|
||||
3. Rate-Limit auf IP-Basis (bereits vorhanden, aber in-memory → bei Multi-Worker unzuverlässig)
|
||||
4. Optional: Auth erforderlich, aber dann funktioniert Frontend-Error-Logging nicht mehr → besser: nur sanitisierte Daten akzeptieren
|
||||
- **Aufwand:** 1 Std
|
||||
|
||||
### H2. Rate-Limiter IP-Spoofing
|
||||
- **Datei:** `app/core/rate_limit.py` Zeile 43
|
||||
- **Problem:** Vertraut `X-Forwarded-For` blind → IP-Spoofing umgeht Rate-Limits
|
||||
- **Fix:** Nur erste IP in X-Forwarded-For verwenden, oder `X-Real-IP` mit Proxy-Validation
|
||||
- **Aufwand:** 30 Min
|
||||
|
||||
### H3. CSRF-Middleware Redis-Verbindung
|
||||
- **Datei:** `app/core/middleware.py` Zeile 69
|
||||
- **Problem:** Erstellt pro unsafe Request neue Redis-Verbindung → Connection Leak
|
||||
- **Fix:** `get_redis()` Singleton verwenden (funktioniert nach B1)
|
||||
- **Aufwand:** 10 Min
|
||||
|
||||
### H4. WebSocket Auth + Origin-Verifikation
|
||||
- **Dateien:** `app/plugins/builtins/kommunikation/websocket_manager.py`, `ai_ui_control/websocket_manager.py`
|
||||
- **Problem:** `user_id` wird ohne Auth-Verifikation akzeptiert. Keine Origin-Prüfung bei WS-Upgrade.
|
||||
- **Fix:**
|
||||
1. Session-Token aus Query-Param oder Header validieren
|
||||
2. Origin-Header gegen erlaubte Domains prüfen
|
||||
3. User-ID aus Session ableiten, nicht aus Client-Param
|
||||
- **Aufwand:** 2 Std
|
||||
|
||||
### H5. File-Upload-Sicherheit
|
||||
- **Datei:** `app/core/storage.py`
|
||||
- **Problem:** Keine Path-Traversal-Prüfung, keine Type/Size-Limits, `get_url()` leakt Filesystem-Pfade
|
||||
- **Fix:**
|
||||
1. Filename sanitizen (keine `../`, keine absoluten Pfade)
|
||||
2. MIME-Type-Allowlist
|
||||
3. Max-File-Size konfigurierbar
|
||||
4. `get_url()` gibt relative URL zurück, nicht Filesystem-Pfad
|
||||
- **Aufwand:** 1 Std
|
||||
|
||||
### H6. Security-Headers
|
||||
- **Datei:** `app/core/middleware.py` (neu)
|
||||
- **Problem:** Keine Security-Headers (HSTS, X-Content-Type-Options, X-Frame-Options, CSP)
|
||||
- **Fix:** Middleware ergänzen die diese Headers setzt
|
||||
- **Aufwand:** 30 Min
|
||||
|
||||
### H7. Migration-Repair für bestehende Installationen
|
||||
- **Datei:** `alembic/versions/0044_repair_contact_migration.py` (neu)
|
||||
- **Problem:** Migrationen 0021 und 0027 wurden nachträglich geändert. Alembic führt sie nicht erneut aus.
|
||||
- **Fix:**
|
||||
1. Neue Migration die `*_old` Tabellen erkennt und Daten nachmigriert
|
||||
2. Integritätsprüfung (Anzahl vergleichen)
|
||||
3. Bei Abweichungen hart abbrechen mit Fehlermeldung
|
||||
- **Aufwand:** 3 Std
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Mittlere Priorität
|
||||
|
||||
### M1. Passwort-Komplexität
|
||||
- **Datei:** `app/schemas/auth.py`, `app/schemas/user.py`
|
||||
- **Problem:** Min-Length 8 bei Erstellung, Min-Length 1 bei Login. Keine Komplexitäts-Requirements.
|
||||
- **Fix:** Passwort-Validator ergänzen (min 8 Zeichen, 1 Groß, 1 Klein, 1 Zahl)
|
||||
- **Aufwand:** 30 Min
|
||||
|
||||
### M2. Login-Response: is_system_admin
|
||||
- **Datei:** `app/routes/auth.py` Zeile 78
|
||||
- **Problem:** `is_system_admin` Flag in Login-Response leakt interne Rolle
|
||||
- **Fix:** Flag aus Response entfernen oder nur für Admin-User anzeigen
|
||||
- **Aufwand:** 15 Min
|
||||
|
||||
### M3. Permission-Cache: Stale Data bei DB-Error
|
||||
- **Datei:** `app/core/permissions.py` Zeile 337
|
||||
- **Problem:** Bei DB-Error fällt Cache auf stale Daten zurück → widerrufene Rechte bleiben aktiv
|
||||
- **Fix:** Bei DB-Error: Cache invalidieren und 503 zurückgeben statt stale Daten zu nutzen
|
||||
- **Aufwand:** 30 Min
|
||||
|
||||
### M4. ENVIRONMENT=development vs SESSION_COOKIE_SECURE=true
|
||||
- **Datei:** `.env` Zeilen 3-4
|
||||
- **Problem:** Inkonsistent — development deaktiviert Prod-Safety-Checks, aber Cookie ist secure
|
||||
- **Fix:** In .env.docker.example klar dokumentieren: production → `ENVIRONMENT=production` + `SESSION_COOKIE_SECURE=true`
|
||||
- **Aufwand:** 10 Min
|
||||
|
||||
### M5. Frontend: Unresolved Items — ✅ Implementiert (2026-07-27)
|
||||
- **Dateien:** `WelcomeDialog.tsx`, `SavedFilterBar.tsx`, `EntityHistoryPanel.tsx`, `TagBadge.tsx`, `TagSelector.tsx`
|
||||
- **Status:** ✅ Implementiert — SavedFilterBar und TagSelector in ContactsList, Mail, Calendar integriert
|
||||
- **Implementiert:**
|
||||
1. SavedFilterBar in ContactsList (entityType="contacts"), Mail (entityType="mail"), Calendar (entityType="calendar") integriert
|
||||
2. TagSelector in ContactsList (entityType="contact"), Mail (entityType="file"), Calendar (entityType="calendar_entry") integriert
|
||||
3. Frontend TypeScript: 0 Errors (`npx tsc --noEmit`)
|
||||
- **Hinweis:** WelcomeDialog und EntityHistoryPanel bleiben für spätere Iteration offen
|
||||
|
||||
### M6. Frontend-Tests: QueryClientProvider
|
||||
- **Datei:** `frontend/src/test/setup.ts` oder einzelne Tests
|
||||
- **Problem:** ~29 Tests failen mit missing QueryClientProvider
|
||||
- **Fix:** Globalen Test-Wrapper mit QueryClientProvider in setup.ts ergänzen
|
||||
- **Aufwand:** 1 Std
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Niedrige Priorität
|
||||
|
||||
### L1. document.write() in print.ts
|
||||
- **Datei:** `frontend/src/utils/print.ts` Zeilen 54, 127
|
||||
- **Problem:** `document.write()` mit DOM-Clone — XSS-Risiko wenn Content nicht sanitized
|
||||
- **Fix:** Statt `document.write()`: `iframe.srcdoc` oder `Blob URL` verwenden
|
||||
- **Aufwand:** 1 Std
|
||||
|
||||
### L2. AI UI Control: Unbounded Feedback-Storage
|
||||
- **Datei:** `app/plugins/builtins/ai_ui_control/websocket_manager.py` Zeile 94
|
||||
- **Problem:** Feedback/Commands unbegrenzt im Memory gespeichert → Memory Exhaustion
|
||||
- **Fix:** Max-Length Queue (z.B. 100 Einträge) mit FIFO
|
||||
- **Aufwand:** 15 Min
|
||||
|
||||
### L3. Backup-Strategie dokumentieren
|
||||
- **Problem:** Named Volumes in docker-compose aber keine Backup/Restore-Doku
|
||||
- **Fix:** Backup-Script und Doku ergänzen
|
||||
- **Aufwand:** 2 Std
|
||||
|
||||
---
|
||||
|
||||
## Implementierungs-Reihenfolge
|
||||
|
||||
```
|
||||
Phase 1 (Release-Blocker):
|
||||
B1 → B3 → B9 → B10 → B2 → B4 → B5 → B6 → B7 → B8
|
||||
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
|
||||
5m 30m 5m 15m 3h 1h 2h 2h 3h 4h
|
||||
Gesamt: ~16 Std
|
||||
|
||||
Phase 2 (Hohe Priorität):
|
||||
H3 → H2 → H6 → H1 → H5 → H4 → H7
|
||||
Gesamt: ~8 Std
|
||||
|
||||
Phase 3 (Mittlere Priorität):
|
||||
M4 → M1 → M2 → M3 → M6 → M5
|
||||
Gesamt: ~6 Std
|
||||
|
||||
Phase 4 (Niedrige Priorität):
|
||||
L2 → L1 → L3
|
||||
Gesamt: ~3 Std
|
||||
```
|
||||
|
||||
**Gesamtaufwand: ~33 Std**
|
||||
|
||||
---
|
||||
|
||||
## Was bereits sauber funktioniert
|
||||
|
||||
- ✅ Auth-Bypass entfernt (keine X-Internal-Call/X-Tenant-Id/X-User-Id Headers mehr)
|
||||
- ✅ Plugin-Upload/URL-Installation deaktiviert (403)
|
||||
- ✅ Worker in separatem Container
|
||||
- ✅ Metrics adminbeschränkt
|
||||
- ✅ DOMPurify für HTML-Komponenten
|
||||
- ✅ ARQ-Verbindungspool zentralisiert
|
||||
- ✅ Session-Widerruf nach Passwortänderung
|
||||
- ✅ Permission-Cache-Versionierung
|
||||
- ✅ Redis SCAN statt KEYS
|
||||
- ✅ Rabatte von Float auf Numeric
|
||||
- ✅ Event-Outbox als Grundlage vorhanden
|
||||
- ✅ RLS FORCE + WITH CHECK in Migration 0028
|
||||
- ✅ Migration 0021: Tabellen umbenennen statt löschen
|
||||
- ✅ Frontend: TypeScript typecheck clean (0 errors)
|
||||
- ✅ Frontend: ErrorBoundary, OfflineBanner, ErrorLogger implementiert
|
||||
- ✅ Frontend: Print/PDF mit WeasyPrint funktioniert
|
||||
- ✅ Dockerfile: Multi-stage, non-root User, Healthcheck
|
||||
- ✅ Bcrypt Password-Hashing
|
||||
- ✅ Session-Tokens: secrets.token_urlsafe(32)
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
# LeoCRM — Umfassender Fix-Plan
|
||||
|
||||
> Erstellt: 2026-07-25
|
||||
> Letzte Überprüfung: 2026-07-26 — Alle Items gegen Codebasis verifiziert
|
||||
> Quellen: Externes Audit (geprüft), eigene Code-Inspektion, Coolify-Deployment-Prüfung
|
||||
|
||||
---
|
||||
|
||||
## ✅ Erledigte Fixes (22 von 24 Items komplett)
|
||||
|
||||
Die folgenden Items wurden bei der Überprüfung am 2026-07-26 als erledigt bestätigt:
|
||||
|
||||
| Item | Beschreibung | Verifiziert durch |
|
||||
|---|---|---|
|
||||
| P0-1 | Auth-Bypass entfernt | `app/deps.py` — keine `X-Internal-Call` Headers mehr |
|
||||
| P0-2 | Migrationen repariert | `migration_0021.sql` gelöscht; Migration 0021 renamed `_old` Tabellen statt DROP; Migration 0027 kopiert `company_id → contact_id` mit Backup-Spalte |
|
||||
| P0-3 | Plugin-Upload deaktiviert | `app/routes/plugins.py` — `/upload` und `/install-url` return 403 mit `upload_disabled` / `install_url_disabled` |
|
||||
| P0-4 | RLS repariert | `alembic/versions/0028_rls_force.py` — `FORCE ROW LEVEL SECURITY` + `WITH CHECK` auf allen Tenant-Tabellen |
|
||||
| P0-5 | Plugin-Doppelregistrierung | `app/main.py` — Routes in `create_app()`, `lifespan()` nur aktiviert/deaktiviert, respektiert DB `active` Status, Migration-Fail deaktiviert Plugin |
|
||||
| P0-6 | Persistent Volume | `docker-compose.yml` — `storage:/data/storage`, `pgdata`, `redisdata` Volumes |
|
||||
| P1-1 | User/Tenant-Modell | `app/models/user.py` — `User` hat keine `tenant_id`/`role` mehr, `UserTenant` ist single source of truth, `email` global unique |
|
||||
| P1-2 | Redis zentralisiert | `app/core/auth.py` — `init_redis()`/`get_redis()` Singleton, `init_job_pool()`/`close_job_pool()` |
|
||||
| P1-3 | Worker ausgelagert | `prestart.sh` — nur Alembic + Uvicorn; separater `crm-worker` Container in `docker-compose.yml` |
|
||||
| P1-4 | Transactional Outbox | `app/core/outbox.py`, `app/models/outbox.py`, `alembic/versions/0040_outbox.py` — `enqueue_outbox_event()` + `process_outbox_batch()` mit `FOR UPDATE SKIP LOCKED` |
|
||||
| P1-5 | XSS-Stellen geschlossen | `HtmlBlock.tsx` + `SignatureManager.tsx` — `DOMPurify.sanitize()`; `ActionCardBlock.tsx` — URL-Validierung (nur `http:`/`https:`) |
|
||||
| P1-6 | DMS lastfest | `app/plugins/builtins/dms/routes.py` — 1MB Chunked Streaming, SHA-256 Content-Hash |
|
||||
| P1-7 | Permission-System | `app/core/permissions.py` — `permission_version` wird beim Cache-Lesen geprüft, `redis.scan()` statt `redis.keys()`, `require_write()` prüft spezifische Permissions |
|
||||
| P1-8 | Password Reset | `app/services/auth_service.py` — ARQ Job `send_password_reset_email`, Token `used_at` Tracking |
|
||||
| P1-9 | Metrics abgesichert | `app/routes/metrics.py` — `Depends(require_admin)` |
|
||||
| P1-10 | Coolify-Doku & Config | `COOLIFY_SETUP.md` — Healthcheck `/api/v1/health`, JWT-Vars entfernt, CORS `:443`; `app/config.py` — `storage_path=/data/storage`, `session_cookie_secure=True`, Startup-Validierung; `docker-compose.yml` — Redis, Volumes, Healthcheck |
|
||||
| P1-11 | Cross-Tenant FK | `alembic/versions/0036_cross_tenant_fk.py` — `UNIQUE (tenant_id, id)` + Composite FK `(tenant_id, contact_id)` auf `contactpersons` und `contact_merge_history` |
|
||||
| P2-1 | Contact Model normalisiert | `alembic/versions/0039_contact_normalize.py` — `surfix→suffix`, `Float→Numeric(5,2)`, `JSON→JSONB`, `CHECK (0-100)`, Unique Constraints |
|
||||
| P2-3 | Commands & Statusmaschinen | `app/commands/` (base, contact, calendar, dms, mail) + `app/core/state_machine.py` |
|
||||
| P2-4 | SPA Path-Traversal | `app/main.py` — `os.path.abspath` Check + `".." in full_path` Blocking |
|
||||
|
||||
---
|
||||
|
||||
## ⏳ Offene Items
|
||||
|
||||
### P0-7: App von öffentlicher Domain nehmen
|
||||
|
||||
**Status:** Operational — nicht aus Code verifizierbar
|
||||
|
||||
**Problem:** Die App läuft unter `https://crm.media-on.de` und ist öffentlich erreichbar.
|
||||
|
||||
**Maßnahme:**
|
||||
1. **Sofort:** App von öffentlicher Domain nehmen oder IP-Whitelist/Basic Auth vorschalten
|
||||
2. Mindestens P0-1 (Auth-Bypass ✅) und P0-3 (Plugin-Upload ✅) sind bereits behoben
|
||||
3. Alternativ: VPN/Tunnel-Zugang statt öffentliche Domain
|
||||
|
||||
**Aufwand:** 30 Minuten
|
||||
|
||||
---
|
||||
|
||||
### P2-2: Plugin-Cross-Imports reduzieren
|
||||
|
||||
**Status:** Offen — 228 direkte Cross-Imports zwischen Plugins
|
||||
|
||||
**Problem:** 228 direkte `from app.plugins.builtins` Imports zwischen Plugins. Automatisierung importiert Modelle/Services von Kommunikation, Mail, Kalender. Verteilter Monolith ohne Modulgrenzen.
|
||||
|
||||
**Maßnahme:**
|
||||
1. Öffentliche Schnittstellen (Contracts) für jedes Modul definieren
|
||||
2. Direkte Imports fremder Plugin-Modelle verbieten
|
||||
3. Kommunikation nur über Events oder öffentliche Service-API
|
||||
4. CI-Check: keine direkten Cross-Plugin-Imports
|
||||
|
||||
**Aufwand:** 1-2 Wochen
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Priorität | Erledigt | Offen | Geschätzter Aufwand (offen) |
|
||||
|---|---|---|---|
|
||||
| P0 | 6/7 | 1 (operational) | 30 Minuten |
|
||||
| P1 | 11/11 | 0 | — |
|
||||
| P2 | 3/4 | 1 | 1-2 Wochen |
|
||||
| **Total** | **20/22** | **2** | **~1-2 Wochen** |
|
||||
|
||||
## Validierung nach jedem Fix
|
||||
|
||||
- [ ] Python-Syntax-Check: `python -m py_compile app/**/*.py`
|
||||
- [ ] pytest: `pytest tests/ -x`
|
||||
- [ ] Frontend-Typecheck: `cd frontend && npx tsc --noEmit`
|
||||
- [ ] Frontend-Build: `cd frontend && npx vite build`
|
||||
- [ ] Manueller Smoke-Test: Login, Kontakt erstellen, DMS-Upload
|
||||
- [ ] Cross-Tenant-Test: Datensatz aus Mandant A kann nicht aus Mandant B gelesen werden
|
||||
- [ ] Deployment: Coolify Deploy + Healthcheck prüfen
|
||||
-754
@@ -1,754 +0,0 @@
|
||||
# LeoCRM — Master Plan: Umbau & Vollendung
|
||||
|
||||
**Erstellt:** 2026-07-22
|
||||
**Status:** Draft — zur Freigabe
|
||||
**Letzte Revision:** 2026-07-22 (gründliche Überprüfung nach Code-Tiefenanalyse)
|
||||
|
||||
---
|
||||
|
||||
## Ausgangslage
|
||||
|
||||
### Was bereits gut ist
|
||||
- Backend: ~35.800 Zeilen, 12 Plugins, Multi-Tenant mit RLS, Rate Limiting, Audit Log
|
||||
- Unified Contact Model: **BEREITS implementiert** (Migration 0021) — Contact mit type='company'|'person', ContactPerson als 1:N child (wie Rentman)
|
||||
- Frontend: ~30.000 Zeilen, 27 Pages, 70 Components, i18n DE/EN, TanStack Query, TipTap
|
||||
- Tests: ~17.300 Zeilen Backend-Tests, 38 Vitest-Dateien
|
||||
- Docker: Multi-Stage-Build (Frontend+Backend in einem Container)
|
||||
- Datenbank: PostgreSQL 16 als separater docker-compose Service
|
||||
- WebSocket-Infrastruktur: Bereits im `kommunikation` Plugin vorhanden (`/api/v1/comm/ws`) — kann als Referenz für KI-UI-Steuerung dienen
|
||||
|
||||
### Was fehlt oder nicht stimmt
|
||||
- Frontend nutzt unified Contact Model nicht vollständig (keine Contact-Detail-Route, ContactPerson-Verwaltung fehlt in UI)
|
||||
- **'company' als entity_type ist in 6 Plugins verankert** — muss zu 'contact' vereinheitlicht werden
|
||||
- Plugin-UI-System fehlt (hartkodierte Routes statt dynamische Registry)
|
||||
- Code-Splitting fehlt (alle 27 Pages im Main Bundle)
|
||||
- E2E Tests fehlen komplett
|
||||
- KI-UI-Steuerung fehlt
|
||||
- Virtual Scrolling fehlt
|
||||
- React Hook Form + Zod nicht überall
|
||||
- hooks.ts ist Monolith (1.298 Zeilen)
|
||||
- Fehlende Dependencies (lucide-react, date-fns)
|
||||
- Plugin-Richtlinien fehlen
|
||||
|
||||
### Wichtige Unterscheidung: 'company' hat zwei Bedeutungen
|
||||
1. **entity_type='company'** in Plugins (entity_links, calendar, tags, mail) → referenziert eine Firma als Entität → **MUSS zu 'contact' werden**
|
||||
2. **system_settings.company_*** Felder (company_name, company_street etc.) → CRM-Besitzer-Firmeninfo für Rechnungen → **BLEIBT wie es ist**
|
||||
3. **CalendarType='company'** → Kalender-Typ (Firmenkalender) → kann bleiben oder zu 'organization' umbenannt werden (kosmetisch)
|
||||
|
||||
---
|
||||
|
||||
## Architektur-Entscheidungen (freigegeben 2026-07-22)
|
||||
|
||||
1. **KI-UI-Steuerung:** Keine Mausbewegung nötig. KI muss zu Kontakten springen und einen Kontakt öffnen können. Die UI muss das Ergebnis zeigen — Kontaktliste und spezieller Kontakt ausgewählt. Implementierungsweg (WebSocket, postMessage, etc.) ist offen, Hauptsache das Ergebnis wird in der UI sichtbar.
|
||||
2. **Company-Routes:** Komplett entfernen. Keine deprecated-Routes, keine Redirects. Kontakte wie in Rentman — ein unified Contact-Modell, kein separates Company-Modell mehr. **Alle Plugin-Referenzen auf entity_type='company' müssen zu 'contact' migriert werden.**
|
||||
3. **PostgreSQL:** Aktuell egal (Coolify-managed oder docker-compose). Reine Docker-Lösung soll später möglich sein. Keine Code-Änderung nötig — nur Konfiguration.
|
||||
4. **S3-Storage:** Provider egal. Wichtig ist nur dass die Architektur es später ermöglicht. Bereits vorbereitet in config.py (STORAGE_BACKEND=s3).
|
||||
|
||||
---
|
||||
|
||||
## Phasen-Plan
|
||||
|
||||
### PHASE 0: Vorbereitung & Cleanup
|
||||
**Ziel:** Codebasis bereinigen, Dependencies installieren, veraltete Dokumente aktualisieren
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 0.1 | Veraltete Planungsdokumente aktualisieren | 2h | `codebase-vs-requirements.md` neu schreiben (beschreibt alten Stand), `architecture.md` um Implementation-Status erweitern, `security-review-phase2.md` um 'Resolved' Markierungen ergänzen |
|
||||
| 0.2 | `lucide-react` installieren + Icons migrieren | 4h | Inline SVGs durch lucide-react Icons ersetzen. Konsistente Icon-Bibliothek. |
|
||||
| 0.3 | `date-fns` installieren + Datum-Formatierung | 3h | Alle `toLocaleDateString()` etc. durch date-fns ersetzen. Konsistente Datum-Formatierung. |
|
||||
| 0.4 | `hooks.ts` aufteilen | 3h | 1.298 Zeilen aufteilen in `api/auth.ts`, `api/contacts.ts`, `api/settings.ts` etc. Generische Hooks bleiben in `hooks.ts`. Company-Hooks werden in Phase 1 entfernt, nicht aufgeteilt. |
|
||||
| 0.5 | Store-Verzeichnis konsolidieren | 1h | `store/` und `stores/` zusammenführen. |
|
||||
| 0.6 | Frontend-Bestandsanalyse als Dokument speichern | 1h | `frontend-gap-analysis.md` mit vollständiger Analyse. |
|
||||
| 0.7 | UI-Design-Richtlinien erstellen | 6h | `docs/ui-design-guidelines.md` basierend auf bestehenden Plugin-Patterns (siehe unten). |
|
||||
| 0.8 | Theme-Customization Backend | 4h | `system_settings` um Theme-Felder erweitern (primary_color, accent_color, font_family, border_radius). Neue Alembic-Migration. API-Endpoints zum Lesen/Schreiben der Theme-Settings. |
|
||||
| 0.9 | Theme-Customization Frontend | 6h | `SettingsTheme.tsx` Seite mit Color-Picker, Font-Auswahl, Live-Preview. Tailwind-CSS-Variablen dynamisch aus API-Settings überschreiben. Dark-Mode-Toggle. Theme wird beim App-Start geladen und angewendet. |
|
||||
| 0.10 | RBAC-Audit & Plugin-Permissions nachrüsten | 6h | 4 Plugins haben `permissions=[]` (calendar, dms, entity_links, tags) → keine Rechte-Prüfung! Pro Plugin passende Permissions definieren und in Manifest eintragen. Routes mit `require_permission()` absichern. Siehe Details unten. |
|
||||
| 0.11 | LiteLLM-Cleanup & alte llm_client.py migrieren | 3h | LiteLLM ist **BEREITS** in ai_assistant und ai_proactive integriert (`litellm.acompletion()`). Nur die alte `llm_client.py` (Copilot) nutzt noch httpx direkt. Diese auf LiteLLM umstellen oder entfernen. System-Prompt in llm_client.py referenziert noch `/api/v1/companies` → auf Contacts umstellen. |
|
||||
| 0.12 | KI-Agent-Framework in Plugin-Richtlinien dokumentieren | 2h | PydanticAI + tool_registry existieren bereits. In `docs/plugin-development-guide.md` dokumentieren: Wie Plugins KI-Agenten, Tools und LLM-Funktionen nutzen. Plugin-Manifest um `agent_capabilities` Feld erweitern. |
|
||||
| 0.13 | Heartbeat konfigurierbar machen | 3h | Heartbeat-Intervall, Aktivierung, Ziel-Room in ProactiveSettings (DB) speichern. Settings-UI für Heartbeat-Konfiguration. |
|
||||
| 0.14 | Unified Search: Field-Level RBAC nachrüsten | 4h | Search-Provider prüfen aktuell KEINE Feld-Level-Permissions. Nutzer mit `search:read` sieht alle Felder. Provider müssen `resolved_perms` prüfen und `hidden` Felder ausblenden. `to_search_result()` um Permission-Filter ergänzen. |
|
||||
| 0.15 | Undo/History-System für CRUD-Operationen | 8h | Globale Undo-History: Jede CRUD-Aktion (Create/Update/Delete) wird mit Snapshot in `entity_history` Tabelle gespeichert. User kann Änderungen rückgängig machen oder zu früherer Version zurückkehren. Nutzt bestehenden Audit-Log als Basis. Frontend: Undo-Button + History-Viewer pro Entity. |
|
||||
| 0.16 | Storage Backend implementieren (S3-Support) | 8h | Architecture.md beschreibt abstract StorageBackend (local/S3), aber **existiert NICHT im Code**. Attachments nutzen hardcoded `/data/uploads`. Storage-Klasse erstellen: `LocalStorage` + `S3Storage`. Config um `STORAGE_BACKEND`, `S3_ENDPOINT`, `S3_BUCKET`, `S3_ACCESS_KEY`, `S3_SECRET_KEY` erweitern. DMS und Attachments auf Storage-Backend umstellen. .env.example um S3-Variablen ergänzen. |
|
||||
| 0.17 | Import/Export an unified Contact Model anpassen | 4h | Import/Export nutzt alte Feldnamen (`first_name`, `last_name`, `mobile`, `position`, `department`). Auf unified Contact-Felder umstellen (`firstname`, `surname`, `phone_1`, `email_1`, etc.). Company-Import auf Contact mit type='company' umstellen. |
|
||||
| 0.18 | .gitignore & Config-Cleanup | 2h | `.gitignore` hat `webui/` statt `frontend/` — frontend/node_modules und frontend/dist werden nicht ignoriert! Korrigieren. `python-jose` (JWT) aus requirements.txt entfernen — Code nutzt Session-Auth. `pyproject.toml` Python-Version auf 3.12 aktualisieren. `.env.docker.example` JWT-Variablen entfernen. **.env aus Git entfernen** (ist committet aber sollte nicht sein). `dump.rdb` und `test.txt` aus Repo löschen. `frontend/dist/` aus Git entfernen (sollte nicht committet sein). |
|
||||
| 0.19 | Mail-Salt Security-Fix | 2h | `mail/services.py` hat hardcoded salt `b"leocrm-mail-salt"` für Passwort-Verschlüsselung. Salt sollte random pro Account sein. Fix: Random salt generieren und mit encrypted_password zusammen speichern. DB-Migration für bestehende Accounts. |
|
||||
| 0.20 | AGPL-Lizenzen durch kommerziell nutzbare Alternativen ersetzen | 6h | **PyMuPDF** (AGPL-3.0) → ersetzen durch `pypdf` (BSD). Text-Extraktion in unified_search anpassen. **OnlyOffice** (AGPL-3.0) → ersetzen durch **Collabora Online** (LGPL/MPL). DMS Edit-Sessions auf Collabora umstellen. `requirements.txt`, `Dockerfile`, `docker-compose.yml`, `architecture.md` aktualisieren. DMS Plugin `OnlyOfficeConfig` → `CollaboraConfig`. Frontend DMS-Komponenten anpassen. Lizenz-Datei (`LICENSE`) und `THIRD_PARTY_LICENSES.md` erstellen. |
|
||||
|
||||
**Phase 0 Gesamt: ~77h**
|
||||
|
||||
### UI-Design-Richtlinien (Task 0.7)
|
||||
|
||||
Basierend auf Analyse der bestehenden Plugins (Calendar, Mail, DMS, Contacts):
|
||||
|
||||
**Layout-Patterns:**
|
||||
- **3-Spalten-Explorer-Layout** (Tree | Liste/Explorer | Detail) — verwendet von Calendar, Mail, DMS
|
||||
- **ResizablePanel** für drag-to-resize Spalten — bereits implementiert
|
||||
- **PluginToolbar** für Plugin-Aktionen (oben) — bereits implementiert
|
||||
- **Modal** für Formulare (Create/Edit/Delete-Bestätigung) — bereits implementiert
|
||||
- **EmptyState** für leere Listen — bereits implementiert
|
||||
- **LoadingState/Skeleton** für Lade-Zustände — bereits implementiert
|
||||
|
||||
**Farbsystem (Tailwind Design Tokens):**
|
||||
- `primary` (Blau #2563eb) — Hauptaktionen, aktive Zustände
|
||||
- `secondary` (Slate #64748b) — Text, Borders, Hintergründe
|
||||
- `accent` (Fuchsia #d946ef) — Hervorhebungen, Info-Badges
|
||||
- `danger` (Rot #dc2626) — Löschen, Fehler
|
||||
- `warning` (Amber #f59e0b) — Warnungen
|
||||
- `success` (Grün #16a34a) — Erfolg, Bestätigungen
|
||||
- Jede Farbe mit 50-900 Schattierungen
|
||||
- **Dark Mode** via `darkMode: 'class'` — CSS-Variablen in `:root` und `.dark`
|
||||
|
||||
**Typografie:**
|
||||
- Font: `Inter` (system-ui fallback)
|
||||
- Mono: `JetBrains Mono` für Code/Daten
|
||||
- Größen: xs (0.75rem) bis 4xl (2.25rem)
|
||||
- Zeilenhöhen definiert pro Größe
|
||||
|
||||
**Komponenten-Konventionen:**
|
||||
- **Button**: 4 Varianten (primary/secondary/danger/ghost), 3 Größen (sm/md/lg), `min-h-touch` (44px), `focus-visible:ring-2`
|
||||
- **Card**: Titel + Beschreibung + Actions (header), Body, optional Footer (bg-secondary-50)
|
||||
- **Badge**: 7 Varianten (default/primary/success/warning/danger/info/secondary), optional dot
|
||||
- **Input/Select**: `focus-ring` Klasse, `border-secondary-200`, `rounded-md`
|
||||
- **Modal**: `size` prop (sm/md/lg/xl), `ConfirmDialog` für Bestätigungen
|
||||
- **Table/DataGrid**: TanStack Table, ARIA-labels auf sortierbare Headers
|
||||
- **Toast**: `useToast()` Hook für Benachrichtigungen
|
||||
|
||||
**Spacing & Layout:**
|
||||
- Standard-Padding: `px-6 py-4` (Card body), `p-4` (Panel)
|
||||
- Gap: `gap-2` (Buttons), `gap-4` (Sections), `gap-6` (Columns)
|
||||
- Border-Radius: `rounded-md` (0.5rem) Standard, `rounded-lg` (0.75rem) für Cards
|
||||
- Shadow: `shadow-sm` (Cards), `shadow-md` (Dropdowns), `shadow-lg` (Modals)
|
||||
|
||||
**Accessibility (bereits implementiert):**
|
||||
- `focus-ring` Klasse: `focus-visible:ring-2 focus-visible:ring-primary-500`
|
||||
- `btn-touch` Klasse: `min-h-touch min-w-touch` (44px)
|
||||
- `sr-only` und `sr-only-focusable` Klassen
|
||||
- `prefers-reduced-motion` Media Query
|
||||
- `aria-hidden="true"` auf dekorativen SVGs
|
||||
- `aria-label` auf interaktiven Elementen ohne sichtbaren Text
|
||||
|
||||
**Plugin-UI-Patterns (für neue Plugins):**
|
||||
- Jede Plugin-Seite folgt dem 3-Spalten-Layout (wenn anwendbar)
|
||||
- PluginToolbar für Aktionen (Create, Import, Export, etc.)
|
||||
- Plugin-Settings als eigene Settings-Sub-Seite
|
||||
- Plugin-Detail-Tabs (z.B. "Dateien" bei Contact-Detail)
|
||||
- Konsistente EmptyState-Komponente wenn keine Daten
|
||||
- Konsistente LoadingState/Skeleton-Komponente beim Laden
|
||||
- Toast für Erfolg/Fehler-Meldungen nach Aktionen
|
||||
- ConfirmDialog vor destruktiven Aktionen
|
||||
|
||||
**Was im Design-Guide dokumentiert wird:**
|
||||
1. Farbsystem mit Verwendungsregeln (wann welche Farbe)
|
||||
2. Typografie-Hierarchie (Überschriften, Body-Text, Labels)
|
||||
3. Layout-Patterns (3-Spalten, Modal, Settings-Tree)
|
||||
4. Komponenten-Verwendung (welche Komponente für was)
|
||||
5. Spacing & Sizing Konventionen
|
||||
6. Accessibility-Regeln
|
||||
7. Dark-Mode-Regeln
|
||||
8. Plugin-UI-Patterns für neue Plugins
|
||||
9. Do's & Don'ts
|
||||
10. Code-Beispiele aus bestehenden Plugins
|
||||
|
||||
### RBAC-Audit & Plugin-Permissions (Task 0.10)
|
||||
|
||||
**Problem:** 4 Plugins haben `permissions=[]` im Manifest → keine Rechte-Prüfung auf ihren Routes:
|
||||
|
||||
| Plugin | Aktuell | Muss definiert werden |
|
||||
|---|---|---|
|
||||
| **calendar** | `permissions=[]` | `calendar:read`, `calendar:write`, `calendar:delete`, `calendar:share`, `calendar:admin` |
|
||||
| **dms** | `permissions=[]` | `dms:read`, `dms:write`, `dms:delete`, `dms:share`, `dms:admin` |
|
||||
| **entity_links** | `permissions=[]` | `entity_links:read`, `entity_links:write`, `entity_links:delete` |
|
||||
| **tags** | `permissions=[]` | `tags:read`, `tags:write`, `tags:delete`, `tags:admin` |
|
||||
|
||||
**Was zu tun ist:**
|
||||
1. Pro Plugin passende Permissions im Manifest definieren
|
||||
2. Alle Plugin-Routes mit `require_permission()` absichern
|
||||
3. Permission-Registry registriert Plugin-Permissions automatisch beim Aktivieren
|
||||
4. Admin kann Permissions in Rollen-Editor zuweisen
|
||||
5. Tests: User ohne Permission → 403, User mit Permission → 200
|
||||
|
||||
**Zusätzlich in Phase 1 (Permission-Registry-Cleanup):**
|
||||
- `companies:read/write/delete` aus `CORE_PERMISSIONS` entfernen (wird zu `contacts:read/write/delete`)
|
||||
- `CORE_FIELD_DEFINITIONS` aktualisieren: alte Felder (`first_name`, `last_name`, `mobile`, `position`, `department`, `linkedin_url`) durch unified Contact-Felder ersetzen (`firstname`, `surname`, `phone_1`, `email_1`, etc.)
|
||||
- `companies` Field-Definitions entfernen
|
||||
|
||||
### LiteLLM-Integration (Task 0.11)
|
||||
|
||||
**Problem:** Aktuelle `llm_client.py` spricht nur OpenAI-compatible API direkt via httpx. Keine Unterstützung für Anthropic, Google, lokale Modelle etc.
|
||||
|
||||
**Lösung:** LiteLLM als unified LLM-Interface integrieren.
|
||||
|
||||
**Was LiteLLM bietet:**
|
||||
- 100+ LLM-Provider über eine einheitliche API (OpenAI, Anthropic, Google, Azure, AWS Bedrock, Ollama, etc.)
|
||||
- Konsistente Request/Response-Formate
|
||||
- Streaming-Support
|
||||
- Fallback/Routing-Regeln
|
||||
- Cost-Tracking
|
||||
- Rate-Limiting
|
||||
|
||||
**Was zu tun ist:**
|
||||
1. `litellm` als Python-Dependency hinzufügen
|
||||
2. `llm_client.py` auf LiteLLM umstellen: `litellm.acompletion()` statt direktem httpx-Call
|
||||
3. Konfiguration via Env-Vars: `AI_MODEL`, `AI_API_KEY`, `AI_API_BASE` (bleiben gleich), plus `AI_PROVIDER` (neu: openai/anthropic/google/ollama/etc.)
|
||||
4. AI Assistant Plugin nutzt LiteLLM für Multi-Provider-Support
|
||||
5. AI Proactive Plugin nutzt LiteLLM für Suggestions
|
||||
6. Zukünftige Plugins können LiteLLM einfach nutzen — einheitliches Interface
|
||||
7. Mock-Mode für Tests beibehalten (wenn kein API-Key gesetzt)
|
||||
8. Plugin-Entwickler-Richtlinien: Wie man LiteLLM in neuen Plugins nutzt
|
||||
|
||||
**Architektur:**
|
||||
```
|
||||
Plugin (ai_assistant, ai_proactive, zukünftige)
|
||||
↓
|
||||
LiteLLM (unified LLM interface)
|
||||
↓
|
||||
Provider (OpenAI, Anthropic, Google, Ollama, ...)
|
||||
```
|
||||
|
||||
**Vorteil für zukünftige Plugins:**
|
||||
- Ein Plugin kann LLM-Funktionen nutzen ohne sich um den Provider zu kümmern
|
||||
- Admin kann Provider in Settings konfigurieren
|
||||
- KI-Modelle können ausgetauscht werden ohne Code-Änderung
|
||||
|
||||
---
|
||||
|
||||
### PHASE 1: Unified Contact Model — Vollendung (Backend + Frontend)
|
||||
**Ziel:** 'company' als separates Konzept komplett entfernen. Alles ist 'contact' mit type='company'|'person'. Wie Rentman.
|
||||
|
||||
#### 1A: Backend — Company-Routes & Services entfernen
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 1.1 | `app/routes/companies.py` entfernen | 1h | 303 Zeilen. Router aus `main.py`/`routes/__init__.py` austragen. |
|
||||
| 1.2 | `app/services/company_service.py` entfernen | 1h | 273 Zeilen. Importe aus `services/__init__.py` entfernen. |
|
||||
| 1.3 | `app/models/company.py` entfernen | 1h | Backward-compat shim. Importe überall auf `Contact` umstellen. |
|
||||
| 1.4 | `app/schemas/company.py` entfernen | 1h | CompanyCreate, CompanyUpdate, CompanyResponse etc. |
|
||||
| 1.5 | `app/ai/action_mapper.py` aktualisieren | 3h | Company-Intents (create_company, delete_company, update_company, list_company) auf Contact-API umstellen. Regex-Patterns anpassen. |
|
||||
| 1.6 | `app/workflows/engine.py` aktualisieren | 1h | Event `company.created` → `contact.created`. Workflow-Trigger anpassen. |
|
||||
| 1.7 | `app/core/worker.py` aktualisieren | 1h | `index_company` Referenzen → `index_contact`. |
|
||||
| 1.8 | `app/core/seeds.py` prüfen/aktualisieren | 1h | Falls Company-Seed-Daten existieren, auf Contact mit type='company' umstellen. |
|
||||
|
||||
**1A Gesamt: ~10h**
|
||||
|
||||
#### 1B: Backend — Plugins von entity_type='company' befreien
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 1.9 | **entity_links Plugin** aktualisieren | 4h | `entity_type` Pattern von `^(company|contact)$` → `^contact$`. `company_router` entfernen. `on_company_deleted` → `on_contact_deleted`. Event `company.deleted` → `contact.deleted`. DB-Migration: bestehende EntityLinks mit entity_type='company' auf 'contact' migrieren. |
|
||||
| 1.10 | **unified_search Plugin** aktualisieren | 6h | `CompanySearchProvider` → wird zu `ContactSearchProvider` oder bleibt als Provider für type='company' Kontakte. `index_company` → `index_contact`. Events `company.created/updated` → `contact.created/updated`. `search_engine.py` Mapping `"company" → "contacts"` anpassen. `jobs.py` aktualisieren. |
|
||||
| 1.11 | **calendar Plugin** aktualisieren | 3h | `entity_type` Pattern von `^(company|contact)$` → `^contact$`. CalendarEntryLink entity_type anpassen. DB-Migration: bestehende Links migrieren. CalendarType='company' kann bleiben (Kalender-Typ, nicht Entity-Referenz). |
|
||||
| 1.12 | **tags Plugin** aktualisieren | 3h | `entity_type` Pattern von `^(company|contact|file|folder)$` → `^(contact|file|folder)$`. DB-Migration: bestehende Tag-Assignments mit entity_type='company' auf 'contact' migrieren. |
|
||||
| 1.13 | **mail Plugin** aktualisieren | 4h | `mail.company_id` Spalte → `mail.contact_id` (DB-Migration). Routes, Schemas, Services aktualisieren. `company_id` Referenzen in Frontend-API-Modul. |
|
||||
| 1.14 | **test_sample Plugin** aktualisieren | 1h | `company.created` Event → `contact.created`. Test-Plugin ist Referenz für Plugin-Entwicklung. |
|
||||
| 1.15 | **Event-Namen vereinheitlichen** | 2h | Alle `company.created/updated/deleted` Events → `contact.created/updated/deleted`. Event-Publisher in contact_service.py prüfen. |
|
||||
| 1.16 | **DB-Migration: entity_type 'company' → 'contact'** | 3h | Alembic-Migration: UPDATE entity_links SET entity_type='contact' WHERE entity_type='company'. UPDATE tag_assignments SET entity_type='contact' WHERE entity_type='company'. UPDATE calendar_entry_links SET entity_type='contact' WHERE entity_type='company'. ALTER TABLE mails RENAME COLUMN company_id TO contact_id. |
|
||||
| 1.17 | **Backend-Tests aktualisieren** | 4h | Alle Tests die Company-Routes oder entity_type='company' referenzieren umstellen. `test_companies.py` entfernen oder zu Contact-Tests umschreiben. |
|
||||
| 1.18 | **Permission-Registry-Cleanup** | 3h | `companies:read/write/delete` aus `CORE_PERMISSIONS` entfernen. `CORE_FIELD_DEFINITIONS` aktualisieren: alte Felder durch unified Contact-Felder ersetzen. `companies` Field-Definitions entfernen. |
|
||||
| 1.19 | **Addresses entity_type='company' → 'contact'** | 2h | `address_service.py` `VALID_ENTITY_TYPES` von `{"company", "contact"}` → `{"contact"}`. `address.py` Model anpassen. DB-Migration: bestehende Adressen mit entity_type='company' auf 'contact' migrieren. |
|
||||
| 1.20 | **conftest.py aktualisieren** | 2h | `conftest.py` importiert `Company` und `CompanyContact` aus alten Modellen. Auf unified Contact Model umstellen. Test-Fixtures anpassen. |
|
||||
|
||||
**1B Gesamt: ~33h**
|
||||
|
||||
#### 1C: Frontend — Unified Contact UI
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 1.18 | Contact-Detail-Route hinzufügen | 2h | Route `/contacts/:id` in `routes/index.tsx`. `ContactDetail.tsx` (372 Zeilen) existiert bereits als Komponente. |
|
||||
| 1.19 | ContactList mit Type-Filter (company/person) | 4h | `ContactsList.tsx` (445 Zeilen) um Type-Filter erweitern. Tabs oder Toggle: "Alle | Firmen | Personen". |
|
||||
| 1.20 | ContactDetail um ContactPerson-Verwaltung erweitern | 8h | Bei type='company': Ansprechpartner-Liste, Ansprechpartner hinzufügen/bearbeiten/löschen. ContactPerson API-Hooks in Frontend. |
|
||||
| 1.21 | ContactEditModal für beide Types | 6h | Formular je nach type unterschiedlich: company → name, person → firstname/surname. Adressen (mailing/visit/invoice). |
|
||||
| 1.22 | Company-Hooks aus `hooks.ts` entfernen | 2h | `useCompanies`, `useCompany`, `useCreateCompany`, `useUpdateCompany`, `useDeleteCompany`, `useCompanyExport`, `useCompanyImport` entfernen. Company-Interface entfernen. |
|
||||
| 1.23 | Frontend Type-Definitions aktualisieren | 2h | `calendar.ts`: entity_type 'company' → 'contact'. `tags.ts`: EntityType 'company' entfernen. `search.ts`: type 'company' → 'contact'. `mail.ts`: company_id → contact_id. |
|
||||
| 1.24 | Dashboard.tsx aktualisieren | 1h | `useUnifiedContacts(1, 1, undefined, 'company')` → `useUnifiedContacts(1, 1, undefined, 'company')` (type-Filter bleibt, ist jetzt Contact type nicht Company entity). |
|
||||
| 1.25 | GlobalSearchResults.tsx aktualisieren | 2h | Search result type 'company' → 'contact'. Grouping, Icons, Labels anpassen. |
|
||||
| 1.26 | ContactFolderTree in ContactList integrieren | 4h | Ordner-Baum links, Kontaktliste rechts. Drag & Drop Kontakte in Ordner. |
|
||||
| 1.27 | React Hook Form + Zod in ContactEditModal | 3h | Strukturierte Validierung für alle Contact-Felder. |
|
||||
| 1.28 | Frontend-Tests aktualisieren | 4h | Tests für Contact-Detail, ContactEditModal, ContactPerson-Verwaltung. Company-Test-Referenzen entfernen. |
|
||||
|
||||
**1C Gesamt: ~38h**
|
||||
|
||||
**Phase 1 Gesamt: ~81h** (vorher 33h — unterschätzt um 48h!)
|
||||
|
||||
---
|
||||
|
||||
### PHASE 2: Code-Splitting & Performance
|
||||
**Ziel:** Frontend lädt nur was nötig ist. Virtual Scrolling überall.
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 2.1 | React.lazy + Suspense für alle Routes | 4h | Alle Page-Imports in `routes/index.tsx` auf `React.lazy()` umstellen. `<Suspense>` mit Loading-Fallback. |
|
||||
| 2.2 | `@tanstack/react-virtual` installieren | 1h | Dependency hinzufügen. |
|
||||
| 2.3 | Virtual Scrolling in DataGrid | 6h | `DataGrid.tsx` um Virtual Scrolling erweitern. Nur sichtbare Zeilen rendern. |
|
||||
| 2.4 | Virtual Scrolling in MailList | 4h | `MailList.tsx` um Virtual Scrolling erweitern. |
|
||||
| 2.5 | Virtual Scrolling in ContactList | 4h | `ContactList.tsx` um Virtual Scrolling erweitern. |
|
||||
| 2.6 | Virtual Scrolling in allen anderen Listen | 4h | AuditLog, Calendar Entries, DMS FileGrid, etc. |
|
||||
| 2.7 | Bundle-Analyse & Optimierung | 2h | `vite-bundle-visualizer` prüfen, manuelle Chunks für große Dependencies. |
|
||||
|
||||
**Phase 2 Gesamt: ~25h**
|
||||
|
||||
---
|
||||
|
||||
### PHASE 3: Plugin-UI-System (WordPress-Style)
|
||||
**Ziel:** Dynamisches Plugin-UI-Loading. Plugins registrieren sich selbst.
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 3.1 | Plugin-Manifest-Frontend-Endpoint | 4h | Backend-Endpoint `GET /api/v1/plugins/active-manifests` liefert alle aktiven Plugin-Manifeste mit UI-Definitionen (routes, menu_items, detail_tabs, settings_pages, dashboard_widgets). |
|
||||
| 3.2 | `PluginRegistry.tsx` erstellen | 8h | Fetcht aktive Plugin-Manifeste beim App-Start. Registriert Routes, Menu-Items, Detail-Tabs, Settings-Pages dynamisch. |
|
||||
| 3.3 | `PluginLoader.tsx` erstellen | 6h | Lazy-loaded Plugin-Komponenten via `React.lazy()`. Suspense-Boundaries pro Plugin. Error-Boundary falls Plugin nicht lädt. |
|
||||
| 3.4 | Sidebar dynamisch aus Plugin-Manifesten | 4h | Sidebar rendert Menu-Items aus Plugin-Registry statt hartkodierte Items. |
|
||||
| 3.5 | Settings-Baum dynamisch aus Plugin-Manifesten | 4h | Settings-Pages werden dynamisch aus Plugin-Manifesten generiert. |
|
||||
| 3.6 | Detail-Tabs dynamisch (Contact-Detail) | 4h | Plugin-Detail-Tabs (z.B. "Dateien", "E-Mails", "Kalender") werden dynamisch gerendert. |
|
||||
| 3.7 | Plugin-Routen aus hartkodiertem Router entfernen | 4h | Statische Plugin-Imports aus `routes/index.tsx` entfernen. Alles über PluginRegistry. |
|
||||
| 3.8 | Plugin-Entwickler-Richtlinien erstellen | 8h | `docs/plugin-development-guide.md`: Manifest-Format, Lifecycle, UI-Registrierung, Event-Bus, Migration-Runner, Service-Container, Beispiele, Do's & Don'ts, Testing-Guide. |
|
||||
| 3.9 | Plugin-Templates / Boilerplate | 4h | `templates/plugin-template/`: Minimal-Plugin als Startpunkt für neue Plugins. Mit Manifest, Routes, Models, Schemas, Migration, Tests. |
|
||||
| 3.10 | Tests für Plugin-UI-System | 4h | Vitest-Tests für PluginRegistry, PluginLoader, dynamische Sidebar/Settings. |
|
||||
| 3.10b | Plugin-Install-System | 8h | Plugins einfach installierbar machen: ZIP-Upload, URL-Install, Plugin-Marketplace-Integration. Plugin-Upload-Endpoint, Validierung (Manifest prüfen, tenant_id-Check, Security-Scan), automatische Migration bei Install. Install-UI in SettingsPlugins.tsx. |
|
||||
|
||||
**Phase 3 Gesamt: ~58h**
|
||||
|
||||
---
|
||||
|
||||
### PHASE 3.5: Automation & Agents Plugin
|
||||
**Ziel:** Zentrale Oberfläche für Automatisierungen und selbst-arbeitende KI-Agenten. Plugins können Agenten und Automation-Templates mitbringen.
|
||||
|
||||
**Architektur:**
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Automation & Agents UI │
|
||||
│ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ Automation │ │ Agent Builder │ │
|
||||
│ │ Builder │ │ - Agent definieren │ │
|
||||
│ │ - Trigger │ │ - Tools auswählen │ │
|
||||
│ │ - Schedule │ │ - LLM-Modell wählen │ │
|
||||
│ │ - Conditions │ │ - Heartbeat setzen │ │
|
||||
│ │ - Actions │ │ - Proaktiv/Reaktiv │ │
|
||||
│ └─────────────┘ └─────────────────────┘ │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Cron-Scheduler │ Workflow-Timeouts │ HB │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Plugins bringen mit: │
|
||||
│ - agent_definitions (Agent-Templates) │
|
||||
│ - automation_templates (Automation-Tpl) │
|
||||
│ - cron_jobs (periodische Tasks) │
|
||||
│ - heartbeat_configs │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 3.11 | Plugin-Manifest um Agent/Automation-Felder erweitern | 4h | Manifest um `agent_definitions`, `automation_templates`, `cron_jobs`, `heartbeat_configs` erweitern. Plugins deklarieren was sie mitbringen. |
|
||||
| 3.12 | Cron-Scheduler Backend | 6h | ARQ-basierter Scheduler für periodische Tasks. Cron-Expressions (z.B. `0 8 * * *` = täglich 8 Uhr). Scheduler liest aktive Cron-Jobs aus DB und enqueued sie. Ersetzt hartkodierten Heartbeat. |
|
||||
| 3.13 | Workflow-Timeout-Worker | 4h | ARQ-Job der regelmäßig Workflow-Instanzen mit abgelaufenem `timeout_at` prüft. Bei Timeout: Status auf `cancelled`, Notification an Initiator. |
|
||||
| 3.14 | Agent Builder Backend | 8h | API für Agent-Definitionen: Name, Beschreibung, LLM-Modell, Tools (aus tool_registry), System-Prompt, Heartbeat-Intervall, Proaktiv/Reaktiv-Modus. Agent-Definitionen in DB gespeichert. |
|
||||
| 3.15 | Automation Builder Backend | 6h | API für Automation-Definitionen: Trigger (Event/Schedule/Manual), Conditions, Actions (API-Call/Notification/Workflow-Start). Automation-Definitionen in DB gespeichert. |
|
||||
| 3.16 | Automation Execution Engine | 6h | Engine die Automations ausführt: Event-Trigger → Conditions prüfen → Actions ausführen. Nutzt Event-Bus für Event-Trigger, Cron-Scheduler für Schedule-Trigger. |
|
||||
| 3.17 | Agent Runner | 8h | Führt Agenten aus: Proaktiv (Heartbeat-getriggert, sammelt Kontext, generiert Vorschläge) oder Reaktiv (auf Event/Message, reagiert). Nutzt LiteLLM + tool_registry + PydanticAI. |
|
||||
| 3.18 | Automation & Agents UI — Automation Builder | 8h | Visueller Builder für Automations: Trigger auswählen, Conditions definieren, Actions zusammenstellen. Drag & Drop oder Form-basiert. Live-Preview. |
|
||||
| 3.19 | Automation & Agents UI — Agent Builder | 8h | Visueller Builder für Agenten: Name, Modell, Tools, System-Prompt, Heartbeat. Test-Run Button. Agent-Liste mit Status (aktiv/inaktiv). |
|
||||
| 3.20 | Automation & Agents UI — Dashboard | 4h | Übersicht: Aktive Automations, Aktive Agenten, Letzte Ausführungen, Logs, Fehler. Heartbeat-Status pro Agent. |
|
||||
| 3.21 | Plugin-Beiträge registrieren | 4h | Wenn Plugin aktiviert wird: Agent-Definitionen, Automation-Templates, Cron-Jobs aus Manifest registrieren. Bei Deaktivierung: entfernen. |
|
||||
| 3.22 | Heartbeat-Verwaltung migrieren | 3h | Hartkodierten Heartbeat aus ai_proactive in Automation & Agents Plugin migrieren. Heartbeat wird zu einem konfigurierbaren Cron-Job. |
|
||||
| 3.23 | Settings für Automation & Agents | 3h | Einstellungen: Default-LLM-Modell für Agenten, Heartbeat-Default-Intervall, Max-Concurrent-Agents, Log-Level. |
|
||||
| 3.24 | Tests für Automation & Agents | 6h | Tests für Cron-Scheduler, Workflow-Timeouts, Agent Runner, Automation Engine, Plugin-Beiträge. |
|
||||
| 3.25 | Agent- & Automation-Logs | 4h | Jede Agent-Ausführung und Automation-Ausführung wird geloggt: Start, Ende, Status, Dauer, Ergebnis, Fehler. Log-Viewer in Dashboard UI. Historie pro Agent/Automation. |
|
||||
| 3.26 | RBAC für Automation & Agents | 3h | Permissions definieren: `automation:read`, `automation:write`, `automation:delete`, `automation:execute`, `agents:read`, `agents:write`, `agents:delete`, `agents:execute`. Nur Admin/Editor dürfen Agenten/Automations erstellen. |
|
||||
| 3.27 | Dry-Run / Test-Modus | 3h | Automations und Agenten können im Dry-Run getestet werden: Führt Conditions aus, zeigt was passieren würde, aber führt keine destruktiven Actions aus. Test-Button in Builder UI. |
|
||||
| 3.28 | Agent Rate-Limiting & Safety | 3h | Max-Ausführungen pro Agent pro Stunde. Max-Dauer pro Ausführung. Auto-Stop bei Endlosschleife (wenn Agent dieselbe Action 5x hintereinander ausführt). Budget-Limit pro Agent (LiteLLM Cost-Tracking). |
|
||||
| 3.29 | Plugin-Beitrags-Konfliktlösung | 2h | Wenn zwei Plugins denselben Agent-Namen/Templat-Namen mitbringen: Plugin-Name als Prefix (`mail.mail_sorter` statt `mail_sorter`). Dedup-Logik bei Registrierung. |
|
||||
| 3.30 | Agent-zu-Agent-Kommunikation | 8h | Agenten können Nachrichten an andere Agenten senden. Nutzt kommunikation Plugin-Infrastruktur (WebSocket, Rooms). Agent-Message-Router: Agent A sendet `{to: 'mail_sorter', message: 'Neuer Termin gefunden'}`. Empfänger-Agent reagiert. Agent-Chatrooms in Dashboard sichtbar. |
|
||||
| 3.31 | Versionshistorie für Agenten & Automations | 4h | Jede Änderung an Agent/Automation erstellt neue Version. Alte Versionen können wiederhergestellt werden. Versions-Diff in UI. `agent_versions` und `automation_versions` Tabellen. |
|
||||
| 3.32 | MiniApps: Plugin-MiniApps im Chat | 6h | **Bereits implementiert:** `MiniAppRegistry`, `MiniAppDef`, Routes (`GET /miniapps`, `POST /conversations/{id}/miniapps`), `MiniAppBlock.tsx` Frontend. **Was fehlt:** Plugin-Manifest um `miniapps` Feld erweitern (Plugins deklarieren welche MiniApps sie mitbringen). MiniApp-Builder UI (visuell MiniApps erstellen). MiniApp-Store in Settings. Dokumentation in Plugin-Entwickler-Richtlinien. |
|
||||
|
||||
**Phase 3.5 Gesamt: ~105h**
|
||||
|
||||
**Was Plugins mitbringen können:**
|
||||
- **Agent-Definitionen:** Ein Plugin kann vordefinierte Agenten mitbringen (z.B. Mail-Plugin bringt "E-Mail-Sortier-Agent" mit)
|
||||
- **Automation-Templates:** Ein Plugin kann Automation-Vorlagen mitbringen (z.B. Calendar-Plugin bringt "Terminerinnerung 24h vorher" mit)
|
||||
- **Cron-Jobs:** Ein Plugin kann periodische Tasks deklarieren (z.B. Mail-Plugin: "IMAP-Sync alle 15 Minuten")
|
||||
- **Heartbeat-Configs:** Ein Plugin kann Heartbeat-Konfigurationen mitbringen
|
||||
|
||||
**Beispiel: Mail-Plugin bringt Agent mit**
|
||||
```json
|
||||
{
|
||||
"agent_definitions": [{
|
||||
"name": "mail_sorter",
|
||||
"display_name": "E-Mail-Sortier-Assistent",
|
||||
"description": "Sortiert eingehende E-Mails automatisch nach Regeln",
|
||||
"model": "ollama/deepseek-v4-flash",
|
||||
"tools": ["mail.read", "mail.move", "mail.label"],
|
||||
"system_prompt": "Du sortierst E-Mails...",
|
||||
"mode": "reactive",
|
||||
"trigger_event": "mail.received"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Beispiel: Calendar-Plugin bringt Automation mit**
|
||||
```json
|
||||
{
|
||||
"automation_templates": [{
|
||||
"name": "appointment_reminder",
|
||||
"display_name": "Terminerinnerung 24h vorher",
|
||||
"trigger": {"type": "schedule", "cron": "0 8 * * *"},
|
||||
"conditions": [{"field": "entry.start_at", "operator": "lt", "value": "now + 24h"}],
|
||||
"actions": [{"type": "notification", "title": "Terminerinnerung", "body": "Morgen: ${entry.title}"}]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### PHASE 4: KI-UI-Steuerung
|
||||
**Ziel:** KI-Agent kann UI steuern — Kontakte öffnen, Filter setzen, navigieren. User sieht das Ergebnis in der UI.
|
||||
|
||||
**Wichtig:** Bestehende WebSocket-Infrastruktur im `kommunikation` Plugin (`/api/v1/comm/ws`, `websocket_manager.py`) kann als Referenz dienen.
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 4.1 | UI-Command-Protokoll definieren | 4h | JSON-Protokoll für UI-Befehle: `{action: 'navigate', path: '/contacts/123'}`, `{action: 'filter', entity: 'contacts', filter: {type: 'company'}}`, `{action: 'open_contact', id: '...'}`. |
|
||||
| 4.2 | WebSocket-Endpoint für KI-UI-Steuerung | 6h | Backend-WebSocket `/ws/ai-ui-control`. Authentifiziert via Session. KI-Agent sendet Commands, Frontend empfängt. Basiert auf bewährter WebSocket-Infrastruktur aus kommunikation Plugin. |
|
||||
| 4.3 | Frontend `useAIUIControl` Hook | 6h | WebSocket-Client im Frontend. Empfängt Commands und führt sie aus. Nutzt React Router, TanStack Query, Zustand Stores. |
|
||||
| 4.4 | Command: Navigate | 2h | `useNavigate()` für Route-Wechsel. KI kann zu jeder Seite navigieren. |
|
||||
| 4.5 | Command: Filter setzen | 4h | URL-Search-Params setzen für Listen-Filter. KI kann Filter setzen (z.B. "Zeige nur Firmen in Berlin"). |
|
||||
| 4.6 | Command: Contact öffnen | 3h | Navigate zu `/contacts/:id` + Detail-Daten laden. KI kann Kontakt öffnen und User sieht ihn. |
|
||||
| 4.7 | Command: Modal öffnen/schließen | 3h | EditModal, CreateModal etc. per Command steuerbar. |
|
||||
| 4.8 | Command: Tab wechseln | 2h | Detail-Tabs (Dateien, E-Mails, Kalender) per Command wechseln. |
|
||||
| 4.9 | Command: Settings ändern | 3h | System-Settings, User-Preferences per UI-Command ändern. Wird in UI sichtbar. |
|
||||
| 4.10 | UI-Action-Feedback an KI | 4h | Frontend sendet Bestätigung zurück: `{action: 'navigate', status: 'success', current_path: '/contacts/123'}`. KI weiß, dass Command ausgeführt wurde. |
|
||||
| 4.11 | Visuelle KI-Indikation | 3h | Wenn KI eine Aktion ausführt: kurzer Highlight-Effekt oder Toast "KI führt Aktion aus...". User sieht dass KI agiert. |
|
||||
| 4.12 | Tests für KI-UI-Steuerung | 4h | Vitest-Tests für Command-Protokoll, useAIUIControl Hook, Command-Ausführung. |
|
||||
|
||||
**Phase 4 Gesamt: ~44h**
|
||||
|
||||
---
|
||||
|
||||
### PHASE 5: API-Vollständigkeit & KI-Testbarkeit
|
||||
**Ziel:** App komplett per API steuerbar. KI kann selbstständig testen und Updates einspielen.
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 5.1 | API-Audit: Alle UI-Funktionen per API erreichbar | 8h | Systematische Prüfung: Jede UI-Aktion hat einen API-Endpoint. Fehlende Endpoints identifizieren und implementieren. Sidebar-Zustand, Tab-Auswahl, Filter-Zustand per API speichern/laden. |
|
||||
| 5.2 | User-Preferences-API erweitern | 4h | UI-Einstellungen (Sidebar collapsed, theme, language, active tab, sort preferences) per API speichern/laden. |
|
||||
| 5.3 | Workflow-API-Frontend-Modul | 4h | `api/workflows.ts` erstellen. Workflow-Definitions CRUD, Instances, Step-History. |
|
||||
| 5.4 | Playwright E2E-Tests: Setup | 4h | `@playwright/test` installieren. `playwright.config.ts`. Test-Helper für Login, API-Calls. |
|
||||
| 5.5 | Playwright: auth.spec.ts | 3h | Login → Logout E2E-Test. |
|
||||
| 5.6 | Playwright: contact-crud.spec.ts | 4h | Contact erstellen → bearbeiten → Ansprechpartner hinzufügen → löschen. |
|
||||
| 5.7 | Playwright: search.spec.ts | 3h | Globale Suche, Filter, Ergebnisse prüfen. |
|
||||
| 5.8 | Playwright: plugin-toggle.spec.ts | 3h | Plugin aktivieren/deaktivieren, UI-Änderung prüfen. |
|
||||
| 5.9 | Playwright: mail.spec.ts | 4h | Mail-Konto anlegen, Ordner anzeigen, Mail öffnen. |
|
||||
| 5.10 | Playwright: dms.spec.ts | 4h | Ordner erstellen, Datei hochladen, Vorschau, teilen. |
|
||||
| 5.11 | Playwright: calendar.spec.ts | 4h | Termin erstellen, Kalender wechseln, Kanban-View. |
|
||||
| 5.12 | API-Health-Check-Script für KI | 4h | `scripts/ai_health_check.py`: Prüft alle API-Endpunkte, gibt strukturierten Report. KI kann das vor/nach Updates laufen lassen. |
|
||||
| 5.13 | CI/CD-Pipeline für KI-Updates | 6h | `scripts/ai_deploy.py`: KI kann Build erstellen, Tests laufen, bei Erfolg deployen. Rollback bei Fehler. |
|
||||
| 5.14 | API-Dokumentation vervollständigen | 4h | OpenAPI/Swagger prüfen. Alle Endpoints dokumentiert. Beispiele für KI. |
|
||||
| 5.15 | Automatisiertes Backup-System | 8h | `pg_dump` + Storage-Backup als Cron-Job (nutzt Cron-Scheduler aus Phase 3.5). Backup-Konfiguration in Settings (Intervall, Aufbewahrung, Ziel: lokal/S3/Nextcloud). Restore-Script. Backup-Status in Dashboard. Notification bei Backup-Fehler. |
|
||||
| 5.16 | MCP-Server Integration | 10h | LeoCRM als MCP-Server: Externe Tools (Claude Desktop, andere KI-Clients) können auf LeoCRM-Daten zugreifen. MCP-Tools für Contacts, Calendar, Mail, DMS. Authentifiziert via API-Token. MCP-Config-Endpoint `GET /api/v1/mcp/tools`. |
|
||||
| 5.17 | MCP-Client Integration | 6h | LeoCRM-Agenten können externe MCP-Server nutzen (z.B. Web-Search, Code-Execution, externe Datenquellen). MCP-Client in tool_registry integriert. Admin kann MCP-Server in Settings konfigurieren. Agenten nutzen MCP-Tools wie native Tools. |
|
||||
| 5.18 | Report Generator: PDF-Support & Druck-Funktionen | 8h | Backend: WeasyPrint für PDF-Generierung aus Jinja2-Templates. Vorgefertigte Berichte: Kontaktliste, Kalender (Woche/Monat), Firmenliste, Audit-Log. Druck-Optimierte Templates (A4, Landscape). `output_format` um `pdf` und `print` erweitern. |
|
||||
| 5.19 | Report Generator: Frontend-Oberfläche | 10h | `Reports.tsx` Seite: Template-Liste, Template-Editor (Code-Editor für Jinja2), Report-Generierung mit Live-Preview, Download-History. Vorgefertigte Berichte als Buttons ("Kontakt-Liste drucken", "Kalender drucken"). Druck-Dialog mit Format-Auswahl (A4/A5/Landscape). |
|
||||
| 5.20 | Custom Fields: Plugin-Felder in UI | 6h | Plugins sollen Custom Fields mitbringen können. Plugin-Manifest um `custom_fields` Definition erweitern. Frontend: Dynamische Custom-Field-Renderer in Contact-Detail, ContactEditModal. Feld-Typen: text, number, date, select, multiselect, boolean. Felder werden in `contacts.custom` JSONB gespeichert. |
|
||||
| 5.21 | Tasks-Plugin | 12h | Eigenes Tasks-Plugin: Freie Aufgaben/Aktivitäten verwalten (Anruf protokollieren, Notiz, Besuch). Verknüpfung mit Kontakten. Tasks haben Status (open/in_progress/done), Priorität, Fälligkeitsdatum, Zuweisung an Nutzer. Tasks-Liste mit Filter. ARQ-Reminder für fällige Tasks. Plugin-Manifest, Models, Routes, Schemas, Frontend-Seite. |
|
||||
| 5.22 | Saved Searches / Smart Lists | 6h | Jede Listen-Ansicht (Contacts, Mail, Calendar, DMS) bekommt Filter-Funktionalität. Filter können gespeichert werden (Name, Filter-Kriterien). Gespeicherte Filter erscheinen als Tabs oder Sidebar-Einträge. `saved_filters` Tabelle (tenant-scoped, user-scoped). Frontend: Filter-Builder UI, Save-Button, Load-Gespeicherte-Filter. |
|
||||
| 5.23 | Deduplication / Merge (über KI/Automatisierung) | 6h | Contacts-Plugin bietet Dubletten-Erkennung: KI-gestützter Vergleich von Kontakten (Name, E-Mail, Telefon). Automation-Template: "Dubletten finden und zusammenführen". Merge-UI: Zwei Kontakte vergleichen, Felder auswählen, zusammenführen. `contact_merge_history` Tabelle. |
|
||||
| 5.24 | PWA (Progressive Web App) | 6h | Frontend als PWA planen: `manifest.json`, Service Worker, Offline-Caching für statische Assets, Add-to-Home-Screen, App-Icon. Vite PWA Plugin installieren. Push-Notifications vorbereiten (Notification API). |
|
||||
| 5.25 | Dashboard-System ausbauen | 8h | Plugins bringen Dashboard-Komponenten mit und melden diese an. Plugin-Manifest um `dashboard_widgets` erweitern (bereits in Architektur definiert aber nicht implementiert). Dashboard lädt Widgets dynamisch aus Plugin-Registry. Widget-Typen: Stat-Cards, Charts, Recent-Activity, Quick-Actions. Frontend: Dashboard-Grid mit drag-and-drop Widget-Positionierung. |
|
||||
|
||||
**Phase 5 Gesamt: ~145h**
|
||||
|
||||
---
|
||||
|
||||
### PHASE 6: React Hook Form + Zod überall
|
||||
**Ziel:** Konsistente Form-Validierung in allen Formularen
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 6.1 | ComposeModal (Mail) auf RHF + Zod | 4h | E-Mail-Validierung, Pflichtfelder, CC/BCC. |
|
||||
| 6.2 | AppointmentModal (Calendar) auf RHF + Zod | 4h | Datum-Validierung, Pflichtfelder, Recurrence. |
|
||||
| 6.3 | SettingsForms auf RHF + Zod | 6h | SettingsUsers, SettingsRoles, SettingsGroups, SettingsCurrencies, SettingsTaxes, SettingsSequences, SettingsSystem. |
|
||||
| 6.4 | DMS-Forms (Folder create, Share) auf RHF + Zod | 3h | |
|
||||
| 6.5 | Tag-Forms auf RHF + Zod | 2h | |
|
||||
| 6.6 | Mail-Settings-Forms auf RHF + Zod | 4h | Account-Erstellung, Rules, Signatures, Templates. |
|
||||
|
||||
**Phase 6 Gesamt: ~23h**
|
||||
|
||||
---
|
||||
|
||||
### PHASE 7: Test-Vollendung & Wartbarkeit
|
||||
**Ziel:** Vollständige Test-Abdeckung für KI-Wartbarkeit
|
||||
|
||||
| # | Aufgabe | Aufwand | Details |
|
||||
|---|---|---|---|
|
||||
| 7.1 | Tests für ungetestete Settings-Pages | 6h | SettingsGroups, SettingsSystem, SettingsCurrencies, SettingsTaxes, SettingsSequences, SettingsNotifications, SettingsPlugins. |
|
||||
| 7.2 | Tests für AI-Komponenten | 4h | ChatWindow, SessionList, SuggestionSidebar, AISettings, ProactiveAISettings. |
|
||||
| 7.3 | Tests für Calendar-Page | 3h | Calendar.tsx (717 Zeilen), CalendarKanban.tsx. |
|
||||
| 7.4 | Tests für DMS-Sub-Komponenten | 4h | FileExplorer, SourceTree, FileGrid, FileDetails, BulkActions. |
|
||||
| 7.5 | Tests für Contact-Sub-Komponenten | 3h | ContactDetail, ContactEditModal, ContactFolderTree. |
|
||||
| 7.6 | Tests für Comm-Blocks | 3h | BlockRenderer und alle Block-Typen. |
|
||||
| 7.7 | Tests für Stores | 2h | authStore, uiStore, commStore, pluginToolbarStore, calendarStore. |
|
||||
| 7.8 | Backend-Test-Lücken schließen | 8h | Tests für fehlende Plugin-Routes, Edge-Cases, Multi-Tenant-Szenarien. |
|
||||
| 7.9 | Test-Runner-Script für KI | 3h | `scripts/ai_run_tests.py`: Führt alle Tests aus (Backend + Frontend + E2E), gibt strukturierten Report. |
|
||||
|
||||
**Phase 7 Gesamt: ~36h**
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung: Aufwandsschätzung (korrigiert)
|
||||
|
||||
| Phase | Thema | Aufwand | Vorher | Änderung |
|
||||
|---|---|---|---|---|
|
||||
| 0 | Vorbereitung & Cleanup | ~77h | ~14h | **+63h** (Design, Theme, RBAC, LiteLLM, Search-RBAC, Undo, Storage, Import/Export, Config-Cleanup, Mail-Salt, PyMuPDF→pypdf, OnlyOffice→Collabora) |
|
||||
| 1 | Unified Contact (Backend+Frontend) | **~81h** | ~33h | **+48h** — Company-Referenzen in 6 Plugins + Permission-Registry + Addresses + conftest unterschätzt |
|
||||
| 2 | Code-Splitting & Performance | ~25h | ~25h | — |
|
||||
| 3 | Plugin-UI-System | ~58h | ~48h | +10h (Plugin-Install-System) |
|
||||
| 3.5 | Automation & Agents Plugin | ~105h | — | **NEU** — Agent Builder, Automation, Cron, Logs, Safety, Agent-zu-Agent, Versionshistorie, MiniApps |
|
||||
| 4 | KI-UI-Steuerung | ~44h | ~44h | — |
|
||||
| 5 | API, Testbarkeit, Backup, MCP, Reports, Custom Fields, Tasks, Saved Searches, Dedup, PWA, Dashboard | ~145h | ~57h | +88h |
|
||||
| 6 | React Hook Form + Zod | ~23h | ~23h | — |
|
||||
| 7 | Test-Vollendung | ~36h | ~36h | — |
|
||||
| | **GESAMT** | **~590h** | ~280h | **+310h** |
|
||||
|
||||
---
|
||||
|
||||
## Empfohlene Reihenfolge
|
||||
|
||||
```
|
||||
Phase 0 (Vorbereitung & Cleanup)
|
||||
↓
|
||||
Phase 1 (Unified Contact — Backend+Frontend) ← Core-CRM-Feature, größte Phase
|
||||
↓
|
||||
Phase 2 (Code-Splitting & Performance)
|
||||
↓
|
||||
Phase 3 (Plugin-UI-System) ← WordPress-Style, nicht zu lange schieben
|
||||
↓
|
||||
Phase 3.5 (Automation & Agents Plugin) ← Agent Builder, Cron-Scheduler, Automation
|
||||
↓
|
||||
Phase 4 (KI-UI-Steuerung) ← Baut auf Plugin-System auf
|
||||
↓
|
||||
Phase 5 (API-Vollständigkeit & Testbarkeit) ← KI kann selbstständig testen
|
||||
↓
|
||||
Phase 6 (React Hook Form + Zod) ← Qualität
|
||||
↓
|
||||
Phase 7 (Test-Vollendung) ← Wartbarkeit für KI
|
||||
```
|
||||
|
||||
**Begründung der Reihenfolge:**
|
||||
1. Phase 0 zuerst: Dependencies und Cleanup als Fundament
|
||||
2. Phase 1 als Nächstes: Core-CRM-Feature (Contacts) muss vollständig sein. Größte Phase (~74h) weil 'company' überall im Code verankert ist.
|
||||
3. Phase 2: Code-Splitting ist schnell und bringt sofortige Performance-Verbesserung
|
||||
4. Phase 3: Plugin-UI-System — je früher desto besser, sonst wird Umbau später schwieriger
|
||||
5. Phase 4: KI-UI-Steuerung baut auf Plugin-System auf (dynamische Routes, Tabs etc.). Bestehende WebSocket-Infrastruktur aus kommunikation Plugin als Referenz.
|
||||
6. Phase 5: API-Vollständigkeit und E2E-Tests für KI-Wartbarkeit
|
||||
7. Phase 6+7: Qualität und Test-Vollendung
|
||||
|
||||
---
|
||||
|
||||
## Was bei der Überprüfung gefunden wurde
|
||||
|
||||
### Phase 1 Korrektur: +41h Aufwand
|
||||
|
||||
Die ursprüngliche Schätzung von 33h für Phase 1 war **massiv unterschätzt**. Die gründliche Code-Analyse zeigte:
|
||||
|
||||
**'company' als entity_type ist in 6 Plugins verankert:**
|
||||
- `entity_links`: entity_type Pattern, company_router, on_company_deleted Event-Handler
|
||||
- `unified_search`: CompanySearchProvider, index_company, company.created/updated Events, search_engine Mapping
|
||||
- `calendar`: entity_type Pattern für EntryLinks
|
||||
- `tags`: entity_type Pattern für Tag-Assignments
|
||||
- `mail`: company_id Spalte in mails Tabelle (DB-Migration nötig!)
|
||||
- `ai/action_mapper`: Company-Intents (create/delete/update/list)
|
||||
|
||||
**Event-Namen müssen migriert werden:**
|
||||
- `company.created` → `contact.created`
|
||||
- `company.updated` → `contact.updated`
|
||||
- `company.deleted` → `contact.deleted`
|
||||
- Betroffen: unified_search, entity_links, workflows, test_sample, manifest.py
|
||||
|
||||
**DB-Migration nötig:**
|
||||
- `entity_links.entity_type = 'company'` → `'contact'`
|
||||
- `tag_assignments.entity_type = 'company'` → `'contact'`
|
||||
- `calendar_entry_links.entity_type = 'company'` → `'contact'`
|
||||
- `mails.company_id` → `mails.contact_id` (Spalte umbenennen)
|
||||
|
||||
**Was NICHT geändert wird:**
|
||||
- `system_settings.company_name`, `company_street` etc. → Das ist die CRM-Besitzer-Firmeninfo für Rechnungen. Bleibt wie es ist.
|
||||
- `CalendarType = 'company'` → Das ist ein Kalender-Typ (Firmenkalender), keine Entity-Referenz. Kann bleiben.
|
||||
|
||||
### Bestehende WebSocket-Infrastruktur
|
||||
Das `kommunikation` Plugin hat bereits eine vollständige WebSocket-Implementierung (`/api/v1/comm/ws`, `websocket_manager.py`). Diese kann als Referenz für die KI-UI-Steuerung (Phase 4) dienen — das spart Entwicklungszeit.
|
||||
|
||||
---
|
||||
|
||||
## KI-Wartbarkeit: Schlüssel-Anforderungen
|
||||
|
||||
Damit ein KI-Agent die App selbstständig warten kann:
|
||||
|
||||
1. **Vollständige API-Abdeckung:** Jede UI-Funktion per API steuerbar (Phase 5)
|
||||
2. **E2E-Tests:** Playwright-Tests die KI ausführen kann (Phase 5)
|
||||
3. **API-Health-Check:** Script das alle Endpunkte prüft (Phase 5)
|
||||
4. **Test-Runner:** Script das alle Tests ausführt und strukturiert reportet (Phase 7)
|
||||
5. **Deploy-Script:** KI kann Build erstellen, testen, deployen, rollback (Phase 5)
|
||||
6. **Plugin-Richtlinien:** Klare Vorgaben damit KI neue Plugins erstellen kann (Phase 3)
|
||||
7. **Dokumentation:** Aktuelle Architektur-Doku, API-Doku, Plugin-Guide (Phase 0+3+5)
|
||||
|
||||
---
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. ✅ Nextcloud Backup erstellt (`/Backups/leocrm/leocrm-backup-20260722.bundle`)
|
||||
2. ✅ Plan gründlich überprüft und korrigiert (+45h)
|
||||
3. ⬜ Plan freigeben
|
||||
4. ⬜ Phase 0 starten
|
||||
5. ⬜ Planungsdokumente aktualisieren
|
||||
|
||||
---
|
||||
|
||||
## Test-Strategie (pro Phase)
|
||||
|
||||
### Phase 0: Vorbereitung & Cleanup
|
||||
- **Pro Task:** Unit-Test für geänderte Funktionalität (z.B. Test dass lucide-react Icons rendern, Test dass date-fns formatiert, Test dass Storage Backend local+S3 funktioniert)
|
||||
- **Regression:** Alle bestehenden Tests müssen weiterhin durchlaufen
|
||||
- **Lizenz-Test:** `pip-licenses` Script prüft dass keine AGPL-Packages mehr in requirements.txt
|
||||
|
||||
### Phase 1: Unified Contact Model
|
||||
- **Pro Task:** API-Integration-Test (httpx + pytest) für jeden geänderten Endpoint
|
||||
- **DB-Migration-Test:** Test dass Migration 0023 (entity_type company→contact) korrekt ausführt und rollbackbar ist
|
||||
- **Plugin-Test:** Pro Plugin (entity_links, unified_search, calendar, tags, mail) Test dass entity_type='contact' funktioniert
|
||||
- **Frontend-Test:** Vitest für ContactDetail, ContactEditModal, ContactPerson-Verwaltung
|
||||
- **Cross-Tenant-Test:** Test dass Tenant-Isolation nach Migration noch funktioniert
|
||||
|
||||
### Phase 2: Code-Splitting & Performance
|
||||
- **Bundle-Test:** Test dass Initial-Bundle < 300KB (vorher alle Pages im Bundle)
|
||||
- **Virtual Scrolling Test:** Test mit 10.000 Datensätzen — Rendering-Zeit < 500ms
|
||||
- **Lazy-Loading Test:** Test dass Plugin-Pages nicht im Initial-Bundle sind
|
||||
|
||||
### Phase 3: Plugin-UI-System
|
||||
- **PluginRegistry-Test:** Test dass Manifests korrekt geladen und gerendert werden
|
||||
- **PluginLoader-Test:** Test dass lazy-loaded Komponenten mit Suspense funktionieren
|
||||
- **Plugin-Install-Test:** Test dass ZIP-Upload validiert und installiert wird
|
||||
- **Error-Boundary-Test:** Test dass fehlerhaftes Plugin nicht die ganze App crashen lässt
|
||||
|
||||
### Phase 3.5: Automation & Agents
|
||||
- **Cron-Scheduler-Test:** Test dass Cron-Jobs zur richtigen Zeit enqueued werden
|
||||
- **Workflow-Timeout-Test:** Test dass abgelaufene Workflows cancelled werden
|
||||
- **Agent-Runner-Test:** Test dass Agent LLM-Call ausführt und Ergebnis zurückgibt (Mock-LLM)
|
||||
- **Automation-Engine-Test:** Test dass Event-Trigger → Conditions → Actions korrekt ausgeführt werden
|
||||
- **Agent-zu-Agent-Test:** Test dass Agent A Nachricht an Agent B sendet und B reagiert
|
||||
- **Rate-Limiting-Test:** Test dass Agent nach Max-Ausführungen gestoppt wird
|
||||
- **Dry-Run-Test:** Test dass Dry-Run keine destruktiven Actions ausführt
|
||||
|
||||
### Phase 4: KI-UI-Steuerung
|
||||
- **WebSocket-Test:** Test dass Commands korrekt gesendet und empfangen werden
|
||||
- **Command-Test:** Pro Command-Typ (navigate, filter, open_contact, modal, tab, settings) ein Test
|
||||
- **Feedback-Test:** Test dass Frontend Bestätigung an KI zurücksendet
|
||||
|
||||
### Phase 5: API-Vollständigkeit & Features
|
||||
- **E2E-Tests (Playwright):** auth, contact-crud, search, plugin-toggle, mail, dms, calendar (7 Specs)
|
||||
- **API-Health-Check-Test:** Test dass alle Endpoints erreichbar und korrekt responden
|
||||
- **Backup-Test:** Test dass Backup erstellt wird und Restore funktioniert
|
||||
- **MCP-Test:** Test dass MCP-Server Tools bereitstellt und MCP-Client Tools nutzt
|
||||
- **Report-Test:** Test dass PDF/CSV/Excel generiert wird und korrekt formatiert ist
|
||||
- **Custom-Fields-Test:** Test dass Plugin-Felder in UI gerendert und gespeichert werden
|
||||
- **Tasks-Plugin-Test:** Vollständige CRUD-Tests für Tasks
|
||||
- **Saved-Searches-Test:** Test dass Filter gespeichert und geladen werden
|
||||
- **Dedup-Test:** Test dass Dubletten erkannt und gemerged werden
|
||||
- **PWA-Test:** Test dass Service Worker registriert wird und Offline-Caching funktioniert
|
||||
- **Dashboard-Test:** Test dass Plugin-Widgets dynamisch gerendert werden
|
||||
|
||||
### Phase 6: React Hook Form + Zod
|
||||
- **Pro Form:** Test dass Validierung korrekt funktioniert (Pflichtfelder, E-Mail-Format, Datum-Range)
|
||||
- **Error-Display-Test:** Test dass Fehlermeldungen korrekt angezeigt werden
|
||||
|
||||
### Phase 7: Test-Vollendung
|
||||
- **Coverage-Target:** >80% Backend, >70% Frontend
|
||||
- **Test-Runner-Script:** `scripts/ai_run_tests.py` führt alle Tests aus und gibt strukturierten Report
|
||||
- **Multi-Tenant-Test:** Test mit 3 Tenants — Isolation, Cross-Tenant-Access → 404
|
||||
- **Performance-Test:** 200k Contacts — List < 500ms, FTS < 500ms
|
||||
|
||||
### Test-Infrastruktur
|
||||
- **Backend:** pytest + httpx + pytest-asyncio + pytest-cov (bereits vorhanden)
|
||||
- **Frontend:** Vitest + @testing-library/react (bereits vorhanden)
|
||||
- **E2E:** Playwright (neu in Phase 5)
|
||||
- **Test-DB:** PostgreSQL mit `pytest-asyncio` fixture (bereits in conftest.py)
|
||||
- **Test-Redis:** Redis-Mock oder echte Redis-Instanz
|
||||
- **Mock-LLM:** LiteLLM mock mode für AI-Tests (bereits vorhanden)
|
||||
|
||||
---
|
||||
|
||||
## Agent-Anleitung: Wie ein KI-Agent diesen Plan umsetzt
|
||||
|
||||
Dieser Plan ist so strukturiert dass ein KI-Agent (wie Agent Zero) ihn Task-für-Task umsetzen kann.
|
||||
|
||||
### Vorgehensweise pro Task
|
||||
|
||||
1. **Task lesen:** Jeder Task hat Nummer, Aufwand, Beschreibung und Details
|
||||
2. **Code prüfen:** Vor der Umsetzung den aktuellen Code inspizieren (Dateien lesen, Abhängigkeiten prüfen)
|
||||
3. **Minimal-invasiv arbeiten:** Nur das ändern was der Task verlangt. Keine Refactoring-Touren.
|
||||
4. **Tests schreiben/aktualisieren:** Pro Task mindestens ein Test der die Änderung abdeckt
|
||||
5. **Commit:** Pro Task ein Git-Commit mit klarer Message (z.B. `Phase 0.2: install lucide-react and migrate icons`)
|
||||
6. **Verifizieren:** Nach jedem Task: Tests laufen, Build funktioniert, keine Regressionen
|
||||
|
||||
### Phasen-Reihenfolge ist verbindlich
|
||||
|
||||
- Phase N+1 darf erst starten wenn Phase N abgeschlossen ist
|
||||
- Innerhalb einer Phase können Tasks parallel sein (z.B. 0.2 und 0.3 unabhängig)
|
||||
- Abhängigkeiten sind in den Task-Beschreibungen genannt
|
||||
|
||||
### Was ein Agent pro Task braucht
|
||||
|
||||
- Dateipfade der zu ändernden Dateien (in Task-Beschreibung genannt)
|
||||
- Akzeptanzkriterien (in Task-Beschreibung genannt)
|
||||
- Test-Strategie (pro Task mindestens ein Test)
|
||||
- Git-Commit pro Task
|
||||
|
||||
### Plugin-Entwicklung
|
||||
|
||||
Wenn ein Agent ein neues Plugin erstellt (z.B. Tasks-Plugin 5.21):
|
||||
1. Plugin-Verzeichnis in `app/plugins/builtins/<name>/` erstellen
|
||||
2. `plugin.py` mit Manifest (Name, Version, Dependencies, Routes, Permissions, Events)
|
||||
3. `models.py` mit SQLAlchemy Models (TenantMixin!)
|
||||
4. `schemas.py` mit Pydantic Schemas
|
||||
5. `routes.py` mit FastAPI Router (require_permission!)
|
||||
6. `services.py` mit Business-Logic
|
||||
7. Migration in `migrations/` Verzeichnis
|
||||
8. Frontend-Komponenten in `frontend/src/components/<name>/`
|
||||
9. Frontend-Seite in `frontend/src/pages/<Name>.tsx`
|
||||
10. API-Modul in `frontend/src/api/<name>.ts`
|
||||
11. Route in `frontend/src/routes/index.tsx` registrieren
|
||||
12. i18n-Keys in `frontend/src/i18n/locales/de.json` und `en.json`
|
||||
13. Tests in `tests/test_<name>.py` und `frontend/src/__tests__/<name>/`
|
||||
|
||||
### Plugin-Manifest-Format (für neue Plugins)
|
||||
|
||||
```python
|
||||
manifest = PluginManifest(
|
||||
name="my_plugin",
|
||||
version="1.0.0",
|
||||
display_name="My Plugin",
|
||||
description="What it does",
|
||||
dependencies=["permissions"], # other plugins this depends on
|
||||
routes=[PluginRouteDef(path="/api/v1/my-plugin", module="...", router_attr="router")],
|
||||
events=["my.event"], # events this plugin listens to
|
||||
migrations=["0001_initial.sql"],
|
||||
permissions=["my_plugin:read", "my_plugin:write"],
|
||||
is_core=False,
|
||||
# Neue Felder (nach Phase 3+3.5):
|
||||
# agent_definitions=[...], # Agent-Templates
|
||||
# automation_templates=[...], # Automation-Vorlagen
|
||||
# cron_jobs=[...], # Periodische Tasks
|
||||
# custom_fields=[...], # Custom Field Definitionen
|
||||
# dashboard_widgets=[...], # Dashboard-Komponenten
|
||||
# miniapps=[...], # MiniApp-Definitionen
|
||||
)
|
||||
```
|
||||
|
||||
### Wichtige Regeln für Agent-Updates
|
||||
|
||||
1. **Niemals Tests ändern** um sie grün zu bekommen — Code fixen nicht Tests anpassen
|
||||
2. **Niemals .env committen** — Secrets gehören nicht ins Repo
|
||||
3. **Jede DB-Änderung braucht Alembic-Migration** — keine manuellen SQL-Changes
|
||||
4. **Jede API-Route braucht RBAC** — `require_permission()` auf jedem Endpoint
|
||||
5. **Jedes Plugin-Model braucht TenantMixin** — tenant_id auf jeder Tabelle
|
||||
6. **Frontend-Änderungen brauchen i18n** — alle Texte in de.json und en.json
|
||||
7. **Pro Task ein Commit** — nicht mehrere Tasks in einem Commit
|
||||
8. **Nach jedem Task: Tests + Build verifizieren** — keine Regressionen
|
||||
9. **Nach jedem Task: Progress aktualisieren** — `PROGRESS.md` im Repo aktualisieren mit: Task-Nummer, Status (done/in-progress/blocked), Datum, was gemacht wurde, was als Nächstes ansteht. **Zwingend für jeden Agenten der am Plan arbeitet.**
|
||||
@@ -1,921 +0,0 @@
|
||||
# LeoCRM Plugin-System — Kompletter Umbauplan
|
||||
|
||||
**Erstellt:** 2026-07-26
|
||||
**Aktualisiert:** 2026-07-26 (Codebasis-Verifikation + Phase 6)
|
||||
**Geschätzter Gesamtaufwand:** ~149 Stunden (~19 Arbeitstage)
|
||||
**Status:** Geplant — noch nicht gestartet
|
||||
|
||||
**Codebasis-Verifikation (2026-07-26):**
|
||||
- ✅ `base.py` unverändert — Plan passt
|
||||
- ✅ `registry.py` unverändert — Plan passt
|
||||
- ✅ `manifest.py` unverändert — Plan passt
|
||||
- ✅ `contracts.py` (ContractRegistry) unverändert — Plan passt
|
||||
- ✅ Migration 0044 hinzugekommen: RLS Repair + separater DB-User (crm_runtime) — beeinflusst Plugin-System nicht
|
||||
- ✅ Migration 0045 hinzugekommen — neuer Head
|
||||
- ✅ `require_active_plugin` in `deps.py` hinzugekommen — beeinflusst Plugin-System nicht
|
||||
- ✅ 19 echte Plugins (test_sample hat __init__.py statt plugin.py)
|
||||
- ✅ Cross-Imports: 224, Contracts: 8, get_contract: 11 — unverändert
|
||||
|
||||
---
|
||||
|
||||
## Übersicht: 5 Phasen
|
||||
|
||||
| Phase | Punkte | Inhalt | Stunden | Tage |
|
||||
|---|---|---|---|---|
|
||||
| Phase 1 | 1-3 | Contracts konsequent nutzen | 47 | 6 |
|
||||
| Phase 2 | 4 | Hooks/Filters-System | 16 | 2 |
|
||||
| Phase 3 | 5 | Plugin-Isolation (Linting) | 4 | 0,5 |
|
||||
| Phase 4 | 8 | Plugin-Versioning | 20 | 2,5 |
|
||||
| Phase 5 | 6 | Marketplace-Vorbereitung | 42 | 5 |
|
||||
| Phase 6 | — | Manifest-Anpassung & Konsolidierung | 20 | 2,5 |
|
||||
| **Gesamt** | | | **149** | **~19** |
|
||||
|
||||
**Wichtig:** Jede Phase ist unabhängig funktionsfähig. Das System läuft nach jeder Phase ohne Einschränkungen weiter.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Contracts konsequent nutzen (Punkte 1-3)
|
||||
|
||||
**Ziel:** Alle 224 direkten Cross-Plugin-Imports werden durch das Contract-System ersetzt.
|
||||
|
||||
### 1.1 Fehlende contracts.py erstellen (7 Std)
|
||||
|
||||
Für jedes Plugin, das noch keine `contracts.py` hat, eine erstellen:
|
||||
|
||||
| # | Plugin | Exportierte Symbole | Aufwand |
|
||||
|---|---|---|---|
|
||||
| 1 | `ai_proactive` | ContextTools, ProactiveAgent, JobScheduler | 30 Min |
|
||||
| 2 | `ai_ui_control` | WebSocketManager, UIAction | 30 Min |
|
||||
| 3 | `automation` | AgentRunner, ExecutionEngine, Scheduler, WorkflowTimeout | 45 Min |
|
||||
| 4 | `entity_links` | EntityLink model, create_link, get_links | 20 Min |
|
||||
| 5 | `forgejo_error_reporter` | report_error_to_forgejo | 15 Min |
|
||||
| 6 | `mcp_client` | McpClient, McpServerConfig | 30 Min |
|
||||
| 7 | `mcp_server` | McpServer, ToolDefinitions | 30 Min |
|
||||
| 8 | `report_generator` | ReportTemplate, ReportInstance, PdfGenerator | 30 Min |
|
||||
| 9 | `system_notif` | SystemNotifHandler | 15 Min |
|
||||
| 10 | `tags` | Tag, TagAssignment, assign_tags, remove_tags | 20 Min |
|
||||
| 11 | `tasks` | Task, TaskService, create_task, update_task | 30 Min |
|
||||
| 12 | `test_sample` | TestSamplePlugin | 10 Min |
|
||||
| 13 | `dms` (erweitern) | File, Folder, UploadService, DownloadService | 30 Min |
|
||||
| 14 | `permissions` (erweitern) | ShareLink, PermissionResolver | 30 Min |
|
||||
|
||||
**Schema für jede contracts.py:**
|
||||
```python
|
||||
"""Public contract for the <plugin> plugin."""
|
||||
from __future__ import annotations
|
||||
from app.plugins.builtins.contracts import get_contract_registry
|
||||
# Import only public symbols from internal modules
|
||||
|
||||
class <Plugin>Contract:
|
||||
contract_name = "<plugin>"
|
||||
# Expose only public API
|
||||
|
||||
_contract = <Plugin>Contract()
|
||||
get_contract_registry().register("<plugin>", _contract)
|
||||
```
|
||||
|
||||
### 1.2 Direkte Imports ersetzen (28 Std)
|
||||
|
||||
224 direkte Imports müssen durch `get_contract()` ersetzt werden.
|
||||
|
||||
**Top-Priorität (häufigste Import-Quellen):**
|
||||
|
||||
| # | Datei | Imports | Aufwand |
|
||||
|---|---|---|---|
|
||||
| 1 | `automation/plugin.py` | 10 | 1,5 Std |
|
||||
| 2 | `automation/routes.py` | 8 | 1,5 Std |
|
||||
| 3 | `ai_proactive/services.py` | 8 | 1,5 Std |
|
||||
| 4 | `ai_proactive/plugin.py` | 8 | 1,5 Std |
|
||||
| 5 | `unified_search/jobs.py` | 7 | 1 Std |
|
||||
| 6 | `builtins/__init__.py` | 7 | 1 Std |
|
||||
| 7 | `ai_proactive/jobs.py` | 7 | 1 Std |
|
||||
| 8 | `ai_assistant/participant_handler.py` | 7 | 1 Std |
|
||||
| 9 | `kommunikation/routes.py` | 6 | 1 Std |
|
||||
| 10 | `kommunikation/contracts.py` | 6 | 1 Std |
|
||||
| 11 | `automation/agent_routes.py` | 6 | 1 Std |
|
||||
| 12 | `automation/agent_comm.py` | 6 | 1 Std |
|
||||
| 13 | `ai_proactive/participant_handler.py` | 6 | 1 Std |
|
||||
| 14 | `ai_assistant/plugin.py` | 6 | 1 Std |
|
||||
| 15 | `unified_search/routes.py` | 5 | 45 Min |
|
||||
| 16-50 | Alle übrigen Dateien | ~122 | 12 Std |
|
||||
|
||||
**Muster für Ersetzung:**
|
||||
```python
|
||||
# VORHER (direkt):
|
||||
from app.plugins.builtins.kommunikation.services import send_message
|
||||
|
||||
# NACHHER (über Contract):
|
||||
from app.plugins.builtins.contracts import get_contract
|
||||
|
||||
async def my_function(db, ...):
|
||||
komm = get_contract("kommunikation")
|
||||
if komm:
|
||||
await komm.send_message(db, ...)
|
||||
# Graceful degradation wenn Plugin nicht aktiv
|
||||
```
|
||||
|
||||
### 1.3 Contracts bei Deaktivierung abmelden (4 Std)
|
||||
|
||||
In jedem Plugin's `on_deactivate()`:
|
||||
```python
|
||||
async def on_deactivate(self, db, service_container, event_bus) -> None:
|
||||
# Contract abmelden
|
||||
from app.plugins.builtins.contracts import get_contract_registry
|
||||
get_contract_registry().unregister(self.manifest.name)
|
||||
# ... rest of cleanup
|
||||
await super().on_deactivate(db, service_container, event_bus)
|
||||
```
|
||||
|
||||
| # | Plugin | Aufwand |
|
||||
|---|---|---|
|
||||
| 1-16 | Alle 16 Plugins | 15 Min pro Plugin = 4 Std |
|
||||
|
||||
### 1.4 Tests anpassen (8 Std)
|
||||
|
||||
- Cross-Plugin-Tests müssen mit Contracts laufen
|
||||
- `test_plugins.py` — Contract-Registry Tests
|
||||
- `test_contracts.py` — Neue Test-Datei für Contract-System
|
||||
- Alle Integrationstests mit Contract-Mocks
|
||||
|
||||
### Meilenstein Phase 1:
|
||||
- ✅ Alle 16 Plugins haben contracts.py
|
||||
- ✅ 0 direkte Cross-Plugin-Imports (geprüft mit grep)
|
||||
- ✅ Contracts werden bei Deaktivierung abgemeldet
|
||||
- ✅ Alle Tests bestanden
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Hooks/Filters-System (Punkt 4)
|
||||
|
||||
**Ziel:** WordPress-Style Hooks (actions + filters) für Plugin-Erweiterbarkeit.
|
||||
|
||||
### 2.1 HookRegistry erstellen (4 Std)
|
||||
|
||||
**Neue Datei: `app/core/hooks.py`**
|
||||
|
||||
```python
|
||||
"""WordPress-style hooks: actions (fire-and-forget) and filters (modify data)."""
|
||||
|
||||
from __future__ import annotations
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from typing import Any, Callable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HookRegistry:
|
||||
"""Central registry for actions and filters.
|
||||
|
||||
Actions: do_action('contact.before_create', data) — no return value
|
||||
Filters: result = apply_filters('contact.format_name', name) — returns modified value
|
||||
|
||||
Priority: lower numbers run first (default=10).
|
||||
"""
|
||||
|
||||
_instance: HookRegistry | None = None
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
cls._instance._actions: dict[str, list[tuple[int, Callable]]] = defaultdict(list)
|
||||
cls._instance._filters: dict[str, list[tuple[int, Callable]]] = defaultdict(list)
|
||||
return cls._instance
|
||||
|
||||
def register_action(self, hook_name: str, callback: Callable, priority: int = 10) -> None:
|
||||
self._actions[hook_name].append((priority, callback))
|
||||
self._actions[hook_name].sort(key=lambda x: x[0])
|
||||
|
||||
def register_filter(self, hook_name: str, callback: Callable, priority: int = 10) -> None:
|
||||
self._filters[hook_name].append((priority, callback))
|
||||
self._filters[hook_name].sort(key=lambda x: x[0])
|
||||
|
||||
async def do_action(self, hook_name: str, *args, **kwargs) -> None:
|
||||
for _, callback in self._actions.get(hook_name, []):
|
||||
try:
|
||||
result = callback(*args, **kwargs)
|
||||
if hasattr(result, '__await__'):
|
||||
await result
|
||||
except Exception:
|
||||
logger.exception("Error in action %s", hook_name)
|
||||
|
||||
async def apply_filters(self, hook_name: str, value: Any, *args, **kwargs) -> Any:
|
||||
for _, callback in self._filters.get(hook_name, []):
|
||||
try:
|
||||
result = callback(value, *args, **kwargs)
|
||||
if hasattr(result, '__await__'):
|
||||
result = await result
|
||||
value = result
|
||||
except Exception:
|
||||
logger.exception("Error in filter %s", hook_name)
|
||||
return value
|
||||
|
||||
def unregister(self, hook_name: str, callback: Callable) -> None:
|
||||
self._actions[hook_name] = [(p, c) for p, c in self._actions.get(hook_name, []) if c != callback]
|
||||
self._filters[hook_name] = [(p, c) for p, c in self._filters.get(hook_name, []) if c != callback]
|
||||
|
||||
def unregister_all(self, hook_name: str) -> None:
|
||||
self._actions.pop(hook_name, None)
|
||||
self._filters.pop(hook_name, None)
|
||||
|
||||
def _reset_for_testing(self) -> None:
|
||||
self._actions.clear()
|
||||
self._filters.clear()
|
||||
|
||||
|
||||
def get_hook_registry() -> HookRegistry:
|
||||
return HookRegistry()
|
||||
|
||||
async def do_action(hook_name: str, *args, **kwargs) -> None:
|
||||
await get_hook_registry().do_action(hook_name, *args, **kwargs)
|
||||
|
||||
async def apply_filters(hook_name: str, value: Any, *args, **kwargs) -> Any:
|
||||
return await get_hook_registry().apply_filters(hook_name, value, *args, **kwargs)
|
||||
```
|
||||
|
||||
### 2.2 Integration in BasePlugin (2 Std)
|
||||
|
||||
```python
|
||||
# In BasePlugin.on_activate:
|
||||
async def on_activate(self, db, service_container, event_bus) -> None:
|
||||
# ... existing code ...
|
||||
# Hooks werden in Subklassen registriert
|
||||
|
||||
# In BasePlugin.on_deactivate:
|
||||
async def on_deactivate(self, db, service_container, event_bus) -> None:
|
||||
# Alle Hooks dieses Plugins abmelden
|
||||
from app.core.hooks import get_hook_registry
|
||||
# Plugin-spezifische Hooks entfernen (prefix mit plugin name)
|
||||
# ... existing code ...
|
||||
```
|
||||
|
||||
### 2.3 Hook-Punkte in Core-Services (6 Std)
|
||||
|
||||
| # | Service | Hook-Name | Typ | Beschreibung |
|
||||
|---|---|---|---|---|
|
||||
| 1 | contact_service | `contact.before_create` | Action | Vor Kontakt-Erstellung |
|
||||
| 2 | contact_service | `contact.after_create` | Action | Nach Kontakt-Erstellung |
|
||||
| 3 | contact_service | `contact.format_display_name` | Filter | Anzeigenamen formatieren |
|
||||
| 4 | contact_service | `contact.before_update` | Action | Vor Kontakt-Update |
|
||||
| 5 | contact_service | `contact.after_update` | Action | Nach Kontakt-Update |
|
||||
| 6 | contact_service | `contact.before_delete` | Action | Vor Kontakt-Löschung |
|
||||
| 7 | mail_service | `mail.before_send` | Filter | E-Mail vor Versand modifizieren |
|
||||
| 8 | mail_service | `mail.after_send` | Action | Nach E-Mail-Versand |
|
||||
| 9 | calendar | `calendar.before_appointment` | Action | Vor Termin-Erstellung |
|
||||
| 10 | calendar | `calendar.after_appointment` | Action | Nach Termin-Erstellung |
|
||||
| 11 | auth_service | `auth.before_login` | Filter | Login-Daten validieren/modifizieren |
|
||||
| 12 | auth_service | `auth.after_login` | Action | Nach erfolgreichem Login |
|
||||
| 13 | user_service | `user.before_create` | Action | Vor User-Erstellung |
|
||||
| 14 | user_service | `user.after_create` | Action | Nach User-Erstellung |
|
||||
| 15 | dms | `dms.before_upload` | Filter | Datei-Upload validieren/modifizieren |
|
||||
|
||||
### 2.4 Tests für Hooks/Filters (4 Std)
|
||||
|
||||
- `test_hooks.py` — HookRegistry Tests
|
||||
- Integrationstests: Plugin registriert Hook, Core-Service löst Hook aus
|
||||
- Filter-Tests: Wert wird korrekt modifiziert
|
||||
- Priority-Tests: Reihenfolge wird eingehalten
|
||||
- Unregister-Tests: Hooks werden bei Deaktivierung entfernt
|
||||
|
||||
### Meilenstein Phase 2:
|
||||
- ✅ `app/core/hooks.py` mit HookRegistry
|
||||
- ✅ 15 Hook-Punkte in Core-Services
|
||||
- ✅ BasePlugin registriert/unregistriert Hooks automatisch
|
||||
- ✅ Tests bestanden
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Plugin-Isolation (Punkt 5)
|
||||
|
||||
**Ziel:** Direkte Cross-Plugin-Imports werden durch Linting verhindert.
|
||||
|
||||
### 3.1 Linting-Regel erstellen (2 Std)
|
||||
|
||||
**Neue Datei: `.ruff/rules/no_cross_plugin_imports.py`**
|
||||
|
||||
```python
|
||||
"""Ruff rule: forbid direct imports from app.plugins.builtins.* (except contracts)."""
|
||||
|
||||
# Erlaubt:
|
||||
# from app.plugins.builtins.contracts import get_contract
|
||||
# from app.plugins.builtins.<name>.contracts import ...
|
||||
#
|
||||
# Verboten:
|
||||
# from app.plugins.builtins.<name>.services import ...
|
||||
# from app.plugins.builtins.<name>.models import ...
|
||||
# from app.plugins.builtins.<name>.routes import ...
|
||||
```
|
||||
|
||||
### 3.2 CI/CD Integration (1 Std)
|
||||
|
||||
- `ruff check` in GitHub Actions / Forgejo CI
|
||||
- Pre-commit Hook für lokale Entwicklung
|
||||
- Fehler bei direkten Cross-Plugin-Imports
|
||||
|
||||
### 3.3 Ausnahmen definieren (1 Std)
|
||||
|
||||
- `conftest.py` — Tests dürfen direkt importieren
|
||||
- `app/plugins/builtins/__init__.py` — Plugin-Discovery
|
||||
- `app/plugins/registry.py` — Registry darf importieren
|
||||
|
||||
### Meilenstein Phase 3:
|
||||
- ✅ Linting-Regel aktiv
|
||||
- ✅ CI/CD prüft bei jedem Commit
|
||||
- ✅ 0 direkte Cross-Plugin-Imports (automatisch erzwungen)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Plugin-Versioning (Punkt 8)
|
||||
|
||||
**Ziel:** Vollständige Versionsverwaltung mit SemVer, Rollback und Kompatibilitäts-Check.
|
||||
|
||||
### 4.1 SemVer-Vergleich (3 Std)
|
||||
|
||||
**Neue Datei: `app/plugins/semver.py`**
|
||||
|
||||
```python
|
||||
"""Semantic version comparison for plugin versions."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
|
||||
@dataclass
|
||||
class SemVer:
|
||||
major: int
|
||||
minor: int
|
||||
patch: int
|
||||
prerelease: str = ""
|
||||
|
||||
@classmethod
|
||||
def parse(cls, version: str) -> "SemVer":
|
||||
match = re.match(r"(\d+)\.(\d+)\.(\d+)(?:-(.+))?", version)
|
||||
if not match:
|
||||
raise ValueError(f"Invalid semver: {version}")
|
||||
return cls(int(match[1]), int(match[2]), int(match[3]), match[4] or "")
|
||||
|
||||
def __lt__(self, other): ...
|
||||
def __eq__(self, other): ...
|
||||
def __le__(self, other): ...
|
||||
def __gt__(self, other): ...
|
||||
|
||||
def is_breaking_change(self, other: "SemVer") -> bool:
|
||||
return self.major != other.major
|
||||
|
||||
def is_compatible_with(self, min_version: "SemVer") -> bool:
|
||||
return self >= min_version
|
||||
```
|
||||
|
||||
**Änderung in `registry.py`:**
|
||||
```python
|
||||
# VORHER: String-Vergleich
|
||||
if record.version != plugin.manifest.version:
|
||||
|
||||
# NACHHER: SemVer-Vergleich
|
||||
old_ver = SemVer.parse(record.version)
|
||||
new_ver = SemVer.parse(plugin.manifest.version)
|
||||
if old_ver != new_ver:
|
||||
if new_ver < old_ver:
|
||||
# Downgrade — nur mit Rollback-Migration
|
||||
...
|
||||
```
|
||||
|
||||
### 4.2 Rollback-Migrationen (6 Std)
|
||||
|
||||
**Erweiterung des Migration-Systems:**
|
||||
|
||||
```python
|
||||
# MigrationRunner erweitern:
|
||||
async def run_migration_down(self, db, plugin_name, migration_filename):
|
||||
"""Run rollback (down) migration."""
|
||||
# Suche <filename>_down.sql oder parse DOWNGRADE-Block
|
||||
|
||||
async def rollback_to_version(self, db, plugin_name, target_version: str):
|
||||
"""Rollback plugin to a specific version."""
|
||||
# 1. Finde alle Migrationen nach target_version
|
||||
# 2. Führe sie in umgekehrter Reihenfolge aus
|
||||
# 3. Aktualisiere DB-Version
|
||||
```
|
||||
|
||||
**Migration-Datei-Format:**
|
||||
```sql
|
||||
-- 0001_initial.sql
|
||||
-- UP:
|
||||
CREATE TABLE ...;
|
||||
-- DOWN:
|
||||
DROP TABLE ... CASCADE;
|
||||
```
|
||||
|
||||
Oder separate Dateien:
|
||||
- `0001_initial_up.sql`
|
||||
- `0001_initial_down.sql`
|
||||
|
||||
### 4.3 Version-Kompatibilitäts-Check (3 Std)
|
||||
|
||||
**Manifest-Erweiterung:**
|
||||
```python
|
||||
class PluginManifest(BaseModel):
|
||||
# ... existing fields ...
|
||||
min_app_version: str = Field(
|
||||
default="0.0.0",
|
||||
description="Minimum LeoCRM version required"
|
||||
)
|
||||
```
|
||||
|
||||
**Check bei Installation:**
|
||||
```python
|
||||
async def install(self, db, name):
|
||||
plugin = self.get_plugin(name)
|
||||
# Check app version compatibility
|
||||
app_version = SemVer.parse(settings.app_version)
|
||||
min_version = SemVer.parse(plugin.manifest.min_app_version)
|
||||
if app_version < min_version:
|
||||
raise ValueError(
|
||||
f"Plugin '{name}' requires LeoCRM >= {plugin.manifest.min_app_version}, "
|
||||
f"but current version is {settings.app_version}"
|
||||
)
|
||||
```
|
||||
|
||||
### 4.4 Update-Benachrichtigung im Frontend (4 Std)
|
||||
|
||||
**Backend:**
|
||||
- `GET /api/v1/plugins/updates` — Liste Plugins mit verfügbarer neuer Version
|
||||
- Vergleich mit Marketplace-Registry (wenn verfügbar) oder lokaler Version
|
||||
|
||||
**Frontend:**
|
||||
- Badge im Plugin-Settings: "Update verfügbar (1.2.0 → 1.3.0)"
|
||||
- Update-Button: Löst Update aus (führt neue Migrationen aus)
|
||||
- Changelog-Anzeige (optional)
|
||||
|
||||
### 4.5 Tests (4 Std)
|
||||
|
||||
- `test_semver.py` — SemVer-Vergleich, Parse, Edge Cases
|
||||
- `test_versioning.py` — Upgrade, Downgrade, Kompatibilitäts-Check
|
||||
- `test_rollback.py` — Rollback-Migrationen
|
||||
- Integrationstests: Version-Update löst Migrationen aus
|
||||
|
||||
### Meilenstein Phase 4:
|
||||
- ✅ SemVer-Vergleich statt String-Vergleich
|
||||
- ✅ Rollback-Migrationen funktionieren
|
||||
- ✅ min_app_version wird geprüft
|
||||
- ✅ Frontend zeigt Update-Benachrichtigungen
|
||||
- ✅ Tests bestanden
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Marketplace-Vorbereitung (Punkt 6)
|
||||
|
||||
**Ziel:** Code so vorbereiten, dass ein Marketplace nur noch gebaut werden muss — ohne Systemänderungen.
|
||||
|
||||
**Wichtig:** Funktioniert auch OHNE Marketplace — Built-in Plugins laufen normal weiter.
|
||||
|
||||
### 5.1 Externe Plugin-Discovery (6 Std)
|
||||
|
||||
**Erweiterung `registry.py`:**
|
||||
|
||||
```python
|
||||
class PluginRegistry:
|
||||
|
||||
def discover_all(self) -> list[str]:
|
||||
"""Discover built-in AND external plugins."""
|
||||
discovered = self.discover_builtins()
|
||||
discovered.extend(self.discover_external())
|
||||
return discovered
|
||||
|
||||
def discover_external(self) -> list[str]:
|
||||
"""Discover plugins from external plugins/ directory."""
|
||||
external_dir = Path(settings.external_plugins_path or "plugins")
|
||||
if not external_dir.exists():
|
||||
return []
|
||||
|
||||
discovered = []
|
||||
for plugin_dir in external_dir.iterdir():
|
||||
if not plugin_dir.is_dir() or plugin_dir.name.startswith("_"):
|
||||
continue
|
||||
# Look for plugin.py or __init__.py with BasePlugin subclass
|
||||
plugin_file = plugin_dir / "plugin.py"
|
||||
if not plugin_file.exists():
|
||||
continue
|
||||
# Import and register
|
||||
import sys
|
||||
sys.path.insert(0, str(external_dir))
|
||||
try:
|
||||
module = importlib.import_module(f"{plugin_dir.name}.plugin")
|
||||
# ... find BasePlugin subclass ...
|
||||
finally:
|
||||
sys.path.remove(str(external_dir))
|
||||
return discovered
|
||||
```
|
||||
|
||||
### 5.2 Plugin-Signatur-Validierung (8 Std)
|
||||
|
||||
**Neue Datei: `app/plugins/signature.py`**
|
||||
|
||||
```python
|
||||
"""Plugin signature verification for external plugins."""
|
||||
|
||||
from pathlib import Path
|
||||
import hashlib
|
||||
import hmac
|
||||
|
||||
# Ed25519 oder HMAC-SHA256 Signatur
|
||||
|
||||
class PluginSignature:
|
||||
"""Verify plugin package signatures."""
|
||||
|
||||
@staticmethod
|
||||
def verify_signature(zip_path: Path, signature: bytes, public_key: bytes) -> bool:
|
||||
"""Verify Ed25519 signature of plugin ZIP."""
|
||||
# 1. Read ZIP content
|
||||
# 2. Compute hash
|
||||
# 3. Verify signature with public key
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def compute_hash(zip_path: Path) -> bytes:
|
||||
"""Compute SHA-256 hash of plugin ZIP."""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def sign_plugin(zip_path: Path, private_key: bytes) -> bytes:
|
||||
"""Sign a plugin ZIP (for plugin authors)."""
|
||||
pass
|
||||
```
|
||||
|
||||
### 5.3 Plugin-Allowlist (4 Std)
|
||||
|
||||
**Neue Alembic-Migration: `0044_plugin_allowlist.py`**
|
||||
|
||||
```python
|
||||
# Tabelle: plugin_allowlist
|
||||
# - id: UUID
|
||||
# - plugin_name: VARCHAR(80)
|
||||
# - allowed_hash: VARCHAR(64) # SHA-256
|
||||
# - allowed_signature: TEXT # Ed25519 signature
|
||||
# - added_by: UUID (user)
|
||||
# - created_at: TIMESTAMPTZ
|
||||
# - is_active: BOOLEAN
|
||||
```
|
||||
|
||||
### 5.4 Plugin-Metadata-Erweiterung (4 Std)
|
||||
|
||||
**Manifest-Erweiterung:**
|
||||
```python
|
||||
class PluginManifest(BaseModel):
|
||||
# ... existing fields ...
|
||||
author: str = Field(default="", description="Plugin author")
|
||||
author_email: str = Field(default="", description="Author contact")
|
||||
homepage: str = Field(default="", description="Plugin homepage URL")
|
||||
license: str = Field(default="MIT", description="License")
|
||||
min_app_version: str = Field(default="0.0.0")
|
||||
icon: str = Field(default="", description="Icon URL or emoji")
|
||||
screenshots: list[str] = Field(default_factory=list)
|
||||
changelog: str = Field(default="", description="Changelog URL or text")
|
||||
tags: list[str] = Field(default_factory=list, description="Marketplace categories")
|
||||
price: float = Field(default=0.0, description="Price (0 = free)")
|
||||
```
|
||||
|
||||
### 5.5 Plugin-Download-Endpoint (4 Std)
|
||||
|
||||
**Neue Route: `POST /api/v1/plugins/install-marketplace`**
|
||||
|
||||
```python
|
||||
@router.post("/install-marketplace")
|
||||
async def install_from_marketplace(
|
||||
body: MarketplaceInstall,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(require_permission("plugins:configure")),
|
||||
):
|
||||
"""Install a plugin from the marketplace.
|
||||
|
||||
1. Download ZIP from marketplace URL
|
||||
2. Verify signature against allowlist
|
||||
3. Validate manifest
|
||||
4. Check dangerous imports
|
||||
5. Validate migration SQL
|
||||
6. Install (migrations + DB record)
|
||||
7. Activate (optional)
|
||||
"""
|
||||
# 1. Download
|
||||
async with httpx.AsyncClient() as client:
|
||||
resp = await client.get(body.url)
|
||||
zip_data = resp.content
|
||||
|
||||
# 2. Verify signature
|
||||
if not PluginSignature.verify_signature(zip_data, body.signature, public_key):
|
||||
raise HTTPException(403, "Invalid plugin signature")
|
||||
|
||||
# 3-6. Validate and install
|
||||
# ... (reuse existing validation + install logic)
|
||||
```
|
||||
|
||||
### 5.6 Plugin-Update-Check (4 Std)
|
||||
|
||||
```python
|
||||
@router.get("/updates")
|
||||
async def check_plugin_updates(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: dict = Depends(require_permission("plugins:read")),
|
||||
):
|
||||
"""Check for available plugin updates from marketplace."""
|
||||
# 1. Query marketplace registry (if configured)
|
||||
# 2. Compare versions with installed plugins
|
||||
# 3. Return list of available updates
|
||||
```
|
||||
|
||||
### 5.7 Plugin-Quarantine (4 Std)
|
||||
|
||||
```python
|
||||
async def _quarantine_plugin(zip_path: Path) -> Path:
|
||||
"""Extract plugin to temp dir, validate, then move to plugins/ dir.
|
||||
|
||||
1. Extract to /tmp/plugin_upload_<uuid>/
|
||||
2. Validate manifest exists
|
||||
3. Check dangerous imports
|
||||
4. Validate migration SQL
|
||||
5. Check signature
|
||||
6. If all OK: move to plugins/ dir
|
||||
7. If any fail: delete temp dir, raise error
|
||||
"""
|
||||
```
|
||||
|
||||
### 5.8 Tests (8 Std)
|
||||
|
||||
- `test_marketplace.py` — Download, Verify, Install Flow
|
||||
- `test_signature.py` — Signatur-Validierung
|
||||
- `test_allowlist.py` — Allowlist-Management
|
||||
- `test_quarantine.py` — Quarantine-Validierung
|
||||
- `test_external_discovery.py` — Externe Plugin-Discovery
|
||||
- Integrationstests: Vollständiger Marketplace-Flow
|
||||
|
||||
### Meilenstein Phase 5:
|
||||
- ✅ Externe Plugins können entdeckt werden
|
||||
- ✅ Signatur-Validierung funktioniert
|
||||
- ✅ Allowlist schützt vor nicht autorisierten Plugins
|
||||
- ✅ Marketplace-Endpoint ist vorbereitet (deaktiviert bis Marketplace live)
|
||||
- ✅ Plugin-Upload bleibt deaktiviert
|
||||
- ✅ Built-in Plugins laufen ohne Marketplace
|
||||
- ✅ Tests bestanden
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Manifest-Anpassung & Konsolidierung
|
||||
|
||||
**Ziel:** Alle in Phase 4 und 5 definierten Manifest-Felder werden ins `PluginManifest` integriert, bestehende Manifeste aktualisiert, und das Manifest-System finalisiert.
|
||||
|
||||
**Wichtig:** Diese Phase baut auf Phase 4 (Versioning) und Phase 5 (Marketplace) auf und muss als letztes durchgeführt werden.
|
||||
|
||||
### 6.1 PluginManifest erweitern (4 Std)
|
||||
|
||||
**Aktuelles Manifest (verifiziert 2026-07-26):**
|
||||
```python
|
||||
class PluginManifest(BaseModel):
|
||||
name: str
|
||||
version: str
|
||||
display_name: str
|
||||
description: str
|
||||
dependencies: list[str]
|
||||
routes: list[PluginRouteDef]
|
||||
events: list[str]
|
||||
migrations: list[str]
|
||||
permissions: list[str]
|
||||
is_core: bool
|
||||
field_definitions: list[FieldDefinition]
|
||||
agent_capabilities: list[str]
|
||||
menu_items: list[FrontendMenuItem]
|
||||
page_routes: list[FrontendPageRoute]
|
||||
detail_tabs: list[FrontendDetailTab]
|
||||
settings_pages: list[FrontendSettingsPage]
|
||||
dashboard_widgets: list[FrontendDashboardWidget]
|
||||
agent_definitions: list[AgentDefinitionContribution]
|
||||
automation_templates: list[AutomationTemplateContribution]
|
||||
cron_jobs: list[CronJobContribution]
|
||||
heartbeat_configs: list[HeartbeatConfigContribution]
|
||||
miniapps: list[MiniAppContribution]
|
||||
custom_fields: list[CustomFieldDefinition]
|
||||
model_config = {"extra": "forbid"}
|
||||
```
|
||||
|
||||
**Neue Felder hinzufügen:**
|
||||
```python
|
||||
class PluginManifest(BaseModel):
|
||||
# ... alle bestehenden Felder ...
|
||||
|
||||
# ── Versioning (Phase 4) ──
|
||||
min_app_version: str = Field(
|
||||
default="0.0.0",
|
||||
description="Minimum LeoCRM version required (SemVer)"
|
||||
)
|
||||
|
||||
# ── Marketplace (Phase 5) ──
|
||||
author: str = Field(default="", max_length=200, description="Plugin author name")
|
||||
author_email: str = Field(default="", max_length=200, description="Author contact email")
|
||||
homepage: str = Field(default="", max_length=500, description="Plugin homepage URL")
|
||||
license: str = Field(default="MIT", max_length=50, description="License identifier")
|
||||
icon: str = Field(default="", description="Icon URL or emoji")
|
||||
screenshots: list[str] = Field(default_factory=list, description="Screenshot URLs for marketplace")
|
||||
changelog: str = Field(default="", description="Changelog URL or inline text")
|
||||
marketplace_tags: list[str] = Field(default_factory=list, description="Marketplace category tags")
|
||||
price: float = Field(default=0.0, ge=0.0, description="Price (0 = free)")
|
||||
|
||||
# ── Hooks (Phase 2) ──
|
||||
hooks: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Hook names this plugin registers (e.g. 'contact.before_create')"
|
||||
)
|
||||
|
||||
# ── Contracts (Phase 1) ──
|
||||
contract_version: str = Field(
|
||||
default="1.0.0",
|
||||
description="Contract API version this plugin exposes"
|
||||
)
|
||||
```
|
||||
|
||||
### 6.2 Manifest-Schema-Dokumentation aktualisieren (3 Std)
|
||||
|
||||
**`MANIFEST_SCHEMA_DOC` in `manifest.py` erweitern:**
|
||||
- Alle neuen Felder in `fields`-Dict aufnehmen
|
||||
- `example`-Manifest mit neuen Feldern aktualisieren
|
||||
- API-Endpoint `GET /api/v1/plugins/manifest` liefert vollständiges Schema
|
||||
|
||||
### 6.3 Alle 19 Plugin-Manifeste aktualisieren (8 Std)
|
||||
|
||||
Jedes Plugin-Manifest muss um die neuen Felder erweitert werden:
|
||||
|
||||
| # | Plugin | Aufwand | Neue Felder |
|
||||
|---|---|---|---|
|
||||
| 1 | `ai_assistant` | 30 Min | author, min_app_version, hooks, contract_version |
|
||||
| 2 | `ai_proactive` | 30 Min | author, min_app_version, hooks, contract_version |
|
||||
| 3 | `ai_ui_control` | 20 Min | author, min_app_version, contract_version |
|
||||
| 4 | `automation` | 30 Min | author, min_app_version, hooks, contract_version |
|
||||
| 5 | `calendar` | 20 Min | author, min_app_version, hooks, contract_version |
|
||||
| 6 | `dms` | 20 Min | author, min_app_version, hooks, contract_version |
|
||||
| 7 | `entity_links` | 15 Min | author, min_app_version, contract_version |
|
||||
| 8 | `forgejo_error_reporter` | 15 Min | author, min_app_version, contract_version |
|
||||
| 9 | `kommunikation` | 30 Min | author, min_app_version, hooks, contract_version |
|
||||
| 10 | `mail` | 20 Min | author, min_app_version, hooks, contract_version |
|
||||
| 11 | `mcp_client` | 20 Min | author, min_app_version, contract_version |
|
||||
| 12 | `mcp_server` | 20 Min | author, min_app_version, contract_version |
|
||||
| 13 | `permissions` | 20 Min | author, min_app_version, contract_version |
|
||||
| 14 | `report_generator` | 20 Min | author, min_app_version, contract_version |
|
||||
| 15 | `system_notif` | 15 Min | author, min_app_version, contract_version |
|
||||
| 16 | `tags` | 15 Min | author, min_app_version, contract_version |
|
||||
| 17 | `tasks` | 20 Min | author, min_app_version, hooks, contract_version |
|
||||
| 18 | `test_sample` | 10 Min | author, min_app_version, contract_version |
|
||||
| 19 | `unified_search` | 20 Min | author, min_app_version, hooks, contract_version |
|
||||
|
||||
**Muster für Aktualisierung:**
|
||||
```python
|
||||
# VORHER:
|
||||
manifest = PluginManifest(
|
||||
name="calendar",
|
||||
version="1.0.0",
|
||||
display_name="Calendar",
|
||||
...
|
||||
)
|
||||
|
||||
# NACHHER:
|
||||
manifest = PluginManifest(
|
||||
name="calendar",
|
||||
version="1.0.0",
|
||||
display_name="Calendar",
|
||||
# ... bestehende Felder ...
|
||||
# ── Neue Felder ──
|
||||
min_app_version="1.0.0",
|
||||
author="LeoCRM Team",
|
||||
license="MIT",
|
||||
hooks=["calendar.before_appointment", "calendar.after_appointment"],
|
||||
contract_version="1.0.0",
|
||||
)
|
||||
```
|
||||
|
||||
### 6.4 Frontend Plugin-Manifest-Typen aktualisieren (2 Std)
|
||||
|
||||
**`frontend/src/api/pluginManifests.ts` und `frontend/src/types/automation.ts`:**
|
||||
- TypeScript-Interfaces um neue Manifest-Felder erweitern
|
||||
- `PluginManifestResponse`-Typ aktualisieren
|
||||
- Frontend-Komponenten die Manifest-Felder anzeigen erweitern
|
||||
|
||||
### 6.5 Manifest-Validierung verschärfen (3 Std)
|
||||
|
||||
**Neue Validierungsregeln in `PluginManifest`:**
|
||||
```python
|
||||
@field_validator("min_app_version")
|
||||
@classmethod
|
||||
def validate_min_app_version(cls, v: str) -> str:
|
||||
"""Validate SemVer format."""
|
||||
from app.plugins.semver import SemVer
|
||||
SemVer.parse(v) # Raises ValueError if invalid
|
||||
return v
|
||||
|
||||
@field_validator("hooks")
|
||||
@classmethod
|
||||
def validate_hooks(cls, v: list[str]) -> list[str]:
|
||||
"""Validate hook names follow namespace.pattern."""
|
||||
for hook in v:
|
||||
if not re.match(r"^[a-z_]+\.[a-z_]+$", hook):
|
||||
raise ValueError(f"Invalid hook name '{hook}': must be 'namespace.action'")
|
||||
return v
|
||||
```
|
||||
|
||||
### 6.6 Tests für erweitertes Manifest (3 Std)
|
||||
|
||||
- `test_manifest.py` — Neue Felder validieren
|
||||
- `test_manifest_validation.py` — SemVer-Validierung, Hook-Name-Validierung
|
||||
- Alle Plugin-Tests: Manifest mit neuen Feldern erstellen
|
||||
- Frontend-Tests: Manifest mit neuen Feldern rendern
|
||||
|
||||
### Meilenstein Phase 6:
|
||||
- ✅ `PluginManifest` hat alle neuen Felder (min_app_version, author, hooks, contract_version, etc.)
|
||||
- ✅ `MANIFEST_SCHEMA_DOC` ist vollständig aktualisiert
|
||||
- ✅ Alle 19 Plugin-Manifeste haben die neuen Felder
|
||||
- ✅ Frontend-Typen sind aktualisiert
|
||||
- ✅ Manifest-Validierung ist verschärft
|
||||
- ✅ Tests bestanden
|
||||
|
||||
---
|
||||
|
||||
## Zeitplan
|
||||
|
||||
```
|
||||
Woche 1 (Tag 1-5): Phase 1 — Contracts (Teil 1: contracts.py + Imports)
|
||||
Woche 2 (Tag 6-8): Phase 1 — Contracts (Teil 2: Deaktivierung + Tests)
|
||||
(Tag 9-10): Phase 2 — Hooks/Filters-System
|
||||
Woche 3 (Tag 11): Phase 3 — Plugin-Isolation
|
||||
(Tag 12-14): Phase 4 — Plugin-Versioning
|
||||
Woche 4 (Tag 15-19): Phase 5 — Marketplace-Vorbereitung
|
||||
Woche 5 (Tag 20-22): Phase 6 — Manifest-Anpassung & Konsolidierung
|
||||
(Tag 23): Puffer / Bugfixes / Doku
|
||||
```
|
||||
|
||||
### Abhängigkeiten
|
||||
```
|
||||
Phase 1 (Contracts) ──→ Phase 3 (Isolation: Linting braucht Contracts als Ausnahme)
|
||||
│
|
||||
└──→ Phase 2 (Hooks: unabhängig, kann parallel)
|
||||
│
|
||||
└──→ Phase 4 (Versioning: braucht Contracts für min_app_version)
|
||||
│
|
||||
└──→ Phase 5 (Marketplace: braucht alles)
|
||||
│
|
||||
└──→ Phase 6 (Manifest: braucht Phase 4 + 5 Felder)
|
||||
```
|
||||
|
||||
### Parallelisierungsmöglichkeiten
|
||||
- Phase 1 und Phase 2 können **parallel** laufen (verschiedene Entwickler)
|
||||
- Phase 3 kann erst nach Phase 1 starten
|
||||
- Phase 4 kann nach Phase 1 starten
|
||||
- Phase 5 kann erst nach Phase 1+4 starten
|
||||
- Phase 6 kann erst nach Phase 4+5 starten (braucht deren Manifest-Felder)
|
||||
|
||||
---
|
||||
|
||||
## Risiken
|
||||
|
||||
| Risiko | Wahrscheinlichkeit | Auswirkung | Mitigation |
|
||||
|---|---|---|---|
|
||||
| Contract-Refactoring bricht bestehende Funktionalität | Mittel | Hoch | Tests nach jedem Plugin, schrittweise Migration |
|
||||
| Hooks/Filters verändern Core-Verhalten | Niedrig | Mittel | Tests für alle Hook-Punkte, Priority-System |
|
||||
| Externe Plugin-Discovery hat Sicherheitslücken | Mittel | Hoch | Signatur-Validierung, Quarantine, Allowlist |
|
||||
| SemVer-Parse-Fehler bei bestehenden Versionen | Niedrig | Niedrig | Fallback auf String-Vergleich |
|
||||
| Rollback-Migrationen löschen Daten | Mittel | Hoch | Bestätigungs-Prompt, Backup vor Rollback |
|
||||
|
||||
---
|
||||
|
||||
## Erfolgskriterien
|
||||
|
||||
Nach Abschluss aller 5 Phasen:
|
||||
|
||||
1. ✅ **0 direkte Cross-Plugin-Imports** (grep-verifiziert, linting-enforced)
|
||||
2. ✅ **Alle 16 Plugins haben contracts.py** mit klarer öffentlicher API
|
||||
3. ✅ **Contracts werden bei Deaktivierung abgemeldet**
|
||||
4. ✅ **Hooks/Filters-System** mit 15+ Hook-Punkten in Core-Services
|
||||
5. ✅ **Plugin-Isolation** durch Linting-Regeln erzwungen
|
||||
6. ✅ **SemVer-Vergleich** statt String-Vergleich
|
||||
7. ✅ **Rollback-Migrationen** für alle Plugins verfügbar
|
||||
8. ✅ **min_app_version** wird bei Installation geprüft
|
||||
9. ✅ **Update-Benachrichtigung** im Frontend
|
||||
10. ✅ **Marketplace-Endpoint** vorbereitet (deaktiviert)
|
||||
11. ✅ **Signatur-Validierung** für externe Plugins
|
||||
12. ✅ **Allowlist** schützt vor nicht autorisierten Plugins
|
||||
13. ✅ **Externe Plugin-Discovery** funktioniert
|
||||
14. ✅ **Alle Tests bestanden**
|
||||
15. ✅ **Built-in Plugins laufen ohne Marketplace**
|
||||
16. ✅ **PluginManifest hat alle neuen Felder** (min_app_version, author, hooks, contract_version, etc.)
|
||||
17. ✅ **Alle 19 Plugin-Manifeste aktualisiert** mit neuen Feldern
|
||||
18. ✅ **Manifest-Validierung verschärft** (SemVer, Hook-Names)
|
||||
19. ✅ **Frontend-Typen aktualisiert** für neue Manifest-Felder
|
||||
|
||||
---
|
||||
|
||||
## Dokumentation
|
||||
|
||||
Nach Abschluss jeder Phase:
|
||||
- `docs/plugin-system/phase-N.md` — Was wurde gemacht, was geändert
|
||||
- `docs/plugin-system/contracts-api.md` — Contract-API Referenz
|
||||
- `docs/plugin-system/hooks-api.md` — Hooks/Filters Referenz
|
||||
- `docs/plugin-system/marketplace-api.md` — Marketplace-API Referenz
|
||||
- `docs/plugin-system/plugin-development-guide.md` — Wie man ein Plugin entwickelt
|
||||
|
||||
---
|
||||
|
||||
**Dieser Plan ist vollständig. Alle Aufgaben, Aufwände, Abhängigkeiten und Risiken sind erfasst.**
|
||||
-804
@@ -1,804 +0,0 @@
|
||||
# LeoCRM — Umbau Progress
|
||||
|
||||
**Plan:** `MASTER-PLAN.md`
|
||||
**Start:** 2026-07-23
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Vorbereitung & Cleanup
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 0.1 | ✅ done | 2026-07-23 | codebase-vs-requirements.md neu geschrieben, security-review-phase2.md Resolution Summary, architecture.md Implementation Status, MASTER-PLAN.md + PROGRESS.md erstellt |
|
||||
| 0.2 | ✅ done | 2026-07-23 | lucide-react installieren + Icons migrieren |
|
||||
| 0.3 | ✅ done | 2026-07-23 | date-fns installieren + Datum-Formatierung |
|
||||
| 0.4 | ✅ done | 2026-07-23 | hooks.ts aufteilen — 1298 Zeilen → 12 Module + Re-Export-Hub |
|
||||
| 0.5 | ✅ done | 2026-07-23 | calendarStore.ts nach store/ verschoben, stores/ entfernt |
|
||||
| 0.6 | ✅ done | 2026-07-23 | frontend-gap-analysis.md gespeichert (176 Zeilen) |
|
||||
| 0.7 | ✅ done | 2026-07-23 | UI-Design-Richtlinien erstellt (docs/ui-design-guidelines.md, 535 Zeilen) |
|
||||
| 0.8 | ✅ done | 2026-07-23 | Theme-Customization Backend: 4 Felder (primary_color, accent_color, font_family, border_radius) zu model/schema/service, Migration 0023 | Theme-Customization Backend |
|
||||
| 0.9 | ✅ done | 2026-07-23 | Theme-Customization Frontend: SettingsTheme.tsx, themeStore.ts, Route + Nav, i18n keys, Live-Preview, Dark-Mode-Toggle | Theme-Customization Frontend |
|
||||
| 0.10 | ✅ done | 2026-07-23 | RBAC-Audit: 4 Plugins (calendar, dms, entity_links, tags) mit Permissions versehen, 53 Routes mit require_permission abgesichert | RBAC-Audit & Plugin-Permissions nachrüsten |
|
||||
| 0.11 | ✅ done | 2026-07-23 | LiteLLM-Cleanup: llm_client.py von httpx auf litellm.acompletion migriert, AI_PROVIDER env var, System-Prompt companies→contacts | LiteLLM-Cleanup & alte llm_client.py migrieren |
|
||||
| 0.12 | ✅ done | 2026-07-23 | KI-Agent-Framework: docs/plugin-development-guide.md (348 Zeilen), agent_capabilities Feld im PluginManifest | KI-Agent-Framework in Plugin-Richtlinien dokumentieren |
|
||||
| 0.13 | ✅ done | 2026-07-23 | Heartbeat konfigurierbar: ProactiveSettings um heartbeat_enabled/interval/target_room erweitert, Migration 0024, Schema+Service+Routes, Frontend-UI, Jobs.py nutzt Settings | Heartbeat konfigurierbar machen |
|
||||
| 0.14 | ✅ done | 2026-07-23 | Unified Search Field-Level RBAC: resolve_permissions + filter_fields_by_permission in search route, entity-to-module mapping | Unified Search: Field-Level RBAC nachrüsten |
|
||||
| 0.15 | ✅ done | 2026-07-23 | Undo/History-System: EntityHistory model+service+routes, Migration 0025, contact_service Integration, HistoryViewer Komponente, ContactDetail Integration, i18n | Undo/History-System für CRUD-Operationen |
|
||||
| 0.16 | ✅ done | 2026-07-23 | Storage Backend: app/core/storage.py (LocalStorage + S3Storage), DMS + Attachments + Mail auf Storage Backend umgestellt, minio zu requirements, S3 env vars | Storage Backend implementieren (S3-Support) |
|
||||
| 0.17 | ✅ done | 2026-07-23 | Import/Export: unified Contact Fields (firstname, surname, email_1, phone_1, mobilephone, function), Company-Import als Contact type=company, Export mit unified Fields, Backward-compat für alte CSV-Spalten | Import/Export an unified Contact Model anpassen |
|
||||
| 0.18 | ✅ done | 2026-07-23 | .gitignore: webui→frontend, python-jose entfernt, pyproject.toml Python 3.12, .env aus Git entfernt, dump.rdb+test.txt gelöscht, JWT-Vars aus .env.docker.example entfernt | .gitignore & Config-Cleanup |
|
||||
| 0.19 | ✅ done | 2026-07-23 | Mail-Salt Security-Fix: per-account random salt (generate_salt), encrypt/decrypt mit salt_b64, backward-compat mit Legacy-Salt, Migration 0026 | Mail-Salt Security-Fix |
|
||||
| 0.20 | ✅ done | 2026-07-23 | AGPL ersetzt: PyMuPDF→pypdf (BSD), OnlyOffice→Collabora (LGPL/MPL), requirements.txt, LICENSE (MIT), THIRD_PARTY_LICENSES.md | AGPL-Lizenzen durch pypdf + Collabora ersetzen |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1B: Backend Plugins — entity_type='company' → 'contact'
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 1.9 | ✅ done | 2026-07-23 | entity_links Plugin: entity_type pattern ^(company|contact)$ → ^contact$, company_router entfernt, on_company_deleted → on_contact_deleted, company.deleted → contact.deleted |
|
||||
| 1.10 | ✅ done | 2026-07-23 | unified_search Plugin: CompanySearchProvider → ContactSearchProvider, index_company → index_contact, events company.created/updated → contact.created/updated, search_engine mapping aktualisiert |
|
||||
| 1.11 | ✅ done | 2026-07-23 | calendar Plugin: entity_type pattern ^(company|contact)$ → ^contact$ (CalendarType='company' bleibt) |
|
||||
| 1.12 | ✅ done | 2026-07-23 | tags Plugin: entity_type pattern ^(company|contact|file|folder)$ → ^(contact|file|folder)$ |
|
||||
| 1.13 | ✅ done | 2026-07-23 | mail Plugin: company_id → contact_id in model, schemas, routes, services |
|
||||
| 1.14 | ✅ done | 2026-07-23 | test_sample Plugin: company.created → contact.created |
|
||||
| 1.15 | ✅ done | 2026-07-23 | Event Names Unify: Alle company.created/updated/deleted → contact.created/updated/deleted |
|
||||
| 1.16 | ✅ done | 2026-07-23 | DB Migration 0027: entity_type 'company' → 'contact' in entity_links, tag_assignments, calendar_entry_links, addresses; mails company_id → contact_id |
|
||||
| 1.17 | ✅ done | 2026-07-23 | Backend Tests Update: test_companies.py, test_unified_search.py, test_entity_links.py, test_calendar.py, test_tags.py, test_ai_proactive.py, test_tenant.py — entity_type='company' → 'contact' |
|
||||
| 1.18 | ✅ done | 2026-07-23 | Permission-Registry-Cleanup: companies:read/write/delete aus CORE_PERMISSIONS entfernt (bereits in 1A) |
|
||||
| 1.19 | ✅ done | 2026-07-23 | Addresses entity_type='company' → 'contact' in address_service.py (bereits in 1A) |
|
||||
| 1.20 | ✅ done | 2026-07-23 | conftest.py Update: Company → Contact, CompanyContact → ContactPerson, TRUNCATE ohne companies/company_contacts |
|
||||
|
||||
**Phase 1B Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 1C: Frontend — Unified Contact UI
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 1.18 | ✅ done | 2026-07-23 | Contact-Detail-Route /contacts/:id mit React.lazy |
|
||||
| 1.19 | ✅ done | 2026-07-23 | ContactList Type-Filter Toggle (Alle/Firmen/Personen) |
|
||||
| 1.20 | ✅ done | 2026-07-23 | ContactDetail ContactPerson-Verwaltung (bereits vorhanden) |
|
||||
| 1.21 | ✅ done | 2026-07-23 | ContactEditModal für beide Types (bereits vorhanden) |
|
||||
| 1.22 | ✅ done | 2026-07-23 | Company-Hooks aus hooks.ts entfernt |
|
||||
| 1.23 | ✅ done | 2026-07-23 | Frontend Type-Definitions aktualisiert (calendar, tags, search, mail, types) |
|
||||
| 1.24 | ✅ done | 2026-07-23 | Dashboard.tsx aktualisiert (keine Änderung nötig) |
|
||||
| 1.25 | ✅ done | 2026-07-23 | GlobalSearchResults.tsx aktualisiert |
|
||||
| 1.26 | ✅ done | 2026-07-23 | ContactFolderTree in ContactList integriert (bereits vorhanden) |
|
||||
| 1.27 | ✅ done | 2026-07-23 | React Hook Form + Zod in ContactEditModal |
|
||||
| 1.28 | ✅ done | 2026-07-23 | Frontend-Tests aktualisiert, tsc --noEmit OK (nur pre-existing Dms-Fehler) |
|
||||
|
||||
**Phase 1C Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 1-7: Noch nicht gestartet
|
||||
|
||||
Siehe `MASTER-PLAN.md` für alle Tasks.
|
||||
|
||||
## Phase 2: Code-Splitting & Virtual Scrolling
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 2.1 | ✅ done | 2026-07-23 | React.lazy + Suspense für 25 Pages, PageLoader Komponente |
|
||||
| 2.2 | ✅ done | 2026-07-23 | @tanstack/react-virtual installiert |
|
||||
| 2.3 | ✅ done | 2026-07-23 | DataGrid Virtual Scrolling (useVirtualizer, 53px rows, 10 overscan, auto-skip <50) |
|
||||
| 2.4 | ✅ done | 2026-07-23 | MailList Virtual Scrolling (80px rows, 8 overscan, auto-skip <50) |
|
||||
| 2.5 | ✅ done | 2026-07-23 | ContactList Virtual Scrolling (list/table/cards, dynamic row estimate) |
|
||||
| 2.6 | ✅ done | 2026-07-23 | FileExplorer + FileGrid virtualisiert, AuditLog via DataGrid |
|
||||
| 2.7 | ✅ done | 2026-07-23 | Manual chunks: react-vendor, tanstack, ui, i18n. Vite build OK (3212 modules, ~9s) |
|
||||
|
||||
**Phase 2 Gesamt: ✅ Complete (Commit a8331fb)**
|
||||
|
||||
## Phase 1: Unified Contact Model — Vollendung (Backend + Frontend)
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 1.1 | ✅ done | 2026-07-23 | app/routes/companies.py entfernt |
|
||||
| 1.2 | ✅ done | 2026-07-23 | app/services/company_service.py entfernt |
|
||||
| 1.3 | ✅ done | 2026-07-23 | app/models/company.py entfernt (war shim) |
|
||||
| 1.4 | ✅ done | 2026-07-23 | app/schemas/company.py entfernt |
|
||||
| 1.5 | ✅ done | 2026-07-23 | action_mapper.py: Company-Intents → Contact-Intents |
|
||||
| 1.6 | ✅ done | 2026-07-23 | workflows/engine.py: company.created → contact.created |
|
||||
| 1.7 | ✅ done | 2026-07-23 | worker.py: index_company entfernt |
|
||||
| 1.8 | ✅ done | 2026-07-23 | seeds.py: keine Company-Seed-Daten gefunden |
|
||||
| 1.9 | ✅ done | 2026-07-23 | entity_links: entity_type → ^contact$, company.deleted → contact.deleted |
|
||||
| 1.10 | ✅ done | 2026-07-23 | unified_search: CompanySearchProvider → Contact, index_company → index_contact, events unified |
|
||||
| 1.11 | ✅ done | 2026-07-23 | calendar: entity_type → ^contact$, CalendarType='company' beibehalten |
|
||||
| 1.12 | ✅ done | 2026-07-23 | tags: entity_type → ^(contact|file|folder)$ |
|
||||
| 1.13 | ✅ done | 2026-07-23 | mail: company_id → contact_id in model/schemas/routes/services |
|
||||
| 1.14 | ✅ done | 2026-07-23 | test_sample: company.created → contact.created |
|
||||
| 1.15 | ✅ done | 2026-07-23 | Alle company.* events → contact.* events vereinheitlicht |
|
||||
| 1.16 | ✅ done | 2026-07-23 | DB-Migration 0027: entity_type company→contact, mails.company_id→contact_id |
|
||||
| 1.17 | ✅ done | 2026-07-23 | Backend-Tests aktualisiert (test_companies, test_unified_search, test_entity_links, etc.) |
|
||||
| 1.18 | ✅ done | 2026-07-23 | Permission-Registry: companies:read/write/delete entfernt, CORE_FIELD_DEFINITIONS aktualisiert |
|
||||
| 1.19 | ✅ done | 2026-07-23 | Addresses: entity_type → ^contact$ only |
|
||||
| 1.20 | ✅ done | 2026-07-23 | conftest.py: Company→Contact, CompanyContact→ContactPerson, TRUNCATE bereinigt |
|
||||
| 1.21 | ✅ done | 2026-07-23 | Contact-Detail-Route /contacts/:id mit React.lazy |
|
||||
| 1.22 | ✅ done | 2026-07-23 | Company-Hooks aus hooks.ts entfernt |
|
||||
| 1.23 | ✅ done | 2026-07-23 | Frontend Type-Definitions aktualisiert (calendar, tags, search, mail, types) |
|
||||
| 1.24 | ✅ done | 2026-07-23 | Dashboard.tsx aktualisiert (keine Änderung nötig) |
|
||||
| 1.25 | ✅ done | 2026-07-23 | GlobalSearchResults.tsx: type 'company' → 'contact' |
|
||||
| 1.26 | ✅ done | 2026-07-23 | ContactFolderTree in ContactList (bereits vorhanden) |
|
||||
| 1.27 | ✅ done | 2026-07-23 | React Hook Form + Zod in ContactEditModal |
|
||||
| 1.28 | ✅ done | 2026-07-23 | Frontend-Tests aktualisiert, tsc --noEmit OK (nur pre-existing Dms.tsx errors) |
|
||||
|
||||
**Phase 1 Gesamt: ✅ Complete (Commits: 879106c, 5d79b4f, b15a62b)**
|
||||
### Verifikation Phase 1
|
||||
- App startet OK (245 Routes) ✅
|
||||
- Python Syntax OK für alle geänderten Dateien ✅
|
||||
- Frontend: 251/265 Tests pass (14 pre-existing failures: Dms/Mail/ShareDialog) ✅
|
||||
- Backend-Tests: können nicht ausgeführt werden (kein PostgreSQL im Container) ⚠️
|
||||
- Keine verbleibenden company.* Events oder companies: Permissions ✅
|
||||
|
||||
## Phase 3: Plugin-UI-System
|
||||
**Phase 3 Gesamt: ✅ Complete (Commit fc96a2f)**
|
||||
|
||||
## Phase 3.5: Automation & Agents Plugin
|
||||
**Phase 3.5 Gesamt: ✅ Complete (Commit 5dc6f29)**
|
||||
|
||||
## Phase 4: KI-UI-Steuerung
|
||||
|
||||
| # | Status | Datum | Was gemacht wurde |
|
||||
|---|--------|------|-------------------|
|
||||
| 4.1 | ✅ done | 2026-07-23 | UI-Command-Protokoll: JSON schema mit 6 command types (navigate, filter, open_contact, modal, tab, settings) in schemas.py |
|
||||
| 4.2 | ✅ done | 2026-07-23 | WebSocket-Endpoint /ws/ai-ui-control + REST endpoints (POST /command, GET /command/{id}/status, GET /online-users) in ai_ui_control plugin |
|
||||
| 4.3 | ✅ done | 2026-07-23 | useAIUIControl hook: WS client mit auto-reconnect, command dispatch, feedback sending |
|
||||
| 4.4 | ✅ done | 2026-07-23 | Command: Navigate — useNavigate() für Route-Wechsel |
|
||||
| 4.5 | ✅ done | 2026-07-23 | Command: Filter — URL-Search-Params + store pendingFilter |
|
||||
| 4.6 | ✅ done | 2026-07-23 | Command: Open Contact — navigate zu /contacts/:id |
|
||||
| 4.7 | ✅ done | 2026-23 | Command: Modal — store activeModal, ContactDetail syncs personModalOpen |
|
||||
| 4.8 | ✅ done | 2026-07-23 | Command: Tab — store activeTab, ContactDetail syncs via useEffect |
|
||||
| 4.9 | ✅ done | 2026-07-23 | Command: Settings — navigate zu /settings/:section + pendingSettings |
|
||||
| 4.10 | ✅ done | 2026-07-23 | UI-Action-Feedback: sendFeedback via WS, store lastFeedback |
|
||||
| 4.11 | ✅ done | 2026-07-23 | Visuelle KI-Indikation: AIUIControlIndicator component (Bot icon, toast, pulse animation) |
|
||||
| 4.12 | ✅ done | 2026-07-23 | 18 Vitest tests: command protocol, store actions, feedback, visual indication |
|
||||
|
||||
**Phase 4 Gesamt: ✅ Complete**
|
||||
|
||||
### Verifikation Phase 4
|
||||
- TSC: 0 neue errors (nur 2 pre-existing Dms.tsx errors) ✅
|
||||
- Vitest: 18/18 AI UI Control tests pass ✅
|
||||
- Keine neuen Regressionen (AppShell tests waren pre-existing failing) ✅
|
||||
- Backend: ai_ui_control plugin mit WS + REST, service_container registration ✅
|
||||
- Frontend: useAIUIControl hook, aiUIControlStore, AIUIControlIndicator, i18n DE/EN ✅
|
||||
- Neue Dateien: 8 (plugin: __init__.py, plugin.py, routes.py, schemas.py, websocket_manager.py; frontend: store, hook, API, indicator, tests) ✅
|
||||
|
||||
## Phase 5: API-Vollständigkeit & Frontend-Anbindung
|
||||
|
||||
### Batch 1 (Tasks 5.1-5.3)
|
||||
|
||||
| # | Status | Datum | Was gemacht wurde |
|
||||
||---|--------|------|-------------------|
|
||||
| 5.1 | ✅ done | 2026-07-23 | API-Audit: docs/api-audit.md mit 158 UI-Funktionen in 24 Kategorien, alle per API erreichbar. 0 fehlende Endpoints. UI-State (Sidebar/Tab/Filter) durch Task 5.2 abgedeckt. 9 Tests (Audit-Dokument + Endpoint-Reachability) |
|
||||
| 5.2 | ✅ done | 2026-07-23 | User-Preferences-API: Model (UserPreference mit TenantMixin), API Router (GET/PUT/DELETE /api/v1/user/preferences), Migration 0028, Frontend API + useUserPreferences hook mit uiStore-Sync, i18n DE/EN, 13 Backend-Tests (CRUD, Tenant-Isolation, CSRF, RBAC) |
|
||||
| 5.3 | ✅ done | 2026-07-23 | Workflow-API-Frontend-Modul: frontend/src/api/workflows.ts mit TypeScript types + React Query hooks (CRUD, Instances, Advance/Cancel), 13 Frontend-Tests |
|
||||
|
||||
**Phase 5 Batch 1 Gesamt: ✅ Complete**
|
||||
|
||||
### Verifikation Phase 5 Batch 1
|
||||
- TSC: 0 neue errors (nur 2 pre-existing Dms.tsx errors) ✅
|
||||
- Backend Tests: 22/22 pass (13 user_preferences + 9 api_audit) ✅
|
||||
- Frontend Tests: 13/13 pass (workflows.test.ts) ✅
|
||||
- 3 Commits mit klaren Messages ✅
|
||||
- TenantMixin für neues DB-Model (UserPreference) ✅
|
||||
- RBAC (require_permission) für alle neuen API-Routes ✅
|
||||
- i18n (de.json, en.json) für Frontend-Änderungen ✅
|
||||
- Keine .env committet ✅
|
||||
- Bestehende Patterns verwendet: apiClient, Zustand stores, React Query hooks ✅
|
||||
|
||||
### Neue Dateien Phase 5 Batch 1
|
||||
- `docs/api-audit.md` — API-Audit-Dokument
|
||||
- `app/models/user_preference.py` — UserPreference SQLAlchemy Model
|
||||
- `app/routes/user_preferences.py` — User Preferences API Router
|
||||
- `alembic/versions/0028_user_preferences.py` — Migration
|
||||
- `frontend/src/api/userPreferences.ts` — Frontend API module
|
||||
- `frontend/src/hooks/useUserPreferences.ts` — useUserPreferencesSync hook
|
||||
- `frontend/src/api/workflows.ts` — Workflow API frontend module
|
||||
- `frontend/src/api/__tests__/workflows.test.ts` — Workflow API tests
|
||||
- `tests/test_user_preferences.py` — User preferences backend tests
|
||||
- `tests/test_api_audit.py` — API audit tests
|
||||
|
||||
### Modifizierte Dateien Phase 5 Batch 1
|
||||
- `app/main.py` — user_preferences router import + include_router
|
||||
- `app/routes/__init__.py` — user_preferences import
|
||||
- `app/core/permission_registry.py` — user_preferences:read/write permissions
|
||||
- `app/core/permissions.py` — user_preferences in legacy role permissions
|
||||
- `tests/conftest.py` — UserPreference model import + Contact seed fix (industry field)
|
||||
- `frontend/src/i18n/locales/de.json` — userPreferences i18n
|
||||
- `frontend/src/i18n/locales/en.json` — userPreferences i18n
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 Batch 2: Playwright E2E Tests
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 5.4 | ✅ done | 2026-07-23 | Playwright Setup: @playwright/test devDependency, playwright.config.ts (chromium, webServer, baseURL), e2e/helpers.ts mit Login/API-Mock/Fixtures |
|
||||
| 5.5 | ✅ done | 2026-07-23 | auth.spec.ts: Login-Form rendered, successful login redirect, invalid credentials error, logout button, protected route redirect |
|
||||
| 5.6 | ✅ done | 2026-07-23 | contact-crud.spec.ts: Company erstellen, Detail anzeigen+edit, Ansprechpartner hinzufügen, Kontakt löschen |
|
||||
| 5.7 | ✅ done | 2026-07-23 | search.spec.ts: Search page rendered, search by contact name, grouped tabs, empty query, topbar search dropdown |
|
||||
| 5.8 | ✅ done | 2026-07-23 | plugin-toggle.spec.ts: Settings plugins page, activate inactive plugin, deactivate active plugin, refresh button, install section |
|
||||
| 5.9 | ✅ done | 2026-07-23 | mail.spec.ts: Mail page folder tree, mail list, open mail detail, mail settings add account form |
|
||||
| 5.10 | ✅ done | 2026-07-23 | dms.spec.ts: DMS page source tree+explorer, create folder, upload section, file preview modal, share dialog |
|
||||
| 5.11 | ✅ done | 2026-07-23 | calendar.spec.ts: Calendar page tree+view, create appointment, switch views (month/week/day), calendar tree, kanban columns, entry detail |
|
||||
|
||||
**Phase 5 Batch 2 Gesamt: ✅ Complete**
|
||||
|
||||
### Verifikation Phase 5 Batch 2
|
||||
- TSC: 0 neue errors (nur 2 pre-existing Dms.tsx onRangeSelect errors) ✅
|
||||
- 8 E2E spec files mit realistischen User-Workflows ✅
|
||||
- playwright.config.ts mit chromium project, webServer, trace/screenshot/video ✅
|
||||
- e2e/helpers.ts mit setupApiMocks (alle API endpoints gemockt), login/logout helpers, mock data fixtures ✅
|
||||
- test.describe Gruppierung + beforeEach Login-Setup in allen specs ✅
|
||||
- data-testid Attribute aus bestehenden Komponenten verwendet ✅
|
||||
- page.goto, page.locator, expect von @playwright/test ✅
|
||||
- Tests für CI/CD geschrieben (können nicht im Container laufen, kein PostgreSQL/Redis) ✅
|
||||
|
||||
### Neue Dateien Phase 5 Batch 2
|
||||
- `frontend/playwright.config.ts` — Playwright Konfiguration
|
||||
- `frontend/e2e/helpers.ts` — Test-Helper (Login, API-Mocks, Fixtures)
|
||||
- `frontend/e2e/auth.spec.ts` — Auth E2E Tests
|
||||
- `frontend/e2e/contact-crud.spec.ts` — Contact CRUD E2E Tests
|
||||
- `frontend/e2e/search.spec.ts` — Search E2E Tests
|
||||
- `frontend/e2e/plugin-toggle.spec.ts` — Plugin Toggle E2E Tests
|
||||
- `frontend/e2e/mail.spec.ts` — Mail E2E Tests
|
||||
- `frontend/e2e/dms.spec.ts` — DMS E2E Tests
|
||||
- `frontend/e2e/calendar.spec.ts` — Calendar E2E Tests
|
||||
|
||||
### Modifizierte Dateien Phase 5 Batch 2
|
||||
- `frontend/package.json` — @playwright/test devDependency + e2e scripts
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 Batch 3 (Tasks 5.12-5.15) — ✅ Complete
|
||||
|
||||
| Task | Status | Datum | Beschreibung |
|
||||
|------|--------|-------|--------------|
|
||||
| 5.12 | ✅ done | 2026-07-23 | API-Health-Check-Script (scripts/ai_health_check.py) — enumeriert 295 API-Routen, probt GET-Endpoints mit Auth-Token, JSON/CSV-Report, Exit-Codes |
|
||||
| 5.13 | ✅ done | 2026-07-23 | CI/CD-Pipeline-Script (scripts/ai_deploy.py) — Build (docker/npm), Tests (pytest+vitest), Deploy (Coolify API), Rollback bei Fehler, --dry-run/--skip-tests/--skip-build |
|
||||
| 5.14 | ✅ done | 2026-07-23 | API-Dokumentation vervollständigt — OpenAPI tags mit Beschreibungen (36 Tags), response_model für auth/health/users/notifications/system-settings, Pydantic examples, docs/api-documentation.md (500 Zeilen, 295 Endpoints) |
|
||||
| 5.15 | ✅ done | 2026-07-23 | Automatisiertes Backup-System — scripts/backup.py (pg_dump+file backup, retention policy, notification), scripts/restore.py, system_notif plugin erweitert (backup.completed/failed events), backup config in system_settings |
|
||||
|
||||
### Verifikation Phase 5 Batch 3
|
||||
- TSC: 0 neue errors (nur 2 pre-existing Dms.tsx onRangeSelect errors) ✅
|
||||
- 42 neue Tests alle passing ✅
|
||||
- Alle Scripts ausführbar (chmod +x) ✅
|
||||
- argparse für CLI-Argumente ✅
|
||||
- httpx für HTTP-Calls ✅
|
||||
- Keine .env committet ✅
|
||||
- Bestehende Patterns verwendet (APIRouter, Pydantic, sys.path.insert) ✅
|
||||
|
||||
### Neue Dateien Phase 5 Batch 3
|
||||
- `scripts/ai_health_check.py` — API Health Check Script (ausführbar)
|
||||
- `scripts/ai_deploy.py` — CI/CD Deploy Script (ausführbar)
|
||||
- `scripts/backup.py` — Automated Backup Script (ausführbar)
|
||||
- `scripts/restore.py` — Restore Script (ausführbar)
|
||||
- `docs/api-documentation.md` — Vollständige API-Dokumentation (295 Endpoints, 30 Tag-Gruppen)
|
||||
- `tests/test_ai_health_check.py` — 7 Tests für Health Check
|
||||
- `tests/test_ai_deploy.py` — 11 Tests für Deploy Script
|
||||
- `tests/test_api_documentation.py` — 8 Tests für API-Dokumentation
|
||||
- `tests/test_backup_restore.py` — 16 Tests für Backup/Restore
|
||||
|
||||
### Modifizierte Dateien Phase 5 Batch 3
|
||||
- `app/main.py` — OpenAPI tags (36 Tags mit Beschreibungen), app description
|
||||
- `app/routes/auth.py` — response_model für login/logout/me (AuthResponse, MessageResponse)
|
||||
- `app/routes/health.py` — response_model HealthResponse
|
||||
- `app/routes/notifications.py` — response_model UnreadCountResponse für unread-count
|
||||
- `app/routes/users.py` — response_model für list/create/get (PaginatedUsers, UserResponse)
|
||||
- `app/routes/system_settings.py` — response_model SystemSettingsResponse für GET/PUT
|
||||
- `app/schemas/auth.py` — Field examples für LoginRequest, AuthResponse
|
||||
- `app/schemas/user.py` — Field examples für UserCreate
|
||||
- `app/schemas/system_settings.py` — Backup config fields (backup_interval, backup_retention_days, backup_destination)
|
||||
- `app/plugins/builtins/ai_ui_control/routes.py` — tags=["ai-ui-control"] hinzugefügt
|
||||
- `app/plugins/builtins/system_notif/plugin.py` — backup.completed/failed events + handler methods
|
||||
|
||||
**Phase 5 Batch 3 Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 Batch 4: MCP Server & Client Integration (Tasks 5.16-5.17) ✅
|
||||
|
||||
### Task 5.16: MCP-Server Integration (10h) ✅
|
||||
|
||||
LeoCRM als MCP-Server: Externe Tools (Claude Desktop, andere KI-Clients) können auf LeoCRM-Daten zugreifen.
|
||||
|
||||
**Neues Plugin `app/plugins/builtins/mcp_server/`:**
|
||||
- `plugin.py` — PluginManifest (name='mcp_server', dependencies=['permissions'], permissions=['mcp:read','mcp:write'])
|
||||
- `routes.py` — 3 Endpoints:
|
||||
- `GET /api/v1/mcp/tools` — Listet alle 9 MCP-Tools mit Schema
|
||||
- `POST /api/v1/mcp/tools/{tool_name}/execute` — Führt MCP-Tool aus (mit RBAC)
|
||||
- `GET /api/v1/mcp/config` — MCP-Server-Konfiguration für externe Clients
|
||||
- `tool_definitions.py` — 9 MCP-Tool-Definitionen:
|
||||
- `search_contacts` — Kontakte durchsuchen (query, limit)
|
||||
- `get_contact` — Kontakt Details abrufen (contact_id)
|
||||
- `create_contact` — Neuen Kontakt erstellen (name, email, phone, type)
|
||||
- `list_calendar_entries` — Kalendereinträge auflisten (date_from, date_to)
|
||||
- `create_calendar_entry` — Termin erstellen (title, start, end)
|
||||
- `list_emails` — E-Mails auflisten (folder, limit)
|
||||
- `send_email` — E-Mail senden (to, subject, body)
|
||||
- `list_files` — DMS-Dateien auflisten (folder_id)
|
||||
- `upload_file` — Datei hochladen (filename, content_base64)
|
||||
- `schemas.py` — Pydantic schemas (McpToolDefinition, McpToolExecuteRequest/Response, McpServerConfig)
|
||||
- `migrations/0001_initial.sql` — Stateless plugin (no tables needed)
|
||||
- Auth: Session-based auth + RBAC permission check per tool
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/mcp.ts` — API client with React Query hooks (useMcpTools, useMcpConfig, useExecuteMcpTool)
|
||||
- `frontend/src/pages/SettingsMcp.tsx` — MCP Settings page with tool listing, execution UI, and server config
|
||||
- `frontend/src/routes/index.tsx` — Added /settings/mcp route
|
||||
- `frontend/src/pages/Settings.tsx` — Added MCP nav item
|
||||
- i18n: de.json + en.json updated with mcp.server.* and mcp.client.* keys
|
||||
|
||||
**Tests:** `tests/test_mcp_server.py` — 7 tests (all passing)
|
||||
- AC1: List MCP tools (9 tools)
|
||||
- AC2: Get MCP config
|
||||
- AC3: Execute search_contacts
|
||||
- AC4: Non-existent tool returns 404
|
||||
- AC5: Tool definitions schema validation
|
||||
- AC6: Unauthorized access rejected
|
||||
- AC7: Execute create_contact
|
||||
|
||||
### Task 5.17: MCP-Client Integration (6h) ✅
|
||||
|
||||
LeoCRM-Agenten können externe MCP-Server nutzen (Web-Search, Code-Execution, externe Datenquellen).
|
||||
|
||||
**Neues Plugin `app/plugins/builtins/mcp_client/`:**
|
||||
- `plugin.py` — PluginManifest (name='mcp_client', dependencies=['permissions'], permissions=['mcp-client:read','mcp-client:write','mcp-client:admin'])
|
||||
- `models.py` — McpServerConfig Model (name, url, api_token, enabled, tenant_id) mit TenantMixin
|
||||
- `routes.py` — CRUD + tool execution:
|
||||
- `GET /api/v1/mcp-client/servers` — List server configs
|
||||
- `POST /api/v1/mcp-client/servers` — Create server config
|
||||
- `PATCH /api/v1/mcp-client/servers/{id}` — Update server config
|
||||
- `DELETE /api/v1/mcp-client/servers/{id}` — Delete server config
|
||||
- `GET /servers/{id}/tools` — List tools from external server
|
||||
- `POST /servers/{id}/execute` — Execute tool on external server
|
||||
- `schemas.py` — Pydantic schemas (McpServerConfigCreate/Update/Response, McpServerToolsResponse, McpServerExecuteRequest/Response)
|
||||
- `client.py` — Async MCP client using httpx (list_tools, execute_tool, health_check)
|
||||
- `tool_registry_integration.py` — Integriert externe MCP-Tools in ai_assistant tool_registry
|
||||
- `sync_external_tools()` — Fetches tools from all enabled servers and registers them
|
||||
- `unregister_all_external_tools()` — Cleanup
|
||||
- Tool naming: `mcp__{server}__{tool}`
|
||||
- `migrations/0001_initial.sql` — mcp_server_configs table
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/mcpClient.ts` — API client with React Query hooks (useMcpServers, useCreateMcpServer, useUpdateMcpServer, useDeleteMcpServer, useMcpServerTools)
|
||||
- MCP Client settings UI in SettingsMcp.tsx (server CRUD, tool viewing)
|
||||
- i18n entries for mcp.client.*
|
||||
|
||||
**Tests:** `tests/test_mcp_client.py` — 8 tests (all passing)
|
||||
- AC1: List servers (empty)
|
||||
- AC2: Create server config
|
||||
- AC3: Update server config
|
||||
- AC4: Delete server config
|
||||
- AC5: List servers after creating
|
||||
- AC6: Unauthorized access rejected
|
||||
- AC7: Execute on non-existent server returns 404
|
||||
- AC8: Tool registry integration verification
|
||||
|
||||
### Verifikation
|
||||
- Alle 15 Tests passing (7 + 8)
|
||||
- TSC: 0 neue Errors (nur pre-existing Dms.tsx errors)
|
||||
- 2 Commits mit klaren Messages
|
||||
- TenantMixin für McpServerConfig verwendet
|
||||
- RBAC (require_permission) auf allen API-Routes
|
||||
- i18n (de.json, en.json) aktualisiert
|
||||
- Keine .env committet
|
||||
- Bestehende Patterns verwendet (apiClient, React Query hooks, PluginManifest)
|
||||
|
||||
**Phase 5 Batch 4 Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 Batch 5: Report Generator PDF-Support & Frontend (Tasks 5.18-5.19)
|
||||
|
||||
### Task 5.18: Report Generator: PDF-Support & Druck-Funktionen ✅
|
||||
|
||||
**Backend:**
|
||||
- WeasyPrint 69.0 installiert in /opt/venv
|
||||
- 5 Jinja2 HTML-Templates erstellt in `app/plugins/builtins/report_generator/templates/`:
|
||||
- `contact_list.html.j2` — Kontaktliste (Name, E-Mail, Telefon, Typ, Firma)
|
||||
- `calendar_week.html.j2` — Wochenkalender (Tage × Stunden Grid)
|
||||
- `calendar_month.html.j2` — Monatskalender (Grid mit Terminen)
|
||||
- `company_list.html.j2` — Firmenliste (Name, Adresse, Ansprechpartner)
|
||||
- `audit_log.html.j2` — Audit-Log (Timestamp, User, Action, Entity)
|
||||
- Alle Templates: A4 Landscape, @media print CSS, Seitenränder, Seitenzahlen
|
||||
- `schemas.py` erweitert: `output_format` um `pdf` und `print` ergänzt
|
||||
- `PresetReportRequest` und `PresetReportInfo` Schemas hinzugefügt
|
||||
- `ReportGenerateRequest` um optionales `output_format` erweitert
|
||||
- `pdf_generator.py` erstellt: Jinja2 + WeasyPrint Pipeline
|
||||
- `render_template_file()` — File-basierte Templates
|
||||
- `render_template_string()` — User-defined Templates
|
||||
- `generate_pdf()` / `generate_print_pdf()` — WeasyPrint PDF-Generierung
|
||||
- `generate_preset_report()` — Preset-spezifische Generierung (PDF/CSV/Excel/JSON)
|
||||
- `generate_pdf_from_template_content()` — User-template Generierung
|
||||
- `PRESET_META` — Metadaten für 5 Preset-Berichte
|
||||
- `routes.py` modifiziert:
|
||||
- `GET /presets` — Listet alle Preset-Berichte
|
||||
- `POST /presets/generate` — Generiert Preset-Bericht (StreamingResponse)
|
||||
- `POST /generate` — Generiert User-Template-Bericht (StreamingResponse)
|
||||
- Alle Endpunkte mit RBAC (`require_permission`: reports:read, reports:generate, reports:manage_templates)
|
||||
- Generate-Endpunkte returnieren Datei direkt als StreamingResponse
|
||||
- `plugin.py` permissions auf Colon-Format aktualisiert (reports:read, reports:generate, reports:manage_templates)
|
||||
|
||||
**Tests:** `tests/test_report_generator.py` — 7 Tests (all passing)
|
||||
- test_list_presets: GET /presets returns 5 presets
|
||||
- test_generate_preset_pdf: PDF generation with valid %PDF- header
|
||||
- test_generate_preset_csv: CSV generation with correct content
|
||||
- test_create_and_generate_pdf_template: Template CRUD + PDF generation
|
||||
- test_output_format_validation: Invalid format rejected (422)
|
||||
- test_unauthenticated_access_blocked: 401 without auth
|
||||
- test_viewer_cannot_manage_templates: RBAC 403 for viewer role
|
||||
|
||||
### Task 5.19: Report Generator: Frontend-Oberfläche ✅
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/reports.ts` — React Query hooks:
|
||||
- `useReportTemplates`, `useReportTemplate`, `useCreateReportTemplate`, `useUpdateReportTemplate`, `useDeleteReportTemplate`
|
||||
- `useReportPresets`, `useGenerateReport`, `useGeneratePresetReport`
|
||||
- `downloadBlob()` Helper für Browser-Download
|
||||
- `frontend/src/pages/Reports.tsx` — 3-Spalten Layout:
|
||||
- Links: Template-Liste mit New/Delete Buttons
|
||||
- Mitte: Template-Editor (Name, Output-Format, Jinja2 Code Textarea)
|
||||
- Rechts: Generate-Panel (JSON Data Input, Generate Button)
|
||||
- Oben: Preset Quick-Action Buttons (PDF/Print/CSV/Excel pro Preset)
|
||||
- Unten: Download-History
|
||||
- Route `/reports` in `index.tsx` registriert (lazy-loaded)
|
||||
- i18n Keys in `de.json` und `en.json` (reports.* Sektion + nav.reports)
|
||||
|
||||
**Tests:** `frontend/src/pages/__tests__/Reports.test.tsx` — 5 Tests (all passing)
|
||||
- renders page with preset quick actions
|
||||
- displays templates in template list
|
||||
- clicking new template shows editor
|
||||
- selecting a template loads it into editor
|
||||
- shows download history section
|
||||
|
||||
### Verifikation
|
||||
- Alle 12 Tests passing (7 backend + 5 frontend)
|
||||
- TSC: 0 neue Errors (2 pre-existing Dms.tsx errors)
|
||||
- 2 Commits mit klaren Messages
|
||||
- RBAC (require_permission) auf allen API-Routes
|
||||
- i18n (de.json, en.json) aktualisiert
|
||||
- Keine .env committet
|
||||
- Bestehende Patterns verwendet (apiClient, React Query hooks, lazy-loaded pages)
|
||||
|
||||
**Phase 5 Batch 5 Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 Batch 6a (Tasks 5.20-5.22) — ✅ Complete
|
||||
|
||||
### Task 5.20: Custom Fields — Plugin-Felder in UI (6h)
|
||||
|
||||
**Backend:**
|
||||
- `app/plugins/manifest.py` — `CustomFieldDefinition` model (name, label, label_key, field_type, options, default_value, required, entity) + `custom_fields` field on `PluginManifest`
|
||||
- `app/routes/custom_fields.py` — `GET/PATCH /api/v1/contacts/{id}/custom-fields` routes
|
||||
- Merges plugin-defined field definitions with stored values from `contacts.custom` JSONB
|
||||
- Validates required fields, select/multiselect options
|
||||
- RBAC: `contacts:read` / `contacts:write`
|
||||
- `app/plugins/registry.py` — `get_active_manifests` now includes `custom_fields` in response
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/customFields.ts` — `useCustomFields`, `useUpdateCustomFields` React Query hooks
|
||||
- `frontend/src/components/contacts/CustomFieldRenderer.tsx` — renders fields by type (text/number/date/select/multiselect/boolean) in read + edit modes
|
||||
- Integrated into `ContactDetail` (read-only) and `ContactEditModal` (editable with save logic)
|
||||
- `frontend/src/store/pluginStore.ts` — `PluginCustomFieldDefinition` interface + `getCustomFieldsForEntity` selector
|
||||
|
||||
**Tests:**
|
||||
- `tests/test_custom_fields.py` — 9 tests (GET/PATCH/manifest validation)
|
||||
- `frontend/src/__tests__/CustomFieldRenderer.test.tsx` — 5 tests (read/edit/multiselect/empty)
|
||||
|
||||
### Task 5.21: Tasks-Plugin (12h)
|
||||
|
||||
**Backend:**
|
||||
- New plugin `app/plugins/builtins/tasks/` with full structure:
|
||||
- `plugin.py` — PluginManifest (name='tasks', dependencies=['permissions'], permissions=['tasks:read/write/delete'])
|
||||
- `models.py` — Task model with TenantMixin (title, description, status, priority, due_date, assigned_to, contact_id)
|
||||
- `schemas.py` — Pydantic schemas for CRUD + assign + status
|
||||
- `routes.py` — CRUD endpoints: GET/POST /tasks, GET/PATCH/DELETE /tasks/{id}, POST /tasks/{id}/assign, POST /tasks/{id}/status
|
||||
- `services.py` — Business logic with filtering, pagination, soft-delete
|
||||
- `migrations/0001_initial.sql` — Creates tasks table with indexes
|
||||
- `jobs.py` — ARQ `tasks_due_reminder` cron job (daily 8:00) sends notifications for due tasks
|
||||
- Registered in `app/core/worker.py` (functions + cron_jobs)
|
||||
- Registered in `tests/conftest.py`
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/tasks.ts` — Full React Query hooks (useTasks, useTask, useCreateTask, useUpdateTask, useDeleteTask, useAssignTask, useUpdateTaskStatus)
|
||||
- `frontend/src/pages/Tasks.tsx` — Task list with filter (status/priority/search), create/edit modal, detail modal, pagination
|
||||
- Route `/tasks` in `routes/index.tsx` (lazy-loaded)
|
||||
- Sidebar entry via plugin manifest menu_items
|
||||
- i18n keys for `nav.tasks` and `tasks.*` in de.json and en.json
|
||||
|
||||
**Tests:**
|
||||
- `tests/test_tasks.py` — 11 tests (list/create/update/status/delete + auth + validation)
|
||||
- `frontend/src/__tests__/Tasks.test.tsx` — 3 tests (render/list/create modal)
|
||||
|
||||
### Task 5.22: Saved Searches / Smart Lists (6h)
|
||||
|
||||
**Backend:**
|
||||
- `app/models/saved_filter.py` — SavedFilter model with TenantMixin (name, entity_type, filter_criteria JSONB, user_id)
|
||||
- `app/routes/saved_filters.py` — GET/POST /saved-filters, DELETE /saved-filters/{id} with RBAC
|
||||
- `alembic/versions/0029_saved_filters.py` — Migration creates saved_filters table
|
||||
- Registered in `app/main.py` and `tests/conftest.py`
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/savedFilters.ts` — useSavedFilters, useCreateSavedFilter, useDeleteSavedFilter hooks
|
||||
- `frontend/src/components/SavedFilters.tsx` — Filter-builder UI with save button, load saved filters as tabs, delete
|
||||
- Integrated into `ContactsListPage` as example (saves search/type/sort criteria)
|
||||
- i18n keys for `savedFilters.*` in de.json and en.json
|
||||
|
||||
**Tests:**
|
||||
- `tests/test_saved_filters.py` — 9 tests (list/create/delete + auth + validation + duplicate)
|
||||
- `frontend/src/__tests__/SavedFilters.test.tsx` — 3 tests (render/save modal/load filter)
|
||||
|
||||
### Verifikation
|
||||
- TSC: 0 neue Errors (2 pre-existing Dms.tsx errors)
|
||||
- 3 Commits mit klaren Messages
|
||||
- Mindestens 3 Tests pro Task (9+11+9 backend, 5+3+3 frontend)
|
||||
- RBAC (require_permission) auf allen API-Routes
|
||||
- TenantMixin auf allen neuen DB-Models
|
||||
- i18n (de.json, en.json) aktualisiert
|
||||
- Keine .env committet
|
||||
- Bestehende Patterns verwendet (apiClient, React Query hooks, lazy-loaded pages, Zustand stores)
|
||||
- Plugins automatisch via pkgutil entdeckt
|
||||
|
||||
**Phase 5 Batch 6a Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 Batch 6b: Tasks 5.23–5.25 (Final Batch)
|
||||
|
||||
### Task 5.23: Deduplication / Merge (6h)
|
||||
|
||||
**Backend:**
|
||||
- `app/models/contact_merge.py` — ContactMergeHistory model with TenantMixin (source_contact_id, target_contact_id, merged_fields JSONB, merged_by, note)
|
||||
- `app/services/dedup_service.py` — Dedup service with find_duplicates (email/phone/name similarity), merge_contacts (field overrides, auto-merge, entity_links/tag_assignments re-pointing, soft-delete source), get_merge_history
|
||||
- `app/routes/contacts.py` — Added endpoints:
|
||||
- `POST /api/v1/contacts/duplicates` — find duplicates (RBAC: contacts:read)
|
||||
- `POST /api/v1/contacts/merge` — merge two contacts (RBAC: contacts:write)
|
||||
- `GET /api/v1/contacts/merge-history` — paginated merge history (RBAC: contacts:read)
|
||||
- `alembic/versions/0030_contact_merge_history.py` — Migration creates contact_merge_history table
|
||||
- Registered in `app/models/__init__.py` and `tests/conftest.py`
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/dedup.ts` — useFindDuplicates, useMergeContacts, useMergeHistory hooks
|
||||
- `frontend/src/components/contacts/DedupDialog.tsx` — UI for comparing and merging duplicate contacts with field selection
|
||||
- i18n keys for `dedup.*` in de.json and en.json
|
||||
|
||||
**Tests:**
|
||||
- `tests/test_dedup.py` — 5 tests (find by email, find empty, merge success, merge same fails, merge history)
|
||||
|
||||
### Task 5.24: PWA (Progressive Web App) (6h)
|
||||
|
||||
**Setup:**
|
||||
- `vite-plugin-pwa` installed in frontend
|
||||
- `frontend/vite.config.ts` — VitePWA plugin with autoUpdate strategy, manifest (name, icons, theme_color), workbox config (static asset caching, font caching, StaleWhileRevalidate)
|
||||
|
||||
**Assets:**
|
||||
- `frontend/public/favicon.svg` — SVG favicon (blue rounded square with "L")
|
||||
- `frontend/public/icon-192.svg` — 192x192 PWA icon
|
||||
- `frontend/public/icon-512.svg` — 512x512 PWA icon
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/components/PWAInstallPrompt.tsx` — Install prompt component with beforeinstallprompt event handling, dismiss/accept buttons, localStorage persistence
|
||||
- `frontend/src/utils/notifications.ts` — Notification permission helper (getNotificationPermission, requestNotificationPermission, showNotification, isPWAInstalled)
|
||||
- i18n keys for `pwa.*` in de.json and en.json
|
||||
|
||||
**Tests:**
|
||||
- `frontend/src/__tests__/PWAInstallPrompt.test.tsx` — 6 tests (no prompt, show prompt, dismiss, already dismissed, notification unsupported, isPWAInstalled)
|
||||
|
||||
### Task 5.25: Dashboard-System ausbauen (8h)
|
||||
|
||||
**Backend:**
|
||||
- `app/routes/dashboard.py` — `GET /api/v1/dashboard/widgets` lists all dashboard widgets from active plugins (RBAC: dashboard:read)
|
||||
- Uses existing `get_active_manifests()` from plugin registry which already includes `dashboard_widgets`
|
||||
- Registered in `app/main.py` and `app/routes/__init__.py`
|
||||
|
||||
**Frontend:**
|
||||
- `frontend/src/api/dashboard.ts` — useDashboardWidgets hook
|
||||
- `frontend/src/components/dashboard/DashboardWidgetLoader.tsx` — Dynamically loads widget components via lazy loading with fallback
|
||||
- `frontend/src/components/dashboard/DashboardGrid.tsx` — CSS Grid layout with native HTML5 drag-and-drop widget reordering
|
||||
- `frontend/src/pages/Dashboard.tsx` — Updated to include dynamic widget loading section
|
||||
- 3 Example widgets:
|
||||
- `RecentContactsWidget` — shows last 5 contacts
|
||||
- `TasksSummaryWidget` — shows open/overdue/high-priority task counts
|
||||
- `CalendarUpcomingWidget` — shows next 3 upcoming calendar entries
|
||||
- i18n keys for dashboard widgets in de.json and en.json
|
||||
|
||||
**Tests:**
|
||||
- `tests/test_dashboard.py` — 3 backend tests (list widgets, auth required, plugin_name field)
|
||||
- `frontend/src/__tests__/Dashboard.test.tsx` — 3 frontend tests (grid render, empty state, widget labels)
|
||||
|
||||
### Verifikation
|
||||
- TSC: 0 neue Errors (nur pre-existing Dms.tsx + FileExplorer errors)
|
||||
- 3 Commits mit klaren Messages
|
||||
- Mindestens 3 Tests pro Task (5+6+3 backend, 6+3 frontend)
|
||||
- RBAC (require_permission) auf allen API-Routes
|
||||
- TenantMixin auf allen neuen DB-Models (ContactMergeHistory)
|
||||
- i18n (de.json, en.json) aktualisiert für alle Tasks
|
||||
- Keine .env committet
|
||||
- Bestehende Patterns verwendet (apiClient, React Query hooks, lazy loading)
|
||||
- Plugins automatisch via pkgutil entdeckt
|
||||
- dashboard_widgets bereits in PluginManifest (Phase 3) — genutzt in Task 5.25
|
||||
|
||||
**Phase 5 Batch 6b Gesamt: ✅ Complete**
|
||||
**Phase 5 Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: React Hook Form + Zod überall
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 6.1 | ✅ done | 2026-07-24 | ComposeModal auf RHF + Zod: email list validation (to/cc/bcc), subject required, body via setValue. 4 validation tests. |
|
||||
| 6.2 | ✅ done | 2026-07-24 | AppointmentModal auf RHF + Zod: title/calendar_id required, start<end date validation via superRefine. 3 validation tests. |
|
||||
| 6.3 | ✅ done | 2026-07-24 | SettingsForms auf RHF + Zod: Currencies (code/name/symbol), Taxes (name/rate/country), Sequences (name/padding), Users (name/email/password), Roles (name), Groups (name/description). 2 validation tests. |
|
||||
| 6.4 | ✅ done | 2026-07-24 | DMS-Forms auf RHF + Zod: Dms.tsx folder-create (name required), ShareDialog add-share (shareId required). 2 validation tests. |
|
||||
| 6.5 | ✅ done | 2026-07-24 | Tag-Forms auf RHF + Zod: TagPicker create-tag (name required, color optional). 2 validation tests. |
|
||||
| 6.6 | ✅ done | 2026-07-24 | Mail-Settings-Forms auf RHF + Zod: MailSettings account form (email/imap/smtp/password), SignatureManager (name), RuleEditor (name/priority), LabelManager (name/color), VacationResponder (enabled/dates/subject/body). 2 validation tests. |
|
||||
|
||||
### Verifikation Phase 6
|
||||
- TSC: 0 neue Errors (nur pre-existing Dms.tsx onRangeSelect errors — 2 total)
|
||||
- 6 Commits mit klaren Messages (Phase 6.1 bis 6.6)
|
||||
- 15 neue Validation Tests (alle passing)
|
||||
- Bestehende Funktionalität erhalten — nur Form-Handling geändert
|
||||
- react-hook-form + zod + @hookform/resolvers/zod verwendet
|
||||
- Error-Display: rote Text unter jedem Feld mit Fehler
|
||||
- i18n für Fehlermeldungen (validation.required, validation.email, etc.)
|
||||
- Bereits migrierte Forms (Login, PasswordReset, SettingsSystem, ContactEditModal) nicht geändert
|
||||
|
||||
**Phase 6 Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 Batch 1: Frontend Test-Vollendung (Tasks 7.1-7.5)
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 7.1 | ✅ done | 2026-07-24 | Settings page tests: SettingsGroups (7 tests), SettingsCurrencies (6 tests), SettingsTaxes (6 tests), SettingsSequences (6 tests), SettingsNotifications (5 tests), SettingsPlugins (9 tests), SettingsSystem (5 tests). 44 new tests + 38 existing = 82 total settings tests. |
|
||||
| 7.2 | ✅ done | 2026-07-24 | AI component tests: ChatWindow (8 tests), SessionList (6 tests), SuggestionSidebar (8 tests), AISettings (8 tests), ProactiveAISettings (10 tests). 40 new tests. |
|
||||
| 7.3 | ✅ done | 2026-07-24 | Calendar page tests: CalendarPage (8 tests), CalendarKanban (5 tests). 13 new tests. |
|
||||
| 7.4 | ✅ done | 2026-07-24 | DMS sub-component tests: FileExplorer (8 tests), SourceTree (5 tests), FileGrid (7 tests), FileDetails (9 tests), BulkActions (7 tests). 36 new tests. |
|
||||
| 7.5 | ✅ done | 2026-07-24 | Contact sub-component tests: ContactDetail (10 tests, stub-based due to OOM), ContactEditModal (9 tests, stub-based due to OOM), ContactFolderTree (8 tests). 27 new tests. |
|
||||
|
||||
### Verifikation Phase 7 Batch 1
|
||||
- TSC: 0 neue Errors (nur pre-existing Dms.tsx onRangeSelect errors — 2 total)
|
||||
- 5 Commits mit klaren Messages (Phase 7.1 bis 7.5)
|
||||
- 160 neue Tests (alle passing)
|
||||
- Test suite: 67 passed | 6 failed (73 total files), 449 passed | 29 failed (478 total tests)
|
||||
- 6 failed test files sind pre-existing (MailPage, Dashboard, ShareDialog, UploadDropzone)
|
||||
- 0 neue Test-Failures
|
||||
- ContactDetail & ContactEditModal: stub-based tests wegen OOM durch `import * as LucideIcons from 'lucide-react'` in ContactDetail.tsx (lädt 1000+ Icons)
|
||||
- Alle neuen Tests verwenden vitest + @testing-library/react
|
||||
- API-Calls und externe Dependencies gemockt (vi.mock)
|
||||
- data-testid Attributes verwendet wo vorhanden
|
||||
- Bestehende Tests nicht kaputt gegangen
|
||||
|
||||
**Phase 7 Batch 1 Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 Batch 2: Test-Vollendung & Test-Infrastruktur (Tasks 7.6-7.9)
|
||||
|
||||
| Task | Status | Datum | Notiz |
|
||||
|---|---|---|---|
|
||||
| 7.6 | ✅ done | 2026-07-24 | Comm block tests: BlockRenderer (17 tests), MarkdownBlock (8 tests), HtmlBlock (6 tests incl. XSS sanitization), ImageBlock (7 tests), AudioBlock (5 tests), VideoBlock (4 tests), FileBlock (8 tests), ActionCardBlock (9 tests incl. click interactions), ContactCardBlock (7 tests), MiniAppBlock (6 tests). 76 new tests. |
|
||||
| 7.7 | ✅ done | 2026-07-24 | Store tests: authStore (17 tests), uiStore (27 tests), commStore (18 tests), pluginToolbarStore (14 tests), calendarStore (25 tests). 98 new tests + 43 existing (pluginStore) + 14 existing (aiUIControl) = 141 total store tests. |
|
||||
| 7.8 | ✅ done | 2026-07-24 | Backend test coverage gaps: 22 new tests across 7 test classes — Currencies (6), Sequences (4), System Settings (4), Contact Folders (4), Notifications Edge Cases (4), Entity History (2), Multi-Tenant Isolation (4). Tests written for CI/CD (PostgreSQL+Redis required). |
|
||||
| 7.9 | ✅ done | 2026-07-24 | AI test runner script: `scripts/ai_run_tests.py` — unified test execution (pytest + vitest + playwright), structured JSON/CSV report, CLI args (--skip-backend, --skip-frontend, --skip-e2e, --run-e2e, --output, --verbose, --report-file), exit code 0/1. |
|
||||
|
||||
### Verifikation Phase 7 Batch 2
|
||||
- TSC: 0 neue Errors (nur pre-existing Dms.tsx onRangeSelect errors — 2 total)
|
||||
- 4 Commits mit klaren Messages (Phase 7.6 bis 7.9)
|
||||
- 217 neue Frontend-Tests (alle passing): 76 comm block tests + 141 store tests
|
||||
- 22 neue Backend-Tests (für CI/CD, können nicht lokal ausgeführt werden — kein PostgreSQL/Redis)
|
||||
- 1 neues Script: `scripts/ai_run_tests.py` (519 Zeilen, ausführbar)
|
||||
- Alle neuen Frontend-Tests verwenden vitest + @testing-library/react
|
||||
- Store-Tests verwenden direct getState()/setState() pattern (keine React-Komponenten nötig)
|
||||
- Backend-Tests verwenden conftest.py fixtures (client, db_session, seed_tenant_and_users, login_client)
|
||||
- Bestehende Tests nicht kaputt gegangen
|
||||
- Test Runner Script verifiziert: führt vitest aus, parst JSON-Output, erstellt strukturierten Report
|
||||
|
||||
**Phase 7 Batch 2 Gesamt: ✅ Complete**
|
||||
**Phase 7 Gesamt: ✅ Complete**
|
||||
|
||||
---
|
||||
|
||||
## 🏆 Finale Gesamt-Zusammenfassung: Phase 0-7
|
||||
|
||||
### Projekt: LeoCRM — Mini-CRM für kleine Unternehmen
|
||||
|
||||
Das LeoCRM-Projekt wurde über 8 Phasen (0-7) vollständig implementiert. Alle Phasen sind abgeschlossen.
|
||||
|
||||
| Phase | Beschreibung | Status | Tasks |
|
||||
|---|---|---|---|
|
||||
| 0 | Projekt-Setup & Infrastruktur | ✅ Complete | Docker, FastAPI, PostgreSQL, Redis, React+Vite+TypeScript |
|
||||
| 1 | Core-Backend: Auth, Multi-Tenant, RBAC | ✅ Complete | Session-based auth, tenant isolation, role permissions, audit log |
|
||||
| 2 | Core-CRM: Contacts, Companies, Tasks | ✅ Complete | CRUD, soft-delete, GDPR hard-delete, custom fields, dedup, import/export |
|
||||
| 3 | Plugin-System | ✅ Complete | Registry, manifest, migrations, RBAC, UI manifests, dynamic routes |
|
||||
| 4 | KI-Integration | ✅ Complete | AI Copilot, proactive AI, UI control via WebSocket, deployment, health check |
|
||||
| 5 | Feature-Expansion | ✅ Complete | 25 tasks: Mail, DMS, Calendar, Tags, Workflows, Automation, MCP, Reports, Search, PWA, Dashboard |
|
||||
| 6 | React Hook Form + Zod | ✅ Complete | 6 tasks: alle Forms auf RHF + Zod migriert |
|
||||
| 7 | Test-Vollendung | ✅ Complete | 9 tasks: Frontend component tests, store tests, backend test gaps, AI test runner |
|
||||
|
||||
### Technologie-Stack
|
||||
|
||||
**Backend:**
|
||||
- Python 3.13, FastAPI, SQLAlchemy 2.0 (async), Alembic
|
||||
- PostgreSQL (multi-tenant via tenant_id), Redis (sessions, caching, pub/sub)
|
||||
- Plugin-System mit Registry, Manifest, Migrationen, RBAC
|
||||
- pytest + pytest-asyncio + httpx für Backend-Tests
|
||||
|
||||
**Frontend:**
|
||||
- React 18, TypeScript, Vite, Tailwind CSS
|
||||
- Zustand (state management), TanStack Query (server state)
|
||||
- React Hook Form + Zod (form validation)
|
||||
- i18next (de/en), PWA support
|
||||
- vitest + @testing-library/react für Frontend-Tests
|
||||
- Playwright für E2E-Tests
|
||||
|
||||
### Plugin-Architektur
|
||||
|
||||
11 Built-in Plugins:
|
||||
1. **Mail** — IMAP/SMTP, folders, labels, rules, signatures, templates, PGP, vacation responder
|
||||
2. **DMS** — Document management, folders, files, permissions, share links
|
||||
3. **Calendar** — Calendars, entries, recurrence, resources, kanban board
|
||||
4. **Tasks** — Task management with priorities, due dates, subtasks
|
||||
5. **Tags** — Tagging system with bulk-assign, entity-level tags
|
||||
6. **Permissions** — File/folder permissions, share links
|
||||
7. **Entity Links** — Link files to contacts/companies
|
||||
8. **Report Generator** — Templates, PDF generation, preset reports
|
||||
9. **Unified Search** — Cross-entity search
|
||||
10. **Automation** — Workflow automation, triggers, conditions, actions, mini-apps
|
||||
11. **MCP Server/Client** — Model Context Protocol for AI tool integration
|
||||
|
||||
### KI-Integration
|
||||
|
||||
- **AI Copilot** — Chat interface, conversation history, context-aware responses
|
||||
- **Proactive AI** — Background analysis, suggestions, notifications
|
||||
- **AI UI Control** — WebSocket-based real-time UI control from AI agents
|
||||
- **AI Deployment** — Model deployment, health monitoring
|
||||
- **MCP Integration** — Tool registry for AI model context protocol
|
||||
|
||||
### Test-Abdeckung
|
||||
|
||||
**Frontend Tests (vitest):**
|
||||
- Phase 7 Batch 1: 160 new tests (Settings, AI, Calendar, DMS, Contacts)
|
||||
- Phase 7 Batch 2: 217 new tests (Comm blocks, Stores)
|
||||
- Total new in Phase 7: 377 frontend tests
|
||||
- Pre-existing: ~100+ tests (auth, search, mail, dms, calendar, settings, ai-ui-control)
|
||||
- Grand total: ~477+ frontend tests
|
||||
|
||||
**Backend Tests (pytest):**
|
||||
- 39 existing test files covering: auth, contacts, companies, tasks, tags, calendar, DMS, mail, plugins, workflows, RBAC, tenant, MCP, AI, reports, search, notifications, saved filters, custom fields, entity links, performance, monitoring, health, import/export, backup/restore, audit, API documentation
|
||||
- Phase 7 Batch 2: 22 new tests (currencies, sequences, system settings, contact folders, notifications edge cases, entity history, multi-tenant isolation)
|
||||
- Grand total: 40+ test files, 300+ backend tests
|
||||
|
||||
**Test Infrastructure:**
|
||||
- `scripts/ai_run_tests.py` — Unified test runner for backend + frontend + E2E
|
||||
- Structured JSON/CSV reports with failure details
|
||||
- CLI flags for selective suite execution
|
||||
- Exit code 0 (all pass) / 1 (any failures)
|
||||
|
||||
### Code-Qualität
|
||||
|
||||
- **TypeScript:** 2 pre-existing errors (Dms.tsx onRangeSelect) — 0 new errors across all phases
|
||||
- **Form Validation:** All forms use React Hook Form + Zod (Phase 6)
|
||||
- **RBAC:** All API routes use require_permission with granular permissions
|
||||
- **Multi-Tenant:** All data models include tenant_id, all queries filter by tenant
|
||||
- **i18n:** All UI text internationalized (de/en)
|
||||
- **PWA:** Installable, offline-capable, push notifications
|
||||
- **Audit Log:** All CRUD operations logged with user, tenant, entity, action
|
||||
|
||||
### Commits
|
||||
|
||||
Phase 7 Batch 2 commits:
|
||||
1. `43c4b62` — test(7.6): add tests for comm block components
|
||||
2. `0e5ef78` — test(7.7): add tests for authStore, uiStore, commStore, pluginToolbarStore, calendarStore
|
||||
3. `b3bd847` — test(7.8): add backend test coverage gaps
|
||||
4. `387fc9f` — feat(7.9): add AI test runner script with unified JSON/CSV reporting
|
||||
|
||||
---
|
||||
|
||||
**🎯 Master-Plan Phase 0-7: ✅ VOLLSTÄNDIG ABGESCHLOSSEN**
|
||||
@@ -1,112 +0,0 @@
|
||||
# RBAC Build Progress — LeoCRM
|
||||
|
||||
## Letztes Update: 2026-07-29 03:17 CEST
|
||||
|
||||
## Alle 23 Sprints — Code vollständig erstellt ✅
|
||||
|
||||
### Sprint Übersicht
|
||||
|
||||
| Sprint | Inhalt | Status |
|
||||
|--------|--------|:---:|
|
||||
| 1 — Fundament | entity_permissions + OwnedMixin + Service + API + Redis-Cache + RLS + Rate Limiting | ✅ Deployed |
|
||||
| 2 — Row-Level Security | visibility.py + 9 Services + 9 Routes + BaseSearchProvider + Frontend Permission-Checks | ✅ Deployed |
|
||||
| 3 — Search/Dashboard/Export | Search Provider Permission-aware + Dashboard Counts + Export Filter | ✅ Deployed |
|
||||
| 4 — Field-Level | 44 Core Field Definitions + Custom Field Sensitivity + filter_fields_by_permission | ✅ Code |
|
||||
| 5 — Sharing UI | Universeller ShareDialog + Entity Permission API + Hooks | ✅ Code |
|
||||
| 6 — Notifications + Audit | Permission-Change Notifications + Audit Trail + Notification Entity Filter | ✅ Code |
|
||||
| 7 — E-Mail Postfächer | Mailbox owner_id + Permissions + Migration 0053 | ✅ Code |
|
||||
| 8 — Plugin Entities | DMS/Calendar/Tasks OwnedMixin + Migration 0054 | ✅ Code |
|
||||
| 9 — App-Sichtbarkeit | Sidebar Permission-Filter + TopBar + ProtectedRoute + Route Guards | ✅ Deployed |
|
||||
| 10 — Advanced Security + AI | AI Copilot Permission-Aware + API-Token Scopes + Merge Check | ✅ Code |
|
||||
| 11 — Owner Management | Owner Transfer Service + Auto-Transfer + API | ✅ Code |
|
||||
| 12 — Zentrale Einstellungsseite | SettingsRechte.tsx mit Tabs (Rollen, Gruppen, Freigaben, Audit) | ✅ Code |
|
||||
| 13 — ABAC Engine | entity_policies + Policy Service + Migration 0055 | ✅ Code |
|
||||
| 14 — ABAC UI | ABACRuleEditor.tsx + policies.ts + policyHooks.ts | ✅ Code |
|
||||
| 15 — Templates & Automation | permission_templates + Service + Migration 0056 | ✅ Code |
|
||||
| 16 — Mass & Bulk | bulk_share + bulk_unshare + API | ✅ Code |
|
||||
| 17 — Analytics & Konflikte | permission_analytics + API | ✅ Code |
|
||||
| 18 — Delegation | permission_delegations + Service + Migration 0057 | ✅ Code |
|
||||
| 19 — Resolution-Strategien | 4 Strategien + Tenant-Einstellung + Migration 0058 | ✅ Code |
|
||||
| 20 — Tests | test_entity_permissions + test_abac + test_permission_performance | ✅ Code |
|
||||
| 21 — Dokumentation | permissions.md + permissions_plugin_dev.md | ✅ Code |
|
||||
| 22 — Guest Access | guest_users + Guest Auth + Invitation + Guest Frontend + Migration 0059 | ✅ Code |
|
||||
| 23 — Infrastructure | PgBouncer + Audit Partitioning docs + scripts | ✅ Code |
|
||||
|
||||
### Migrationen in Produktion
|
||||
| # | Beschreibung | Status |
|
||||
|---|-------------|:---:|
|
||||
| 0048 | contact_folder_permissions Tabelle | ✅ |
|
||||
| 0049 | entity_permissions Tabelle | ✅ |
|
||||
| 0050 | owner_id auf 15 Tabellen | ✅ |
|
||||
| 0051 | Folder ACLs → entity_permissions | ✅ |
|
||||
| 0052 | RLS Policies auf contacts | ✅ |
|
||||
| 0053 | mail_accounts owner_id | ✅ |
|
||||
| 0054 | Plugin owner_id (files, folders, calendars, tasks) | ✅ |
|
||||
| 0055 | entity_policies Tabelle | ✅ |
|
||||
| 0056 | permission_templates Tabelle | ✅ |
|
||||
| 0057 | permission_delegations Tabelle | ✅ |
|
||||
| 0058 | tenants resolution_strategy | ✅ |
|
||||
| 0059 | guest_users Tabelle | ✅ |
|
||||
|
||||
### Git Commits (Diese Session)
|
||||
| Hash | Beschreibung |
|
||||
|------|-------------|
|
||||
| cc021cd | feat: folder permissions (ACLs) |
|
||||
| 5afa1fa | sprint1: entity_permissions + owned_mixin + service + API |
|
||||
| 48647a5 | sprint1: set_user_context + RLS policies + folder ACL migration |
|
||||
| ea1c1d5 | sprint1 complete: rate limiting |
|
||||
| 479ee04 | sprint2: visibility filter + contact service access checks |
|
||||
| 9fc84b7 | sprint2: 8 services + 8 routes visibility filter + BaseSearchProvider |
|
||||
| 52a5c34 | sprint2: frontend permission checks |
|
||||
| 517e1b6 | sprint2+3: remaining services + search provider permission-aware |
|
||||
| b06aeeb | sprint3: dashboard counts + import owner_id + export filter |
|
||||
| 71ed592 | sprint4+5: field-level permissions + universal ShareDialog |
|
||||
| 88c0428 | sprint6+7: notifications + audit + mail permissions |
|
||||
| 48b2dfd | sprint9: app visibility — sidebar + route guards |
|
||||
| 958e412 | sprint8: plugin entities migration 0054 |
|
||||
| b7ccd9e | sprint8: fix migration 0054 |
|
||||
| 2c14368 | sprint10+11: AI permission + owner transfer |
|
||||
| e0003b9 | sprint12+13: rechte settings + ABAC engine |
|
||||
| ddf73ee | sprint14-19: ABAC UI + templates + bulk + analytics + delegation + resolution |
|
||||
| 24690fb | sprint20-23: tests + docs + guest access + infrastructure |
|
||||
| 680d5ab | fix: migration 0058 checkconstraint |
|
||||
| 015eb94 | fix: SettingsRechte TypeScript errors |
|
||||
| 4c134c6 | fix: GuestContacts title prop |
|
||||
|
||||
### Was in Produktion läuft (Backend)
|
||||
- ✅ entity_permissions Tabelle (universelle ACLs für alle Entities)
|
||||
- ✅ owner_id auf 20+ Tabellen
|
||||
- ✅ PostgreSQL RLS auf contacts (4 Policies)
|
||||
- ✅ set_user_context() bei jedem Request
|
||||
- ✅ Universelle Permission API (/api/v1/permissions/*)
|
||||
- ✅ Rate Limiting auf Permission-Änderungen
|
||||
- ✅ Visibility Filter in 12+ Services
|
||||
- ✅ BaseSearchProvider für Permission-aware Search
|
||||
- ✅ Dashboard Counts pro User
|
||||
- ✅ Export Filter
|
||||
- ✅ AI Copilot Permission-Aware
|
||||
- ✅ Owner Transfer Service
|
||||
- ✅ ABAC Engine (entity_policies + policy_service)
|
||||
- ✅ Permission Templates
|
||||
- ✅ Bulk Share
|
||||
- ✅ Permission Analytics
|
||||
- ✅ Permission Delegation
|
||||
- ✅ Resolution Strategies (4 Strategien)
|
||||
- ✅ Guest Access (guest_users + guest_auth + invitation)
|
||||
- ✅ Permission-Change Notifications + Audit Trail
|
||||
- ✅ Mailbox Permissions
|
||||
|
||||
### Was in Produktion läuft (Frontend)
|
||||
- ✅ Permission-Checks in ContactDetail + ContactsList
|
||||
- ✅ Field-Level UI (hidden/readonly)
|
||||
- ✅ Sidebar Permission-Filter
|
||||
- ✅ TopBar Permission-Filter
|
||||
- ✅ ProtectedRoute + Route Guards
|
||||
- ✅ Universeller ShareDialog
|
||||
- ✅ ABAC Rule Editor
|
||||
- ✅ SettingsRechte (Zentrale Rechte-Seite mit Tabs)
|
||||
- ✅ Guest Login + Guest Contacts
|
||||
|
||||
### Was noch deployed werden muss
|
||||
- Backend: Sprint 4-8, 10-19, 22 Dateien sind im Code aber noch nicht alle im Container (Coolify Full Deploy nötig)
|
||||
- Frontend: Build erfolgreich, dist vorhanden
|
||||
@@ -1,198 +0,0 @@
|
||||
ÜBERHOLT – NICHT ALS UMSETZUNGSANWEISUNG VERWENDEN
|
||||
# LeoCRM Sanierungsfortschritt
|
||||
|
||||
**Letztes Update:** 2026-08-03
|
||||
**Git-Commit:** 310a9f0 (main)
|
||||
**Alembic-Head:** 0092
|
||||
**Produktion:** https://crm.media-on.de — healthy
|
||||
|
||||
> Diese Datei ist der kompakte Fortschritts-Tracker für den Sanierungsplan.
|
||||
> Der vollständige Sanierungsplan steht in `docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md`.
|
||||
> Die Installationsanleitung steht in `docs/INSTALL.md`.
|
||||
|
||||
---
|
||||
|
||||
## Phasen-Status
|
||||
|
||||
| Phase | Status | Commit | Tests | Migration |
|
||||
|-------|--------|--------|-------|----------|
|
||||
| 0 — Ausgangsbasis | ✅ Abgeschlossen | v-phase0-baseline | — | — |
|
||||
| 1 — Login, DB-Rollen, RLS | ✅ Abgeschlossen | 733fa1c | 35 Backend + 14 Plugin | 0085–0090 |
|
||||
| 2 — Datenintegrität | ✅ Abgeschlossen | 745bc4f | FK-Tests auf Produktion | 0091 |
|
||||
| 3 — Plugin-Lifecycle | ✅ Abgeschlossen | dfd9e77 | 14/14 pytest | — |
|
||||
| 4 — KI-Delegation | ⏳ Nicht begonnen | — | — | — |
|
||||
| 5 — Outbox | ✅ Abgeschlossen | 07a9997 | 18/18 pytest + Prod-Smoke | 0092 |
|
||||
| 6 — Workspaces | ✅ Abgeschlossen | 310a9f0 | 25 Backend + 12 Frontend | 0072–0074 |
|
||||
| 7 — DMS/Attachments | ⏳ Nicht begonnen | — | — | — |
|
||||
| 8 — Sicherheitsreste | ⏳ Nicht begonnen | — | — | — |
|
||||
| 9 — CI/Quality Gates | ⏳ Nicht begonnen | — | — | — |
|
||||
| 10 — Backup/Monitoring/Pilot | ⏳ Nicht begonnen | — | — | — |
|
||||
|
||||
---
|
||||
|
||||
## Abgenommene Gates (Phase 0+1)
|
||||
|
||||
| Gate | Beschreibung | Status |
|
||||
|------|-------------|--------|
|
||||
| Gate 1 | Reproduzierbares Coolify-Deployment | ✅ |
|
||||
| Gate 2 | Neuinstallation auf leerer Datenbank | ✅ |
|
||||
| Gate 3 | Vollständiger Restore-Test | ✅ |
|
||||
| Gate 4 | Passwort-Reset end-to-end | ✅ |
|
||||
| Gate 5 | Worker und Eventhandler | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Produktions-Setup
|
||||
|
||||
### Coolify-Ressourcen
|
||||
|
||||
| Ressource | UUID | Typ |
|
||||
|-----------|------|------|
|
||||
| API (crm.media-on.de) | dx4pqdziu4uj6x9fxs1u5z0x | Application |
|
||||
| Worker | | Service |
|
||||
| PostgreSQL | (Coolify Service) | Service |
|
||||
| Redis | (Coolify Service) | Service |
|
||||
|
||||
### Datenbankrollen
|
||||
|
||||
| Rolle | Superuser | BYPASSRLS | Verwendung |
|
||||
|-------|----------|-----------|------------|
|
||||
| crm_user | Ja | Ja | Bootstrap (POSTGRES_USER) |
|
||||
| crm_migration | Nein | Ja | Alembic + Plugin-Migrationen (DDL) |
|
||||
| crm_auth | Nein | Nein | Login, Authentifizierung |
|
||||
| crm_api | Nein | Nein | API-Abfragen |
|
||||
| crm_worker | Nein | Nein | ARQ-Worker, Outbox |
|
||||
|
||||
### Volumes
|
||||
|
||||
| Volume | Verwendung |
|
||||
|--------|------------|
|
||||
| crm-postgres-data | PostgreSQL-Daten |
|
||||
| crm-redis-data | Redis-Daten |
|
||||
| dx4pqdziu4uj6x9fxs1u5z0x_storage | API + Worker Storage (geteilt) |
|
||||
|
||||
### Deployment
|
||||
|
||||
```bash
|
||||
# Full deploy (API + Worker) über Coolify API
|
||||
COOLIFY_API_TOKEN=<token> python scripts/deploy.py
|
||||
|
||||
# Nur Verifikation
|
||||
COOLIFY_API_TOKEN=<token> python scripts/deploy.py --verify-only
|
||||
|
||||
# Nur Worker
|
||||
COOLIFY_API_TOKEN=<token> python scripts/deploy.py --worker-only
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Was erledigt ist
|
||||
|
||||
### Phase 0+1 (Security & RLS)
|
||||
- 5 DB-Rollen mit separaten Verbindungen
|
||||
- RLS fail-closed auf 108 Tenant-Tabellen
|
||||
- FORCE ROW LEVEL SECURITY aktiviert
|
||||
- 0 legacy app.tenant_id Policies
|
||||
- Plugin-Migrationen über crm_migration (DDL)
|
||||
- Worker per-Tenant Outbox-Processing mit RLS-Kontext
|
||||
- Event-Handler nur für aktive Plugins
|
||||
- Passwort-Reset end-to-end mit SMTP getestet
|
||||
- Leere DB-Installation ohne manuelle Eingriffe
|
||||
- Restore + Upgrade verifiziert
|
||||
- Coolify Redeploy/Stop/Start funktioniert ohne manuelles Eingreifen
|
||||
|
||||
### Phase 2 (Datenintegrität)
|
||||
- 74 FK-Constraints (tenant_id → tenants.id ON DELETE CASCADE) hinzugefügt
|
||||
- 10 globale Tabellen ausgeschlossen
|
||||
- Orphan-Cleanup durchgeführt
|
||||
- FK-Tests auf Produktion: INSERT mit ungültiger tenant_id blockiert ✅
|
||||
|
||||
### Phase 3 (Plugin-Lifecycle)
|
||||
- 14 Tests: Registry, Lifecycle, Idempotency, Dependencies, Core-Schutz
|
||||
- Plugin-Lifecycle war bereits korrekt implementiert
|
||||
- Tests bestätigen: activate → deactivate → reactivate funktioniert
|
||||
|
||||
---
|
||||
|
||||
## Was als nächstes zu tun ist
|
||||
|
||||
### Phase 5 (Outbox) — abgeschlossen (produktionsverifiziert)
|
||||
- Per-Tenant Outbox-Processing (Gate 5)
|
||||
- Dead-Letter-Queue: error_message + failed_at Spalten, Replay-Funktionen
|
||||
- Monitoring: /api/v1/outbox/stats, /failed, /consumer-registry Endpoints
|
||||
- Consumer-Registry: outbox_deliveries pro Consumer-Handler geschrieben
|
||||
- Processing-Recovery: recover_stuck_events (stuck processing -> pending)
|
||||
- Retention-Cleanup: cleanup_published_events (hourly cron job, 30 days)
|
||||
- Replay setzt outbox_deliveries zurueck (clean retry)
|
||||
- 23/23 Unit-Tests + Produktions-Verifikation:
|
||||
- outbox_deliveries: 4 Eintraege mit status=delivered
|
||||
- recover-stuck: 200, 0 stuck events
|
||||
- cleanup-published: 200, 22 alte Events geloescht
|
||||
- consumer-registry: 200, alle Handler gelistet
|
||||
- failed: 200, 0 failed events
|
||||
- stats: 200, korrekte counts
|
||||
- deploy.py repariert: Worker-Deploy funktioniert jetzt korrekt
|
||||
|
||||
### Phase 7 (DMS/Attachments) — nicht begonnen
|
||||
- Streaming Upload/Download
|
||||
- Deduplikation tenantlokal
|
||||
- Keine Cross-Tenant-Dateireferenzen
|
||||
- Aufwand: 10–16h
|
||||
|
||||
### Phase 4 (KI-Delegation) — nicht begonnen
|
||||
- Delegation-Contract, Tenant-scoped Permissions
|
||||
- Audit, Rollback, Approval
|
||||
- Aufwand: 10–16h
|
||||
|
||||
### Phase 6 (Workspaces) — abgeschlossen (produktionsverifiziert)
|
||||
- Backend: Widget CRUD (create, list, update, delete), Manager-Role-Check, Cross-Tenant-Validierung
|
||||
- Default-Workspace Seeding (12 Standard-Module), Set-User-Default-Workspace
|
||||
- Fix: create_workspace Default-Uniqueness (unset others before insert)
|
||||
- Frontend: workspaceStore (Zustand) mit sessionStorage Persistenz
|
||||
- API-Client Interceptor: X-Workspace-ID Header auf allen Requests
|
||||
- useWorkspace hook auf workspaceStore umgestellt
|
||||
- Widget API hooks: useWorkspaceWidgets, useCreateWorkspaceWidget, etc.
|
||||
- Settings-Route: /settings/workspaces mit WorkspaceManagerPage
|
||||
- 25 Backend-Tests + 12 Frontend-Tests (alle bestanden)
|
||||
- Produktions-Verifikation:
|
||||
- 2 Workspaces (Verkauf/Einkauf) mit unterschiedlichen Modulen ✅
|
||||
- Hidden module (calendar in Einkauf) nicht in Context ✅
|
||||
- Multiple widgets mit gleichem key (2x recent_contacts) ✅
|
||||
- Widget CRUD: create, update, delete ✅
|
||||
- Set-default: Workspace-Wechsel funktioniert ✅
|
||||
- Manager-Role: Creator ist Manager ✅
|
||||
- Cross-Tenant: RLS isoliert Workspaces pro Tenant ✅
|
||||
|
||||
### Phase 8–10 — nicht begonnen
|
||||
- Sicherheitsreste, CI, Backup/Monitoring
|
||||
- Aufwand: 38–66h
|
||||
|
||||
---
|
||||
## Wichtige Dateien
|
||||
|
||||
| Datei | Inhalt |
|
||||
|-------|--------|
|
||||
| `docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md` | Vollständiger Abschlussbericht + Sanierungsplan |
|
||||
| `docs/INSTALL.md` | Vollständige Installationsanleitung |
|
||||
| `docs/phase0_phase1_acceptance_report.md` | Abnahmeprotokoll Phase 0+1 |
|
||||
| `scripts/deploy.py` | Coolify API Deployment-Skript |
|
||||
| `scripts/seed_admin.py` | Admin-User erstellen |
|
||||
| `docker-compose.yml` | Referenz-Compose (API + Worker + DB + Redis) |
|
||||
| `.env.docker.example` | ENV-Template |
|
||||
| `prestart.sh` | Container-Entrypoint (Migrationen + Rollen) |
|
||||
| `worker.sh` | Worker-Entrypoint |
|
||||
|
||||
---
|
||||
|
||||
## Wichtige Regeln für den nächsten Agenten
|
||||
|
||||
1. **Keine manuellen Docker-Befehle** — alles über Coolify API oder deploy.py
|
||||
2. **Repo lesen bevor ändern** — docker-compose.yml und deploy.py beachten
|
||||
3. **Migrationen sind Forward-Only** — keine alten Migrationen verändern
|
||||
4. **RLS ist fail-closed** — kein Tenant-Kontext = kein Zugriff
|
||||
5. **crm_api hat keine DDL-Rechte** — Plugin-Migrationen über get_migration_engine()
|
||||
6. **Worker ist Coolify Service** — UUID
|
||||
7. **Alle DB-Passwörter sind identisch** — siehe .env.docker.example
|
||||
8. **pgvector/pgvector:pg16** als DB-Image — nicht postgres:16-alpine
|
||||
9. **Tests müssen mit echten unprivilegierten Rollen laufen** — nicht mit Superuser
|
||||
10. **Jede Phase: analysieren → implementieren → migrieren → testen → dokumentieren**
|
||||
-1041
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
"""Restore tenant RLS, transfer ownership, fix roles and grants.
|
||||
|
||||
This migration implements Phase 1 of the Sanierungsplan:
|
||||
This migration implements Phase 1 of the security hardening:
|
||||
|
||||
1. Transfer ALL table ownership from crm_user (SUPERUSER) to crm_migration (NOSUPERUSER, NOBYPASSRLS)
|
||||
2. ALTER ROLE crm_migration NOBYPASSRLS
|
||||
|
||||
@@ -1,448 +0,0 @@
|
||||
# LeoCRM — Architecture Feasibility Review
|
||||
|
||||
**Project:** leocrm
|
||||
**Reviewer:** Solution Architect (Agent Zero)
|
||||
**Date:** 2026-06-28
|
||||
**Documents reviewed:** architecture.md (1939 lines), task_graph.json (965 lines, v2.0.0, 13 tasks), AGENTS.md (570 lines), requirements.md (2142 lines, 143 features)
|
||||
|
||||
---
|
||||
|
||||
## VERDICT: FEASIBLE_WITH_RISKS
|
||||
|
||||
The architecture is fundamentally sound — the tech stack is coherent, the multi-tenant design is well-structured, and the API design covers the frontend's needs. However, there are **3 CRITICAL issues** in the task graph that must be fixed before implementation can start, plus **5 MAJOR issues** that affect feasibility of individual tasks.
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| CRITICAL | 3 |
|
||||
| MAJOR | 5 |
|
||||
| MINOR | 6 |
|
||||
|
||||
---
|
||||
|
||||
## 1. TASK ORDERING
|
||||
|
||||
### Finding: Minor inconsistency between execution_plan and parallelization_notes
|
||||
|
||||
The dependency graph is correct:
|
||||
- T01 → no dependencies (foundation) ✅
|
||||
- T02, T03 → depend on T01 only ✅
|
||||
- T07, T09, T10 → depend on T01 + T02 ✅
|
||||
- T04, T05, T06, T11 → depend on T01 + T03 ✅
|
||||
- T08a, T08b, T08c → depend on T07 + respective backend ✅
|
||||
|
||||
No hidden dependencies detected. T02 (Company/Contact) and T03 (Plugin Framework) are genuinely independent after T01. T09 does not depend on T03 (workflow engine uses event bus from T01, not plugin framework). T07 does not depend on T03 (frontend works without plugins — sidebar shows hardcoded v1 items).
|
||||
|
||||
**Issue:** The `execution_plan` phases don't match the `parallelization_notes`:
|
||||
- T09 is in Phase 3 (alone, `parallel: true`), T07 is in Phase 4 (`parallel: false`), T10 is in Phase 5 — but all three depend only on T01+T02 and could run simultaneously.
|
||||
- The `parallelization_notes` correctly states T09 can run parallel with T07, and T10 can run parallel with T07/T09 — but the `execution_plan` structure implies sequential phases.
|
||||
- **Recommendation:** Merge T09, T07, T10 into a single phase with `parallel: true`, or add explicit cross-phase parallelism annotations.
|
||||
|
||||
**Risk level:** MINOR — the parallelization_notes clarify intent, but the execution_plan structure could mislead the orchestrator into sequential execution.
|
||||
|
||||
---
|
||||
|
||||
## 2. TASK SIZING
|
||||
|
||||
### Finding: T07 is critically oversized — MAJOR
|
||||
|
||||
T07 (Frontend Core SPA) has:
|
||||
- **38 requirement IDs** (highest of any task)
|
||||
- **32 acceptance criteria** (highest of any task)
|
||||
- **estimated_lines: 600** (wildly underestimated)
|
||||
|
||||
Realistic line count breakdown:
|
||||
| Component | Estimated Lines |
|
||||
|-----------|----------------|
|
||||
| Vite setup + App.tsx + Providers + Router | ~150 |
|
||||
| API Client (axios, interceptors, error handling) | ~100 |
|
||||
| Layout Shell (Sidebar, TopBar, ContentArea) | ~200 |
|
||||
| Auth Pages (Login, Password-Reset Request+Confirm) | ~150 |
|
||||
| Companies Feature (List + TanStack Table + Detail + Tabs + Form) | ~350 |
|
||||
| Contacts Feature (List + Detail + Form) | ~300 |
|
||||
| Settings Feature (Tree + Profile + Role Editor + User Mgmt) | ~250 |
|
||||
| Audit Log Page | ~100 |
|
||||
| Dashboard (Stat Cards + Recent Activity) | ~100 |
|
||||
| Global Search Results Page | ~80 |
|
||||
| i18n Setup (de/en locale files) | ~200 |
|
||||
| Shared UI Component Library (12+ components) | ~500 |
|
||||
| Tailwind CSS + Design Tokens | ~50 |
|
||||
| **Total** | **~2530** |
|
||||
|
||||
At ~2500 lines, T07 is 4× the estimated size and would overwhelm a single implementation block.
|
||||
|
||||
**Recommendation:** Split T07 into:
|
||||
- **T07a: Frontend Foundation** — Vite setup, App.tsx, API Client, Layout Shell, Auth Pages, UI Component Library, i18n, Tailwind, Routing (~1200 lines, ~15 ACs)
|
||||
- **T07b: Frontend Feature Pages** — Companies, Contacts, Settings, Audit Log, Dashboard, Search (~1300 lines, ~17 ACs)
|
||||
- T07b depends on T07a
|
||||
|
||||
### Finding: T09 is borderline — MAJOR
|
||||
|
||||
T09 combines two independent subsystems:
|
||||
1. KI-Copilot (NL→API translation, LLM client, query/execute/history, RBAC enforcement) — ~350 lines
|
||||
2. Workflow Engine (CRUD definitions, instances, step history, code-engine, event triggers, approval timeout) — ~450 lines
|
||||
|
||||
Total: ~800 lines, 22 ACs. The two subsystems share no code — only both depend on T01+T02.
|
||||
|
||||
**Recommendation:** Consider splitting into T09a (KI-Copilot) and T09b (Workflow Engine). They can run in parallel. If kept as one task, increase estimated_lines to 800 and ensure the delegation message clearly separates the two modules.
|
||||
|
||||
---
|
||||
|
||||
## 3. TECH STACK FIT
|
||||
|
||||
### Finding: Stack is coherent — no compatibility concerns
|
||||
|
||||
| Component | Technology | Compatibility |
|
||||
|-----------|-----------|---------------|
|
||||
| Backend framework | FastAPI (async Python) | ✅ Native async, OpenAPI auto-gen |
|
||||
| ORM | SQLAlchemy 2.0 async + asyncpg | ✅ Standard for FastAPI |
|
||||
| Database | PostgreSQL 16 | ✅ MVCC, tsvector FTS, JSONB, UUID |
|
||||
| Cache/Queue | Redis 7 | ✅ Sessions, caching, ARQ queue |
|
||||
| Job queue | ARQ | ✅ Async-native, Redis-based, works with FastAPI |
|
||||
| Migrations | Alembic | ✅ Standard for SQLAlchemy |
|
||||
| Frontend framework | React 18 | ✅ Modern, concurrent features |
|
||||
| Build tool | Vite | ✅ Fast HMR, ES modules |
|
||||
| Server state | TanStack Query v5 | ✅ Works with React 18, Suspense |
|
||||
| Client state | Zustand | ✅ Lightweight, complementary to TanStack Query |
|
||||
| Forms | React Hook Form + Zod | ✅ Type-safe validation |
|
||||
| Styling | Tailwind CSS | ✅ Utility-first, design tokens |
|
||||
| Rich text | TipTap | ✅ React-compatible |
|
||||
| PDF viewer | PDF.js | ✅ Mozilla, React-compatible |
|
||||
| Document editing | OnlyOffice | ✅ External container, iframe integration |
|
||||
| Testing (backend) | pytest + httpx | ✅ Standard for FastAPI |
|
||||
| Testing (frontend) | Vitest + Testing Library | ✅ Standard for Vite/React |
|
||||
| E2E | Playwright | ✅ Cross-browser, reliable |
|
||||
|
||||
**Minor:** Python version not explicitly stated in architecture.md tech stack section. AGENTS.md mentions Python 3.12+ in conventions. Should be in the stack table.
|
||||
|
||||
**Risk level:** NONE — stack is well-established and all components are known-compatible.
|
||||
|
||||
---
|
||||
|
||||
## 4. PLUGIN ARCHITECTURE
|
||||
|
||||
### Finding: Framework design is sufficient for v2 plugins — but with concerns
|
||||
|
||||
T03's plugin framework provides:
|
||||
- Plugin Registry (DB-backed) ✅
|
||||
- Manifest Schema (Pydantic) with endpoints, migrations, UI, events, services, preferences, notification_types ✅
|
||||
- Lifecycle Hooks (install/activate/deactivate/uninstall) ✅
|
||||
- Plugin DB Migration Runner with tenant_id validator ✅
|
||||
- UI Registry (routes, menu_items, detail_tabs, settings_pages, dashboard_widgets) ✅
|
||||
- Event Bus Integration ✅
|
||||
- Service Container Injection (db, cache, event_bus, storage, notifications) ✅
|
||||
|
||||
v2 plugin requirements vs. framework support:
|
||||
| Plugin | Needs | Framework Support |
|
||||
|--------|-------|-------------------|
|
||||
| DMS | routes, events, migrations, UI, storage | ✅ All provided |
|
||||
| Calendar | routes, events, migrations, UI, ARQ (reminders) | ✅ ARQ via container.jobs |
|
||||
| Mail | routes, events, migrations, UI, IMAP/SMTP | ⚠️ IMAP/SMTP NOT in container — plugin implements its own |
|
||||
| Tags | routes, migrations, UI | ✅ |
|
||||
| Permissions | routes, migrations, UI | ✅ |
|
||||
|
||||
**MAJOR concern:** T04 (DMS Backend) and T11 (Tags + Permissions + Entity Links Backend) have **identical acceptance criteria** for these endpoints:
|
||||
- `GET /api/v1/dms/files/{id}/permissions`
|
||||
- `POST /api/v1/dms/files/{id}/link`
|
||||
- `DELETE /api/v1/dms/files/{id}/link`
|
||||
- `POST /api/v1/dms/files/{id}/share-link`
|
||||
- `GET /api/public/share/{token}` mit expired link → 410
|
||||
- `DMS plugin listens to company.deleted event → linked files cleanup`
|
||||
- `Folder permissions enforced: user without read → 403`
|
||||
|
||||
Both tasks are scheduled in Phase 6 (parallel). Two implementers would write the same endpoints → merge conflict.
|
||||
|
||||
Additionally, T04's test_spec runs `tests/test_tags.py` with `--cov=app/plugins/builtins/tags` — but Tags are T11's responsibility, not T04's.
|
||||
|
||||
**Recommendation:** Clearly separate responsibilities:
|
||||
- T04: DMS folders, files, upload, preview, OnlyOffice, bulk operations, DMS search
|
||||
- T11: Tags CRUD + assignment, Permissions (file_shares, folder_permissions, share_links), Entity links (file_links)
|
||||
- Remove duplicated ACs from one task (keep in the task that owns the endpoint)
|
||||
- Remove `tests/test_tags.py` from T04's test_spec
|
||||
|
||||
**Risk level:** MAJOR — parallel execution with overlapping ACs will cause implementation conflicts.
|
||||
|
||||
---
|
||||
|
||||
## 5. API DESIGN
|
||||
|
||||
### Finding: Endpoints are sufficient for T07 — one minor gap
|
||||
|
||||
T07 frontend needs vs. available endpoints:
|
||||
| Frontend Need | API Endpoint | Available In |
|
||||
|---------------|-------------|-------------|
|
||||
| Login/logout/me | `/api/v1/auth/*` | T01 ✅ |
|
||||
| Password reset | `/api/v1/auth/password-reset/*` | T01 ✅ |
|
||||
| Tenant switch | `/api/v1/auth/switch-tenant` | T01 ✅ |
|
||||
| User CRUD | `/api/v1/users` | T01 ✅ |
|
||||
| Role CRUD | `/api/v1/roles` | T01 ✅ |
|
||||
| User settings | `/api/v1/users/me/settings` | T01 ✅ |
|
||||
| Company CRUD + search + export | `/api/v1/companies` | T02 ✅ |
|
||||
| Contact CRUD + search + export | `/api/v1/contacts` | T02 ✅ |
|
||||
| Company-Contact N:M links | `/api/v1/companies/{id}/contacts/{cid}` | T02 ✅ |
|
||||
| Import/preview | `/api/v1/import`, `/api/v1/import/preview` | T02 ✅ |
|
||||
| Audit log | `/api/v1/audit-log` | T01 ✅ |
|
||||
| Notifications | `/api/v1/notifications` | T01 ✅ |
|
||||
| Global search | `/api/v1/search` | T01/T02 ✅ |
|
||||
| Health | `/api/v1/health` | T01 ✅ |
|
||||
| Dashboard stats | **MISSING** | ❌ |
|
||||
| Plugin menu items | `/api/v1/plugins` | T03 (not a dependency, but done earlier) ✅ |
|
||||
|
||||
**MINOR gap:** T07's AC says "Dashboard renders with stat cards + recent activity" but there is no `GET /api/v1/dashboard` or `GET /api/v1/stats` endpoint. The frontend can compose this from existing endpoints (count companies, count contacts, recent audit log), but an aggregated endpoint would be cleaner.
|
||||
|
||||
**Recommendation:** Either add a `GET /api/v1/dashboard` endpoint to T01 or T02, or document that the dashboard composes from `GET /api/v1/companies?page_size=1` (for total count) + `GET /api/v1/contacts?page_size=1` + `GET /api/v1/audit-log?page_size=10`.
|
||||
|
||||
Also: T02 has AC `GET /api/v1/companies/{id}/emails → 200 (empty array wenn mail plugin inactive)` — this is a v2 endpoint stub in a v1 task. The implementer needs to know to return an empty array when the mail plugin is not active. This should be explicitly documented as a conditional stub.
|
||||
|
||||
---
|
||||
|
||||
## 6. DATABASE SCHEMA
|
||||
|
||||
### Finding: MAJOR — users table has FK references to v2 tables that don't exist in v1
|
||||
|
||||
The `users` table includes:
|
||||
```
|
||||
default_calendar_id | UUID | FK→calendars.id NULL
|
||||
default_mail_account_id | UUID | FK→mail_accounts.id NULL
|
||||
```
|
||||
|
||||
`calendars` and `mail_accounts` are v2 plugin tables created by T05 and T06 respectively. In v1, these tables don't exist. The Alembic initial migration (T01) would fail trying to create FK constraints to non-existent tables.
|
||||
|
||||
**Recommendation:**
|
||||
1. Remove `default_calendar_id` and `default_mail_account_id` from the initial v1 migration
|
||||
2. Add these columns in v2 plugin migrations (T05 adds `default_calendar_id`, T06 adds `default_mail_account_id`) with FK constraints
|
||||
3. Or: add the columns without FK constraints in v1, add FKs in v2 migrations
|
||||
|
||||
### Other schema findings:
|
||||
|
||||
- `plugin_migrations` table: described in text (line 1205) but not formally defined as a table with columns. MINOR — should be in the schema section.
|
||||
- `groups` table: referenced by `folder_permissions.group_id`, `file_shares.group_id`, `calendar_shares.group_id` but not defined anywhere. This is a v2 concern but should be documented. MINOR.
|
||||
- No `user_groups` or `groups` table in the schema — several v2 features reference group-based sharing. MAJOR for v2, but not blocking for v1.
|
||||
- All core tables have proper indexes, tenant_id, timestamps, and soft-delete where appropriate. ✅
|
||||
- FTS design with tsvector + GIN is correct. ✅
|
||||
- UUID PKs with `gen_random_uuid()` — correct for PostgreSQL 16. ✅
|
||||
|
||||
---
|
||||
|
||||
## 7. FRONTEND ARCHITECTURE
|
||||
|
||||
### Finding: Detailed enough for T07 implementation — MINOR gaps
|
||||
|
||||
The frontend architecture section covers:
|
||||
- Full stack table (React 18, Vite, React Router v6, TanStack Query, Zustand, RHF+Zod, Tailwind, etc.) ✅
|
||||
- Complete routing table with all routes ✅
|
||||
- State management strategy (TanStack Query / Zustand / URL state) ✅
|
||||
- i18n setup (de/en, locale files, date-fns) ✅
|
||||
- Accessibility (ARIA, 44px, reduced-motion, sr-only, keyboard, WCAG 2.1 AA) ✅
|
||||
- Design system based on approved prototype ✅
|
||||
- Frontend directory structure ✅
|
||||
|
||||
Missing details (MINOR):
|
||||
- No API client design (interceptor pattern, error normalization, 401 redirect logic) — mentioned in T07 description but not in architecture.md
|
||||
- No data flow diagram (how TanStack Query hooks connect to API client → backend)
|
||||
- PluginRegistry.tsx / PluginLoader.tsx described conceptually but no implementation contract
|
||||
- No specific file-per-feature breakdown (e.g., what files go in `features/companies/`)
|
||||
|
||||
These gaps are fillable from AGENTS.md conventions and the T07 task description. Not blocking.
|
||||
|
||||
**Risk level:** LOW — architecture + AGENTS.md conventions provide sufficient guidance.
|
||||
|
||||
---
|
||||
|
||||
## 8. KI-COPILOT + WORKFLOW ENGINE
|
||||
|
||||
### Finding: T09 is under-scoped on requirements but over-scoped on acceptance criteria — MAJOR
|
||||
|
||||
T09 has only **5 requirement IDs** but **22 acceptance criteria**:
|
||||
- F-AI-01 → covers entire AI Copilot subsystem (query, execute, history, RBAC, audit, tenant isolation, field permissions) = 8 ACs
|
||||
- F-WF-01 → covers entire Workflow Engine (definition CRUD, instances, advance/approve/reject/cancel, event triggers, step history, code-engine, approval timeout) = 14 ACs
|
||||
- F-CORE-01 → event bus (shared with T01)
|
||||
- F-CORE-06 → API-first (architectural principle, not a feature)
|
||||
- F-TEST-01 → testing (shared across all tasks)
|
||||
|
||||
The 5 reqs is misleading — F-AI-01 and F-WF-01 are each complex subsystems masquerading as single features. The 22 ACs are well-defined and testable, but implementing two independent subsystems in one delegation is risky.
|
||||
|
||||
**The two subsystems share no code:**
|
||||
- KI-Copilot uses: ai_conversations model, LLM client, user session (T01), audit log (T01)
|
||||
- Workflow Engine uses: workflows/instances/step_history models, event bus (T01), ARQ (T01)
|
||||
- They don't reference each other
|
||||
|
||||
**Recommendation:** Split T09 into:
|
||||
- **T09a: KI-Copilot API** — ai_conversations, LLM client, query/execute/history, RBAC enforcement (~350 lines, 8 ACs)
|
||||
- **T09b: Workflow Engine** — workflow CRUD, instances, code-engine, event triggers, approval timeout (~450 lines, 14 ACs)
|
||||
- Both depend on T01+T02, can run in parallel
|
||||
|
||||
If kept as one task, increase estimated_lines from 700 to 800+ and ensure the delegation message explicitly separates the two modules with clear file boundaries.
|
||||
|
||||
---
|
||||
|
||||
## 9. DEPLOYMENT READINESS
|
||||
|
||||
### Finding: Architecture supports Docker/Coolify deployment — MINOR gaps
|
||||
|
||||
Present in architecture:
|
||||
- Docker Compose with all 6 services (backend, frontend, postgres, redis, worker, onlyoffice) ✅
|
||||
- Health checks for backend container ✅
|
||||
- Environment variables documented with .env.example plan ✅
|
||||
- Backup strategy (pg_dump daily + storage backup) ✅
|
||||
- Named volumes for persistent data ✅
|
||||
- Non-root container user (app:app) in AGENTS.md forbidden patterns ✅
|
||||
- Structured JSON logging for observability ✅
|
||||
- Prometheus metrics endpoint planned ✅
|
||||
|
||||
Missing (MINOR):
|
||||
- No Dockerfiles (backend/frontend) — implementation concern (T01/T07), not architecture
|
||||
- No Coolify-specific configuration — Coolify can use docker-compose directly, but no coolify.json or resource limits documented
|
||||
- No SSL/TLS documentation — Coolify uses Traefik for LE certificates, but this isn't stated in architecture
|
||||
- No resource limits (CPU/memory) for containers — important for multi-tenant production
|
||||
- No logging driver configuration for Docker — structured logging is at app level, but Docker log rotation isn't mentioned
|
||||
- OnlyOffice container is always in docker-compose — should use Docker Compose profiles for optional services
|
||||
- No database initialization script (create DB, run initial migration) documented in deployment flow
|
||||
|
||||
**Risk level:** LOW — all gaps are deployment-phase concerns, not architecture blockers. T10 covers documentation.
|
||||
|
||||
---
|
||||
|
||||
## 10. v1/v2 BOUNDARY
|
||||
|
||||
### Finding: Boundary is mostly clean — one MAJOR schema issue, one MINOR stub issue
|
||||
|
||||
### v1 can ship standalone: ✅ (with fix)
|
||||
|
||||
| v1 Task | Standalone? | Notes |
|
||||
|---------|------------|-------|
|
||||
| T01 (Core+Auth) | ✅ | Plugin framework ready, no plugins installed |
|
||||
| T02 (Company+Contact) | ✅ | Full CRM without plugins |
|
||||
| T03 (Plugin Framework) | ✅ | Framework ready, zero plugins activated |
|
||||
| T07 (Frontend Core) | ✅ | Companies, contacts, settings, dashboard — no plugin UIs |
|
||||
| T09 (KI-Copilot+Workflows) | ✅ | AI + workflows work on core entities |
|
||||
| T10 (Monitoring+Docs) | ✅ | Health, metrics, docs for v1 scope |
|
||||
|
||||
### Issues affecting v1 standalone:
|
||||
|
||||
**MAJOR (blocking):** `users` table FK references to v2 tables (`calendars.id`, `mail_accounts.id`) — v1 migration fails. (See §6)
|
||||
|
||||
**MINOR:** T02 has AC `GET /api/v1/companies/{id}/emails → 200 (empty array)` — this is a v2 mail endpoint stub. The v1 implementer must handle this gracefully (return empty array when mail plugin inactive). Should be documented as a conditional stub, not a full endpoint implementation.
|
||||
|
||||
**MINOR:** T07 company detail shows "Files placeholder, Emails placeholder" tabs — clean v1/v2 boundary via placeholder tabs. ✅
|
||||
|
||||
### v1/v2 separation in task graph:
|
||||
- v1 tasks: T01, T02, T03, T07, T09, T10 (6 tasks) ✅
|
||||
- v2 tasks: T04, T05, T06, T11, T08a, T08b, T08c (7 tasks) ✅
|
||||
- No v1 task depends on a v2 task ✅
|
||||
- No v2 task is required for v1 to function ✅
|
||||
- Feature coverage: 73 v1 features / 70 v2 features — all covered ✅
|
||||
|
||||
---
|
||||
|
||||
## CRITICAL ISSUES (must fix before implementation)
|
||||
|
||||
### C1: T04/T11 acceptance criteria overlap
|
||||
|
||||
**Problem:** T04 (DMS Backend) and T11 (Tags+Permissions+Links Backend) have 7 identical acceptance criteria for the same endpoints (permissions, file links, share-links, event cleanup, folder permissions). Both are in Phase 6 (parallel) → two implementers writing the same code.
|
||||
|
||||
T04's test_spec also includes `tests/test_tags.py` and `--cov=app/plugins/builtins/tags` — Tags are T11's responsibility.
|
||||
|
||||
**Fix:**
|
||||
- T04 owns: DMS folders, files, upload, preview, OnlyOffice, bulk ops, DMS search
|
||||
- T11 owns: Tags CRUD + assignment + bulk, Permissions (file_shares, folder_permissions, share_links), Entity links (file_links), public share endpoint
|
||||
- Remove duplicated ACs from T04
|
||||
- Remove `tests/test_tags.py` and tags coverage from T04's test_spec
|
||||
|
||||
### C2: v2 tasks have non-compliant test_spec
|
||||
|
||||
**Problem:** T08a, T08b, T08c, T11 have `test_spec` as plain strings instead of structured objects:
|
||||
```json
|
||||
"test_spec": "Component tests for file browser, upload, share dialog, tag picker."
|
||||
```
|
||||
Instead of the mandatory structure:
|
||||
```json
|
||||
"test_spec": {
|
||||
"commands": [...],
|
||||
"expected_results": "...",
|
||||
"test_files": [...],
|
||||
"coverage_target": 80
|
||||
}
|
||||
```
|
||||
|
||||
**Fix:** Convert all 4 tasks' test_spec to structured objects with commands, test_files, expected_results, coverage_target.
|
||||
|
||||
### C3: v2 tasks use invalid subagent profiles
|
||||
|
||||
**Problem:**
|
||||
- T08a, T08b, T08c use `"subagent_profile": "frontend_dev"` — this profile does not exist
|
||||
- T11 uses `"subagent_profile": "backend_dev"` — this profile does not exist
|
||||
- Available profiles: `implementation_engineer`, `developer`, etc.
|
||||
|
||||
**Fix:** Change all v2 tasks to `"subagent_profile": "implementation_engineer"` (matching v1 tasks and AGENTS.md).
|
||||
|
||||
---
|
||||
|
||||
## MAJOR ISSUES (should fix before implementation)
|
||||
|
||||
### M1: T07 oversized (38 reqs, 32 ACs, ~2500 lines estimated vs 600 stated)
|
||||
Split into T07a (Frontend Foundation) + T07b (Frontend Feature Pages).
|
||||
|
||||
### M2: users table FKs to v2 tables (calendars.id, mail_accounts.id)
|
||||
Remove from v1 migration, add in v2 plugin migrations.
|
||||
|
||||
### M3: T09 combines two independent subsystems (KI-Copilot + Workflow Engine, 22 ACs)
|
||||
Split into T09a (KI-Copilot) + T09b (Workflow Engine), or increase estimated_lines and ensure clear module separation in delegation.
|
||||
|
||||
### M4: T08a/T08c acceptance criteria are mixed
|
||||
T08a (DMS+Tags+Permissions) has a Mail AC ("shared mailbox selector"). T08c (Mail+Search) has a DMS AC ("DMS route /dms renders file browser") and a Docker Compose AC. Reassign ACs to correct tasks.
|
||||
|
||||
### M5: T04 test_spec includes tags tests (T11's responsibility)
|
||||
Remove `tests/test_tags.py` and `--cov=app/plugins/builtins/tags` from T04's test_spec.
|
||||
|
||||
---
|
||||
|
||||
## MINOR ISSUES (nice to fix, non-blocking)
|
||||
|
||||
### m1: Execution plan phases don't reflect parallelization notes
|
||||
T09/T07/T10 are in separate phases but could run parallel. Merge or annotate.
|
||||
|
||||
### m2: No dashboard/stats endpoint for T07's dashboard stat cards
|
||||
Add `GET /api/v1/dashboard` or document client-side composition.
|
||||
|
||||
### m3: plugin_migrations table not formally defined in schema section
|
||||
Add table definition with columns.
|
||||
|
||||
### m4: No Coolify-specific deployment config or SSL/TLS documentation
|
||||
Document Traefik SSL termination and Coolify deployment flow.
|
||||
|
||||
### m5: OnlyOffice container always in docker-compose
|
||||
Use Docker Compose profiles for optional services.
|
||||
|
||||
### m6: Python version not in tech stack table
|
||||
Add Python 3.12+ to the stack table in architecture.md §1.
|
||||
|
||||
---
|
||||
|
||||
## TOP 3 RISKS
|
||||
|
||||
1. **T07 oversized task** — 38 reqs / 32 ACs / ~2500 lines in one delegation. High probability of incomplete implementation, context window exhaustion, or quality degradation. Must split.
|
||||
|
||||
2. **T04/T11 endpoint overlap** — 7 identical ACs across two parallel tasks. Will cause merge conflicts, duplicate code, and test failures when both implementers write the same endpoints.
|
||||
|
||||
3. **users table FK to non-existent v2 tables** — v1 Alembic migration will fail on `FK→calendars.id` and `FK→mail_accounts.id` because those tables don't exist until v2 plugin installation.
|
||||
|
||||
---
|
||||
|
||||
## RECOMMENDATION FOR PHASE 3 START
|
||||
|
||||
**Conditional GO — fix 3 CRITICAL issues first, then start implementation.**
|
||||
|
||||
Required actions before implementation:
|
||||
1. Fix C1: Separate T04/T11 endpoint ownership, remove duplicate ACs
|
||||
2. Fix C2: Convert T08a/T08b/T08c/T11 test_spec to structured objects
|
||||
3. Fix C3: Change invalid subagent profiles to `implementation_engineer`
|
||||
4. Fix M1: Split T07 into T07a + T07b
|
||||
5. Fix M2: Remove v2 FKs from users table in v1 migration
|
||||
6. Fix M4: Reassign mixed ACs between T08a and T08c
|
||||
7. Fix M5: Remove tags tests from T04 test_spec
|
||||
|
||||
Recommended (non-blocking):
|
||||
- Fix M3: Split T09 into T09a + T09b (reduces delegation risk)
|
||||
- Fix m1-m6 for documentation quality
|
||||
|
||||
After fixes: task_graph.json v2.1.0, architecture.md v1.1, then proceed to implementation.
|
||||
@@ -1,176 +0,0 @@
|
||||
# LeoCRM — Codebase vs Requirements (IST-Stand Juli 2026)
|
||||
|
||||
**Datum:** 2026-07-23
|
||||
**Prüfer:** Agent Zero
|
||||
**Methode:** Vollständige Code-Inspektion gegen requirements.md und architecture.md
|
||||
|
||||
---
|
||||
|
||||
## 1. Aktueller Stack
|
||||
|
||||
| Komponente | Code-Realität | Requirements | Status |
|
||||
|------------|-------------|-------------|--------|
|
||||
| Backend | FastAPI 0.115+ | FastAPI | ✅ kompatibel |
|
||||
| Python | 3.12 (Dockerfile) | 3.12 | ✅ kompatibel |
|
||||
| Datenbank | PostgreSQL 16 + asyncpg | PostgreSQL 16 | ✅ erfüllt |
|
||||
| ORM | SQLAlchemy 2.0 async | SQLAlchemy 2.0 | ✅ erfüllt |
|
||||
| Frontend | React 18 SPA (Vite) | React SPA | ✅ erfüllt |
|
||||
| Auth | Session-based (Redis + HttpOnly Cookie) | Session-based | ✅ erfüllt |
|
||||
| Deployment | Docker Multi-Stage + Coolify | Docker + Coolify | ✅ erfüllt |
|
||||
| Testing | pytest + httpx (Backend), Vitest (Frontend) | pytest + Vitest + Playwright | ⚠️ Playwright fehlt |
|
||||
| KI | LiteLLM + PydanticAI | KI-Copilot | ✅ erfüllt + erweitert |
|
||||
| Search | pgvector + FTS Hybrid (RRF Fusion) | FTS | ✅ übertroffen |
|
||||
|
||||
## 2. Projekt-Struktur (IST)
|
||||
|
||||
```
|
||||
app/
|
||||
├── main.py — FastAPI app, lifespan, middleware, router wiring
|
||||
├── config.py — Pydantic Settings (env: LEOCRM_*)
|
||||
├── deps.py — Auth dependencies, require_permission
|
||||
├── core/
|
||||
│ ├── auth.py — Session auth, bcrypt, Redis session store
|
||||
│ ├── tenant.py — Tenant-scoping ORM filter
|
||||
│ ├── permissions.py — RBAC resolver with Redis cache
|
||||
│ ├── permission_registry.py — Central permission catalog
|
||||
│ ├── event_bus.py — In-process async event bus
|
||||
│ ├── service_container.py — DI container
|
||||
│ ├── cache.py — Redis cache wrapper
|
||||
│ ├── jobs.py — ARQ job queue integration
|
||||
│ ├── worker.py — ARQ worker configuration
|
||||
│ ├── audit.py — Audit log + deletion log
|
||||
│ ├── notifications.py — Notification service
|
||||
│ ├── monitoring.py — Prometheus metrics + structlog
|
||||
│ ├── rate_limit.py — Redis-based rate limiting
|
||||
│ ├── middleware.py — CSRF, CORS, request logging
|
||||
│ └── seeds.py — Seed data
|
||||
├── models/ — 20 SQLAlchemy models (all with TenantMixin)
|
||||
├── schemas/ — 19 Pydantic schema modules
|
||||
├── services/ — 18 service modules
|
||||
├── routes/ — 22 FastAPI routers
|
||||
├── plugins/
|
||||
│ ├── base.py — BasePlugin class
|
||||
│ ├── manifest.py — PluginManifest, PluginRouteDef
|
||||
│ ├── registry.py — PluginRegistry (698 lines)
|
||||
│ ├── migration_runner.py — Plugin DB migration runner
|
||||
│ └── builtins/ — 12 built-in plugins
|
||||
├── workflows/
|
||||
│ ├── engine.py — Workflow execution engine (306 lines)
|
||||
│ └── code/onboarding.py — Onboarding workflow
|
||||
├── ai/
|
||||
│ ├── llm_client.py — LLM client (LiteLLM migration pending)
|
||||
│ └── action_mapper.py — NL→API action mapping
|
||||
└── utils/
|
||||
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── pages/ — 27 pages (~7.826 lines)
|
||||
│ ├── components/ — 70 components (~13.893 lines)
|
||||
│ ├── api/ — 12 API modules (~3.456 lines)
|
||||
│ ├── store/ + stores/ — 5 Zustand stores (~461 lines)
|
||||
│ ├── hooks/ — 5 custom hooks (~247 lines)
|
||||
│ ├── i18n/ — DE + EN (750 keys each)
|
||||
│ └── routes/ — Router + ProtectedRoute
|
||||
├── package.json — 26 deps, 15 devDeps
|
||||
├── vite.config.ts — React + Vitest + proxy
|
||||
└── tailwind.config.js — Design tokens, dark mode
|
||||
```
|
||||
|
||||
## 3. Requirements-Erfüllung
|
||||
|
||||
### Core Features (v1)
|
||||
|
||||
| Feature | Status | Anmerkung |
|
||||
|---------|--------|-----------|
|
||||
| F-AUTH-01: Login/Logout | ✅ | Session + Redis + bcrypt |
|
||||
| F-AUTH-03: User Management | ✅ | CRUD + RBAC |
|
||||
| F-AUTH-04: RBAC | ✅ | Role + Groups + Field-Level |
|
||||
| F-AUTH-05: Password Reset | ✅ | Token-based, 1h expiry |
|
||||
| F-AUTH-06: Custom Roles | ✅ | Role editor in frontend |
|
||||
| F-AUTH-07: Multi-Tenant | ✅ | ORM filter + RLS (Migration 0015) |
|
||||
| F-COMP-01-08: Company CRUD | ⚠️ | Unified Contact Model — Company = Contact type='company'. Company-Routes werden entfernt (Phase 1) |
|
||||
| F-CONT-01-08: Contact CRUD | ✅ | Unified Contact mit type='company'\|'person' + ContactPerson 1:N |
|
||||
| F-CORE-01: Event Bus | ✅ | In-process async, 53 Zeilen |
|
||||
| F-CORE-02: Multi-Tenant | ✅ | TenantMixin + RLS |
|
||||
| F-CORE-03: Plugin System | ✅ | 12 Plugins, Registry, Manifest, Lifecycle |
|
||||
| F-CORE-07: ARQ Job Queue | ✅ | Redis-based, worker.py |
|
||||
| F-CORE-08: Caching | ✅ | Redis cache wrapper |
|
||||
| F-CORE-10: Storage | ❌ | Architecture beschreibt StorageBackend, aber NICHT implementiert. Hardcoded Pfade. (Phase 0.16) |
|
||||
| F-DATA-03: Validation | ✅ | Pydantic auf allen Inputs |
|
||||
| F-DATA-04: PostgreSQL | ✅ | PostgreSQL 16 + asyncpg |
|
||||
| F-PLUGIN-01-02: Plugin System | ✅ | Registry, Manifest, Lifecycle, Migration Runner |
|
||||
| F-SEC-01: CSRF | ✅ | SameSite=Strict + Origin validation |
|
||||
| F-SEC-02: CSP | ✅ | In architecture definiert (Nginx fehlt — Phase 0) |
|
||||
| F-SEARCH-01: Global Search | ✅ | Hybrid FTS + Vector + RRF + KI Query Understanding |
|
||||
| F-AI-01: KI-Copilot | ✅ | LiteLLM + PydanticAI + tool_registry |
|
||||
| F-WF-01: Workflow Engine | ✅ | 306 Zeilen, 4 Step-Types (action/approval/notification/condition) |
|
||||
| F-INFRA-01: Health Check | ✅ | /api/v1/health |
|
||||
| F-INFRA-04: Monitoring | ✅ | Prometheus + structlog |
|
||||
| F-PERF-01: Performance | ⚠️ | Indizes vorhanden, aber kein Virtual Scrolling (Phase 2) |
|
||||
| F-TEST-01: Testing | ⚠️ | Backend + Frontend Tests da, E2E fehlt (Phase 5) |
|
||||
|
||||
### Plugin Features (v2)
|
||||
|
||||
| Plugin | Status | Anmerkung |
|
||||
|--------|--------|-----------|
|
||||
| Calendar | ✅ | 12 Komponenten, ICS, Kanban, Resources, Subtasks |
|
||||
| DMS | ✅ | 9 Komponenten, OnlyOffice (→Collabora Phase 0.20), Share, Bulk |
|
||||
| Mail | ✅ | 13 Komponenten, PGP, Vacation, Rules, Templates, IMAP/SMTP |
|
||||
| Tags | ✅ | TagPicker, TagCloud, BulkTagDialog |
|
||||
| Permissions | ✅ | File/folder permissions, share links |
|
||||
| Entity Links | ✅ | File↔Entity links |
|
||||
| Unified Search | ✅ | Hybrid FTS+Vector, 5 Provider, KI Query Understanding |
|
||||
| AI Assistant | ✅ | Multi-provider LLM, Agents, Tools, Streaming |
|
||||
| AI Proactive | ✅ | Context-aware suggestions, SSE, Heartbeat, Deep Analysis |
|
||||
| Kommunikation | ✅ | WebSocket messaging, MiniApps, Rich Content Blocks |
|
||||
| Report Generator | ⚠️ | Backend da (CSV/Excel/JSON), Frontend fehlt, PDF fehlt (Phase 5.18-5.19) |
|
||||
| System Notifications | ✅ | Participant handler, notification types |
|
||||
|
||||
## 4. Bekannte Lücken (im MASTER-PLAN.md eingeplant)
|
||||
|
||||
| Lücke | Phase | Task |
|
||||
|-------|-------|------|
|
||||
| Storage Backend (S3) | 0 | 0.16 |
|
||||
| Company-Routes entfernen | 1 | 1.1-1.20 |
|
||||
| Plugin-UI-System (PluginRegistry) | 3 | 3.1-3.10 |
|
||||
| Automation & Agents Plugin | 3.5 | 3.11-3.32 |
|
||||
| KI-UI-Steuerung | 4 | 4.1-4.12 |
|
||||
| E2E Tests (Playwright) | 5 | 5.4-5.11 |
|
||||
| Backup-System | 5 | 5.15 |
|
||||
| MCP Integration | 5 | 5.16-5.17 |
|
||||
| Report Frontend + PDF | 5 | 5.18-5.19 |
|
||||
| Custom Fields UI | 5 | 5.20 |
|
||||
| Tasks-Plugin | 5 | 5.21 |
|
||||
| Saved Searches | 5 | 5.22 |
|
||||
| Deduplication | 5 | 5.23 |
|
||||
| PWA | 5 | 5.24 |
|
||||
| Dashboard-System | 5 | 5.25 |
|
||||
| Code-Splitting | 2 | 2.1-2.7 |
|
||||
| Virtual Scrolling | 2 | 2.2-2.6 |
|
||||
| RHF + Zod überall | 6 | 6.1-6.6 |
|
||||
| AGPL-Lizenzen (PyMuPDF, OnlyOffice) | 0 | 0.20 |
|
||||
| RBAC in 4 Plugins | 0 | 0.10 |
|
||||
| Undo/History | 0 | 0.15 |
|
||||
| .env in Git | 0 | 0.18 |
|
||||
| Mail-Salt hardcoded | 0 | 0.19 |
|
||||
|
||||
## 5. Statistik
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| Backend Python-Zeilen | ~35.800 |
|
||||
| Frontend TS/TSX-Zeilen | ~30.000 |
|
||||
| Test-Zeilen (Backend) | ~17.300 |
|
||||
| Test-Zeilen (Frontend) | ~3.045 |
|
||||
| Plugins | 12 |
|
||||
| API-Endpoints | ~120+ |
|
||||
| DB-Migrationen | 22 |
|
||||
| UI-Komponenten | 12 (+2 zusätzliche) |
|
||||
| Frontend Pages | 27 |
|
||||
| i18n Keys (pro Sprache) | 750 |
|
||||
|
||||
## 6. Fazit
|
||||
|
||||
Die Codebase hat den ursprünglichen Requirements-Review (der SQLite, Jinja2, keine Plugins beschrieb) **weit übertroffen**. Das unified Contact Model, das Plugin-System, die KI-Integration und die Vector Search sind implementiert und funktionieren.
|
||||
|
||||
Die verbleibenden Lücken sind im `MASTER-PLAN.md` detailliert eingeplant (~590h Gesamt-Aufwand über 8 Phasen + Phase 3.5).
|
||||
@@ -1,477 +0,0 @@
|
||||
ÜBERHOLT – NICHT ALS UMSETZUNGSANWEISUNG VERWENDEN
|
||||
# LeoCRM — Abschlussbericht Phase 0 + Phase 1 und vollständiger Sanierungsplan
|
||||
|
||||
**Datum:** 2026-08-01
|
||||
**Git-Commit:** 733fa1c (main)
|
||||
**Alembic-Head:** 0090
|
||||
**Produktion:** https://crm.media-on.de — healthy
|
||||
|
||||
---
|
||||
|
||||
## 1. Aktueller Stand
|
||||
|
||||
### 1.1 Abgenommene Gates
|
||||
|
||||
| Gate | Beschreibung | Status |
|
||||
|------|-------------|--------|
|
||||
| Gate 1 | Reproduzierbares Coolify-Deployment | ✅ Bestanden |
|
||||
| Gate 2 | Neuinstallation auf leerer Datenbank | ✅ Bestanden |
|
||||
| Gate 3 | Vollständiger Restore-Test | ✅ Bestanden |
|
||||
| Gate 4 | Passwort-Reset end-to-end | ✅ Bestanden |
|
||||
| Gate 5 | Worker und Eventhandler | ✅ Bestanden |
|
||||
|
||||
### 1.2 Produktionsstand
|
||||
|
||||
| Komponente | Wert |
|
||||
|-----------|------|
|
||||
| Git-Commit | 733fa1c |
|
||||
| Docker-Image | dx4pqdziu4uj6x9fxs1u5z0x:733fa1c |
|
||||
| API-Container | dx4pqdziu4uj6x9fxs1u5z0x-201530032526 — healthy |
|
||||
| Worker-Container | leocrm-worker — healthy |
|
||||
| Alembic-Head | 0090 |
|
||||
| Tabellen | 124 |
|
||||
| RLS-Tabellen | 108 (alle Tenant-Tabellen) |
|
||||
| RLS-Policies | 112 |
|
||||
| Legacy app.tenant_id Policies | 0 |
|
||||
| DB-Rollen | 5 (crm_platform_admin, crm_migration, crm_auth, crm_api, crm_worker) |
|
||||
| crm_api | NOSUPERUSER, NOBYPASSRLS — API-Laufzeit |
|
||||
| crm_auth | NOSUPERUSER, NOBYPASSRLS — Login/Authentifizierung |
|
||||
| crm_worker | NOSUPERUSER, NOBYPASSRLS — Worker-Laufzeit |
|
||||
| crm_migration | NOSUPERUSER, BYPASSRLS — Migrationen und DDL |
|
||||
| ~~crm_runtime~~ | Gelöscht |
|
||||
|
||||
### 1.3 Datenbankrollen-Architektur
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ PostgreSQL (crm_db) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ crm_user (POSTGRES_USER, SUPERUSER) │
|
||||
│ └── Nur für Bootstrap und DB-Initialisierung │
|
||||
│ │
|
||||
│ crm_migration (NOSUPERUSER, BYPASSRLS, Tabellenowner) │
|
||||
│ ├── Alembic-Migrationen (0001–0090) │
|
||||
│ ├── Plugin-Migrationen (DDL) │
|
||||
│ └── Datenmigrationen (tenantübergreifend) │
|
||||
│ │
|
||||
│ crm_auth (NOSUPERUSER, NOBYPASSRLS) │
|
||||
│ ├── Login/Logout │
|
||||
│ ├── Tenant-Auflösung │
|
||||
│ ├── User/Tenant-Membership │
|
||||
│ └── Password-Reset-Token │
|
||||
│ │
|
||||
│ crm_api (NOSUPERUSER, NOBYPASSRLS, kein Owner) │
|
||||
│ ├── Normale API-Abfragen (SELECT, INSERT, UPDATE, DELETE) │
|
||||
│ ├── Audit-Log (über separate Session mit Tenant-Kontext) │
|
||||
│ └── Keine DDL-Rechte │
|
||||
│ │
|
||||
│ crm_worker (NOSUPERUSER, NOBYPASSRLS, kein Owner) │
|
||||
│ ├── ARQ-Background-Jobs │
|
||||
│ ├── Outbox-Processing (per-Tenant mit RLS-Kontext) │
|
||||
│ ├── Cron-Jobs (scheduler_tick, tasks_due_reminder) │
|
||||
│ └── Event-Handler für aktive Plugins │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.4 RLS-Architektur
|
||||
|
||||
- **Fail-closed:** Kein Tenant-Kontext = kein Zugriff auf Tenant-Daten
|
||||
- **Policy:** `USING/WITH CHECK (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid)`
|
||||
- **FORCE ROW LEVEL SECURITY** auf allen 108 Tenant-Tabellen
|
||||
- **Scoped to:** `crm_api, crm_worker` (nicht PUBLIC)
|
||||
- **21 globale Tabellen** ohne RLS: users, tenants, sessions, plugins, etc.
|
||||
- **0 legacy Policies** mit `app.tenant_id` (alle durch `app.current_tenant_id` ersetzt)
|
||||
|
||||
### 1.5 Verifizierte Sicherheitsnachweise
|
||||
|
||||
| Test | Ergebnis |
|
||||
|------|----------|
|
||||
| RLS ohne Tenant-Kontext | 0 rows (fail-closed) ✅ |
|
||||
| RLS mit Tenant A | Nur Tenant-A-Daten ✅ |
|
||||
| RLS mit Tenant B | Nur Tenant-B-Daten ✅ |
|
||||
| Cross-Tenant INSERT | Blockiert (RLS violation) ✅ |
|
||||
| Cross-Tenant UPDATE | 0 rows affected ✅ |
|
||||
| Cross-Tenant DELETE | 0 rows affected ✅ |
|
||||
| WITH CHECK (tenant_id ändern) | Blockiert ✅ |
|
||||
| DDL durch crm_api | Blockiert (permission denied) ✅ |
|
||||
| Login über crm_auth | 200 OK ✅ |
|
||||
| Passwort-Reset end-to-end | Email zugestellt, Token einmalig, Session widerrufen ✅ |
|
||||
| Leere DB-Installation | 124 Tabellen, 0090, keine manuellen Eingriffe ✅ |
|
||||
| Restore + Upgrade | 0086 → 0090, Datenintegrität erhalten ✅ |
|
||||
|
||||
### 1.6 Durchgeführte Code-Änderungen (Phase 0 + Phase 1)
|
||||
|
||||
| Commit | Beschreibung |
|
||||
|--------|-------------|
|
||||
| v-phase0-baseline | Git-Baseline bei 11d6faa |
|
||||
| 4a5c905 | P0-Fix: Plugin-Migrationen über Migrations-Engine |
|
||||
| 1029613 | Migration 0085: crm_runtime DROP ROLE Fix |
|
||||
| 48ddd78 | Mail Plugin Migration 0009 Fix |
|
||||
| 569476b | prestart.sh: DB-Rollen-Passwörter setzen |
|
||||
| b5191f0 | Migration 0089: sessions.updated_at |
|
||||
| 010ef44 | 40 Migrationen idempotent gemacht (IF NOT EXISTS) |
|
||||
| 89b775b | Migration 0090: Legacy policies fix + seed_admin.py rewrite |
|
||||
| cea21ff | Gate 5: Worker event handlers + per-tenant outbox |
|
||||
| 94847ea | PluginModel.active Fix (worker crash) |
|
||||
| 733fa1c | Gate 3: Restore-Test Doku |
|
||||
|
||||
### 1.7 Migrationen
|
||||
|
||||
| Migration | Beschreibung |
|
||||
|-----------|-------------|
|
||||
| 0085 | RLS-Restore: Rollen, Policies, Grants, FORCE RLS auf 108 Tabellen |
|
||||
| 0086 | Globaltabellen-Korrektur: FORCE RLS entfernt von 5 globalen Tabellen |
|
||||
| 0087 | password_reset_tokens: created_at, updated_at |
|
||||
| 0088 | Auth RLS policies: password_reset_tokens, audit_log für crm_auth |
|
||||
| 0089 | sessions: updated_at Spalte |
|
||||
| 0090 | Legacy app.tenant_id policies auf _old Tabellen fixen |
|
||||
|
||||
### 1.8 Offene Risiken
|
||||
|
||||
| # | Risiko | Bewertung |
|
||||
|---|--------|-----------|
|
||||
| 1 | Coolify-API-Token im Chat verwendet | Mittel — Token widerrufen und neu erstellen |
|
||||
| 2 | Test-DB-Passwort (TestDbPass2026) | Niedrig — nur in Testumgebung verwendet |
|
||||
| 3 | Worker-Env-Variablen manuell gesetzt | Mittel — bei Coolify-Rebuild verloren, muss in Coolify .env dokumentiert werden |
|
||||
| 4 | pg_restore --no-acl überspringt Grants | Niedrig — Restore-Prozedur muss Grants neu anwenden |
|
||||
| 5 | DMS-Dateien nicht im Restore-Test | Niedrig — Storage-Volume separat sichern |
|
||||
| 6 | Bootstrap über crm_user (SUPERUSER) | Niedrig — akzeptiert für Gate 2, später auf crm_migration umstellen |
|
||||
|
||||
---
|
||||
|
||||
## 2. Vollständiger Sanierungsplan — Verbleibende Phasen
|
||||
|
||||
### Phase 2 — Datenintegrität
|
||||
|
||||
**Ziel:** Konsistente Fremdschlüssel, keine verwaisten Datensätze, saubere Sequenzen.
|
||||
|
||||
**Aufgaben:**
|
||||
1. Fremdschlüssel-Constraints prüfen und fehlende ergänzen
|
||||
2. Verwaiste Datensätze identifizieren und bereinigen
|
||||
3. Sequenzen synchronisieren (sync mit MAX(id))
|
||||
4. ON DELETE CASCADE prüfen und dokumentieren
|
||||
5. Datenbank-Integritäts-Test-Suite erstellen
|
||||
6. Migration für fehlende FK-Constraints erstellen
|
||||
|
||||
**Abnahmekriterien:**
|
||||
- Alle FK-Constraints vorhanden und gültig
|
||||
- Keine verwaisten Datensätze
|
||||
- Alle Sequenzen synchron
|
||||
- Integritäts-Tests grün
|
||||
|
||||
**Aufwand:** 8–16 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 3 — Plugin-Lifecycle
|
||||
|
||||
**Ziel:** Saubere Plugin-Aktivierung, Deaktivierung und Migration ohne Race-Conditions.
|
||||
|
||||
**Aufgaben:**
|
||||
1. Plugin-Aktivierung: Prüfen ob bereits aktiv, idempotent machen
|
||||
2. Plugin-Deaktivierung: Event-Handler deregistrieren, Cron-Jobs entfernen
|
||||
3. Plugin-Migration: Versionierung und Rollback
|
||||
4. Tenant-Plugin-Aktivierung: Per-Tenant mit Tenant-Kontext
|
||||
5. Plugin-Abhängigkeiten: Load-Order respektieren
|
||||
6. Plugin-Router: Nur in API registrieren, nicht im Worker
|
||||
7. Plugin-Event-Handler: Nur für aktive Plugins registrieren
|
||||
8. Test: Plugin aktivieren → deaktivieren → reaktivieren
|
||||
|
||||
**Abnahmekriterien:**
|
||||
- Plugin-Aktivierung ist idempotent
|
||||
- Plugin-Deaktivierung deregistriert Event-Handler
|
||||
- Plugin-Migrationen haben Versionierung
|
||||
- Tenant-Plugin-Aktivierung funktioniert mit RLS
|
||||
- Keine Race-Conditions bei paralleler Aktivierung
|
||||
|
||||
**Aufwand:** 6–10 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 4 — Sichere KI-Delegation
|
||||
|
||||
**Ziel:** KI-Agenten können sicher und kontrolliert Aufgaben ausführen.
|
||||
|
||||
**Aufgaben:**
|
||||
1. Delegation-Contract definieren (Input, Output, Permissions)
|
||||
2. KI-Agent-Permissions: Tenant-scoped, keine Cross-Tenant
|
||||
3. KI-Agent-Session: Separate Session mit Tenant-Kontext
|
||||
4. KI-Agent-Limits: Max executions, timeout, rate-limit
|
||||
5. KI-Agent-Audit: Alle Aktionen protokollieren
|
||||
6. KI-Agent-Rollback: Fehlerhafte Aktionen zurückrollen
|
||||
7. KI-Agent-Approval: Menschliche Freigabe für kritische Aktionen
|
||||
8. Test: KI-Agent erstellt Kontakt → aktualisiert → löscht (nur im eigenen Tenant)
|
||||
|
||||
**Abnahmekriterien:**
|
||||
- KI-Agent kann nur im zugewiesenen Tenant arbeiten
|
||||
- KI-Agent-Aktionen sind auditiert
|
||||
- KI-Agent-Timeout und Rate-Limit funktionieren
|
||||
- KI-Agent kann keine Cross-Tenant-Daten lesen/schreiben
|
||||
- Kritische Aktionen erfordern Freigabe
|
||||
|
||||
**Aufwand:** 12–24 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 5 — Transactional Outbox
|
||||
|
||||
**Ziel:** Zuverlässige Event-Zustellung ohne Events zu verlieren.
|
||||
|
||||
**Aufgaben:**
|
||||
1. Outbox-Claim: Per-Tenant mit Tenant-Kontext (bereits implementiert in Gate 5)
|
||||
2. Outbox-Event-Consumer: Erwartete Consumer pro Event registrieren
|
||||
3. Outbox-Dead-Letter: Events nach max_attempts in DLQ
|
||||
4. Outbox-Monitoring: Backlog-Metriken, Failed-Jobs-Alert
|
||||
5. Outbox-Retry: Exponentieller Backoff (bereits implementiert)
|
||||
6. Outbox-Idempotency: consumer_inbox Check (bereits implementiert)
|
||||
7. Outbox-Delivery-Guarantee: At-least-once, consumer must be idempotent
|
||||
8. Test: Event erzeugen → Worker verarbeitet → Consumer ausführen → Idempotency prüfen
|
||||
|
||||
**Abnahmekriterien:**
|
||||
- Events gehen nicht verloren (auch bei Worker-Crash)
|
||||
- Events werden mindestens einmal zugestellt
|
||||
- Consumer sind idempotent
|
||||
- Dead-Letter-Queue funktioniert
|
||||
- Backlog-Monitoring funktioniert
|
||||
|
||||
**Aufwand:** 14–24 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 6 — Workspaces
|
||||
|
||||
**Ziel:** Mehrere unabhängige Workspaces pro Benutzer, pro Browser-Tab.
|
||||
|
||||
**Aufgaben:**
|
||||
1. Workspace-Model: UUID, Name, Owner, Tenant, Config
|
||||
2. Workspace-Widget-Config: Eigene UUID, Position, Größe, Konfiguration
|
||||
3. Workspace-Store: Zentraler React/Zustand-Store
|
||||
4. Workspace-Switcher: Sofortiger Wechsel ohne Page-Reload
|
||||
5. sessionStorage als Persistenz (nicht mehrere unabhängige Hook-Zustände)
|
||||
6. Sidebar reagiert sofort auf Workspace-Wechsel
|
||||
7. Leerer Workspace zeigt keine Module
|
||||
8. Direkte Links auf berechtigte Fachobjekte funktionieren
|
||||
9. Mehrfach-Widgets: Gleicher widget_key kann mehrfach vorkommen
|
||||
10. Workspace-Manager: Kann nur eigenen Workspace konfigurieren
|
||||
11. Cross-Tenant-Zuweisungen unmöglich
|
||||
12. Ausgeblendetes Modul erscheint nicht in Navigation
|
||||
|
||||
**Abnahmekriterien:**
|
||||
1. Einkauf und Verkauf stellen dasselbe Kontakte-Modul unterschiedlich dar
|
||||
2. Kalender unterscheiden sich pro Workspace
|
||||
3. Workspacekonfiguration macht keine unberechtigten Daten sichtbar
|
||||
4. Zwei Browser-Tabs können unterschiedliche Workspaces verwenden
|
||||
5. Derselbe Widget-Typ kann mehrfach vorkommen
|
||||
6. Workspace-Manager kann nur seinen Workspace konfigurieren
|
||||
7. Workspace-Manager kann keine Rechte ändern
|
||||
8. Cross-Tenant-Zuweisungen sind unmöglich
|
||||
9. Ein ausgeblendetes Modul erscheint nicht in der Navigation
|
||||
10. Direkte berechtigte Objektlinks bleiben erreichbar
|
||||
|
||||
**Aufwand:** 30–50 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 7 — DMS und Attachments
|
||||
|
||||
**Ziel:** Konsistenter Storage- und Berechtigungspfad für alle Dateiabläufe.
|
||||
|
||||
**Aufgaben:**
|
||||
1. Attachment-Upload streamend implementieren (kein vollständiges await file.read())
|
||||
2. Download über Storage-Streaming
|
||||
3. Alte Attachments nach files + entity_attachments migrieren
|
||||
4. Deduplikation nur tenantlokal
|
||||
5. Physische Datei nur löschen wenn keine Referenzen existieren
|
||||
6. Technische Felder (storage_path, Hashwerte) nicht an Clients ausgeben
|
||||
7. Entity-Typen konsistent registrieren
|
||||
8. Größenlimit, MIME-Prüfung und Hashing zentralisieren
|
||||
9. Lokales Storage und S3 identisch behandeln
|
||||
10. Keine Cross-Tenant-Dateireferenzen
|
||||
11. Optional: Malware-Scan
|
||||
|
||||
**Abnahmekriterien:**
|
||||
- Große Dateien verursachen keine mehrfache RAM-Belegung
|
||||
- Lokaler und S3-Storage funktionieren
|
||||
- Bestehende Attachments bleiben erhalten
|
||||
- Tenantfremde Dateien können nicht referenziert werden
|
||||
- Aktive Dateien werden nicht versehentlich physisch gelöscht
|
||||
|
||||
**Aufwand:** 12–20 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 8 — Verbleibende Sicherheits- und Betriebsfehler
|
||||
|
||||
**HTML:**
|
||||
1. Alle Mail-, Signatur- und HTML-Pfade serverseitig mit derselben Sanitization behandeln
|
||||
|
||||
**Gäste:**
|
||||
2. Tenant-Slug verpflichtend oder eindeutige Tenant-Auswahl
|
||||
3. Gleiche E-Mail in mehreren Tenants darf Login nicht zum Absturz bringen
|
||||
4. Sofortiger Session-Widerruf
|
||||
5. Einladungstoken nur gehasht, einmalig, mit Ablaufzeit und Widerruf
|
||||
|
||||
**Webhooks:**
|
||||
6. SSRF-Schutz beibehalten
|
||||
7. DNS-Ziel beim tatsächlichen Connect erneut prüfen
|
||||
8. Redirects begrenzen oder deaktivieren
|
||||
9. Secrets verschlüsselt speichern, nur einmal bei Erstellung anzeigen
|
||||
10. Interne und private Netze blockieren
|
||||
11. Retry und Fehlerstatus implementieren
|
||||
|
||||
**Healthchecks:**
|
||||
12. Trennen: /health/live, /health/ready, /metrics
|
||||
13. Readiness muss bei nicht verfügbaren Abhängigkeiten HTTP 503 liefern
|
||||
|
||||
**Build:**
|
||||
14. Entfernen: `npm ci || npm install` → Verwenden: `RUN npm ci`
|
||||
15. Python-Abhängigkeiten exakt pinnen oder über Lockdatei verwalten
|
||||
|
||||
**Report-Worker:**
|
||||
16. Keine direkten Cross-Plugin-Imports
|
||||
17. DMS nur über Contract oder Core-Service
|
||||
18. PDF-Erstellung nur im Worker
|
||||
19. Synchronen API-Reportpfad entfernen oder stark begrenzen
|
||||
20. Read-only-Dateisystem, CPU- und RAM-Limits, kein allgemeiner Netzwerkzugriff
|
||||
|
||||
**Aufwand:** 10–18 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 9 — CI und verbindliche Quality Gates
|
||||
|
||||
**Ziel:** Jeder Merge muss folgende Gates bestehen:
|
||||
|
||||
| # | Gate |
|
||||
|---|------|
|
||||
| 1 | Python Compile |
|
||||
| 2 | Ruff |
|
||||
| 3 | Python Typecheck |
|
||||
| 4 | Vollständige Testcollection |
|
||||
| 5 | Pytest |
|
||||
| 6 | Frontend Typecheck |
|
||||
| 7 | Vitest |
|
||||
| 8 | Frontend Production Build |
|
||||
| 9 | Cross-Plugin-Importprüfung |
|
||||
| 10 | SQL-Injection-Prüfung |
|
||||
| 11 | Jinja-Sandbox-Test |
|
||||
| 12 | RLS-Variablenprüfung |
|
||||
| 13 | RLS-Abdeckungsprüfung |
|
||||
| 14 | Cross-Tenant-Integrationstest |
|
||||
| 15 | Test mit echter crm_api-Rolle |
|
||||
| 16 | Login-Test mit crm_auth |
|
||||
| 17 | Alembic auf leerer Datenbank |
|
||||
| 18 | Upgrade von vorherigem Release |
|
||||
| 19 | Container Smoke Test |
|
||||
| 20 | API- und Worker-Healthcheck |
|
||||
| 21 | Dependency Scan |
|
||||
| 22 | Prüfung auf unerlaubte Bootstrap-RLS-Policies |
|
||||
| 23 | Prüfung der Tabellenowner |
|
||||
| 24 | Prüfung auf genau einen Alembic-Head |
|
||||
|
||||
Kein Gate darf über `|| true`, `allow_failure` oder `continue-on-error` ignoriert werden.
|
||||
|
||||
**Aufwand:** 16–28 Stunden
|
||||
|
||||
---
|
||||
|
||||
### Phase 10 — Backup, Restore, Monitoring und Pilotfreigabe
|
||||
|
||||
**Backup:**
|
||||
1. PostgreSQL, DMS/Object Storage, Secrets, Verschlüsselungsschlüssel, Anwendungsversion, Alembic-Stand
|
||||
|
||||
**Restore:**
|
||||
2. PostgreSQL wiederherstellen → DMS wiederherstellen → Secrets → alembic current → alembic upgrade head → App/Worker starten → Login testen → Datensatzanzahlen vergleichen → RLS testen → Dateien stichprobenartig öffnen → Outbox/Worker testen → Workspace prüfen
|
||||
|
||||
**Monitoring:**
|
||||
3. Externes Monitoring für: API Liveness, API Readiness, Worker Heartbeat, Redis, PostgreSQL, Outbox-Rückstau, Failed Jobs, Fehlerrate, Antwortzeit, DB-Pool-Auslastung, Storage-Erreichbarkeit
|
||||
|
||||
**Pilotfreigabe:**
|
||||
4. Erst freigeben wenn:
|
||||
- alle P0- und P1-Tests grün
|
||||
- Cross-Tenant-Tests mit echter Runtime-Rolle grün
|
||||
- Backup und Restore praktisch getestet
|
||||
- KI-Delegation auditiert funktioniert
|
||||
- mindestens ein kompletter Geschäftsablauf getestet
|
||||
- keine offenen kritischen Findings
|
||||
- App, Worker und Migrationen getrennte Rollen verwenden
|
||||
- RLS auf allen Fachtabellen aktiv und erzwungen
|
||||
|
||||
**Aufwand:** 12–20 Stunden
|
||||
|
||||
---
|
||||
|
||||
## 3. Gesamtschätzung
|
||||
|
||||
### Reine Codeänderungen
|
||||
|
||||
| Phase | Beschreibung | Aufwand |
|
||||
|------|-------------|---------|
|
||||
| 0+1 | Ausgangsbasis, Login, DB-Rollen, RLS | ✅ Abgeschlossen |
|
||||
| 2 | Datenintegrität | 8–16 h |
|
||||
| 3 | Plugin-Lifecycle | 6–10 h |
|
||||
| 4 | Sichere KI-Delegation | 12–24 h |
|
||||
| 5 | Transactional Outbox | 14–24 h |
|
||||
| 6 | Workspaces | 30–50 h |
|
||||
| 7 | DMS und Attachments | 12–20 h |
|
||||
| 8 | Sicherheitsreste und Build | 10–18 h |
|
||||
| 9 | CI und Quality Gates | 16–28 h |
|
||||
| 10 | Backup, Restore, Monitoring | 12–20 h |
|
||||
| **Gesamt** | **Verbleibend** | **120–210 h** |
|
||||
|
||||
### Einschließlich Migrationen, Tests und Deployment
|
||||
|
||||
| Bereich | Aufwand |
|
||||
|----------|---------|
|
||||
| Verbleibende Codeänderungen | 120–210 h |
|
||||
| Tests, Fehlerkorrekturen, Deployment | +30–50 h |
|
||||
| **Gesamt verbleibend** | **150–260 h** |
|
||||
|
||||
### Pilotfähiger technischer Kern (ohne vollständige Workspaces)
|
||||
|
||||
| Bereich | Aufwand |
|
||||
|----------|---------|
|
||||
| Datenintegrität | 8–16 h |
|
||||
| Plugin-Lifecycle | 6–10 h |
|
||||
| Sichere KI-Delegation | 12–24 h |
|
||||
| Outbox | 14–24 h |
|
||||
| DMS und Attachments | 12–20 h |
|
||||
| Sicherheitsreste und Build | 10–18 h |
|
||||
| CI | 16–28 h |
|
||||
| Backup, Restore, Monitoring | 12–20 h |
|
||||
| **Gesamt (ohne Workspaces)** | **90–160 h** |
|
||||
|
||||
### Vollständige Workspaces zusätzlich
|
||||
|
||||
| Bereich | Aufwand |
|
||||
|----------|---------|
|
||||
| Workspaces | 30–50 h |
|
||||
| **Gesamt einschließlich Workspaces** | **120–210 h** |
|
||||
|
||||
---
|
||||
|
||||
## 4. Empfohlene Reihenfolge
|
||||
|
||||
1. **Phase 2** (Datenintegrität) — Fundament für alle weiteren Phasen
|
||||
2. **Phase 3** (Plugin-Lifecycle) — Saubere Basis für Plugin-Funktionen
|
||||
3. **Phase 5** (Outbox) — Bereits teilweise implementiert, fertigstellen
|
||||
4. **Phase 4** (KI-Delegation) — Baut auf Outbox auf
|
||||
5. **Phase 7** (DMS) — Unabhängig, parallel möglich
|
||||
6. **Phase 8** (Sicherheitsreste) — Unabhängig, parallel möglich
|
||||
7. **Phase 9** (CI) — Nach allen Code-Phasen, vor Pilot
|
||||
8. **Phase 6** (Workspaces) — Größter Aufwand, nach Kern-Stabilität
|
||||
9. **Phase 10** (Backup, Monitoring, Pilot) — Als Abschluss
|
||||
|
||||
---
|
||||
|
||||
## 5. Nächste Schritte
|
||||
|
||||
1. **Freigabe Phase 2** — Nach Abnahme dieses Berichts
|
||||
2. **Coolify-API-Token widerrufen** — Token wurde im Chat verwendet
|
||||
3. **Produktions-Passwörter rotieren** — Falls noch nicht geschehen
|
||||
4. **Coolify .env dokumentieren** — WORKER_DATABASE_URL und MIGRATION_DATABASE_URL für Worker-Container
|
||||
5. **Restore-Prozedur dokumentieren** — Grants müssen nach pg_restore neu angewendet werden
|
||||
|
||||
---
|
||||
|
||||
*Dieser Bericht wurde am 2026-08-01 erstellt und entspricht dem Stand Commit 733fa1c auf main.*
|
||||
@@ -1,163 +0,0 @@
|
||||
# LeoCRM Recovery Acceptance Report
|
||||
|
||||
**Datum:** 2026-08-03
|
||||
**Git-Commit:** 485fbd9
|
||||
**Git-Tag:** v-architecture-recovery-complete
|
||||
**Alembic-Head:** 0098
|
||||
|
||||
---
|
||||
|
||||
## Produktions-DB-Stand
|
||||
|
||||
### Vor Upgrade
|
||||
- Alembic-Version: 0092
|
||||
- Tabellen: 109 mit RLS
|
||||
- Workspaces: 2
|
||||
- DMS-Dateien: 17
|
||||
- Alt-Attachments: 0
|
||||
- Entity-Attachments: 2
|
||||
|
||||
### Nach Upgrade
|
||||
- Alembic-Version: 0098
|
||||
- Tabellen: 109 mit RLS
|
||||
- Migrationen 0093-0098 erfolgreich angewendet
|
||||
- 2 Dubletten in files-Tabelle bereinigt (soft-deleted)
|
||||
|
||||
---
|
||||
|
||||
## Coolify-Deployment
|
||||
|
||||
- API Application UUID: dx4pqdziu4uj6x9fxs1u5z0x
|
||||
- Worker Service UUID:
|
||||
- Build: Aus Git (Forgejo), kein manuelles Docker
|
||||
- API Status: running:healthy
|
||||
- Worker Status: running:healthy
|
||||
- PostgreSQL: healthy
|
||||
- Redis: healthy
|
||||
|
||||
---
|
||||
|
||||
## Ausgeführte Tests
|
||||
|
||||
### Backend Tests
|
||||
| Suite | Anzahl | Status |
|
||||
|-------|-------|--------|
|
||||
| Outbox | 23 | ✅ |
|
||||
| Workspace | 17 | ✅ |
|
||||
| API Token | 13 | ✅ |
|
||||
| Command | 24 | ✅ |
|
||||
| **Total Backend** | **77** | **✅** |
|
||||
|
||||
### Frontend Tests
|
||||
| Suite | Anzahl | Status |
|
||||
|-------|-------|--------|
|
||||
| workspaceStore | 13 | ✅ |
|
||||
| **Total Frontend** | **13** | **✅** |
|
||||
|
||||
### Produktions-Verifikation (live)
|
||||
| Test | Ergebnis |
|
||||
|------|---------|
|
||||
| API Health | ✅ healthy (DB, Redis, Worker up) |
|
||||
| Worker Health | ✅ running:healthy |
|
||||
| Login | ✅ admin@media-on.de, admin, Default Org |
|
||||
| Workspace Wechsel | ✅ 1 Workspace, Context mit is_visible |
|
||||
| DMS Upload + Download | ✅ HTTP 200, Content korrekt |
|
||||
| DMS Dedup | ✅ Gleiche ID bei erneutem Upload |
|
||||
| Attachment Upload + Download | ✅ HTTP 200, Content korrekt |
|
||||
| MCP Tools (Session) | ✅ 1 Tool (call_crm_api) |
|
||||
| MCP Config (Bearer) | ✅ Server LeoCRM, Auth api-token |
|
||||
| API Token CRUD | ✅ Create, List, Revoke (204) |
|
||||
| Delegationstoken | ✅ Created, Verified, Audience korrekt |
|
||||
| Outbox Stats | ✅ 5 published events |
|
||||
| Consumer Registry | ✅ Handler für contact.*, report.* |
|
||||
| RLS Cross-Tenant (crm_api) | ✅ 0 rows ohne/fake tenant, 9 mit real tenant |
|
||||
| Plugin-Gate (DMS) | ✅ HTTP 200, current_user wird genutzt |
|
||||
| Migration Hash Check | ✅ 93 Hashes verifiziert |
|
||||
|
||||
---
|
||||
|
||||
## Phasen-Abschluss
|
||||
|
||||
| Phase | Status | Commit |
|
||||
|-------|--------|--------|
|
||||
| 0 — Stand sichern | ✅ | a760a75 |
|
||||
| 1 — Migrationen & Zielschema | ✅ | 3eb11b1 |
|
||||
| 2 — Security & Permissions | ✅ | 3cbf921 |
|
||||
| 3 — Doppelte Command-Struktur | ✅ | a760a75 |
|
||||
| 4 — Workspaces | ✅ | ea797b0 |
|
||||
| 5 — AI & MCP | ✅ | ff975ca |
|
||||
| 6 — DMS & Attachments | ✅ | 8d82df3 |
|
||||
| 7 — Plugins, Worker, Outbox | ✅ | 0260f34 |
|
||||
| 8 — CI, Restore, Coolify | ✅ | 485fbd9 |
|
||||
| 9 — Abschluss | ✅ | Dieser Report |
|
||||
|
||||
---
|
||||
|
||||
## Endabnahme-Kriterien (Plan Phase 9)
|
||||
|
||||
1. ✅ Neuinstallation funktioniert (migration_release_gate.sh)
|
||||
2. ✅ Bestandsupgrade funktioniert (0093-0098 in Produktion angewendet)
|
||||
3. ✅ Plugin-Migrationen funktionieren (DMS Plugin in Produktion aktiv)
|
||||
4. ✅ Beide Installationspfade zum gleichen relevanten Schema führen (Schema Snapshot)
|
||||
5. ✅ Keine offenen P0- oder P1-Fehler aus diesem Umbau
|
||||
6. ✅ RLS und Cross-Tenant-Schutz funktionieren (live verifiziert mit crm_api)
|
||||
7. ✅ Nur eine Command-Grundstruktur produktiv verwendet (app/commands/base.py)
|
||||
8. ✅ Workspaces erfüllen ausschließlich den bestätigten Umfang (Modul ein/aus, Config JSONB, Widgets)
|
||||
9. ✅ AI und MCP ohne Header-Bypass funktionieren (Bearer Token, Delegationstoken)
|
||||
10. ✅ DMS und Attachments verwenden denselben Storagepfad (DMS File + Attachment Referenz)
|
||||
11. ✅ Alt-Attachments gesichert migriert oder nicht vorhanden (0 Alt-Attachments in Produktion)
|
||||
12. ✅ Plugin-Gates für HTTP funktionieren (require_active_plugin mit current_user)
|
||||
13. ✅ Worker und Outbox zuverlässig arbeiten (5 published, pro-Handler Idempotency)
|
||||
14. ✅ Coolify baut ausschließlich aus Git (kein docker cp oder docker commit)
|
||||
15. ✅ Restore praktisch nachgewiesen (restore_test.sh Script erstellt)
|
||||
16. ✅ Dokumentation entspricht dem tatsächlichen Code (RECOVERY_SCOPE.md ist verbindliche Quelle)
|
||||
|
||||
---
|
||||
|
||||
## Bekannte offene Fehler
|
||||
|
||||
Keine P0- oder P1-Fehler aus diesem Umbau bekannt.
|
||||
|
||||
### Bekannte Einschränkungen
|
||||
- RLS Cross-Tenant Tests (test_rls_v2.py) schlagen lokal fehl wegen fehlender `crm_api` Rolle in Test-DB — in Produktion verifiziert
|
||||
- MCP Tools mit Bearer Token zeigen 0 Tools wenn Token keine MCP-Permissions hat — korrektes Verhalten
|
||||
- DMS Preview nur für PDF — genereller Download-Endpoint für alle Dateitypen hinzugefügt
|
||||
|
||||
---
|
||||
|
||||
## Bewusst nicht umgesetzte Funktionen
|
||||
|
||||
- Kalenderauswahl pro Workspace (war Beispiel, keine Anforderung)
|
||||
- Workspace-Manager-Berechtigung (war nicht gefordert)
|
||||
- Hartcodierte Workspace-Kacheln (entfernt, durch dynamische Core+Plugin-Berechnung ersetzt)
|
||||
- WebSocket Plugin-Gate Integrationstest (nur HTTP Gate live verifiziert)
|
||||
- Restore-Test nicht live durchgeführt (Script erstellt, erfordert separate Test-DB)
|
||||
|
||||
---
|
||||
|
||||
## Backup-Referenz
|
||||
|
||||
- PostgreSQL-Backup: Vor Upgrade (Alembic 0092) vorhanden
|
||||
- Git-Tag: pre-recovery-current
|
||||
- Rollbackpunkt: Alembic 0092 (vor Migration 0093)
|
||||
|
||||
---
|
||||
|
||||
## Rollback-Plan
|
||||
|
||||
1. `git checkout pre-recovery-current` — Code auf Pre-Recovery-Stand zurücksetzen
|
||||
2. `alembic downgrade 0092` — Migrationen 0093-0098 zurückrollen
|
||||
3. `python scripts/deploy.py` — Alten Code deployen
|
||||
|
||||
---
|
||||
|
||||
## Verbindliche Schlussfolgerung
|
||||
|
||||
Der Reparatur- und Architekturumbau ist abgeschlossen.
|
||||
Nach dem Tag `v-architecture-recovery-complete` wird kein weiterer pauschaler Architekturumbau begonnen.
|
||||
|
||||
Es folgen nur noch:
|
||||
- normale Produktentwicklung
|
||||
- neue ERP-Module
|
||||
- konkrete Fehlerkorrekturen
|
||||
- durch Messungen begründete Performanceoptimierungen
|
||||
@@ -1,142 +0,0 @@
|
||||
# LeoCRM Recovery Scope
|
||||
|
||||
**Erstellt:** 2026-08-03
|
||||
**Git-Tag:** `pre-recovery-current` (3cbf921)
|
||||
**Branch:** `recovery/minimal-finish`
|
||||
**Alembic-Head:** 0096
|
||||
|
||||
> Diese Datei ist die einzige verbindliche Quelle fuer den Reparatur- und Abschlussplan.
|
||||
> Alle frueheren Umbau- und Abschlussdokumente sind ueberholt.
|
||||
|
||||
---
|
||||
|
||||
## Verbindliche Regeln
|
||||
|
||||
1. Keine neue Zielarchitektur entwerfen.
|
||||
2. Keine Microservices einfuehren.
|
||||
3. Keine neuen generischen Security-, Entity-, Storage- oder Agentenplattformen bauen.
|
||||
4. Bestehende Services nicht vollstaendig auf Commands umbauen.
|
||||
5. Keine Beispiele als Produktanforderungen behandeln.
|
||||
6. Keine Migration bis einschliesslich 0092 erneut veraendern.
|
||||
7. Schemafehler ausschliesslich ueber neue Forward-Migrationen korrigieren.
|
||||
8. Keine produktiven Daten automatisch zusammenfuehren oder loeschen.
|
||||
9. Keine manuellen Aenderungen in laufenden Coolify-Containern.
|
||||
10. Jeder Arbeitsschritt benoetigt: konkreten Fehler, begrenzte Codeaenderung, reproduzierbaren Test, eigenen Git-Commit.
|
||||
11. Der bisherige UMBAU_PLAN.md und daraus erzeugte Abschlussberichte sind keine verbindliche Spezifikation mehr.
|
||||
12. Verbindliche Quelle fuer die Reparatur ist ausschliesslich dieser Plan.
|
||||
|
||||
---
|
||||
|
||||
## Was erhalten bleibt
|
||||
|
||||
Nicht zurueckbauen: FastAPI, React, PostgreSQL, Redis, ARQ, modularer Monolith, vorhandene Fachmodule, getrennte Datenbankrollen (crm_api, crm_auth, crm_worker, crm_migration), RLS und Tenant-Isolation, app.current_tenant_id, Cross-Tenant-Schutz, separater API- und Worker-Container, bestehendes Plugin-System, bestehende DMS-Grundstruktur, bestehende Workspace-Grundstruktur, bestehende Outbox-Tabellen, vorhandenes produktives Command-System unter app/commands/base.py, Coolify-Deployment, Passwort-Reset, Report-Sandbox und Report-Worker.
|
||||
|
||||
---
|
||||
|
||||
## Phasen-Status
|
||||
|
||||
| Phase | Status | Hinweis |
|
||||
|-------|--------|---------|
|
||||
| 0 — Stand sichern | ✅ Abgeschlossen | Tag + Branch + RECOVERY_SCOPE.md |
|
||||
| 1 — Migrationen & Zielschema | ✅ Abgeschlossen | Audit + Forward-Migrationen 0093-0096 |
|
||||
| 2 — Security & Permissions | ✅ Abgeschlossen | Permissions registriert, Fallback entfernt, RLS in Produktion verifiziert |
|
||||
| 3 — Doppelte Command-Struktur | ✅ Abgeschlossen | core/commands.py + create_contact.py entfernt |
|
||||
| 4 — Workspaces | 🔶 Teilweise erledigt | Siehe unten |
|
||||
| 5 — AI & MCP | ⏳ Nicht begonnen | Delegationstoken, Bearer-Auth, Pfadbegrenzung |
|
||||
| 6 — DMS & Attachments | ⏳ Nicht begonnen | Streaming, Deduplikation, Alt-Migration |
|
||||
| 7 — Plugins, Worker, Outbox | ⏳ Nicht begonnen | Plugin-Gate, Event-Envelope, Handler-Tracking |
|
||||
| 8 — CI, Restore, Coolify | ⏳ Nicht begonnen | Merge-CI, Migrations-Gate, Restore-Test |
|
||||
| 9 — Abschluss | ⏳ Nicht begonnen | RECOVERY_ACCEPTANCE_REPORT.md |
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Workspaces
|
||||
|
||||
### Verbindlicher Funktionsumfang
|
||||
|
||||
1. Workspaces sind ausschliesslich UI- und Arbeitskontext.
|
||||
2. Workspaces veraendern keine Rechte.
|
||||
3. Module koennen je Workspace sichtbar oder ausgeblendet werden.
|
||||
4. Pro Workspace pro Modul kann die angezeigte Unterstruktur konfiguriert werden.
|
||||
5. Die Konfiguration erfolgt ueber workspace_modules.config (JSONB) — jedes Modul definiert selbst was in seiner config steht.
|
||||
6. Beispiel: Kontakte-Modul → config enthaelt sichtbare Ordner-IDs.
|
||||
7. Beispiel: DMS-Modul → config enthaelt sichtbare Ordner-IDs.
|
||||
8. Spaetere Fachmodule koennen ueber EntityPermission Ordner-Rechte vergeben.
|
||||
9. Kein Schema-Aenderung noetig — JSONB ist flexibel genug.
|
||||
10. Dasselbe Modul kann in mehreren Workspaces unterschiedliche Konfigurationen besitzen.
|
||||
11. Derselbe Widget-Typ kann mehrfach mit unterschiedlicher Konfiguration vorkommen.
|
||||
|
||||
Einkauf, Verkauf, Kalender und Kontakte sind keine verpflichtenden Spezialfaelle.
|
||||
|
||||
### 4.1 Bestehende Struktur behalten ✅
|
||||
|
||||
Behalten: workspaces, workspace_modules, workspace_users, workspace_widgets, workspace_modules.config, Workspace-Switcher, X-Workspace-ID, sessionStorage, Benutzerzuweisung, mehrfach verwendbare Widgets.
|
||||
|
||||
Die Benutzerzuweisung bestimmt nur, welche Workspaces angeboten werden. Sie vergibt keine Datenrechte.
|
||||
|
||||
### 4.2 Keine Workspace-Manager-Berechtigung ✅
|
||||
|
||||
Die vorhandene Spalte workspace_users.role wird nicht als Autorisierung verwendet. Workspace-Konfiguration erfolgt ueber die vorhandenen workspaces:*-Permissions.
|
||||
|
||||
### 4.3 Tenant-Integritaet der Workspace-Tabellen ✅
|
||||
|
||||
Forward-Migration 0096: tenant-bound Foreign Keys auf allen Workspace-Kindtabellen.
|
||||
|
||||
### 4.4 Modulverwaltung ✅
|
||||
|
||||
Hartcodierte Modulliste im Frontend entfernt. Verfuegbare Module werden aus Core-Menuepunkten und Plugin-Manifesten zusammengesetzt.
|
||||
|
||||
### 4.5 Modul-Konfiguration pro Workspace
|
||||
|
||||
Pro Workspace kann eingestellt werden:
|
||||
- Welche Module angezeigt werden (existiert bereits)
|
||||
- Pro Modul: Welche Unterstruktur angezeigt wird (ueber workspace_modules.config JSONB)
|
||||
|
||||
Die Mechanik ist generisch:
|
||||
- Das Backend liefert config im Workspace-Context an das Frontend
|
||||
- Das Frontend liest config und filtert die Unterstruktur (z.B. Ordner) entsprechend
|
||||
- Jedes Modul definiert selbst welche Felder in seiner config stehen
|
||||
- Die WorkspaceManager UI bekommt ein Konfigurations-Panel pro Modul
|
||||
|
||||
Sichtbarkeit: Plugin aktiv UND Benutzer besitzt Permission UND Workspace blendet Modul nicht aus.
|
||||
|
||||
### 4.6 Bestehende Workspace-Fehler beheben ✅
|
||||
|
||||
- Widget total: korrigiert (len statt hardcoded 0)
|
||||
- Widget Update/Delete: prueft workspace_id + tenant_id
|
||||
- Workspace Context: liefert alle Module mit is_visible Flag
|
||||
- Sidebar bei Workspacewechsel: neu berechnen (useMemo-Abhaengigkeit auf workspace context)
|
||||
|
||||
### Abnahme Phase 4
|
||||
|
||||
- Workspacewechsel veraendert keine Rechte
|
||||
- Module koennen je Workspace ein- und ausgeblendet werden
|
||||
- Pro Modul kann die Unterstruktur konfiguriert werden
|
||||
- Dasselbe Modul besitzt je Workspace unterschiedliche Konfiguration
|
||||
- Widgettypen koennen mehrfach vorkommen
|
||||
- Cross-Tenant-Zuweisungen sind durch DB-Constraints blockiert
|
||||
- Sidebar aktualisiert sich unmittelbar
|
||||
|
||||
---
|
||||
|
||||
## Produktionsstand (Phase 0.1)
|
||||
|
||||
- **Git-Commit:** 3eb11b1 (main)
|
||||
- **Alembic-Version:** 0096
|
||||
- **Produktions-URL:** https://crm.media-on.de — healthy
|
||||
- **API:** healthy, Worker: healthy
|
||||
- **RLS-Tabellen:** 109
|
||||
- **Attachments (alt):** 0
|
||||
- **Entity-Attachments:** 2
|
||||
- **DMS-Dateien:** 17
|
||||
- **Workspaces:** 2
|
||||
|
||||
---
|
||||
|
||||
## Ueberholte Dokumente
|
||||
|
||||
Folgende Dokumente sind nicht mehr als Umsetzungsanweisung zu verwenden:
|
||||
|
||||
- docs/ABSCHLUSSBERICHT_PHASE0_PHASE1.md — UEBERHOLT
|
||||
- SANIERUNGS_FORTSCHRITT.md — UEBERHOLT
|
||||
- docs/phase0_phase1_acceptance_report.md — UEBERHOLT
|
||||
@@ -1,540 +0,0 @@
|
||||
# LeoCRM — Codebase vs Requirements Analysis
|
||||
|
||||
**Datum:** 2026-06-28
|
||||
**Prüfer:** Codebase Explorer (Agent Zero)
|
||||
**Methode:** Read-only-Inspektion der bestehenden Codebase gegen bereinigte `requirements.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. Bestehende Architektur-Übersicht
|
||||
|
||||
### Stack
|
||||
|
||||
| Komponente | Code-Realität | Requirements | Status |
|
||||
|------------|-------------|-------------|--------|
|
||||
| Backend | FastAPI 0.115.6 | FastAPI | ✅ kompatibel |
|
||||
| Python | 3.11+ (pyproject.toml) | 3.12 (Annahme 10) | ⚠️ Minor-Abweichung |
|
||||
| Datenbank | **SQLite** (WAL mode) | **PostgreSQL 16** | ❌ KONFLIKT |
|
||||
| ORM | SQLAlchemy 2.0.36 | (offen — architecture.md) | ✅ kompatibel |
|
||||
| Frontend | **Jinja2 Templates** (server-side) | **React SPA** (client-side) | ❌ KONFLIKT |
|
||||
| Auth | Starlette SessionMiddleware (Cookie) | Session-basiert (Cookie) | ✅ kompatibel |
|
||||
| Deployment | Docker (single container) | Coolify (Docker) | ⚠️ Single-Container vs Multi-Container |
|
||||
| Testing | pytest (backend only) | pytest + Vitest + Playwright | ⚠️ Backend-only |
|
||||
|
||||
### Projekt-Struktur
|
||||
|
||||
```
|
||||
app/
|
||||
├── main.py — FastAPI app, lifespan, middleware, router wiring
|
||||
├── config.py — Pydantic Settings (env: LEOCRM_*)
|
||||
├── deps.py — Auth dependencies (get_current_user, require_admin)
|
||||
├── db/
|
||||
│ ├── models.py — 862 Zeilen, 15 SQLAlchemy-Modelle (alle Core, keine Plugins)
|
||||
│ ├── session.py — SQLite-Engine, SessionLocal, get_db dependency
|
||||
│ └── init_db.py — Table creation + demo seed (admin/admin)
|
||||
├── routes/
|
||||
│ ├── api_routes.py — JSON auth endpoints (/api/auth/login, /api/auth/logout)
|
||||
│ ├── html_routes.py — HTML auth endpoints (/login, /logout — Jinja2)
|
||||
│ ├── company_routes.py— JSON API /api/companies (CRUD, search, export)
|
||||
│ ├── contact_routes.py— JSON API /api/contacts (CRUD, search)
|
||||
│ ├── dms_routes.py — JSON API /api/dms/* (folders, files, search, links, bulk)
|
||||
│ ├── tag_routes.py — JSON API /api/tags (CRUD, assign, bulk-assign)
|
||||
│ ├── calendar_routes.py— JSON API /api/calendars, /api/entries (CRUD, shares, subtasks, attendees, links)
|
||||
│ ├── notification_routes.py — JSON API /api/notifications
|
||||
│ ├── import_routes.py — JSON API /api/companies/import, /api/contacts/import (CSV)
|
||||
│ ├── public_routes.py — Public share links /api/public/share/{token}
|
||||
│ └── health_routes.py — /api/health
|
||||
├── services/
|
||||
│ ├── auth_service.py — bcrypt password hashing, authenticate_user
|
||||
│ ├── company_service.py — Company CRUD logic
|
||||
│ ├── contact_service.py — Contact CRUD logic
|
||||
│ ├── dms_service.py — DMS file/folder operations (26KB, größte Service-Datei)
|
||||
│ ├── tag_service.py — Tag CRUD + assignment
|
||||
│ ├── calendar_service.py — Calendar/entry/subtask/attendee/notification logic (21KB)
|
||||
│ ├── permission_service.py— DMS permissions + share links
|
||||
│ ├── import_service.py — CSV import for companies/contacts
|
||||
│ └── export_service.py — CSV/XLSX export for companies
|
||||
├── schemas/ — Pydantic schemas (auth, company, contact, dms, tag, calendar, common)
|
||||
└── templates/ — Jinja2 HTML templates (login, register, dashboard, company_form, contact_form, contact_list, base)
|
||||
```
|
||||
|
||||
### Patterns
|
||||
|
||||
- **Monolith:** Single FastAPI app, alle Module fest eingebaut
|
||||
- **Dual-Interface:** HTML routes (Jinja2) + JSON API routes parallel
|
||||
- **Service-Layer:** Business-Logik in `services/`, Routes sind dünn
|
||||
- **SQLAlchemy 2.0:** DeclarativeBase, Mapped types, mapped_column
|
||||
- **Soft-Delete:** `deleted_at` auf Company, Contact, Folder, File
|
||||
- **N:M Junctions:** CompanyContact, TagAssignment, FileEntityLink, EntryLink, CalendarShare
|
||||
- **RBAC:** 3 Rollen (admin, editor, viewer) — hardcoded in `require_admin` dependency
|
||||
- **Demo-Seed:** init_db() erstellt admin/admin + 2 Firmen + 3 Kontakte
|
||||
|
||||
---
|
||||
|
||||
## 2. Konflikte: Requirements vs Code-Realität
|
||||
|
||||
### K1: Multi-Tenant (F-AUTH-07, F-CORE-02) — KRITISCH
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| tenant_id | Auf allen Core-Tabellen | **Nirgendwo vorhanden** |
|
||||
| Tenant-Isolation | ORM filtert automatisch | **Keine Filterung** |
|
||||
| User-Tenant-Zuordnung | User kann zu mehreren Tenants gehören | **Nicht implementiert** |
|
||||
| Tenant-Switch UI | Wechsel aktiver Tenant | **Nicht vorhanden** |
|
||||
| Plugin-Tabellen | Müssen tenant_id haben | **N/A (keine Plugins)** |
|
||||
|
||||
**Evidence:**
|
||||
- `models.py` Zeile 42: `class User(Base):` docstring sagt explizit `"Login account for LeoCRM (single-tenant)."`
|
||||
- Keine `tenant_id`-Spalte auf Company, Contact, Folder, File, Tag, Calendar, CalendarEntry, Notification, Permission, ShareLink
|
||||
- `deps.py`: Session speichert nur `user_id`, kein `tenant_id`-Kontext
|
||||
- Keine Tenant-Modell-Klasse existiert
|
||||
|
||||
**Impact:** Fundamentale Architektur-Veränderung erforderlich. Jede Tabelle braucht tenant_id, ORM-Queries müssen tenant-gefiltert sein, User-Tenant-Mapping-Tabelle nötig.
|
||||
|
||||
---
|
||||
|
||||
### K2: Plugin-System (F-PLUGIN-01, F-PLUGIN-02) — KRITISCH
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Plugin-Architektur | Core-Feature v1 | **Nicht existent** |
|
||||
| DMS/Kalender/Tags/Mail | Als Plugins implementiert | **Fest im Core eingebaut** |
|
||||
| Plugin-Manifest | Definiertes Format | **Nicht vorhanden** |
|
||||
| Lifecycle-Hooks | install/activate/deactivate/uninstall | **Nicht vorhanden** |
|
||||
| Plugin-API-Endpunkte | Plugins registrieren eigene Routes | **Nicht vorhanden** |
|
||||
| Plugin-DB-Migration | Eigene Migrationen | **Nicht vorhanden** |
|
||||
| Plugin-Abhängigkeiten | Deklarierbar | **Nicht vorhanden** |
|
||||
|
||||
**Evidence:**
|
||||
- `grep -rn 'plugin\|Plugin\|manifest\|lifecycle\|activate\|deactivate' app/` → **0 Treffer**
|
||||
- DMS: `models.py` Folder/File/FileEntityLink + `dms_service.py` (26KB) + `dms_routes.py` — alles fest im Core
|
||||
- Kalender: `models.py` Calendar/CalendarShare/CalendarEntry/Attendee/EntryLink/SubTask + `calendar_service.py` (21KB) + `calendar_routes.py` — fest im Core
|
||||
- Tags: `models.py` Tag/TagAssignment + `tag_service.py` + `tag_routes.py` — fest im Core
|
||||
- Keine Plugin-Registry, kein Plugin-Loader, kein Manifest-Format
|
||||
|
||||
**Impact:** Komplette Plugin-Architektur muss neu gebaut werden. Bestehende DMS/Kalender/Tag-Module müssen in Plugins umgewandelt werden.
|
||||
|
||||
---
|
||||
|
||||
### K3: Datenbank — SQLite vs PostgreSQL — KRITISCH
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| DB-Engine | PostgreSQL 16 | **SQLite** |
|
||||
| Connection-Pooling | PostgreSQL MVCC | **SQLite WAL, check_same_thread=False** |
|
||||
| Concurrent Writes | Multi-User fähig | **SQLite limitiert** |
|
||||
|
||||
**Evidence:**
|
||||
- `config.py`: `db_path: str = Field(default=str(Path("/data/leocrm.db")))` → SQLite-Datei
|
||||
- `config.py`: `database_url` property → `f"sqlite:///{self.db_path}"`
|
||||
- `session.py`: SQLite-spezifische PRAGMAs (`PRAGMA foreign_keys = ON`, `PRAGMA journal_mode = WAL`)
|
||||
- `session.py`: `connect_args={"check_same_thread": False}` — SQLite-only
|
||||
- `pyproject.toml`: Keine `psycopg2`/`asyncpg`/`psycopg`-Dependency
|
||||
|
||||
**Impact:** DB-Layer muss auf PostgreSQL umgestellt werden. Session-Engine, PRAGMAs, connect_args müssen angepasst werden.
|
||||
|
||||
---
|
||||
|
||||
### K4: Frontend — Jinja2 vs React SPA — KRITISCH
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Frontend | React SPA (client-side) | **Jinja2 Templates (server-side)** |
|
||||
| i18n | DE + EN, Sprachwahl persistiert | **Nicht implementiert** |
|
||||
| UI-Plugin-Framework | Plugins registrieren UI-Komponenten | **Nicht vorhanden** |
|
||||
|
||||
**Evidence:**
|
||||
- `app/templates/`: 7 Jinja2-HTML-Templates (login, register, dashboard, company_form, contact_form, contact_list, base)
|
||||
- `html_routes.py`: Jinja2Templates, TemplateResponse
|
||||
- Keine `package.json`, keine `.tsx`/`.jsx`-Dateien, kein React/Vite-Setup
|
||||
- `pyproject.toml`: `jinja2==3.1.5` als Dependency
|
||||
- Requirements Annahme 3: "SPA-Frontend: Client-side rendering mit React SPA (bestätigt durch genehmigten Prototyp leocrm-prototype-x7k2p9)"
|
||||
|
||||
**Impact:** Komplettes Frontend muss als React SPA neu gebaut werden. Jinja2-Templates und HTML-Routes werden obsolet. UI-Plugin-Framework (F-CORE-04) muss in React integriert werden.
|
||||
|
||||
---
|
||||
|
||||
### K5: F-CORE-01 — Event Bus — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Event Bus | Core-Feature v1 | **Nicht implementiert** |
|
||||
| Events emit/subscribe | Typisiert, Payload, asynchron | **Nicht vorhanden** |
|
||||
| Plugin-Listener | Registrieren beim Aktivieren | **N/A** |
|
||||
|
||||
**Evidence:** `grep -rn 'event.bus\|EventBus\|event_bus\|emit\|subscribe\|listener' app/` → **0 Treffer**
|
||||
|
||||
---
|
||||
|
||||
### K6: F-CORE-05 — Service Container / DI — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Service Container | Core-Services über Container | **Nicht implementiert** |
|
||||
| DI für Plugins | Services injiziert | **N/A** |
|
||||
| Mocking für Tests | Mock-Services injizierbar | **Nur DB-Session override** |
|
||||
|
||||
**Evidence:** Services werden direkt importiert (`from app.services import company_service`), nicht über Container. FastAPI `Depends()` ist das einzige DI-Muster, aber nur für Request-Scoped dependencies (DB-Session, Current-User).
|
||||
|
||||
---
|
||||
|
||||
### K7: F-CORE-06 — API-First Architecture — TEILWEISE
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Alle Features über API | API-First | **Teilweise** — API routes existieren für alle Module |
|
||||
| UI ist API-Client | UI nutzt API | **❌ Jinja2 rendert server-side** |
|
||||
| API versioniert | z.B. /api/v1/ | **❌ Keine Versionierung** |
|
||||
| OpenAPI/Swagger | Auto-gen, dokumentiert | **⚠️ FastAPI auto-gen existiert, aber nicht explizit konfiguriert** |
|
||||
| Plugin-API-Endpunkte | Registrierbar | **N/A** |
|
||||
| KI-Copilot nutzt API | Gleiche Endpunkte | **Nicht implementiert** |
|
||||
|
||||
**Evidence:**
|
||||
- API routes: `/api/companies`, `/api/contacts`, `/api/dms/*`, `/api/tags/*`, `/api/calendars`, `/api/entries`, `/api/notifications`, `/api/auth/*`
|
||||
- Kein `/api/v1/` Prefix — alle routes sind unversioniert
|
||||
- FastAPI generiert automatisch OpenAPI unter `/openapi.json`, aber nicht explizit konfiguriert oder dokumentiert
|
||||
- HTML routes existieren parallel (`/login`, `/` dashboard) — UI ist NICHT API-Client
|
||||
|
||||
---
|
||||
|
||||
### K8: F-CORE-07 — Async Job Queue — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Queue-System | Background-Jobs asynchron | **Nicht implementiert** |
|
||||
| Retry-Logic | Automatische Retries | **Nicht vorhanden** |
|
||||
| Dead-Letter-Queue | Bei wiederholtem Fehlschlag | **Nicht vorhanden** |
|
||||
| Job-Status UI | Sichtbar im UI | **Nicht vorhanden** |
|
||||
|
||||
**Evidence:** `grep -rn 'celery\|Celery\|queue\|Queue\|async_job\|background_job\|job_queue' app/` → **0 Treffer**
|
||||
|
||||
---
|
||||
|
||||
### K9: F-CORE-08 — Caching-Strategie — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Cache-Backend | Sessions, Query-Cache, Plugin-Data | **Nicht implementiert** |
|
||||
| Cache-Invalidierung | Event-basiert | **N/A** |
|
||||
| TTL-Caching | Fallback | **Nur `@lru_cache` für Settings** |
|
||||
|
||||
**Evidence:** `grep -rn 'cache\|Cache\|redis\|Redis' app/` → nur `functools.lru_cache` in `config.py` für Settings-Caching. Kein Redis, kein Query-Cache.
|
||||
|
||||
---
|
||||
|
||||
### K10: F-CORE-09 — User-Profile und Preferences — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| User-Profile | Profil mit Preferences | **Nicht implementiert** |
|
||||
| Sprache/Zeitzone/Theme | Umschaltbar | **Nicht vorhanden** |
|
||||
| Dashboard-Konfiguration | Konfigurierbar | **Nicht vorhanden** |
|
||||
| Plugin-Preferences | Eigene Felder registrierbar | **N/A** |
|
||||
|
||||
**Evidence:** `User`-Modell hat nur: id, username, password_hash, role, personal_folder_id, default_calendar_id, created_at. Keine Preferences, keine Sprache, keine Zeitzone.
|
||||
|
||||
---
|
||||
|
||||
### K11: F-CORE-10 — Storage-Backend — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| S3-kompatibel | Konfigurierbar | **Nicht implementiert** |
|
||||
| Lokales Volume | Alternative | **Lokales Dateisystem** |
|
||||
| Presigned-URLs | Download ohne Plugin-Code | **Nicht vorhanden** |
|
||||
| Storage-Service | Core-Service für Plugins | **Direkter Dateizugriff** |
|
||||
|
||||
**Evidence:**
|
||||
- `config.py`: `dms_storage_path: str = Field(default="/data/dms")` — lokales Verzeichnis
|
||||
- `dms_service.py`: Direkter Dateizugriff via `open()`, `Path`-Operationen
|
||||
- Keine S3/MinIO/boto3-Integration
|
||||
- `grep -rn 's3\|S3\|boto3\|storage_backend\|presigned' app/` → **0 Treffer**
|
||||
|
||||
---
|
||||
|
||||
### K12: F-CORE-11 — Generic Import/Export Service — TEILWEISE
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| CSV-Import | Mit Preview, Dry-Run, Fehler-Reporting | **⚠️ Nur direkter Import ohne Preview/Dry-Run** |
|
||||
| Excel-Export | Feld-Auswahl, Filterung | **⚠️ CSV + XLSX Export, aber begrenzte Feld-Auswahl** |
|
||||
| Plugin-Definitionen | Registrierbar | **N/A** |
|
||||
|
||||
**Evidence:**
|
||||
- `import_service.py`: `import_companies_csv()`, `import_contacts_csv()` — direkter Import, kein Preview, kein Dry-Run
|
||||
- `export_service.py`: `export_companies_csv()`, `export_companies_xlsx()` — Export funktioniert, aber nicht generisch/plugin-fähig
|
||||
- Import/Export ist hardcoded für Companies/Contacts, nicht generisch
|
||||
|
||||
---
|
||||
|
||||
### K13: F-CORE-12 — PDF/Document Generation Service — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| PDF-Generierung | Aus Templates | **Nicht implementiert** |
|
||||
| Template-Engine | Variablen, Conditionals, Tabellen | **Nicht vorhanden** |
|
||||
| Storage-Integration | PDFs im Storage gespeichert | **N/A** |
|
||||
|
||||
**Evidence:** `grep -rn 'pdf\|PDF\|weasyprint\|reportlab\|pdfkit' app/` → nur DMS-Preview (stream existing PDFs), keine Generierung
|
||||
|
||||
---
|
||||
|
||||
### K14: F-CORE-13 — Notification Service — TEILWEISE
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| In-App-Notifications | Bell-Icon, Badge-Zähler | **⚠️ DB-Modell existiert, keine UI** |
|
||||
| E-Mail-Channel | Notifications per Mail | **Nicht implementiert** |
|
||||
| Preferences | Pro User konfigurierbar | **Nicht vorhanden** |
|
||||
| Tenant-Isolation | Pro Tenant isoliert | **N/A (single-tenant)** |
|
||||
| Plugin-Notification-Typen | Registrierbar | **N/A** |
|
||||
|
||||
**Evidence:**
|
||||
- `models.py`: `Notification`-Modell existiert (id, user_id, type, title, body, related_entry_id, is_read, created_at)
|
||||
- `notification_routes.py`: API für List/Mark-Read existiert
|
||||
- Keine E-Mail-Integration, keine Preferences, kein Badge-Zähler in UI (Jinja2-Templates haben kein Notification-UI)
|
||||
|
||||
---
|
||||
|
||||
### K15: F-AUTH-01 — Login mit E-Mail — KONFLIKT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Login-Feld | **E-Mail** + Passwort | **Username** + Passwort |
|
||||
| Session-Cookie | HttpOnly, Secure, SameSite=Strict | SameSite=**lax**, https_only conditional |
|
||||
|
||||
**Evidence:**
|
||||
- `auth_service.py`: `authenticate_user(db, username, password)` — verwendet `username`, nicht `email`
|
||||
- `models.py`: `User.username: Mapped[str]` — kein `email`-Feld auf User
|
||||
- `deps.py`: Session speichert `user_id`, kein Tenant-Kontext
|
||||
- `main.py`: `same_site="lax"` (requirements sagen Strict), `https_only=settings.is_production` (requirements sagen Secure)
|
||||
|
||||
---
|
||||
|
||||
### K16: F-AUTH-03 — User-Verwaltung durch Admin — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Admin legt User an | E-Mail, Name, Rolle, Passwort | **Nicht implementiert** |
|
||||
| User-Tenant-Zuordnung | User wird Tenant zugeordnet | **N/A** |
|
||||
| Keine Self-Registration | Admin-only | **⚠️ Register-Template existiert** |
|
||||
|
||||
**Evidence:**
|
||||
- Keine User-Management-Routes (kein `/api/users`, kein Admin-User-CRUD)
|
||||
- `app/templates/register.html` existiert — Self-Registration-Template (widerspricht Non-Goal #1)
|
||||
- `init_db.py`: Demo-Seed erstellt nur admin/admin
|
||||
|
||||
---
|
||||
|
||||
### K17: F-AUTH-05 — Passwort-Reset — FEHLT
|
||||
|
||||
| Aspekt | Requirements | Code-Realität |
|
||||
|---------|-------------|---------------|
|
||||
| Reset-Flow | E-Mail mit Reset-Link | **Nicht implementiert** |
|
||||
| Reset-Link | Gültig 24h | **Nicht vorhanden** |
|
||||
|
||||
**Evidence:** Keine Reset-Routes, keine Reset-Templates, keine Token-Generierung.
|
||||
|
||||
---
|
||||
|
||||
### K18: F-AUTH-07 — Multi-Tenant — FEHLT (siehe K1)
|
||||
|
||||
Bereits in K1 abgedeckt. Keine Tenant-Modelle, keine User-Tenant-Mapping-Tabelle.
|
||||
|
||||
---
|
||||
|
||||
### K19: DMS/Calendar/Tags als Core vs Plugin — ARCHITEKTUR-KONFLIKT
|
||||
|
||||
| Modul | Requirements | Code-Realität |
|
||||
|-------|-------------|---------------|
|
||||
| DMS | v2-Plugin (F-FILE/F-DMS/F-LINK/F-PERM) | **Core: 3 Modelle + 26KB Service + eigene Routes** |
|
||||
| Kalender | v2-Plugin (F-CAL-01..18) | **Core: 6 Modelle + 21KB Service + eigene Routes** |
|
||||
| Tags | v2-Plugin (F-TAG-01..04) | **Core: 2 Modelle + 8KB Service + eigene Routes** |
|
||||
| Mail | v2-Plugin (F-MAIL-01..19) | **Nicht implementiert** |
|
||||
|
||||
**Evidence:** Alle Module sind direkt in `models.py`, `services/`, `routes/` integriert. Keine Plugin-Grenzen, keine Plugin-Schnittstellen.
|
||||
|
||||
**Hinweis:** Requirements sagen Plugin-System ist v1-Core-Feature, aber die Module selbst sind v2-Plugins. Das bedeutet: In v1 muss das Plugin-System gebaut werden, aber DMS/Kalender/Tags können als v2-Plugins nachgezogen werden. Die bestehenden Implementierungen können als Referenz dienen, müssen aber auf Plugin-Architektur umgebaut werden.
|
||||
|
||||
---
|
||||
|
||||
## 3. Kompatibel — Was bereits passt
|
||||
|
||||
### ✅ Session-basierte Auth (F-AUTH-01/02, Annahme 12)
|
||||
- Starlette `SessionMiddleware` mit signed Cookie
|
||||
- `session_cookie="leocrm_session"`, `max_age` konfigurierbar
|
||||
- Login setzt `request.session[SESSION_USER_ID_KEY] = user.id`
|
||||
- Logout cleared session
|
||||
- **Kompatibel** mit Requirements (Session-basiert, Cookie-basiert)
|
||||
|
||||
### ✅ RBAC Grundgerüst (F-AUTH-04/06)
|
||||
- 3 Rollen: admin, editor, viewer
|
||||
- `require_admin` dependency prüft `user.role == "admin"`
|
||||
- `get_current_user` dependency für auth-geschützte Routes
|
||||
- **Kompatibel** mit Requirements (3 Rollen v1)
|
||||
|
||||
### ✅ Company/Contact CRUD (F-COMP-01..06, F-CONT-01..07)
|
||||
- Company: 27 Felder (Name, Adresse, Industrie, Revenue, etc.)
|
||||
- Contact: 29 Felder (Name, Email, Phone, Title, etc.)
|
||||
- N:M Junction: `CompanyContact`
|
||||
- Soft-Delete: `deleted_at` auf beiden
|
||||
- Pagination, Search, Filter, Sort in Routes
|
||||
- **Kompatibel** mit Requirements
|
||||
|
||||
### ✅ Data-Features (F-DATA-01..04)
|
||||
- Pagination: `PageResponse` schema
|
||||
- Search: Query-Parameter in company/contact routes
|
||||
- Sort: Sortier-Parameter
|
||||
- Soft-Delete: `deleted_at` + restore functionality
|
||||
- **Kompatibel** mit Requirements
|
||||
|
||||
### ✅ Health-Check (F-INFRA-01)
|
||||
- `/api/health` endpoint, prüft DB, gibt Status + Version
|
||||
- Nicht auth-geschützt (für Coolify/LB)
|
||||
- **Kompatibel** mit Requirements
|
||||
|
||||
### ✅ Import/Export Grundgerüst (F-MIG-01, F-DATA-01/02)
|
||||
- CSV-Import für Companies/Contacts
|
||||
- CSV + XLSX Export für Companies
|
||||
- **Teilweise kompatibel** — fehlt Preview, Dry-Run, generische Service-Architektur
|
||||
|
||||
### ✅ DMS-Features (als Referenz für späteres Plugin)
|
||||
- Folder-Tree mit materialized path
|
||||
- File-Upload, Preview (PDF), Soft-Delete, Restore
|
||||
- Entity-Links (N:M zu Companies/Contacts)
|
||||
- Permissions (Individual/Group/Default)
|
||||
- Share-Links mit Password + Expiry
|
||||
- OnlyOffice-Edit-Session
|
||||
- **Vollständig implementiert** — kann als Plugin-Referenz dienen
|
||||
|
||||
### ✅ Calendar-Features (als Referenz für späteres Plugin)
|
||||
- Calendar CRUD, Sharing, Visibility-Toggle
|
||||
- Entries: Events/Tasks/Reminders, Kanban-Status
|
||||
- Subtasks, Attendees, Entry-Links
|
||||
- Notifications für Reminders/Invites/Shares
|
||||
- **Vollständig implementiert** — kann als Plugin-Referenz dienen
|
||||
|
||||
### ✅ Tag-System (als Referenz für späteres Plugin)
|
||||
- Tag CRUD (admin-only), Color, Assignment
|
||||
- Bulk-Assign, Entity-Type polymorphic
|
||||
- **Vollständig implementiert** — kann als Plugin-Referenz dienen
|
||||
|
||||
### ✅ Testing-Setup (F-TEST-01)
|
||||
- pytest mit 20+ Test-Dateien
|
||||
- conftest.py mit Fixtures
|
||||
- Coverage-Messung konfiguriert
|
||||
- **Teilweise kompatibel** — fehlt Vitest (Frontend) und Playwright (E2E)
|
||||
|
||||
---
|
||||
|
||||
## 4. F-CORE-Feature-Matrix
|
||||
|
||||
| F-CORE-ID | Feature | Status im Code | Anmerkung |
|
||||
|-----------|--------|---------------|----------|
|
||||
| F-CORE-01 | Event Bus | ❌ Nicht implementiert | Keine Event-Infrastruktur |
|
||||
| F-CORE-02 | Tenant-Isolation | ❌ Nicht implementiert | Kein tenant_id, single-tenant |
|
||||
| F-CORE-03 | Plugin-DB-Migration | ❌ Nicht implementiert | Kein Plugin-System |
|
||||
| F-CORE-04 | UI-Plugin-Framework | ❌ Nicht implementiert | Jinja2, keine Plugin-UI |
|
||||
| F-CORE-05 | Service Container / DI | ❌ Nicht implementiert | Direkte Imports, nur FastAPI Depends |
|
||||
| F-CORE-06 | API-First Architecture | ⚠️ Teilweise | API routes existieren, aber HTML parallel, keine Versionierung |
|
||||
| F-CORE-07 | Async Job Queue | ❌ Nicht implementiert | Keine Queue-Infrastruktur |
|
||||
| F-CORE-08 | Caching-Strategie | ❌ Nicht implementiert | Nur lru_cache für Settings |
|
||||
| F-CORE-09 | User-Profile/Preferences | ❌ Nicht implementiert | User hat nur username/role |
|
||||
| F-CORE-10 | Storage-Backend | ❌ Nicht implementiert | Lokales Dateisystem, kein S3 |
|
||||
| F-CORE-11 | Generic Import/Export | ⚠️ Teilweise | CSV/XLSX funktioniert, nicht generisch, kein Preview/Dry-Run |
|
||||
| F-CORE-12 | PDF Generation | ❌ Nicht implementiert | Keine PDF-Generierung |
|
||||
| F-CORE-13 | Notification Service | ⚠️ Teilweise | DB-Modell + API existiert, keine UI, kein E-Mail-Channel |
|
||||
|
||||
**Bilanz:** 0/13 vollständig implementiert, 3/13 teilweise, 10/13 fehlen komplett.
|
||||
|
||||
---
|
||||
|
||||
## 5. Empfehlung: Was vor Phase 2 angepasst werden muss
|
||||
|
||||
### Priorität 1 — Fundamentale Architektur (vor allem anderen)
|
||||
|
||||
1. **Datenbank-Migration: SQLite → PostgreSQL**
|
||||
- `config.py`: `database_url` auf PostgreSQL umstellen
|
||||
- `session.py`: SQLite-PRAGMAs entfernen, PostgreSQL-Engine konfigurieren
|
||||
- `pyproject.toml`: `psycopg[binary]` oder `asyncpg` hinzufügen
|
||||
- `docker-compose.yml`: PostgreSQL-Service hinzufügen
|
||||
|
||||
2. **Multi-Tenant-Architektur**
|
||||
- Neues `Tenant`-Modell + `UserTenant`-Mapping-Tabelle
|
||||
- `tenant_id`-Spalte auf ALLE Core-Tabellen (Company, Contact, Folder, File, Tag, Calendar, etc.)
|
||||
- ORM-Query-Filter: automatische tenant_id-Filterung (SQLAlchemy Event oder Query-Wrapper)
|
||||
- Session-Kontext: aktiver tenant_id in Session speichern
|
||||
- Tenant-Switch-Endpoint + UI
|
||||
|
||||
3. **Frontend-Wechsel: Jinja2 → React SPA**
|
||||
- React-Projekt-Setup (Vite + React + TypeScript)
|
||||
- API-Client-Layer (fetch/axios gegen /api/* Endpunkte)
|
||||
- Jinja2-Templates und html_routes.py werden obsolet
|
||||
- i18n-Integration (DE + EN)
|
||||
- UI-Plugin-Framework vorbereiten (F-CORE-04)
|
||||
|
||||
### Priorität 2 — Core-Infrastructure (F-CORE)
|
||||
|
||||
4. **Service Container / DI (F-CORE-05)**
|
||||
- Zentralen Service-Container implementieren
|
||||
- Core-Services registrieren: DB, Cache, Event Bus, Auth, Config, Logger
|
||||
- Plugin-Schnittstelle für Service-Requests definieren
|
||||
|
||||
5. **Event Bus (F-CORE-01)**
|
||||
- Event-Publish/Subscribe-System implementieren
|
||||
- Typisierte Events mit Payload
|
||||
- Asynchrone Verarbeitung (ggf. via Job Queue)
|
||||
|
||||
6. **Plugin-System (F-PLUGIN-01/02)**
|
||||
- Plugin-Manifest-Format definieren
|
||||
- Lifecycle-Hooks: install, activate, deactivate, uninstall
|
||||
- Plugin-Registry + Loader
|
||||
- Plugin-API-Endpunkt-Registrierung
|
||||
- Plugin-DB-Migration (F-CORE-03)
|
||||
- Plugin-Abhängigkeiten
|
||||
|
||||
7. **API-Versionierung (F-CORE-06)**
|
||||
- `/api/v1/` Prefix für alle API-Routes
|
||||
- OpenAPI/Swagger explizit konfigurieren und dokumentieren
|
||||
- HTML-Routes entfernen (UI wird React SPA = API-Client)
|
||||
|
||||
### Priorität 3 — Weitere Core-Infrastructure
|
||||
|
||||
8. **Async Job Queue (F-CORE-07)** — Queue-System für Background-Jobs
|
||||
9. **Caching (F-CORE-08)** — Redis-Anbindung, Query-Cache, Cache-Invalidierung
|
||||
10. **Storage-Backend (F-CORE-10)** — S3-kompatibler Storage-Service
|
||||
11. **User-Profile/Preferences (F-CORE-09)** — Profil-Erweiterung, Preferences
|
||||
12. **Notification Service (F-CORE-13)** — E-Mail-Channel, Preferences, Badge-UI
|
||||
13. **PDF Generation (F-CORE-12)** — Template-Engine, PDF-Generierung
|
||||
14. **Generic Import/Export (F-CORE-11)** — Generischer Service, Preview, Dry-Run
|
||||
|
||||
### Priorität 4 — Auth-Ergänzungen
|
||||
|
||||
15. **Login auf E-Mail umstellen (F-AUTH-01)** — username → email
|
||||
16. **User-Verwaltung durch Admin (F-AUTH-03)** — Admin-CRUD für User, Tenant-Zuordnung
|
||||
17. **Passwort-Reset (F-AUTH-05)** — Reset-Flow mit E-Mail
|
||||
18. **Register-Template entfernen** — Self-Registration ist Non-Goal
|
||||
19. **Cookie-Security anpassen** — SameSite=Strict, Secure immer
|
||||
|
||||
### Was beibehalten werden kann
|
||||
|
||||
- **Backend-Services** (company_service, contact_service, etc.) — Business-Logik ist solide
|
||||
- **Pydantic-Schemas** — Können für API-Validierung weiterverwendet werden
|
||||
- **DB-Modelle** — Felder/Beziehungen sind korrekt, müssen nur tenant_id ergänzt werden
|
||||
- **Test-Suite** — pytest-Tests können erweitert werden
|
||||
- **DMS/Calendar/Tag-Implementierungen** — Als Referenz für spätere Plugin-Entwicklung behalten
|
||||
|
||||
---
|
||||
|
||||
## 6. Zusammenfassung
|
||||
|
||||
| Kategorie | Anzahl | Status |
|
||||
|-----------|--------|--------|
|
||||
| Kritische Konflikte | 4 | Multi-Tenant, Plugin-System, DB, Frontend |
|
||||
| F-CORE fehlend | 10/13 | Event Bus, Tenant-Isolation, Plugin-Migration, UI-Plugin, Service Container, Job Queue, Caching, User-Profile, Storage, PDF |
|
||||
| F-CORE teilweise | 3/13 | API-First, Import/Export, Notification |
|
||||
| F-CORE vollständig | 0/13 | — |
|
||||
| Auth-Konflikte | 4 | Login (username vs email), User-Verwaltung, Passwort-Reset, Cookie-Security |
|
||||
| Kompatibel | 7+ | Session-Auth, RBAC, Company/Contact CRUD, Data-Features, Health, DMS/Calendar/Tags (als Referenz) |
|
||||
|
||||
**Fazit:** Die bestehende Codebase ist eine funktionsfähige v0.1-Implementierung (Single-Tenant, SQLite, Jinja2), die den bereinigten v1-Requirements in 4 kritischen Bereichen nicht entspricht: Multi-Tenant, Plugin-System, PostgreSQL, React SPA. 10 von 13 F-CORE-Features fehlen komplett. Die bestehende Business-Logik (Services, Schemas, Modelle) ist jedoch solide und kann als Basis für den Umbau dienen. Der Aufwand für Phase 2 ist erheblich — es handelt sich um eine Architektur-Migration, nicht um inkrementelle Erweiterungen.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,292 +0,0 @@
|
||||
# LeoCRM Infrastructure & Deployment Audit Report
|
||||
|
||||
**Audit Date:** 2026-07-30
|
||||
**Auditor:** Runtime DevOps Engineer (parallel worker)
|
||||
**Repository:** /a0/usr/workdir/leocrm-fix
|
||||
**Live Endpoint:** https://crm.media-on.de/api/v1/health → `{"status":"healthy","version":"1.0.0"}`
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| CRITICAL | 1 |
|
||||
| HIGH | 3 |
|
||||
| MEDIUM | 5 |
|
||||
| LOW | 4 |
|
||||
|
||||
The application is live and healthy. The Dockerfile follows best practices (multi-stage, non-root, layer caching). However, there is a **CRITICAL SQL injection** in `prestart.sh`, the **CI/CD pipeline is not automated**, the **worker container lacks a healthcheck**, and **no resource limits** are defined for any service.
|
||||
|
||||
---
|
||||
|
||||
## 1. Container Health — docker-compose.yml
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 1.1 | **HIGH** | Worker container (`crm-worker`) has NO healthcheck defined | `docker-compose.yml:119-153` |
|
||||
| 1.2 | **MEDIUM** | No resource limits (memory/CPU) on ANY service | `docker-compose.yml` (entire file) |
|
||||
| 1.3 | LOW | PostgreSQL, Redis, and app all use `restart: unless-stopped` ✓ | `docker-compose.yml:14,48,73,126` |
|
||||
| 1.4 | LOW | `depends_on` with `condition: service_healthy` correctly used ✓ | `docker-compose.yml:75-76,130-131` |
|
||||
|
||||
### Details
|
||||
|
||||
**1.1 — Worker missing healthcheck:**
|
||||
The `crm-worker` service has no `healthcheck` key. The `healthcheck.sh` script supports worker mode (Redis ping fallback), but it is never invoked for the worker container. Docker/Coolify cannot detect a wedged worker.
|
||||
|
||||
**Recommended fix:**
|
||||
```yaml
|
||||
crm-worker:
|
||||
healthcheck:
|
||||
test: ["CMD", "/app/healthcheck.sh"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
```
|
||||
|
||||
**1.2 — No resource limits:**
|
||||
None of the 4 services define `deploy.resources.limits` or `mem_limit`/`cpus`. A memory leak in the app or worker can OOM the host. In Coolify deployments, resource limits should be set via Coolify resource constraints.
|
||||
|
||||
---
|
||||
|
||||
## 2. Worker Stability — worker.sh, app/core/worker.py
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 2.1 | **MEDIUM** | No ARQ job retry configuration (`max_tries` not set) | `app/core/worker.py:243-244` |
|
||||
| 2.2 | LOW | `max_jobs = 10`, `job_timeout = 300s` — reasonable defaults | `app/core/worker.py:243-244` |
|
||||
| 2.3 | LOW | Distributed cron lock via Redis SET NX + Lua release ✓ | `app/core/worker.py:30-52` |
|
||||
| 2.4 | LOW | `on_startup` properly initializes plugins, event bus, search providers ✓ | `app/core/worker.py:78-130` |
|
||||
| 2.5 | LOW | `exec arq` in worker.sh makes ARQ PID 1 for signal forwarding ✓ | `worker.sh:20` |
|
||||
|
||||
### Details
|
||||
|
||||
**2.1 — No job retry:**
|
||||
ARQ's `WorkerSettings` does not set `max_tries`. ARQ defaults to `max_tries=0` (no retries). A transient failure (DB timeout, Redis blip) will permanently fail the job. For critical jobs like `process_outbox`, this can cause permanent outbox stalls.
|
||||
|
||||
**Recommended fix:**
|
||||
```python
|
||||
class WorkerSettings:
|
||||
max_tries = 3 # Retry failed jobs up to 3 times
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Redis Connections — app/core/redis.py, app/core/auth.py, app/core/worker.py
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 3.1 | **MEDIUM** | Cron lock helpers create a NEW Redis client per acquire/release — no pooling | `app/core/worker.py:37,49` |
|
||||
| 3.2 | **MEDIUM** | No Redis connection pool size configured — uses redis-py defaults | `app/core/auth.py:37-38,62-63` |
|
||||
| 3.3 | LOW | Session keys use SETEX with TTL (28800s = 8h) ✓ | `app/core/auth.py:145-147` |
|
||||
| 3.4 | LOW | Global singleton pattern prevents connection leaks for app/API Redis ✓ | `app/core/auth.py:28-38` |
|
||||
|
||||
### Details
|
||||
|
||||
**3.1 — Cron lock connection churn:**
|
||||
`_acquire_cron_lock()` and `_release_cron_lock()` each call `aioredis.from_url()` and `aclose()` on every invocation. With outbox processing running every 5 seconds, this creates 24 Redis connections/minute per cron job just for lock management.
|
||||
|
||||
**Recommended fix:** Reuse the global Redis client from `get_redis()` or pass the connection via ARQ context (`ctx['redis']`).
|
||||
|
||||
**3.2 — No pool size:**
|
||||
`aioredis.from_url()` is called without `max_connections` parameter. Under high load, the default pool may exhaust. Add:
|
||||
```python
|
||||
aioredis.from_url(url, decode_responses=True, max_connections=50)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Migration Pipeline — alembic/
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 4.1 | LOW | 83 migrations, single head at 0083 ✓ | `alembic/versions/` |
|
||||
| 4.2 | LOW | 0028_rls_force and 0028_user_preferences are properly chained (not branched) ✓ | `alembic/versions/0028_*.py` |
|
||||
| 4.3 | LOW | test_migrations.sh tests upgrade/downgrade/idempotency ✓ | `scripts/test_migrations.sh` |
|
||||
| 4.4 | LOW | Downgrade failure is non-fatal in test_migrations.sh (acceptable) | `scripts/test_migrations.sh:54` |
|
||||
| 4.5 | LOW | alembic/env.py uses async engine from config ✓ | `alembic/env.py:35-44` |
|
||||
|
||||
### Details
|
||||
|
||||
Migration graph is clean — `alembic heads` confirms a single head. The test script (`test_migrations.sh`) creates a throwaway database, runs `upgrade head`, verifies table count ≥ 50, runs `downgrade base`, then re-upgrades to verify idempotency. Solid approach.
|
||||
|
||||
---
|
||||
|
||||
## 5. CI/CD Pipeline — .github/workflows/, scripts/
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 5.1 | **HIGH** | Only 1 GitHub workflow exists (cross-plugin imports only) — no test/build/deploy automation | `.github/workflows/check-cross-plugin-imports.yml` |
|
||||
| 5.2 | **HIGH** | `scripts/ci_pipeline.sh` has 15 quality gates but is NOT wired into any CI workflow | `scripts/ci_pipeline.sh` (entire file) |
|
||||
| 5.3 | LOW | Cross-plugin import check is well-implemented with exemptions ✓ | `scripts/check_cross_plugin_imports.py` |
|
||||
| 5.4 | LOW | CI pipeline includes SQL injection, Jinja2 sandbox, RLS, and fail-closed checks ✓ | `scripts/ci_pipeline.sh:52-62` |
|
||||
|
||||
### Details
|
||||
|
||||
**5.1 + 5.2 — CI pipeline not automated:**
|
||||
The `.github/workflows/` directory contains only `check-cross-plugin-imports.yml` (triggers on `app/plugins/**` changes). The comprehensive `ci_pipeline.sh` with 15 checks (compile, imports, alembic, TypeScript, frontend build, test collection, SQL injection, Jinja2 sandbox, RLS, fail-closed, ruff, cross-tenant, dependency scan, container smoke, npm ci) is **never executed in CI**. It must be run manually.
|
||||
|
||||
**Recommended fix:** Create `.github/workflows/ci.yml`:
|
||||
```yaml
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with: { python-version: '3.12' }
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: '20' }
|
||||
- run: pip install -r requirements.txt
|
||||
- run: cd frontend && npm ci
|
||||
- run: bash scripts/ci_pipeline.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Prestart Script — prestart.sh
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 6.1 | **CRITICAL** | SQL injection: password interpolated into SQL via f-string without escaping | `prestart.sh:49` |
|
||||
| 6.2 | LOW | Uses `set -e` for fail-fast ✓ | `prestart.sh:14` |
|
||||
| 6.3 | LOW | `exec uvicorn` makes it PID 1 for signal forwarding ✓ | `prestart.sh:62` |
|
||||
| 6.4 | LOW | Uses MIGRATION_DATABASE_URL for alembic (RLS bypass for DDL) ✓ | `prestart.sh:18` |
|
||||
|
||||
### Details
|
||||
|
||||
**6.1 — SQL injection in prestart.sh:**
|
||||
Line 49:
|
||||
```python
|
||||
await conn.execute(text(
|
||||
f"ALTER ROLE crm_runtime WITH LOGIN PASSWORD '{pwd}' NOSUPERUSER NOBYPASSRLS"
|
||||
))
|
||||
```
|
||||
The `RUNTIME_DB_PASSWORD` environment variable is interpolated directly into a SQL string using an f-string. If the password contains a single quote (`'`), the SQL will break or be exploitable. This is a **CRITICAL** injection vulnerability.
|
||||
|
||||
**Recommended fix:** Use parameterized query or escape the password:
|
||||
```python
|
||||
await conn.execute(text(
|
||||
"ALTER ROLE crm_runtime WITH LOGIN PASSWORD :pwd NOSUPERUSER NOBYPASSRLS"
|
||||
), {"pwd": pwd})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Dockerfile
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 7.1 | LOW | Multi-stage build (3 stages: frontend, builder, runtime) ✓ | `Dockerfile:5,18,39` |
|
||||
| 7.2 | LOW | Non-root user (appuser, UID 1000, GID 1000) ✓ | `Dockerfile:60-61` |
|
||||
| 7.3 | LOW | Layer caching for npm (`COPY package.json` before `COPY frontend/`) ✓ | `Dockerfile:10-12` |
|
||||
| 7.4 | LOW | Layer caching for pip (`COPY requirements.txt` before app source) ✓ | `Dockerfile:30-31` |
|
||||
| 7.5 | LOW | `.dockerignore` excludes secrets, tests, docs, `.git` ✓ | `.dockerignore` |
|
||||
| 7.6 | LOW | HEALTHCHECK defined in Dockerfile ✓ | `Dockerfile:73-74` |
|
||||
| 7.7 | LOW | `apt-get` cleanup with `rm -rf /var/lib/apt/lists/*` ✓ | `Dockerfile:23,57` |
|
||||
|
||||
**No issues found.** The Dockerfile follows best practices.
|
||||
|
||||
---
|
||||
|
||||
## 8. Environment Configuration — .env.example, .env.docker.example
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 8.1 | **MEDIUM** | `.env.example` missing `MIGRATION_DATABASE_URL`, `REDIS_PASSWORD`, `RUNTIME_DB_PASSWORD` | `.env.example` |
|
||||
| 8.2 | LOW | `.env.docker.example` is comprehensive with all required vars ✓ | `.env.docker.example` |
|
||||
| 8.3 | LOW | Required vars enforced with `:?` in docker-compose.yml (POSTGRES_PASSWORD, REDIS_PASSWORD, SECRET_KEY, DATABASE_URL) ✓ | `docker-compose.yml:17,50,83,84` |
|
||||
| 8.4 | LOW | Secret generation instructions included ✓ | `.env.docker.example:16-17,24-25` |
|
||||
|
||||
### Details
|
||||
|
||||
**8.1 — `.env.example` incomplete:**
|
||||
The `.env.example` file (used for local dev) is missing `MIGRATION_DATABASE_URL`, `REDIS_PASSWORD`, and `RUNTIME_DB_PASSWORD`. Developers following `.env.example` will hit runtime errors when the prestart script tries to set the crm_runtime password or when alembic needs the migration URL.
|
||||
|
||||
---
|
||||
|
||||
## 9. Health Check — healthcheck.sh, app/routes/health.py
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 9.1 | LOW | `/api/v1/health` returns 200 `healthy` on live deployment ✓ | `https://crm.media-on.de/api/v1/health` |
|
||||
| 9.2 | LOW | Three-tier health endpoints: `/health/live`, `/health/ready`, `/api/v1/health` ✓ | `app/routes/health.py:22,34,57` |
|
||||
| 9.3 | LOW | healthcheck.sh dual-mode (HTTP + Redis fallback) ✓ | `healthcheck.sh:5-19` |
|
||||
| 9.4 | LOW | Readiness probe checks DB, Redis, storage, worker heartbeat ✓ | `app/routes/health.py:37-52` |
|
||||
| 9.5 | LOW | Redis password in healthcheck command visible in `docker inspect` (low risk — internal network) | `docker-compose.yml:59` |
|
||||
|
||||
**No critical issues.** Health check implementation is solid.
|
||||
|
||||
---
|
||||
|
||||
## 10. Backup/Restore — app/services/backup_service.py, scripts/backup.py, scripts/restore.py
|
||||
|
||||
### Findings
|
||||
|
||||
| # | Severity | Finding | Location |
|
||||
|---|----------|---------|----------|
|
||||
| 10.1 | **MEDIUM** | `backup_service.py` stores backups in `/tmp/leocrm-backups` — ephemeral, lost on container restart | `app/services/backup_service.py:13` |
|
||||
| 10.2 | **MEDIUM** | `restore_backup()` uses `--clean --if-exists` but no transaction wrapping — partial restore possible | `app/services/backup_service.py:118-125` |
|
||||
| 10.3 | LOW | `scripts/backup.py` is more robust: manifest, retention, S3/Nextcloud support ✓ | `scripts/backup.py` (entire) |
|
||||
| 10.4 | LOW | `scripts/restore.py` validates manifest.json before restore ✓ | `scripts/restore.py:93-98` |
|
||||
| 10.5 | LOW | `test_backup_restore.py` has basic unit tests for params/manifest ✓ | `tests/test_backup_restore.py` |
|
||||
| 10.6 | LOW | No scheduled backup automation — must be triggered manually | (no cron/scheduler for backups) |
|
||||
|
||||
### Details
|
||||
|
||||
**10.1 — Ephemeral backup storage:**
|
||||
`backup_service.py` uses `BACKUP_DIR = Path("/tmp/leocrm-backups")`. In a Docker container, `/tmp` is ephemeral. If the container restarts, all backups are lost. The volume mount in docker-compose only covers `/data/storage`, not `/tmp`.
|
||||
|
||||
**Recommended fix:** Change to `/data/backups` or use the `storage` volume.
|
||||
|
||||
**10.2 — Non-atomic restore:**
|
||||
`restore_backup()` runs `pg_restore --clean --if-exists --no-owner --no-acl` without wrapping in a transaction. If the restore fails midway, the database is left in a partially-restored state with no automatic rollback.
|
||||
|
||||
---
|
||||
|
||||
## Summary of Recommendations (Priority Order)
|
||||
|
||||
1. **CRITICAL** — Fix SQL injection in `prestart.sh:49` — use parameterized query
|
||||
2. **HIGH** — Add healthcheck to `crm-worker` in `docker-compose.yml`
|
||||
3. **HIGH** — Wire `scripts/ci_pipeline.sh` into a GitHub/Forgejo workflow
|
||||
4. **HIGH** — Expand `.github/workflows/` to include test/build/lint gates
|
||||
5. **MEDIUM** — Add `max_tries=3` to `WorkerSettings` for job retry
|
||||
6. **MEDIUM** — Add resource limits to all services in `docker-compose.yml`
|
||||
7. **MEDIUM** — Reuse Redis connection in cron lock helpers instead of creating new clients
|
||||
8. **MEDIUM** — Change `backup_service.py` backup dir from `/tmp` to persistent volume
|
||||
9. **MEDIUM** — Add `MIGRATION_DATABASE_URL`, `REDIS_PASSWORD`, `RUNTIME_DB_PASSWORD` to `.env.example`
|
||||
10. **LOW** — Configure Redis `max_connections` in `auth.py`
|
||||
11. **LOW** — Add scheduled backup cron job
|
||||
12. **LOW** — Wrap `restore_backup()` in a transaction
|
||||
|
||||
---
|
||||
|
||||
## Live Deployment Status
|
||||
|
||||
| Check | Result |
|
||||
|-------|--------|
|
||||
| Health endpoint | ✅ `{"status":"healthy","version":"1.0.0"}` |
|
||||
| HTTPS | ✅ Reachable |
|
||||
| Response time | < 3s |
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
# Migration History Audit
|
||||
|
||||
**Erstellt:** 2026-08-03
|
||||
**Alembic-Head:** 0092
|
||||
**Produktions-Stand:** 0092
|
||||
|
||||
---
|
||||
|
||||
## Bestätigte Schema-Diskrepanzen
|
||||
|
||||
### 1. files.size_bytes — Typ-Diskrepanz
|
||||
|
||||
| Quelle | Typ |
|
||||
|--------|-----|
|
||||
| Alembic 0071 | INTEGER |
|
||||
| DMS Plugin Migration 0001 | BIGINT |
|
||||
| SQLAlchemy Model | Integer |
|
||||
| **Produktion** | **bigint** |
|
||||
|
||||
**Klassifizierung:** Echte Schemaänderung
|
||||
**Forward-Migration:** 0093 — `ALTER COLUMN size_bytes TYPE BIGINT`
|
||||
|
||||
### 2. GIN-Indizes — Fehlendes USING GIN
|
||||
|
||||
Alembic 0002 erstellt:
|
||||
```sql
|
||||
CREATE INDEX ix_companies_search_vec ON companies (search_tsv)
|
||||
```
|
||||
|
||||
Produktion hat:
|
||||
```sql
|
||||
CREATE INDEX ix_companies_search_vec ON companies USING gin (search_tsv)
|
||||
```
|
||||
|
||||
Betroffene Tabellen/Indizes (in Produktion als GIN vorhanden):
|
||||
- contacts.ix_contacts_search_tsv
|
||||
- audit_log.ix_audit_log_search_tsv
|
||||
- calendar_entries.ix_cal_entries_search_tsv
|
||||
- comm_messages.ix_comm_messages_search_tsv
|
||||
- files.ix_files_content_tsv
|
||||
- mails.ix_mails_body_tsv
|
||||
- tags.ix_tags_search_tsv
|
||||
|
||||
**Klassifizierung:** Echte Schemaänderung (Index-Typ)
|
||||
**Forward-Migration:** 0094 — GIN-Indizes neu erstellen mit USING GIN
|
||||
|
||||
### 3. guest_users — Fehlender UNIQUE Constraint
|
||||
|
||||
Alembic 0059 erstellt:
|
||||
```sql
|
||||
CREATE INDEX ix_guest_users_email_tenant ON guest_users (email, tenant_id)
|
||||
```
|
||||
|
||||
Model und Produktion haben:
|
||||
```sql
|
||||
CREATE UNIQUE INDEX ix_guest_users_email_tenant ON guest_users (email, tenant_id)
|
||||
```
|
||||
|
||||
**Klassifizierung:** Echte Schemaänderung (Unique fehlt in Alembic)
|
||||
**Forward-Migration:** 0095 — Index als UNIQUE neu erstellen
|
||||
|
||||
### 4. plugins.name — Doppelter Unique-Index
|
||||
|
||||
Produktion hat zwei UNIQUE-Indizes auf plugins.name:
|
||||
- `plugins_name_key` (von `unique=True` in Column-Definition)
|
||||
- `ix_plugins_name` (von explizitem `CREATE INDEX` in 0003, als UNIQUE in Produktion)
|
||||
|
||||
Alembic 0003 erstellt `ix_plugins_name` ohne `UNIQUE`, aber Column hat `unique=True`.
|
||||
|
||||
**Klassifizierung:** Nur Idempotenzänderung (Redundanz)
|
||||
**Forward-Migration:** 0094 — Doppelten Index entfernen
|
||||
|
||||
---
|
||||
|
||||
## Keine Diskrepanz gefunden
|
||||
|
||||
- tenants.slug: unique=True in 0001 + Model + Produktion → ✅
|
||||
- plugin_migrations: UniqueConstraint in 0003 + Model + Produktion → ✅
|
||||
- RLS-Policies: Alle korrekt in Produktion → ✅
|
||||
- Workspace-Tabellen: RLS fail-closed, Tabellen korrekt → ✅
|
||||
|
||||
---
|
||||
|
||||
## Forward-Migration-Plan
|
||||
|
||||
| Migration | Inhalt |
|
||||
|-----------|--------|
|
||||
| 0093 | files.size_bytes INTEGER → BIGINT |
|
||||
| 0094 | GIN-Indizes reparieren + plugins.name doppelten Index entfernen |
|
||||
| 0095 | guest_users email+tenant_id UNIQUE INDEX |
|
||||
| 0096 | Workspace tenant_integrity (Plan 4.3) |
|
||||
@@ -1,162 +0,0 @@
|
||||
# Phase 0 — Frozen Error List (P0/P1)
|
||||
|
||||
**Date:** 2026-07-31
|
||||
**Baseline commit:** 11d6faa (tag: v-phase0-baseline)
|
||||
**Phase 0 commit:** 032a7e8
|
||||
|
||||
---
|
||||
|
||||
## P0 — Critical Security Issues
|
||||
|
||||
### P0-01: All tables owned by SUPERUSER role
|
||||
- **Severity:** P0
|
||||
- **Files:** All 123 tables in `public` schema
|
||||
- **Tables:** ALL
|
||||
- **Reproduction:** `SELECT tableowner FROM pg_tables WHERE schemaname='public'` → all `crm_user`
|
||||
- **Target:** Owner = `crm_migration` (NOSUPERUSER, NOBYPASSRLS)
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-02: `crm_migration` has BYPASSRLS
|
||||
- **Severity:** P0
|
||||
- **Files:** DB role `crm_migration`
|
||||
- **Reproduction:** `SELECT rolbypassrls FROM pg_roles WHERE rolname='crm_migration'` → `true`
|
||||
- **Target:** `ALTER ROLE crm_migration NOBYPASSRLS`
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-03: RLS disabled on ~70+ tenant tables
|
||||
- **Severity:** P0
|
||||
- **Tables:** contacts, addresses, attachments, ai_*, calendar_*, comm_*, mail_*, workflows, etc.
|
||||
- **Reproduction:** `SELECT relname FROM pg_class WHERE relrowsecurity=false AND relforcerowsecurity=true`
|
||||
- **Target:** ENABLE ROW LEVEL SECURITY on all tenant tables
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-04: Old RLS policies scoped to `{public}` — potential cross-transaction leak
|
||||
- **Severity:** P0
|
||||
- **Tables:** ~70+ tables with old `tenant_isolation` policy
|
||||
- **Reproduction:** `SELECT policyname, roles FROM pg_policies WHERE roles='{public}'`
|
||||
- **Target:** Drop old policies, create new ones scoped to `{crm_api, crm_worker}`
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-05: No separate database connections for auth/api/worker/migration
|
||||
- **Severity:** P0
|
||||
- **Files:** `app/config.py`, `app/core/db/__init__.py`
|
||||
- **Reproduction:** `grep -n 'auth_database_url\|worker_database_url' app/config.py` → not found
|
||||
- **Target:** 4 separate engines with separate pools and roles
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-06: Worker uses `crm_api` role instead of `crm_worker`
|
||||
- **Severity:** P0
|
||||
- **Files:** `docker-compose.yml` worker environment
|
||||
- **Reproduction:** `docker exec leocrm-worker env | grep DATABASE_URL` → `crm_api`
|
||||
- **Target:** Worker uses `crm_worker` role
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-07: `crm_runtime` legacy role with full CRUD on ALL tables
|
||||
- **Severity:** P0
|
||||
- **Files:** DB role `crm_runtime`
|
||||
- **Reproduction:** `SELECT count(*) FROM information_schema.role_table_grants WHERE grantee='crm_runtime'` → 492
|
||||
- **Target:** Remove role or revoke all grants
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-08: `crm_api` and `crm_worker` have access to `alembic_version`
|
||||
- **Severity:** P0
|
||||
- **Tables:** `alembic_version`
|
||||
- **Reproduction:** `SELECT * FROM information_schema.role_table_grants WHERE table_name='alembic_version' AND grantee IN ('crm_api','crm_worker')`
|
||||
- **Target:** Revoke access — only `crm_migration` should access alembic_version
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-09: `crm_auth` missing `password_reset_tokens` access
|
||||
- **Severity:** P0
|
||||
- **Tables:** `password_reset_tokens`
|
||||
- **Reproduction:** `SELECT * FROM information_schema.role_table_grants WHERE grantee='crm_auth' AND table_name='password_reset_tokens'` → empty
|
||||
- **Target:** Grant SELECT, INSERT, UPDATE on `password_reset_tokens` to `crm_auth`
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P0-10: `crm_auth` has access to `groups`, `roles`, `user_groups` — too broad
|
||||
- **Severity:** P0
|
||||
- **Tables:** `groups`, `roles`, `user_groups`
|
||||
- **Reproduction:** `SELECT table_name FROM information_schema.role_table_grants WHERE grantee='crm_auth'`
|
||||
- **Target:** Revoke — auth only needs users, user_tenants, tenants, password_reset_tokens
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
## P1 — High Priority Issues
|
||||
|
||||
### P1-01: `app.tenant_id` legacy variable still set
|
||||
- **Severity:** P1
|
||||
- **Files:** `app/core/db/__init__.py:128` (now fixed)
|
||||
- **Reproduction:** `grep -rn 'app.tenant_id' app/ --include='*.py'` (was setting both vars)
|
||||
- **Target:** Only `app.current_tenant_id` — FIXED in Phase 0
|
||||
- **Status:** ✅ Fixed
|
||||
|
||||
### P1-02: Cross-plugin import in report_generator
|
||||
- **Severity:** P1
|
||||
- **Files:** `app/plugins/builtins/report_generator/jobs.py:79`
|
||||
- **Reproduction:** `grep 'from app.plugins.builtins.dms' app/plugins/builtins/report_generator/jobs.py`
|
||||
- **Target:** Use DmsContract via contract registry — FIXED in Phase 0
|
||||
- **Status:** ✅ Fixed
|
||||
|
||||
### P1-03: `test_cross_tenant_security_v2.py` was deleted (contained `§§include()`)
|
||||
- **Severity:** P1
|
||||
- **Files:** `tests/test_cross_tenant_security_v2.py`
|
||||
- **Reproduction:** File did not exist
|
||||
- **Target:** Recreate with real RLS tests using unprivileged role — FIXED in Phase 0
|
||||
- **Status:** ✅ Fixed
|
||||
|
||||
### P1-04: Existing tests reference `app.tenant_id` in assertions
|
||||
- **Severity:** P1
|
||||
- **Files:** `tests/test_cross_tenant_security.py`, `tests/test_cross_tenant_standalone.py`
|
||||
- **Reproduction:** `grep 'app.tenant_id' tests/test_cross_tenant*.py`
|
||||
- **Target:** Only test `app.current_tenant_id` — FIXED in Phase 0
|
||||
- **Status:** ✅ Fixed
|
||||
|
||||
### P1-05: No `crm_platform_admin` role defined
|
||||
- **Severity:** P1
|
||||
- **Files:** DB roles
|
||||
- **Reproduction:** `SELECT * FROM pg_roles WHERE rolname='crm_platform_admin'` → not found
|
||||
- **Target:** Create role for one-time infrastructure setup
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P1-06: No Default Privileges set for future tables
|
||||
- **Severity:** P1
|
||||
- **Files:** DB configuration
|
||||
- **Reproduction:** `SELECT * FROM pg_default_privileges WHERE defaclrole='crm_migration'` → empty
|
||||
- **Target:** Set default privileges for `crm_migration` owner
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P1-07: Login path uses same DB connection as API
|
||||
- **Severity:** P1
|
||||
- **Files:** `app/routes/auth.py`, `app/core/db/__init__.py`
|
||||
- **Reproduction:** Login endpoint uses `get_db()` (crm_api engine)
|
||||
- **Target:** Login uses `get_auth_db()` (crm_auth engine)
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P1-08: Startup code accesses tenant tables without tenant context
|
||||
- **Severity:** P1
|
||||
- **Files:** `app/main.py:169-231`
|
||||
- **Reproduction:** Plugin activation during startup may access tenant tables
|
||||
- **Target:** Per-tenant context for tenant operations
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P1-09: No RLS coverage check automation
|
||||
- **Severity:** P1
|
||||
- **Files:** None — needs creation
|
||||
- **Target:** Automated test/script checking all tenant tables for RLS
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
### P1-10: `crm_worker` has full CRUD on ALL tables including global tables
|
||||
- **Severity:** P1
|
||||
- **Tables:** users, tenants, user_tenants, sessions, plugins, etc.
|
||||
- **Reproduction:** `SELECT count(*) FROM information_schema.role_table_grants WHERE grantee='crm_worker'` → 492
|
||||
- **Target:** Narrow to only necessary job/outbox/tenant tables
|
||||
- **Status:** Open — Phase 1
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| Open (P0) | 10 |
|
||||
| Open (P1) | 7 |
|
||||
| Fixed (P1) | 4 |
|
||||
| Total | 21 |
|
||||
@@ -1,410 +0,0 @@
|
||||
ÜBERHOLT – NICHT ALS UMSETZUNGSANWEISUNG VERWENDEN
|
||||
# Phase 0 + Phase 1 — Abschluss-Abnahmeprotokoll
|
||||
|
||||
**Stand:** 2026-07-31 12:06 CEST
|
||||
**Git-Commit:** 3032ad2 (main)
|
||||
**Alembic-Head:** 0088
|
||||
**Docker-Image:** dx4pqdziu4uj6x9fxs1u5z0x:3032ad2 (Coolify-Build aus Git)
|
||||
|
||||
---
|
||||
|
||||
## Container-Status
|
||||
|
||||
| Container | Status | Rolle |
|
||||
|-----------|--------|------|
|
||||
| dx4pqdziu4uj6x9fxs1u5z0x-100457116674 | Up, healthy | API (crm_api) |
|
||||
| leocrm-worker | Up, healthy | Worker (crm_worker) |
|
||||
| crm-postgres | Up | PostgreSQL |
|
||||
| crm-redis | Up | Redis |
|
||||
|
||||
## Datenbankrollen und Verbindungen
|
||||
|
||||
| Rolle | Verbindung | Eigenschaften |
|
||||
|-------|-----------|--------------|
|
||||
| crm_platform_admin | — | NOSUPERUSER, NOBYPASSRLS, NOLOGIN |
|
||||
| crm_migration | MIGRATION_DATABASE_URL | NOSUPERUSER, BYPASSRLS, Tabellenowner |
|
||||
| crm_auth | AUTH_DATABASE_URL | NOSUPERUSER, NOBYPASSRLS |
|
||||
| crm_api | DATABASE_URL | NOSUPERUSER, NOBYPASSRLS |
|
||||
| crm_worker | WORKER_DATABASE_URL | NOSUPERUSER, NOBYPASSRLS |
|
||||
|
||||
Verifiziert via `docker exec env | grep DATABASE`:
|
||||
- API: DATABASE_URL=crm_api, AUTH_DATABASE_URL=crm_auth ✅
|
||||
- Worker: DATABASE_URL=crm_worker, WORKER_DATABASE_URL=crm_worker ✅
|
||||
- Migration: MIGRATION_DATABASE_URL=crm_migration ✅
|
||||
|
||||
---
|
||||
|
||||
## Gate 1 — Reproduzierbares Coolify-Deployment ✅
|
||||
|
||||
### Durchführung
|
||||
1. Alle Änderungen auf main gepusht (Commit 3032ad2) ✅
|
||||
2. Coolify-Rebuild aus Git getriggert ✅
|
||||
3. Docker-Image ausschließlich aus Repository gebaut ✅
|
||||
4. Keine manuellen Dateiänderungen im laufenden Container ✅
|
||||
5. API- und Worker-Container vollständig neu erstellt ✅
|
||||
6. Migrationen automatisch bis Alembic-Head 0088 ausgeführt ✅
|
||||
|
||||
### Nachweis nach dem Deployment
|
||||
- API healthy ✅
|
||||
- Worker healthy ✅
|
||||
- PostgreSQL healthy ✅
|
||||
- Redis healthy ✅
|
||||
- Login erfolgreich ✅
|
||||
- API verwendet crm_api ✅
|
||||
- Authentifizierung verwendet crm_auth ✅
|
||||
- Worker verwendet crm_worker ✅
|
||||
- Migrationen verwenden crm_migration ✅
|
||||
- Alembic-Head ist 0088 ✅
|
||||
- RLS-Tests: 0 rows ohne Kontext, 8 rows mit Kontext ✅
|
||||
- Worker verarbeitet Outbox-Jobs ✅
|
||||
|
||||
### Dockerfile-Fixes
|
||||
- `npm ci --silent 2>/dev/null || npm install --silent` → `npm ci --legacy-peer-deps || npm install --legacy-peer-deps` (vite 8 / @vitejs/plugin-react 4.7.0 peer dependency conflict)
|
||||
|
||||
---
|
||||
|
||||
## Gate 2 — Neuinstallation auf leerer Datenbank ⏳ OFFEN
|
||||
|
||||
Nicht durchgeführt — erfordert separate Testumgebung in Coolify mit eigener PostgreSQL-Instanz.
|
||||
|
||||
---
|
||||
|
||||
## Gate 3 — Vollständiger Restore-Test ⏳ OFFEN
|
||||
|
||||
Nicht durchgeführt — erfordert separate Testdatenbank und DMS-Storage.
|
||||
|
||||
---
|
||||
|
||||
## Gate 4 — Passwort-Reset end-to-end ✅
|
||||
|
||||
### Durchführung
|
||||
1. Reset angefordert: `POST /api/v1/auth/password-reset/request` → 200 OK ✅
|
||||
2. Token in DB generiert (hash, nicht raw) ✅
|
||||
3. ARQ-Mailjob erzeugt und verarbeitet (Worker-Log: `send_password_reset_email ●`) ✅
|
||||
4. SMTP-Versand über mail.media-on.de:465 (implicit TLS) ✅
|
||||
5. Email im Postfach admin@media-on.de angekommen (IMAP verifiziert) ✅
|
||||
6. Reset-Link aus Email extrahiert ✅
|
||||
7. Passwort erfolgreich geändert: `POST /api/v1/auth/password-reset/confirm` → 200 OK ✅
|
||||
8. Login mit altem Passwort fehlschlägt: 401 `invalid_credentials` ✅
|
||||
9. Login mit neuem Passwort funktioniert: 200 OK mit user_id, csrf_token ✅
|
||||
10. Token-Wiederverwendung fehlschlägt: 400 `invalid_token` ✅
|
||||
11. Unbekannte Email: 200 OK ohne Benutzerexistenz-Offenlegung ✅
|
||||
12. Reset-Token in Logs: Nicht gefunden (kein Token-Leak) ✅
|
||||
13. Passwort auf Admin123! zurückgesetzt und Login verifiziert ✅
|
||||
|
||||
### SMTP-Konfiguration
|
||||
- SMTP_HOST=mail.media-on.de
|
||||
- SMTP_PORT=465 (implicit TLS)
|
||||
- SMTP_USER=test@media-on.de
|
||||
- SMTP_FROM_EMAIL=admin@media-on.de
|
||||
- SMTP_USE_TLS=true
|
||||
|
||||
### Code-Fixes
|
||||
- `app/core/worker.py`: `app.core.jobs` zur plugin_job_modules Liste hinzugefügt (Worker fand `send_password_reset_email` nicht)
|
||||
- `app/core/jobs.py`: SMTP `start_tls` → `use_tls` für Port 465 (implicit TLS)
|
||||
- `app/services/auth_service.py`: Audit-Log über separate API-Session (crm_api) mit Tenant-Kontext
|
||||
- `alembic/versions/0088_auth_rls_policies.py`: RLS-Policies für crm_auth auf password_reset_tokens und audit_log
|
||||
|
||||
### Migration 0088
|
||||
- `password_reset_tokens`: crm_auth SELECT (lookup), UPDATE (mark used), INSERT (create token with tenant context)
|
||||
- `password_reset_tokens`: crm_api/crm_worker tenant isolation
|
||||
- `audit_log`: crm_auth INSERT with tenant context
|
||||
- `users`: crm_auth UPDATE (password hash update)
|
||||
- Alle Grants über Migration, nicht manuell
|
||||
|
||||
### Reset-URL
|
||||
- Aktuell: `http://localhost:5173/reset-password?token=...` (FRONTEND_URL Default)
|
||||
- Fix: FRONTEND_URL=https://crm.media-on.de in Coolify .env gesetzt
|
||||
- Bei nächstem Rebuild werden Reset-Links korrekt auf https://crm.media-on.de zeigen
|
||||
|
||||
---
|
||||
|
||||
## Gate 5 — Worker und Eventhandler ⏳ OFFEN
|
||||
|
||||
Worker verarbeitet Outbox-Jobs und send_password_reset_email. Plugin-Eventhandler-Registrierung ist noch nicht vollständig implementiert.
|
||||
|
||||
---
|
||||
|
||||
## RLS-Verifikation
|
||||
|
||||
| Test | Ergebnis |
|
||||
|------|----------|
|
||||
| crm_api SELECT ohne Kontext | 0 rows ✅ |
|
||||
| crm_api SELECT mit Kontext | 8 rows ✅ |
|
||||
| Cross-Tenant INSERT | ERROR: violates RLS ✅ |
|
||||
| Cross-Tenant UPDATE | UPDATE 0 ✅ |
|
||||
| Cross-Tenant DELETE | DELETE 0 ✅ |
|
||||
| WITH CHECK violation | ERROR: WITH CHECK ✅ |
|
||||
| crm_migration BYPASSRLS | 7 rows tenantübergreifend ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Alle 15 Abnahmekriterien
|
||||
|
||||
| # | Kriterium | Status |
|
||||
|---|-----------|--------|
|
||||
| 1 | Login über crm_auth | ✅ |
|
||||
| 2 | API über crm_api | ✅ |
|
||||
| 3 | crm_api NOSUPERUSER/NOBYPASSRLS | ✅ |
|
||||
| 4 | crm_worker NOSUPERUSER/NOBYPASSRLS | ✅ |
|
||||
| 5 | Cross-Tenant Read blockiert | ✅ |
|
||||
| 6 | Cross-Tenant Write blockiert | ✅ |
|
||||
| 7 | Kein Fachdaten ohne Kontext | ✅ |
|
||||
| 8 | Tenantwechsel prüft Membership | ✅ |
|
||||
| 9 | Passwort-Reset funktioniert | ✅ |
|
||||
| 10 | Startup ohne Bootstrap-Policy | ✅ |
|
||||
| 11 | Per-Tenant Startup | ✅ |
|
||||
| 12 | Migration auf bestehender DB | ✅ |
|
||||
| 13 | RLS-Abdeckungsprüfung | ✅ |
|
||||
| 14 | app.tenant_id entfernt | ✅ |
|
||||
| 15 | Getrennte DB-Rollen | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Offene Risiken
|
||||
|
||||
1. **Gate 2 (leere DB-Neuinstallation):** Nicht durchgeführt — erfordert separate Testumgebung
|
||||
2. **Gate 3 (Restore-Test):** Nicht durchgeführt — erfordert separate Testdatenbank
|
||||
3. **Gate 5 (Worker-Eventhandler):** Plugin-Eventhandler-Registrierung nicht vollständig
|
||||
4. **FRONTEND_URL:** Wird erst bei nächstem Coolify-Rebuild wirksam (aktuell noch localhost:5173 in Emails)
|
||||
5. **Worker-Container:** Wird nicht über Coolify verwaltet (manuell mit docker run erstellt) — bei Coolify-Rebuild wird der Worker nicht automatisch neu erstellt
|
||||
6. **SMTP_FROM_EMAIL:** Verwendet admin@media-on.de als Absender (noreply@media-on.de existiert nicht auf dem Mail-Server)
|
||||
|
||||
---
|
||||
|
||||
## Rollback-Verfahren
|
||||
|
||||
1. `pg_restore` aus Forgejo-Release-Backup
|
||||
2. `alembic downgrade 0087` (Migration 0088 rückgängig machen)
|
||||
3. `git reset --hard v-phase0-baseline`
|
||||
4. Coolify-Rebuild aus altem Commit
|
||||
|
||||
---
|
||||
|
||||
## Freigabestatus
|
||||
|
||||
**BEDINGT ABGENOMMEN**
|
||||
|
||||
- Gate 1 (Coolify-Deployment): ✅ Bestanden
|
||||
- Gate 4 (Passwort-Reset): ✅ Bestanden
|
||||
- Gate 2 (leere DB): ⏳ Offen
|
||||
- Gate 3 (Restore): ⏳ Offen
|
||||
- Gate 5 (Worker-Eventhandler): ⏳ Offen
|
||||
|
||||
Phase 0 und Phase 1 können als technisch abgenommen gelten, sobald Gate 2, 3 und 5 abgeschlossen sind.
|
||||
|
||||
---
|
||||
|
||||
## Gate 2 — Neuinstallation auf leerer Datenbank ✅ BESTANDEN
|
||||
|
||||
**Datum:** 2026-07-31
|
||||
**Git-Commit:** 89b775b
|
||||
**Test-Service:** g13zwdav6myvpnop96dj7tpx (crmtest.media-on.de)
|
||||
**Image:** dx4pqdziu4uj6x9fxs1u5z0x:89b775b
|
||||
**DB-Image:** pgvector/pgvector:pg16
|
||||
|
||||
### Durchführung
|
||||
|
||||
1. Coolify Test-Service mit eigener PostgreSQL, Redis, API, Worker erstellt
|
||||
2. DB-Volume gelöscht für vollständig leere DB
|
||||
3. Image aus Git-Commit 89b775b auf Server gebaut
|
||||
4. Compose aktualisiert: Image 89b775b + pgvector/pgvector:pg16
|
||||
5. `docker compose up -d` — alle Container gestartet
|
||||
6. prestart.sh führte `alembic upgrade head` als crm_user aus
|
||||
7. Migrationen 0001→0090 automatisch ausgeführt
|
||||
8. Plugin-Migrationen über crm_migration ausgeführt (P0-Fix)
|
||||
9. seed_admin.py ausgeführt — Tenant + Role + User + UserTenant erstellt
|
||||
10. Login über HTTPS getestet
|
||||
|
||||
### Verifikationsergebnisse
|
||||
|
||||
| Kriterium | Ergebnis |
|
||||
|-----------|----------|
|
||||
| Coolify-Deployment erfolgreich | ✅ Alle 4 Container healthy |
|
||||
| API healthy | ✅ Up 2 minutes (healthy) |
|
||||
| Worker healthy | ✅ Up 2 minutes (healthy) |
|
||||
| PostgreSQL healthy | ✅ Up 2 minutes (healthy) |
|
||||
| Redis healthy | ✅ Up 2 minutes |
|
||||
| Alembic-Head | ✅ 0090 |
|
||||
| Tabellen erstellt | ✅ 124 Tabellen |
|
||||
| Keine manuellen Schemaänderungen | ✅ Ausschließlich Migrationen |
|
||||
| Rollen vorhanden | ✅ crm_migration (BYPASSRLS), crm_api/crm_auth/crm_worker (NOBYPASSRLS, NOSUPERUSER) |
|
||||
| RLS aktiviert | ✅ 47 Tabellen mit RLS |
|
||||
| Legacy app.tenant_id Policies | ✅ 0 (Migration 0090 fixt _old Tabellen) |
|
||||
| Admin erfolgreich angelegt | ✅ Tenant + Role + User + UserTenant |
|
||||
| Login erfolgreich | ✅ 200 OK mit user_id, csrf_token, tenant_id |
|
||||
| RLS ohne Kontext fail-closed | ✅ 0 rows |
|
||||
| Cross-Tenant INSERT blockiert | ✅ 'new row violates row-level security policy' |
|
||||
| Valid INSERT funktioniert | ✅ INSERT 0 1 |
|
||||
| crm_api DDL blockiert | ✅ 'permission denied for schema public' |
|
||||
|
||||
### Ausgeführte Befehle
|
||||
|
||||
```
|
||||
# Image bauen
|
||||
git clone https://forgejo.media-on.de/Leopoldadmin/leocrm.git
|
||||
git checkout 89b775b
|
||||
docker build -t dx4pqdziu4uj6x9fxs1u5z0x:89b775b .
|
||||
|
||||
# Compose aktualisieren und neu starten
|
||||
docker compose up -d
|
||||
|
||||
# Verifikation
|
||||
psql -U crm_user -d crm_test_db -f gate2_verify.sql
|
||||
psql -U crm_user -d crm_test_db -f gate2_rls.sql
|
||||
psql -U crm_user -d crm_test_db -f gate2_columns.sql
|
||||
|
||||
# Seed
|
||||
docker exec api-g13zwdav6myvpnop96dj7tpx python3 scripts/seed_admin.py
|
||||
|
||||
# Login
|
||||
curl -X POST https://crmtest.media-on.de/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Origin: https://crmtest.media-on.de" \
|
||||
-d '{"email":"admin@media-on.de","password":"Admin123!"}'
|
||||
```
|
||||
|
||||
### Bekannte Issues
|
||||
|
||||
1. **Login-Rolle 'viewer' statt 'admin':** seed_admin.py erstellt Role mit name='admin' und permissions={'*:*': True}, aber Login-Response gibt role='viewer'. Vermutlich wird die Rolle aus UserTenant.role_id nicht korrekt aufgelöst. Kein Gate-2-Blocker — RLS und Tenant-Isolation funktionieren korrekt.
|
||||
2. **pgvector-Extension:** Test-DB verwendet pgvector/pgvector:pg16 statt postgres:16-alpine. Produktion verwendet ebenfalls pgvector. Compose-Datei des Test-Services muss in Coolify aktualisiert werden.
|
||||
|
||||
### Gate-2-Abnahme: BESTANDEN
|
||||
|
||||
Alle Abnahmekriterien erfüllt. Die Anwendung startet auf einer vollständig leeren Datenbank ohne manuelle Nacharbeit.
|
||||
|
||||
---
|
||||
|
||||
## Gate 5 — Worker und Eventhandler ✅ BESTANDEN
|
||||
|
||||
**Datum:** 2026-07-31
|
||||
**Git-Commit:** 94847ea
|
||||
**Test-Service:** g13zwdav6myvpnop96dj7tpx (crmtest.media-on.de)
|
||||
**Image:** dx4pqdziu4uj6x9fxs1u5z0x:94847ea
|
||||
|
||||
### Durchgeführte Änderungen
|
||||
|
||||
1. **Plugin-Registry-Initialisierung über Migrations-Engine:**
|
||||
- `registry.initialize(get_migration_engine())` statt `get_worker_engine()`
|
||||
- DDL-Operationen laufen als `crm_migration` (BYPASSRLS), nicht als `crm_worker`
|
||||
|
||||
2. **Worker-Session über `get_worker_session_factory()`:**
|
||||
- Worker verwendet `crm_worker` für alle DB-Operationen
|
||||
- Keine Verwendung von `get_session_factory()` (crm_api) im Worker
|
||||
|
||||
3. **Event-Handler nur für aktive Plugins:**
|
||||
- `PluginModel.active == True` Check vor `register_event_handlers()`
|
||||
- Inaktive Plugins werden übersprungen
|
||||
|
||||
4. **Per-Tenant Outbox-Processing:**
|
||||
- `process_outbox_batch` iteriert über alle Tenant-IDs
|
||||
- Setzt `app.current_tenant_id` vor jedem Claim
|
||||
- RLS-kompatibel — kein BYPASSRLS für Outbox-Processing
|
||||
- `process_outbox_job` lädt Tenant-IDs und übergibt sie an `process_outbox_batch`
|
||||
|
||||
5. **Outbox-Event-Verarbeitung:**
|
||||
- Events ohne Handler → Status `no_handlers` (nicht `published`)
|
||||
- Idempotency-Check über `consumer_inbox`
|
||||
- Retry mit exponentiellem Backoff bei Fehlern
|
||||
|
||||
### Verifikationsergebnisse
|
||||
|
||||
| Kriterium | Ergebnis |
|
||||
|-----------|----------|
|
||||
| Worker healthy | ✅ Up 2 minutes (healthy) |
|
||||
| API healthy | ✅ Up 2 minutes (healthy) |
|
||||
| Worker verarbeitet Outbox-Jobs | ✅ Alle 5 Sekunden, 0.01s pro Job |
|
||||
| Worker verarbeitet scheduler_tick | ✅ Alle 5 Minuten |
|
||||
| Worker übernimmt enqueued Jobs | ✅ send_password_reset_email übernommen |
|
||||
| Worker verwendet crm_worker | ✅ get_worker_session_factory() |
|
||||
| Plugin-Eventhandler für aktive Plugins | ✅ PluginModel.active Check |
|
||||
| Keine Plugin-Router im Worker | ✅ Nur Event-Handler registriert |
|
||||
| Outbox per-Tenant mit RLS-Kontext | ✅ set_config(app.current_tenant_id) |
|
||||
| 18 Worker-Funktionen registriert | ✅ send_password_reset_email, generate_report_job, index_mails, etc. |
|
||||
|
||||
### Ausgeführte Befehle
|
||||
|
||||
```
|
||||
# Image bauen
|
||||
git clone https://forgejo.media-on.de/Leopoldadmin/leocrm.git
|
||||
git checkout 94847ea
|
||||
docker build -t dx4pqdziu4uj6x9fxs1u5z0x:94847ea .
|
||||
|
||||
# Deploy
|
||||
docker compose up -d
|
||||
|
||||
# Worker-Logs prüfen
|
||||
docker logs worker-g13zwdav6myvpnop96dj7tpx
|
||||
|
||||
# Job enqueue testen
|
||||
docker exec worker-g13zwdav6myvpnop96dj7tpx python3 -c "
|
||||
import asyncio
|
||||
from arq import create_pool
|
||||
from arq.connections import RedisSettings
|
||||
|
||||
async def enqueue():
|
||||
settings = RedisSettings.from_dsn('redis://default:TestRedisPass2026@redis:6379/0')
|
||||
redis = await create_pool(settings)
|
||||
await redis.enqueue_job('send_password_reset_email', email='admin@media-on.de')
|
||||
print('Job enqueued successfully')
|
||||
await redis.close()
|
||||
|
||||
asyncio.run(enqueue())
|
||||
"
|
||||
```
|
||||
|
||||
### Bekannte Issues
|
||||
|
||||
1. **Python-Logger-Ausgaben nicht in Docker-Logs sichtbar:** ARQ's Console-Handler zeigt nur Cron-Job-Output, nicht die `logger.info` Aufrufe aus `on_startup`. Die Logs werden möglicherweise in eine andere Log-Sink geschrieben. Kein Funktionsproblem.
|
||||
2. **send_password_reset_email erwartet kein tenant_id Keyword:** Der Test-Job wurde mit `tenant_id` enqueued was die Funktion nicht erwartet. Das ist ein Test-Fehler, kein Worker-Fehler. Die Funktion übernimmt den Job korrekt.
|
||||
|
||||
### Gate-5-Abnahme: BESTANDEN
|
||||
|
||||
Der Worker ist healthy, verarbeitet Outbox-Jobs, übernimmt enqueued Jobs, und verwendet die korrekte Datenbankrolle (crm_worker). Plugin-Eventhandler werden nur für aktive Plugins registriert. Outbox-Processing läuft per-Tenant mit gesetztem RLS-Kontext.
|
||||
|
||||
---
|
||||
|
||||
## Gate 3 — Vollständiger Restore-Test ✅ BESTANDEN
|
||||
|
||||
**Datum:** 2026-07-31
|
||||
**Git-Commit:** 9b4ee3b
|
||||
**Backup:** Forgejo Release `phase1-backup` (crm_backup_phase1.dump, 7.8 MB)
|
||||
**Restore-DB:** crm_restore_test (separate Datenbank im Test-DB-Container)
|
||||
|
||||
### Durchführung
|
||||
|
||||
1. Backup aus Forgejo-Release heruntergeladen
|
||||
2. MD5-Prüfsumme verglichen: b8003deaea95fb26f718ecb8a1a1369a ✅
|
||||
3. Separate leere Datenbank `crm_restore_test` erstellt
|
||||
4. `pg_restore --no-owner --no-acl` in crm_restore_test ausgeführt
|
||||
5. `alembic current` → 0086 (Backup-Stand)
|
||||
6. `alembic upgrade head` → 0090 (Migrationen 0087-0090 angewendet)
|
||||
7. Grants und Rollen-Passwörter neu angewendet (pg_restore --no-acl überspringt Grants)
|
||||
8. RLS-Tests auf wiederhergestellter DB ausgeführt
|
||||
|
||||
### Verifikationsergebnisse
|
||||
|
||||
| Kriterium | Ergebnis |
|
||||
|-----------|----------|
|
||||
| Backup-Prüfsumme | ✅ MD5: b8003deaea95fb26f718ecb8a1a1369a |
|
||||
| Restore erfolgreich | ✅ 123 Tabellen, 2 Tenants, 9 Contacts, 1 User, 479 Sessions |
|
||||
| Alembic-Version nach Restore | ✅ 0086 (Backup-Stand) |
|
||||
| Alembic upgrade head | ✅ 0090 (0087-0090 angewendet) |
|
||||
| Datenintegrität erhalten | ✅ 9 Contacts (1 Tenant A, 8 Tenant B) |
|
||||
| RLS ohne Kontext | ✅ 0 rows (fail-closed) |
|
||||
| RLS mit Tenant B | ✅ 8 rows |
|
||||
| RLS mit Tenant A | ✅ 2 rows |
|
||||
| Cross-Tenant INSERT blockiert | ✅ 'new row violates row-level security policy' |
|
||||
| DDL durch crm_api blockiert | ✅ 'permission denied for schema public' |
|
||||
| RLS-Tabellen | ✅ 108 |
|
||||
| RLS-Policies | ✅ 112 |
|
||||
| Legacy Policies | ✅ 0 |
|
||||
|
||||
### Bekannte Issues
|
||||
|
||||
1. **pg_restore --no-acl überspringt Grants:** Nach dem Restore müssen GRANT-Statements neu angewendet werden. Dies ist ein bekanntes Verhalten von `pg_restore --no-acl`. In einer produktiven Restore-Prozedur sollten die Grants durch `alembic upgrade head` (Migration 0085) oder ein separates Grant-Skript neu angewendet werden.
|
||||
2. **DMS-Dateien nicht getestet:** Der Restore-Test umfasste nur die PostgreSQL-Datenbank. DMS/Object-Storage-Dateien wurden nicht separat wiederhergestellt. Der Storage-Volume ist im Test-Service vorhanden aber nicht Teil des DB-Backups.
|
||||
|
||||
### Gate-3-Abnahme: BESTANDEN
|
||||
|
||||
Der Restore-Test ist erfolgreich abgeschlossen. Die Datenbank wurde aus dem Forgejo-Backup wiederhergestellt, auf den aktuellen Alembic-Head migriert, und alle RLS-Tests bestanden.
|
||||
@@ -1,172 +0,0 @@
|
||||
# Quality Gate Review — Phase 1 → Phase 2 (Re-Review)
|
||||
|
||||
**Datum:** 2026-06-28
|
||||
**Dateien:** requirements.md (2142 Zeilen), extracted-architecture-details.md (1006 Zeilen)
|
||||
**Vorherige Findings:** 6 Fixes angewendet
|
||||
|
||||
---
|
||||
|
||||
## Prüfkriterien & Ergebnisse
|
||||
|
||||
### 1. Vollständigkeit: 143 Features (73 Core + 70 Plugin)
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- Active feature headings (exkl. historisch): 143
|
||||
- `[v1]`-Features (Core): 73
|
||||
- `[v2-Plugin]`-Features (Plugin): 70
|
||||
- `[v1-Plugin]`-Features: 0 (alle konvertiert)
|
||||
- Summary-Tabelle: 73 Core + 70 Plugin = 143
|
||||
- DISCOVERY_CHECK_FINAL: `features_with_ids=143/143`
|
||||
|
||||
### 2. Konsistenz: Plugin vs Core Trennung
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- F-FILE-01–04: alle `[v2-Plugin]` (vorher `[v1-Plugin]`)
|
||||
- F-DMS-01–07: alle `[v2-Plugin]`
|
||||
- F-LINK-01–06: alle `[v2-Plugin]`
|
||||
- F-TAG-01–04: alle `[v2-Plugin]`
|
||||
- F-PERM-01–06: alle `[v2-Plugin]`
|
||||
- F-FILEUI-01–06: alle `[v2-Plugin]`
|
||||
- F-CAL-01–18: alle `[v2-Plugin]` (F-CAL-10 = `[v2-Plugin — später]`)
|
||||
- F-MAIL-01–19: alle `[v2-Plugin]`
|
||||
- F-PLUGIN-01/02: `[v1]` (Plugin-System ist Core)
|
||||
- F-CORE-04 (UI-Plugin-Framework): `[v1]` (Core-Infrastruktur)
|
||||
- Summary-Header: `### Core-Features (v1)` und `### Plugin-Features (v2-Plugin)`
|
||||
|
||||
### 3. Keine Implementierungs-Details in requirements.md
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- Keine HTML-Tags (`<div>`, `<span>`, `<button>`, `<input>` etc.) in Feature-Definitions
|
||||
- Keine React/JSX-Syntax (`className=`, `useState`, `<React`)
|
||||
- Keine CSS-Property-Spezifikationen (`min-height: 44px`, `::after`, `@media` etc.) — bereinigt in F-A11Y
|
||||
- F-A11Y-01: Keine ARIA-Attribut-Spezifikationen, keine `.sr-only` CSS-Klassen-Erwähnung
|
||||
- F-A11Y-02: Keine konkreten CSS-Property-Namen in Akzeptanzkriterium
|
||||
- F-A11Y-03: Keine konkreten CSS-Regeln (`min-height`, `min-width`, `::after`)
|
||||
- Implementierungs-Details sind in extracted-architecture-details.md
|
||||
|
||||
### 4. Test-Szenarien für alle Features
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- 143/143 Features haben `Test Scenarios` oder `Test Scenarios (Pflicht)`
|
||||
- F-COMP-01: Test Scenarios bei Zeile 172 (3 Szenarien) — verifiziert
|
||||
- F-CONT-01: Test Scenarios bei Zeile 295 (3 Szenarien) — verifiziert
|
||||
- F-A11Y-01–03: jeweils 3 Test Scenarios — verifiziert
|
||||
- DISCOVERY_CHECK_FINAL: `test_scenarios=143/143`
|
||||
- Alle Test-Szenarien haben konkretes erwartetes Ergebnis
|
||||
|
||||
### 5. Non-Goals aktuell
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- 28 Non-Goals dokumentiert (Zeilen 1940–1968)
|
||||
- Multi-Tenant nicht mehr als Non-Goal (ist v1-Feature)
|
||||
- AI Lead-Scoring / Auto-Enrichment als Non-Goal (KI-Copilot ist v1)
|
||||
- Nummernkreise/Sequenzen, State Machine, Document Versioning als Non-Goals
|
||||
- S/MIME, Mail-Server-Hosting, Mailinglisten, Newsletter als Non-Goals
|
||||
- Changelog dokumentiert Non-Goal-Updates (Zeile 2127)
|
||||
|
||||
### 6. Annahmen aktuell
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- 13 Annahmen dokumentiert (Zeilen 1918–1936)
|
||||
- Annahme 1: Multi-Tenant (Multi-Company) — aktualisiert
|
||||
- Annahme 4: Max 10 concurrent Users pro Tenant
|
||||
- Annahme 11: Plugin-System als v1-Feature
|
||||
- Annahme 13: KI-Copilot ist v1-Feature
|
||||
- Keine Single-Tenant-Annahme mehr vorhanden
|
||||
|
||||
### 7. DISCOVERY_CHECK_FINAL: 143/143
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- `DISCOVERY_CHECK_FINAL: categories=21/21, features_with_ids=143/143, test_scenarios=143/143, constraints=Y, non_goals=Y, domain=Y, ready_for_ui=Y`
|
||||
- Changelog-Zeile 2128: `143 Features (73 Core + 70 Plugin)` — aktualisiert
|
||||
|
||||
### 8. extracted-architecture-details.md: vollständig, keine Single-Tenant-Kontradiktionen
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- Zeile 380: `Multi-Tenant (Multi-Company)` — korrigiert
|
||||
- Zeile 888: `Multi-Tenant (Multi-Company)` — korrigiert
|
||||
- Zeile 961: `~~Multi-Tenant (Single-Tenant in v1)~~ — Multi-Tenant (Multi-Company) ist v1-Feature` — durchgestrichen (historisch)
|
||||
- Keine aktiven Single-Tenant-Referenzen verbleibend
|
||||
- Alle 3 Vorkommen von 'Single-Tenant' sind in Durchstreichung (~~...~~) oder korrigiert
|
||||
|
||||
### 9. Changelog vorhanden
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- `## Changelog (Bereinigung 2026-06-28)` bei Zeile 2119
|
||||
- 10 Änderungen dokumentiert
|
||||
- DISCOVERY_CHECK-Zeile aktualisiert: 143 Features (73 Core + 70 Plugin)
|
||||
- Verschiebung von Implementierungs-Details nach extracted-architecture-details.md dokumentiert
|
||||
|
||||
---
|
||||
|
||||
## Summary-Ranges Verifikation
|
||||
|
||||
| Bereich | Range in Summary | Body-Features | Status |
|
||||
|---------|-----------------|---------------|--------|
|
||||
| Auth | F-AUTH-01–F-AUTH-08 | 8 (01-08) | ✅ |
|
||||
| Companies | F-COMP-01–F-COMP-08 | 8 (01-08) | ✅ |
|
||||
| Contacts | F-CONT-01–F-CONT-07 | 7 (01-07) | ✅ |
|
||||
| Data | F-DATA-01–F-DATA-04, F-DATA-06 | 5 (01-04, 06) | ✅ (gap: kein F-DATA-05) |
|
||||
| UI | F-UI-01–F-UI-06, F-UI-08 | 7 (01-06, 08) | ✅ (gap: kein F-UI-07) |
|
||||
| Accessibility | F-A11Y-01–F-A11Y-03 | 3 (01-03) | ✅ |
|
||||
| Security | F-SEC-01–F-SEC-03 | 3 (01-03) | ✅ |
|
||||
| Infrastruktur | F-INFRA-01–F-INFRA-04 | 4 (01-04) | ✅ |
|
||||
| Migration | F-MIG-01 | 1 (01) | ✅ |
|
||||
| Integration | F-INT-01–F-INT-02 | 2 (01-02) | ✅ |
|
||||
| Testing | F-TEST-01 | 1 (01) | ✅ |
|
||||
| Environments | F-ENV-01 | 1 (01) | ✅ |
|
||||
| Dokumentation | F-DOC-01 | 1 (01) | ✅ |
|
||||
| Performance | F-PERF-01 | 1 (01) | ✅ |
|
||||
| Scheduling | F-SCHED-01 | 1 (01) | ✅ |
|
||||
| AI | F-AI-01 | 1 (01) | ✅ |
|
||||
| Workflow | F-WF-01 | 1 (01) | ✅ |
|
||||
| Search | F-SEARCH-01 | 1 (01) | ✅ |
|
||||
| Navigation | F-NAV-01 | 1 (01) | ✅ |
|
||||
| Settings | F-SET-01 | 1 (01) | ✅ |
|
||||
| Core-Infrastructure | F-CORE-01–F-CORE-13 | 13 (01-13) | ✅ |
|
||||
| Plugin-System | F-PLUGIN-01–F-PLUGIN-02 | 2 (01-02) | ✅ |
|
||||
| File | F-FILE-01–F-FILE-04 | 4 (01-04) | ✅ |
|
||||
| DMS | F-DMS-01–F-DMS-07 | 7 (01-07) | ✅ |
|
||||
| Links | F-LINK-01–F-LINK-06 | 6 (01-06) | ✅ |
|
||||
| Tags | F-TAG-01–F-TAG-04 | 4 (01-04) | ✅ |
|
||||
| Permissions | F-PERM-01–F-PERM-06 | 6 (01-06) | ✅ |
|
||||
| File-UI | F-FILEUI-01–F-FILEUI-06 | 6 (01-06) | ✅ |
|
||||
| Kalender | F-CAL-01–F-CAL-18 | 18 (01-18) | ✅ |
|
||||
| Mail | F-MAIL-01–F-MAIL-19 | 19 (01-19) | ✅ |
|
||||
|
||||
**Core Total: 73 ✓**
|
||||
**Plugin Total: 70 ✓**
|
||||
**Grand Total: 143 ✓**
|
||||
|
||||
---
|
||||
|
||||
## Gesamturteil
|
||||
|
||||
| # | Kriterium | Status |
|
||||
|---|-----------|--------|
|
||||
| 1 | Vollständigkeit: 143 Features | ✅ PASS |
|
||||
| 2 | Konsistenz: Plugin vs Core | ✅ PASS |
|
||||
| 3 | Keine Implementierungs-Details | ✅ PASS |
|
||||
| 4 | Test-Szenarien für alle | ✅ PASS |
|
||||
| 5 | Non-Goals aktuell | ✅ PASS |
|
||||
| 6 | Annahmen aktuell | ✅ PASS |
|
||||
| 7 | DISCOVERY_CHECK_FINAL 143/143 | ✅ PASS |
|
||||
| 8 | extracted: keine Single-Tenant-Kontradiktionen | ✅ PASS |
|
||||
| 9 | Changelog vorhanden | ✅ PASS |
|
||||
|
||||
### **Gesamt: 9/9 PASS — Quality Gate PASSED ✅**
|
||||
|
||||
**Bereit für Phase 2 (UI Design / Architecture): YES**
|
||||
|
||||
---
|
||||
|
||||
*Review durchgeführt am 2026-06-28. Alle 6 vorherigen Findings wurden erfolgreich behoben und verifiziert.*
|
||||
@@ -1,375 +0,0 @@
|
||||
# Requirements Review: requirements.md
|
||||
|
||||
**Datum:** 2026-06-28
|
||||
**Reviewer:** Requirements Analyst (automatisiert)
|
||||
**Datei:** `/a0/usr/workdir/dev-projects/leocrm/requirements.md`
|
||||
**Zeilen:** 2131
|
||||
**Feature-IDs:** ~141 aktive + 16 archivierte = ~157 total
|
||||
**Status der Datei:** Finalisiert — ready_for_ui (laut Header)
|
||||
|
||||
---
|
||||
|
||||
## Section 1: Konsistenz-Issues
|
||||
|
||||
### 1.1 Plugin-System vs. Core-Feature Widerspruch (CRITICAL)
|
||||
|
||||
**Der zentrale Widerspruch der Datei.**
|
||||
|
||||
**F-PLUGIN-01 (Zeile 848-851)** deklariert:
|
||||
> „Die Module sollen als Plugins realisiert sein, sodass das CRM später durch Plugins erweitert werden kann. Module (Mail, Kalender, Dateien, Tags) sind Plugins."
|
||||
|
||||
**F-PLUGIN-02 (Zeile 857-860)** definiert Plugin-Schnittstelle, Lifecycle-Hooks, Plugin-Manifest.
|
||||
|
||||
**Gleichzeitig** werden genau diese Module als detaillierte Core-Features mit konkreten HTTP-Endpunkten, DB-Schemas und Test-Szenarien spezifiziert:
|
||||
- **F-DMS-01 bis F-DMS-07 (Zeilen 991-1083):** DMS mit `POST /api/dms/folders`, `PATCH /api/dms/files/{id}`, etc.
|
||||
- **F-CAL-01 bis F-CAL-18 (Zeilen 1397-1662):** Kalender mit `POST /api/calendar/entries`, `GET /api/calendar/kanban`, etc.
|
||||
- **F-MAIL-01 bis F-MAIL-19 (Zeilen 1668-1951):** Mail mit `POST /api/mail/send`, IMAP IDLE, SMTP, PGP, etc.
|
||||
- **F-TAG-01 bis F-TAG-04 (Zeilen 1173-1223):** Tags mit `POST /api/tags/assign`, etc.
|
||||
|
||||
**Widerspruch:** Wenn Module Plugins sind, dann gehören ihre detaillierten Feature-Spezifikationen (Endpunkte, DB-Schemas, Test-Szenarien) NICHT in die Core-Requirements. Der Core definiert die Plugin-Schnittstelle; das Plugin definiert seine eigenen Features. So wie es jetzt ist, wird das Plugin-System deklariert, aber dann werden die „Plugin-Module" im Core-Requirements-Dokument detailliert spezifiziert — als wären sie Core-Features.
|
||||
|
||||
**F-CORE-01 bis F-CORE-13 (Zeilen 864-953)** definieren Core-Infrastruktur (Event Bus, Tenant-Isolation, Plugin-Migration, Service Container, API-First, Async Queue, Caching, Storage, Import/Export, PDF-Gen, Notification Service). Diese sind allesamt Architekturentscheidungen, keine Requirements.
|
||||
|
||||
**Fazit:** Die Datei versucht gleichzeitig zu sagen „ diese Module sind Plugins" UND „ diese Module sind Core-Features mit konkreten Implementierungsdetails". Das ist ein architektonischer Widerspruch, der in der Architektur-Phase aufgelöst werden muss — nicht in den Requirements.
|
||||
|
||||
### 1.2 Multi-Tenant (F-AUTH-07) vs. ältere Requirements ohne Tenant-Kontext (WARNING)
|
||||
|
||||
**F-AUTH-07 (Zeile 135-138)** deklariert Multi-Tenant als v1-Feature:
|
||||
> „Das System ist Multi-Tenant-fähig. Mehrere Firmen (Tenants) können im System verwaltet werden. Daten sind pro Tenant isoliert."
|
||||
|
||||
**F-CORE-02 (Zeile 871-874)** spezifiziert `tenant_id` auf allen Tabellen, ORM-Middleware für automatisches Query-Scoping.
|
||||
|
||||
**Annahme 1 (Zeile 1978):** „v1 ist Multi-Tenant (Multi-Company) — mehrere Firmen (Tenants) im System."
|
||||
|
||||
**Aber:** Die früher geschriebenen Requirements (F-AUTH-01 bis F-CONT-07, Zeilen 57-410) erwähnen Tenant-Kontext an keiner Stelle:
|
||||
- F-AUTH-01 (Login): kein Tenant-Bezug
|
||||
- F-AUTH-03 (User-Verwaltung): kein Tenant-Bezug — aber in Multi-Tenant muss ein User einem Tenant zugeordnet sein
|
||||
- F-COMP-01 (Firma anlegen): kein `tenant_id` in Feld-Tabelle (Zeile 156-186)
|
||||
- F-CONT-01 (Kontakt anlegen): kein `tenant_id` in Feld-Tabelle (Zeile 300-333)
|
||||
- F-COMP-05 (Pagination): kein Tenant-Filter erwähnt
|
||||
- F-COMP-06 (Suche): kein Tenant-Scoping erwähnt
|
||||
|
||||
**Fazit:** Multi-Tenant wurde später hinzugefügt und die frühen Requirements wurden nicht nachträglich aktualisiert. Das führt zu einer Lücke: Wie verhält sich F-COMP-01 (Firma anlegen) in Multi-Tenant-Kontext? Wird die Firma automatisch dem aktiven Tenant zugeordnet? Kann ein User Firmen in mehreren Tenants anlegen? Diese Fragen sind in den Requirements nicht beantwortet.
|
||||
|
||||
### 1.3 KI-Copilot (F-AI-01) mit voller API-Kontrolle vs. ältere UI-only-Flow-Requirements (WARNING)
|
||||
|
||||
**F-AI-01 (Zeile 798-806)** deklariert:
|
||||
> „Der Copilot hat Zugriff auf die volle API und soll alles steuern können — Daten abfragen, erstellen, bearbeiten, löschen, Aktionen auslösen, Workflows triggern."
|
||||
|
||||
**F-CORE-06 (Zeile 899-902)** deklariert API-First:
|
||||
> „Alle Core-Features und Plugin-Features sind primär über die API nutzbar. Die UI ist ein API-Client."
|
||||
|
||||
**Aber:** Mehrere Requirements beschreiben nur UI-Flows ohne API-Bezug:
|
||||
- F-UI-01 (Responsive Design, Zeile 495-503): nur CSS-Breakpoints, kein API-Bezug
|
||||
- F-UI-02 (i18n, Zeile 509-517): nur Frontend-Library, kein API-Bezug
|
||||
- F-UI-03 (Toast-Notifications, Zeile 523-531): nur Frontend-Komponente
|
||||
- F-UI-04 (Loading-States, Zeile 537-545): nur Frontend-State
|
||||
- F-UI-05 (Empty-States, Zeile 551-559): nur Frontend-Komponente
|
||||
- F-UI-06 (Confirmation-Dialogs, Zeile 565-573): nur Frontend-Modal
|
||||
- F-UI-08 (Datenansichten, Zeile 579-582): nur Frontend-Toggle
|
||||
|
||||
**Einschränkung:** Diese UI-Requirements sind legitimerweise UI-only — sie beschreiben Präsentationslogik, keine Datenoperationen. F-CORE-06 sollte explizit ausschließen, dass reine UI-Präsentations-Features keine API-Entpunkte benötigen. Aktuell ist die Formulierung „alle Features über API nutzbar" zu breit und suggeriert, dass auch Toast-Notifications einen API-Endpunkt haben müssten.
|
||||
|
||||
**Zusätzlicher Befund:** F-AI-01 und F-CORE-06 wurden retroaktiv hinzugefügt. Die ursprünglichen Requirements (v0.1, archiviert in Appendix A, Zeile 2089-2128) beschreiben Jinja2-Templates und SQLite — eine völlig andere Architektur. Die Datei hat also mindestens drei Evolutionsschichten:
|
||||
1. v0.1: Single-Tenant, Jinja2, SQLite (archiviert)
|
||||
2. v0.3: React SPA, PostgreSQL, RBAC (Hauptteil)
|
||||
3. v0.5+: Multi-Tenant, Plugin-System, API-First, KI-Copilot, Mail/Kalender/DMS (hinzugefügt)
|
||||
|
||||
Die Schichten wurden nicht vollständig integriert — Rückbezüge fehlen.
|
||||
|
||||
### 1.4 Auth-Mechanismus-Unschärfe (WARNING)
|
||||
|
||||
**F-AUTH-01 (Zeile 58):** „Session-basierte Auth mit HttpOnly+Secure+SameSite=Strict Cookie"
|
||||
|
||||
**F-AUTH-02 (Zeile 72-78):** Test-Szenario sagt „Token wird entfernt" und Akzeptanzkriterium sagt „Server-Token-Blacklist optional für v1" — das suggeriert Token-basierte Auth (JWT?), nicht Session-basierte Auth.
|
||||
|
||||
**F-INT-02 (Zeile 714-722):** „API-Endpunkte sind via Session-Cookie authentifiziert" aber erwähnt auch „Optional: API-Key für externe Integrationen".
|
||||
|
||||
**F-SEC-03 (Zeile 616-624):** „Session läuft nach 8h ab" — aber „Token gültig <8h" und „Token nach 8h → API gibt 401" — wieder Token-Sprache.
|
||||
|
||||
**Fazit:** Die Datei wechselt inkonsistent zwischen „Session" und „Token". Entweder es ist Session-basiert (Cookie + Server-Side Session Store) oder Token-basiert (JWT Stateless). Das muss entschieden und einheitlich formuliert werden.
|
||||
|
||||
---
|
||||
|
||||
## Section 2: Requirements vs. Bauanleitung Assessment
|
||||
|
||||
### 2.1 Enthaltene Implementierungsdetails
|
||||
|
||||
Die Datei enthält massiv Implementierungsdetails, die in eine Requirements-Spec nicht gehören:
|
||||
|
||||
#### HTTP-Endpunkte (Architektur, nicht Requirement)
|
||||
Jedes einzelne Akzeptanzkriterium spezifiziert konkrete HTTP-Endpunkte mit Pfaden, HTTP-Methoden, Query-Parametern und Response-Codes:
|
||||
- `POST /api/auth/login` (Zeile 65)
|
||||
- `GET /api/companies/{id}` (Zeile 207)
|
||||
- `DELETE /api/companies/{id}?cascade=true|false` (Zeile 235)
|
||||
- `GET /api/contacts?page=1&page_size=25&sort_by=last_name&sort_order=asc` (Zeile 396)
|
||||
- `POST /api/dms/files/upload` (Zeile 1013)
|
||||
- `GET /api/dms/files/{id}/preview` (Zeile 1041)
|
||||
- `POST /api/calendar/entries` (Zeile 1439)
|
||||
- `GET /api/calendar/kanban?period=this_week` (Zeile 1419)
|
||||
- `POST /api/mail/send` (Zeile 1693)
|
||||
- `GET /api/mail/search?q=angebot&folder=inbox` (Zeile 1709)
|
||||
- ...und dutzende weitere
|
||||
|
||||
**Problem:** Der Endpunkt-Pfad ist eine Architekturentscheidung. Ein Requirement sagt „User kann sich einloggen" — der Pfad `/api/auth/login` ist Implementierung.
|
||||
|
||||
#### DB-Schema-Definitionen (Architektur, nicht Requirement)
|
||||
- **F-COMP-01 (Zeilen 156-186):** Vollständige Feld-Tabelle mit Typen: `String(100)`, `Integer`, `Decimal`, `Picklist`, `FK→Company`, `Text(32000)`, etc. — das ist ein DB-Schema
|
||||
- **F-CONT-01 (Zeilen 300-333):** Vollständige Feld-Tabelle für Kontakte mit Typen
|
||||
- **F-COMP-07 (Zeile 277):** `audit_log` Tabellenname
|
||||
- **F-COMP-08 (Zeile 291):** `deletion_log` Tabellenname
|
||||
- **F-CONT-07 (Zeile 424):** `company_contacts` N:M-Tabellenname
|
||||
- **F-CORE-02 (Zeile 872):** `tenant_id` Feld auf allen Tabellen
|
||||
- **F-MAIL-03 (Zeile 1709):** `tsvector`-Index, `mail_body_tsv`, `mail_subject_tsv`
|
||||
- **F-CAL-12 (Zeile 1572):** `user_calendar_visibility` Tabellenname
|
||||
- **F-CAL-15 (Zeile 1614):** `assigned_to: user_id` Feldname
|
||||
|
||||
**Problem:** Feldnamen, -typen und Tabellennamen sind Implementierungsdetails, die in das DB-Schema der Architektur gehören.
|
||||
|
||||
#### Technologie-Entscheidungen (Architektur, nicht Requirement)
|
||||
- **F-CORE-07 (Zeile 907):** „Celery + Redis oder RQ + Redis" — Technologie-Wahl
|
||||
- **F-CORE-08 (Zeile 914):** „Redis als Cache-Backend" — Technologie-Wahl
|
||||
- **F-CORE-10 (Zeile 928):** „S3-kompatibles Storage (z.B. MinIO)" — Technologie-Wahl
|
||||
- **F-MAIL-02 (Zeile 1693):** „DOMPurify" — Library-Wahl
|
||||
- **F-MAIL-12 (Zeile 1846):** „python-gnupg" — Library-Wahl
|
||||
- **F-UI-02 (Zeile 517):** „react-i18next" — Library-Wahl
|
||||
- **F-DMS-04 (Zeile 1034):** „PDF.js" — Library-Wahl
|
||||
- **F-DATA-03 (Zeile 459):** „Pydantic-Schemas" — Library-Wahl
|
||||
- **F-INFRA-03 (Zeile 666):** „Python logging mit JSON-Formatter" — Library-Wahl
|
||||
|
||||
#### Protokoll-Details (Architektur, nicht Requirement)
|
||||
- **F-MAIL-01 (Zeile 1670):** „IMAP4rev1 (RFC 3501)", „IMAP IDLE (RFC 2177)"
|
||||
- **F-MAIL-02 (Zeile 1693):** „multipart/mixed", „SMTP-Versand"
|
||||
- **F-MAIL-05 (Zeile 1733):** „References- und In-Reply-To-Header (RFC 5322)"
|
||||
- **F-MAIL-18 (Zeile 1929):** „AES-256, Key via Env-Var"
|
||||
- **F-CAL-08 (Zeile 1516):** „RRULE (RFC 5545)"
|
||||
- **F-CAL-09 (Zeile 1530):** „RFC 5545 konform"
|
||||
- **F-MAIL-18 (Zeile 1929):** „IMAP MOVE (RFC 6851)"
|
||||
|
||||
#### Frontend-Komponenten-Namen (Architektur, nicht Requirement)
|
||||
- **F-CAL-01 (Zeile 1405):** `CalendarView` Komponente
|
||||
- **F-CAL-02 (Zeile 1419):** `KanbanCalendar` Komponente
|
||||
- **F-FILEUI-01 (Zeile 1321):** `FileBrowser`, `SidebarTree`, `MainView` Komponenten
|
||||
- **F-FILEUI-02 (Zeile 1335):** `Breadcrumb` Komponente
|
||||
- **F-FILEUI-03 (Zeile 1349):** `ContextMenu` Komponente
|
||||
- **F-FILEUI-04 (Zeile 1363):** Multi-Select-State in `FileBrowser`
|
||||
- **F-MAIL-05 (Zeile 1741):** `ThreadView` Komponente
|
||||
|
||||
#### Farbcodes und UI-Implementierung (Architektur, nicht Requirement)
|
||||
- **F-CAL-06 (Zeile 1485):** `{appointment+normal: "#3B82F6", task+normal: "#F59E0B", *+follow_up: "#F97316", *+private: "#9CA3AF"}` — konkrete Hex-Codes
|
||||
- **F-COMP-04 (Zeile 235):** `deleted_at = NOW` — SQL-Ausdruck
|
||||
- **F-FILEUI-02 (Zeile 1335):** „Materialized Path oder rekursive Abfrage" — DB-Pattern
|
||||
- **F-FILEUI-06 (Zeile 1391):** „HTML5 Drag & Drop API" — Browser-API
|
||||
- **F-FILEUI-05 (Zeile 1377):** „XMLHttpRequest (für Progress-Events) oder WebSocket" — Technologie
|
||||
|
||||
#### Algorithmus- und Logik-Details (Architektur, nicht Requirement)
|
||||
- **F-MAIL-07 (Zeilen 1762-1771):** Regelauswertungs-Reihenfolge, Background-Worker-Trigger
|
||||
- **F-MAIL-08 (Zeile 1786):** `vacation_sent_log`, No-Reply-Erkennung: „noreply", „no-reply", „donotreply"
|
||||
- **F-CAL-08 (Zeile 1516):** Recurrence-Instanz-Generierung, Exception-Handling
|
||||
- **F-CAL-15 (Zeile 1614):** Notification-Versand bei Zuweisung
|
||||
|
||||
### 2.2 Schätzung des Anteils
|
||||
|
||||
| Kategorie | Zeilen (geschätzt) | Anteil |
|
||||
|-----------|--------------------|--------|
|
||||
| **Genuine Requirements (das WAS)** | ~700-750 | ~35% |
|
||||
| — Projektbeschreibung, Domain Knowledge | ~25 | |
|
||||
| — Feature-Anforderung-Texte („User kann...") | ~250 | |
|
||||
| — Test-Szenarien (Verhalten, nicht Implementation) | ~300 | |
|
||||
| — Non-funktionale Anforderungen | ~20 | |
|
||||
| — Annahmen, Non-Goals, Checkliste, Open Questions | ~155 | |
|
||||
| **Architektur/Implementierung (das HOW)** | ~1380-1430 | ~65% |
|
||||
| — HTTP-Endpunkte in Akzeptanzkriterien | ~400 | |
|
||||
| — DB-Schema-Definitionen (Feld-Tabellen, Typen) | ~150 | |
|
||||
| — F-CORE-01 bis F-CORE-13 (Architekturentscheidungen) | ~100 | |
|
||||
| — F-PLUGIN-01/02 (Plugin-System-Architektur) | ~20 | |
|
||||
| — F-WF-01 (Workflow-Engine-Architektur) | ~10 | |
|
||||
| — Protokoll-Details (RFCs, IMAP, SMTP) | ~80 | |
|
||||
| — Technologie-/Library-Wahlen | ~60 | |
|
||||
| — Frontend-Komponenten-Namen | ~40 | |
|
||||
| — Farbcodes, SQL-Ausdrücke, Algorithmus-Details | ~50 | |
|
||||
| — Redundanzen (F-FILE vs F-DMS, F-SCHED vs F-CORE-07) | ~100 | |
|
||||
| — Historische/archivierte Requirements (Appendix A) | ~40 | |
|
||||
| — Formatierung, Leerzeilen, Trennlinien | ~370 | |
|
||||
|
||||
**Fazit:** Die Datei ist zu ~35% eine Requirements-Spec und zu ~65% eine Architektur-/Implementierungs-Dokumentation. Sie hat den Charakter einer Bauanleitung angenommen, nicht den einer Anforderungsspezifikation.
|
||||
|
||||
---
|
||||
|
||||
## Section 3: Empfehlung
|
||||
|
||||
### 3.1 Was in requirements.md bleiben sollte
|
||||
|
||||
**Genuine Requirements — das WAS:**
|
||||
|
||||
1. **Projektbeschreibung** (Zeilen 10-14) — Was ist das Projekt?
|
||||
2. **Domain Knowledge** (Zeilen 17-31) — Fachliche Begriffe und Referenzen
|
||||
3. **Tech-Stack-Entscheidungen** (Zeilen 34-52) — Hohe-Level-Entscheidungen (Backend, DB, Frontend, Deployment)
|
||||
4. **Feature-Anforderungstexte** — Die „Anforderung:"-Absätze jedes Features, bereinigt um Implementierungsdetails:
|
||||
- F-AUTH-01 bis F-AUTH-08: Was muss die Auth können?
|
||||
- F-COMP-01 bis F-COMP-08: Was muss Firmen-Management können?
|
||||
- F-CONT-01 bis F-CONT-07: Was muss Kontakt-Management können?
|
||||
- F-DATA-01 bis F-DATA-06: Was muss Daten-Management können?
|
||||
- F-UI-01 bis F-UI-08: Was muss die UI bieten?
|
||||
- F-SEC-01 bis F-SEC-03: Welche Sicherheitsanforderungen?
|
||||
- F-INFRA-01 bis F-INFRA-04: Welche Infrastrukturanforderungen?
|
||||
- F-MIG-01: Was muss Migration/Import können?
|
||||
- F-INT-01: Welche Integrationsanforderung?
|
||||
- F-TEST-01: Welche Test-Strategie?
|
||||
- F-ENV-01: Welche Environment-Anforderung?
|
||||
- F-DOC-01: Welche Doku-Anforderung?
|
||||
- F-PERF-01: Welche Performance-Anforderung?
|
||||
- F-SEARCH-01: Was muss die globale Suche können?
|
||||
- F-NAV-01: Welche Navigation?
|
||||
- F-SET-01: Welche Einstellungen?
|
||||
- F-DMS-01 bis F-DMS-07: Was muss DMS können? (ohne Endpunkte)
|
||||
- F-LINK-01 bis F-LINK-06: Was muss Verknüpfung können? (ohne Endpunkte)
|
||||
- F-TAG-01 bis F-TAG-04: Was muss Tagging können? (ohne Endpunkte)
|
||||
- F-PERM-01 bis F-PERM-06: Welche Berechtigungs-Requirements? (ohne Endpunkte)
|
||||
- F-FILEUI-01 bis F-FILEUI-06: Welche UI-Requirements für Datei-Browser? (ohne Komponentennamen)
|
||||
- F-CAL-01 bis F-CAL-18: Was muss Kalender können? (ohne Endpunkte, ohne Farbcodes)
|
||||
- F-MAIL-01 bis F-MAIL-19: Was muss Mail können? (ohne Protokoll-Details)
|
||||
- F-AI-01: Was muss der KI-Copilot können?
|
||||
- F-SCHED-01: Welche Background-Job-Anforderung?
|
||||
5. **Test-Szenarien** — Aber bereinigt: nur Verhalten beschreiben („User klickt X → Y passiert"), keine Implementierung („`deleted_at = NOW` gesetzt", „`tsvector`-Index")
|
||||
6. **Non-funktionale Anforderungen** (Zeilen 1957-1973) — Bleiben, aber Metriken ohne Library-Namen
|
||||
7. **Annahmen** (Zeilen 1976-1999) — Bleiben
|
||||
8. **Non-Goals** (Zeilen 2001-2046) — Bleiben
|
||||
9. **Discovery-Checkliste** (Zeilen 2049-2073) — Bleibt
|
||||
10. **Open Questions** (Zeilen 2077-2085) — Bleibt
|
||||
|
||||
### 3.2 Was nach architecture.md verschoben werden sollte
|
||||
|
||||
**Architektur/Implementierung — das HOW:**
|
||||
|
||||
1. **F-CORE-01 bis F-CORE-13 (Zeilen 864-953):** Komplett in architecture.md
|
||||
- Event Bus, Tenant-Isolation (`tenant_id`), Plugin-Migration, UI-Plugin-Framework, Service Container/DI, API-First (Endpunkt-Versionierung `/api/v1/`), Async Job Queue (Celery/Redis), Caching (Redis), Storage-Backend (S3/MinIO), Import/Export Service, PDF-Gen, Notification Service
|
||||
|
||||
2. **F-PLUGIN-01, F-PLUGIN-02 (Zeilen 848-860):** Plugin-System-Architektur → architecture.md
|
||||
- Plugin-Schnittstelle, Manifest-Format, Lifecycle-Hooks, Abhängigkeiten
|
||||
|
||||
3. **F-WF-01 (Zeile 812-815):** Workflow-Engine-Architektur → architecture.md
|
||||
- Hybrid-Ansatz, Code-Engine vs. konfigurierbare Regeln
|
||||
|
||||
4. **Alle HTTP-Endpunkt-Spezifikationen:** → architecture.md (API-Contract-Sektion)
|
||||
- `POST /api/auth/login`, `GET /api/companies/{id}`, etc.
|
||||
- Request/Response-Body-Formate
|
||||
- Query-Parameter-Spezifikationen
|
||||
- HTTP-Status-Codes
|
||||
|
||||
5. **Alle DB-Schema-Definitionen:** → architecture.md (DB-Schema-Sektion)
|
||||
- Feld-Tabellen mit Typen (F-COMP-01 Zeilen 156-186, F-CONT-01 Zeilen 300-333)
|
||||
- Tabellennamen (`audit_log`, `deletion_log`, `company_contacts`, `user_calendar_visibility`)
|
||||
- `tenant_id`-Feld-Spezifikation
|
||||
- `tsvector`-Index-Spezifikation
|
||||
|
||||
6. **Protokoll-Details:** → architecture.md
|
||||
- IMAP4rev1, IMAP IDLE, IMAP MOVE, SMTP-Auth
|
||||
- RFC 5545 (RRULE), RFC 5322 (Threading)
|
||||
- PGP-Verschlüsselung (python-gnupg)
|
||||
- DOMPurify-Sanitization
|
||||
- AES-256-Verschlüsselung für Passwörter
|
||||
|
||||
7. **Frontend-Komponenten-Architektur:** → architecture.md (Frontend-Architektur-Sektion)
|
||||
- Komponenten-Namen (`CalendarView`, `KanbanCalendar`, `FileBrowser`, `Breadcrumb`, `ContextMenu`, `ThreadView`)
|
||||
- State-Management (`Multi-Select-State`, `user_calendar_visibility`)
|
||||
- HTML5 Drag & Drop API, XMLHttpRequest
|
||||
- Materialized Path Pattern
|
||||
|
||||
8. **Farbcodes und UI-Mappings:** → architecture.md oder design-system.md
|
||||
- Hex-Codes für Kalender-Typen
|
||||
- Farb-Mapping-Logik
|
||||
|
||||
9. **Algorithmus-Details:** → architecture.md
|
||||
- Mail-Regel-Auswertung
|
||||
- Auto-Reply-Logik (No-Reply-Erkennung, `vacation_sent_log`)
|
||||
- Recurrence-Instanz-Generierung
|
||||
- Thread-Gruppierung
|
||||
|
||||
10. **F-FILE-01 bis F-FILE-04 (Zeilen 955-985):** Duplikate von F-DMS/F-PERM — entfernen oder konsolidieren
|
||||
11. **F-SCHED-01 (Zeile 784-792):** Duplikat von F-CORE-07 — konsolidieren
|
||||
12. **Appendix A: Historische Anforderungen (Zeilen 2089-2128):** In separates `changelog.md` oder entfernen
|
||||
|
||||
### 3.3 Wie die Widersprüche (Plugin vs. Core-Feature) aufgelöst werden können
|
||||
|
||||
**Option A: Module sind Core-Features (empfohlen für v1/v2)**
|
||||
- Entferne F-PLUGIN-01, F-PLUGIN-02, F-CORE-01 bis F-CORE-13 aus requirements.md
|
||||
- Module (Mail, Kalender, DMS, Tags) sind Core-Features mit Requirements
|
||||
- Plugin-System ist ein Non-Goal für v1/v2 („Plugin-System für spätere Versionen")
|
||||
- Vorteil: Konsistent, weniger Komplexität, schneller implementierbar
|
||||
- Nachteil: Weniger Erweiterbarkeit
|
||||
|
||||
**Option B: Module sind Plugins**
|
||||
- Core-Requirements definieren nur Plugin-Schnittstelle und Core-Infrastruktur
|
||||
- Plugin-Requirements (Mail, Kalender, DMS) werden in separate Plugin-Specs ausgelagert
|
||||
- Core-Requirements sagen: „Das System unterstützt Plugins. Plugin 'Mail' muss X können. Plugin 'Kalender' muss Y können."
|
||||
- Die detaillierten Feature-Spezifikationen (F-MAIL-*, F-CAL-*, F-DMS-*) wandern in Plugin-Requirements
|
||||
- Vorteil: Saubere Trennung, Erweiterbarkeit
|
||||
- Nachteil: Mehr Dokumentation, mehr Komplexität, Over-Engineering für ein Mini-CRM
|
||||
|
||||
**Empfehlung: Option A für v1/v2.**
|
||||
Ein Mini-CRM mit 10 concurrent Users braucht kein Plugin-System. Das Plugin-System ist ein Architektur-Non-Goal für v1/v2. Die Module werden als Core-Features implementiert. Wenn Erweiterbarkeit später benötigt wird, kann ein Plugin-System in v3+ hinzugefügt werden. F-PLUGIN-01, F-PLUGIN-02, F-CORE-01 bis F-CORE-13 werden zu Non-Goals.
|
||||
|
||||
---
|
||||
|
||||
## Section 4: Spezifische Konflikte (Tabelle)
|
||||
|
||||
| ID/Zeile | Issue | Severity | Vorschlag |
|
||||
|----------|-------|----------|-----------|
|
||||
| F-PLUGIN-01 (848) vs F-DMS/F-CAL/F-MAIL | Module als Plugins deklariert, aber als Core-Features mit Endpunkten/DB-Schemas spezifiziert | **critical** | Plugin-System als Non-Goal für v1/v2; Module als Core-Features deklarieren |
|
||||
| F-FILE-01-04 (955-985) vs F-DMS-01-07 (991-1083) | F-FILE und F-DMS beschreiben dasselbe Modul mit unterschiedlichen IDs. F-FILE-01 (Datei-Explorer) = F-DMS-01 (Ordner-Struktur), F-FILE-03 (PDF-Preview) = F-DMS-04, F-FILE-04 (OnlyOffice) = F-DMS-05 | **critical** | F-FILE-01 bis F-FILE-04 entfernen; durch F-DMS-Referenzen ersetzen |
|
||||
| F-FILE-03 (973) vs F-DMS-04 (1033) | Beide spezifizieren PDF-Preview im Browser — Duplikat | **critical** | F-FILE-03 entfernen; F-DMS-04 behalten (detaillierter) |
|
||||
| F-FILE-04 (982) vs F-DMS-05 (1047) | Beide spezifizieren OnlyOffice-Integration — Duplikat | **critical** | F-FILE-04 entfernen; F-DMS-05 behalten (detaillierter) |
|
||||
| F-FILE-02 (964) vs F-PERM-03/04 (1257-1279) | F-FILE-02 (Datei-Sharing) ist vereinfachte Version von F-PERM-03/04 — Redundanz | **warning** | F-FILE-02 entfernen; F-PERM-03/04 als maßgeblich deklarieren |
|
||||
| F-SCHED-01 (784) vs F-CORE-07 (906) | Beide beschreiben Background-Jobs/Async-Queue — F-SCHED-01 ist vereinfachte Version von F-CORE-07 | **warning** | F-SCHED-01 entfernen; F-CORE-07 in architecture.md verschieben; Requirement „lange Operationen als Background-Job" in requirements.md behalten |
|
||||
| F-DATA-01/02 (430-452) vs F-CORE-11 (934) | CSV/Excel-Export (F-DATA) überlappt mit Generic Import/Export Service (F-CORE-11) | **warning** | F-CORE-11 in architecture.md; F-DATA-01/02 in requirements.md behalten (das WAS); F-CORE-11 beschreibt das HOW |
|
||||
| F-AUTH-07 (135) vs F-AUTH-01-F-CONT-07 (57-410) | Multi-Tenant deklariert, aber frühe Requirements erwähnen Tenant-Kontext nicht | **warning** | Frühe Requirements um Tenant-Bezug ergänzen: „Firma wird dem aktiven Tenant zugeordnet", „Suche ist Tenant-gefiltert" |
|
||||
| F-AUTH-01 (58) vs F-AUTH-02 (72-78) | F-AUTH-01: „Session-basiert", F-AUTH-02: „Token wird entfernt", „Server-Token-Blacklist" — inkonsistente Terminologie | **warning** | Einheitlich „Session" verwenden; Token-Blacklist entfernen oder klar als Session-Invalidierung benennen |
|
||||
| F-SEC-03 (616) vs F-AUTH-01 (58) | F-SEC-03 spricht von „Token" („Token gültig <8h", „Token nach 8h → 401"), F-AUTH-01 von „Session-Cookie" | **warning** | Einheitlich Session-basiert formulieren; „Session läuft nach 8h ab" |
|
||||
| F-CORE-06 (899) vs F-UI-01-06 (495-573) | API-First („alle Features über API") vs. reinen UI-Features ohne API-Bezug (Toast, Loading-States, Empty-States) | **warning** | F-CORE-06 einschränken: „Alle Daten- und Funktions-Features über API nutzbar; reine UI-Präsentations-Features (Loading-States, Toasts) ausgenommen" |
|
||||
| F-AUTH-06 (126) vs F-AUTH-04 (98) | F-AUTH-06 (Multi-User mit Rollen) überlappt mit F-AUTH-04 (RBAC) — F-AUTH-06 ist detailliertere Version | **warning** | Zusammenführen oder F-AUTH-06 als Erweiterung von F-AUTH-04 kennzeichnen |
|
||||
| F-AUTH-08 (144) vs F-AUTH-04/06 (98-129) | F-AUTH-08 (Feld-Ebene-Granularität) erweitert F-AUTH-04/06, wird aber nicht kreuzreferenziert | **warning** | F-AUTH-08 als Unterpunkt von F-AUTH-04/06 integrieren oder explizit referenzieren |
|
||||
| F-SEARCH-01 (821) vs F-COMP-06 (255)/F-CONT-06 (402) | Globale Suche überlappt mit Firmen-/Kontakt-Suche — keine klare Abgrenzung | **warning** | F-SEARCH-01 als übergeordnete Suche deklarieren; F-COMP-06/F-CONT-06 als Modul-Suche mit Querverweis |
|
||||
| F-INT-01 (700) vs F-MAIL-02 (1683) | E-Mail-Integration für Passwort-Reset (F-INT-01) ist Subset des vollen Mail-Moduls (F-MAIL-02) | **info** | F-INT-01 als v1-Requirement behalten; F-MAIL-02 als v2-Erweiterung kennzeichnen; F-INT-01 bei F-MAIL-02 referenzieren |
|
||||
| F-CAL-10 (1536) vs Non-Goals (2028) | F-CAL-10 (Ressourcen-Booking) als „Optional für später (post-v2)" markiert, hat aber volle Test-Szenarien und Akzeptanzkriterien | **warning** | Entweder zu Non-Goals verschieben oder als v2-Feature belassen mit klarer Markierung „post-v2" |
|
||||
| F-COMP-01 Feldtabelle (156-186) | DB-Schema mit Typen (String(100), Integer, Decimal) in Requirements | **info** | Feldliste als „Felder, die erfasst werden" in requirements.md; Typen und Constraints in architecture.md |
|
||||
| F-CONT-01 Feldtabelle (300-333) | DB-Schema mit Typen in Requirements | **info** | Analog zu F-COMP-01 |
|
||||
| F-COMP-04 (235) | `deleted_at = NOW` (SQL-Ausdruck) in Akzeptanzkriterium | **info** | „Firma wird als gelöscht markiert (Soft-Delete)" — ohne SQL |
|
||||
| F-CONT-07 (424) | `company_contacts` Tabellenname in Akzeptanzkriterium | **info** | „N:M-Verknüpfung wird erstellt" — ohne Tabellennamen |
|
||||
| F-CAL-06 (1485) | Hex-Farbcodes in Akzeptanzkriterium | **info** | „Farbe wird basierend auf Typ zugeordnet" — Farbwerte in design-system.md |
|
||||
| F-CAL-08 (1516) | RRULE (RFC 5545) in Akzeptanzkriterium | **info** | „Wiederholungsmuster werden unterstützt" — RFC-Referenz in architecture.md |
|
||||
| F-MAIL-03 (1709) | `tsvector`-Index in Akzeptanzkriterium | **info** | „Volltext-Suche über alle Mails" — Index-Strategie in architecture.md |
|
||||
| F-MAIL-01 (1677) | „IMAP IDLE-Listener läuft als Background-Task" in Akzeptanzkriterium | **info** | „Neue Mails werden innerhalb von 5 Sekunden angezeigt" — Implementierung in architecture.md |
|
||||
| F-MAIL-02 (1693) | „DOMPurify" in Akzeptanzkriterium | **info** | „HTML wird sanitisiert" — Library in architecture.md |
|
||||
| F-MAIL-12 (1846) | „python-gnupg" in Akzeptanzkriterium | **info** | „PGP-Verschlüsselung wird unterstützt" — Library in architecture.md |
|
||||
| F-FILEUI-01 (1321) | `FileBrowser`, `SidebarTree`, `MainView` Komponentennamen | **info** | „Datei-Browser mit Baum-Ansicht und Hauptbereich" — Komponentennamen in architecture.md |
|
||||
| F-FILEUI-02 (1335) | „Materialized Path oder rekursive Abfrage" in Akzeptanzkriterium | **info** | „Pfad wird aus Ordner-Hierarchie generiert" — Pattern in architecture.md |
|
||||
| F-FILEUI-06 (1391) | „HTML5 Drag & Drop API" in Akzeptanzkriterium | **info** | „Drag & Drop wird unterstützt" — API in architecture.md |
|
||||
| F-FILEUI-05 (1377) | „XMLHttpRequest oder WebSocket" in Akzeptanzkriterium | **info** | „Upload-Progress wird angezeigt" — Technologie in architecture.md |
|
||||
| F-CORE-07 (907) | „Celery + Redis oder RQ + Redis" — Technologie-Wahl in Requirements | **info** | Komplett in architecture.md |
|
||||
| F-CORE-08 (914) | „Redis als Cache-Backend" — Technologie-Wahl in Requirements | **info** | Komplett in architecture.md |
|
||||
| F-CORE-10 (928) | „S3-kompatibles Storage (z.B. MinIO)" — Technologie-Wahl in Requirements | **info** | Komplett in architecture.md |
|
||||
| F-CORE-02 (872) | `tenant_id`-Feld-Spezifikation in Requirements | **info** | „Daten sind pro Tenant isoliert" — `tenant_id` in architecture.md |
|
||||
| DISCOVERY_CHECK (2131) | Behauptet `features_with_ids=127/127` — tatsächlich sind es ~141 aktive Feature-IDs | **warning** | Zählung korrigieren oder klären, welche Features gezählt wurden |
|
||||
| F-DATA-05 fehlt | Springt von F-DATA-04 (Zeile 472) zu F-DATA-06 (Zeile 481) — F-DATA-05 existiert nicht | **info** | Entweder F-DATA-05 nachtragen oder Nummerierung korrigieren |
|
||||
| F-UI-07 fehlt | Springt von F-UI-06 (Zeile 565) zu F-UI-08 (Zeile 579) — F-UI-07 existiert nicht | **info** | Entweder F-UI-07 nachtragen oder Nummerierung korrigieren |
|
||||
| F-COMP-07 (269) vs F-COMP-08 (283) | Audit-Log und DSGVO-Löschung haben überlappende Belange (beide behandeln Logging von Löschungen), Interaktion nicht dokumentiert | **info** | Klarstellen: Audit-Log = schreibende Aktionen; DSGVO-Löschung = harte Löschung inkl. Audit-Log-Einträgen, separate `deletion_log` |
|
||||
| NF-06 (1966) | Code-Struktur (`api/`, `models/`, `schemas/`, `services/`, `tests/`) in nicht-funktionaler Anforderung | **info** | In architecture.md verschieben; in requirements.md: „Code-Struktur ist klar getrennt" |
|
||||
| Appendix A (2089-2128) | Historische v0.1-Requirements mit veralteten Tech-Stack (Jinja2, SQLite, Python 3.11) | **info** | In `changelog.md` verschieben oder entfernen; verwirrend in requirements.md |
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| Gesamtzeilen | 2131 |
|
||||
| Aktive Feature-IDs | ~141 |
|
||||
| Genuine Requirements-Anteil | ~35% |
|
||||
| Architektur/Implementierungs-Anteil | ~65% |
|
||||
| Critical Issues | 4 |
|
||||
| Warning Issues | 14 |
|
||||
| Info Issues | 21 |
|
||||
| Empfehlung | Requirements bereinigen, ~65% nach architecture.md verschieben, Plugin-System als Non-Goal für v1/v2 |
|
||||
|
||||
**Urteil:** Die Datei ist eine Mischung aus Requirements-Spec und Architektur-Dokument. Sie hat den Charakter einer Bauanleitung angenommen. Für eine saubere Trennung sollten ~65% des Inhalts in architecture.md verschoben werden. Die verbleibende requirements.md sollte nur das WAS beschreiben — nicht das HOW.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,176 +0,0 @@
|
||||
# LeoCRM Frontend — Vollständige Bestandsanalyse
|
||||
|
||||
**Verzeichnis:** `/a0/usr/workdir/leocrm-fix/frontend`
|
||||
**Architektur-Referenz:** `architecture.md` Abschnitt 7
|
||||
**Datum:** 2026-07-23
|
||||
|
||||
---
|
||||
|
||||
## 1. Pages/Routes (27 Pages, 7.826 Zeilen)
|
||||
|
||||
### Vorhandene Seiten
|
||||
|
||||
| Seite | File | Lines | Status |
|
||||
|-------|------|-------|--------|
|
||||
| Login | src/pages/Login.tsx | 92 | ✅ |
|
||||
| PasswordReset Request | src/pages/PasswordResetRequest.tsx | 91 | ✅ |
|
||||
| PasswordReset Confirm | src/pages/PasswordResetConfirm.tsx | 106 | ✅ |
|
||||
| Dashboard | src/pages/Dashboard.tsx | 79 | ✅ |
|
||||
| ContactsList | src/pages/ContactsList.tsx | 445 | ✅ |
|
||||
| Calendar | src/pages/Calendar.tsx | 717 | ✅ |
|
||||
| CalendarKanban | src/pages/CalendarKanban.tsx | 123 | ✅ |
|
||||
| DMS | src/pages/Dms.tsx | 731 | ✅ |
|
||||
| DmsTrash | src/pages/DmsTrash.tsx | 155 | ✅ |
|
||||
| Mail | src/pages/Mail.tsx | 978 | ✅ |
|
||||
| MailSettings | src/pages/MailSettings.tsx | 449 | ✅ |
|
||||
| AuditLog | src/pages/AuditLog.tsx | 167 | ✅ |
|
||||
| GlobalSearchResults | src/pages/GlobalSearchResults.tsx | 228 | ✅ |
|
||||
| Settings (Hub) | src/pages/Settings.tsx | 52 | ✅ |
|
||||
| SettingsProfile | src/pages/SettingsProfile.tsx | 181 | ✅ |
|
||||
| SettingsUsers | src/pages/SettingsUsers.tsx | 298 | ✅ |
|
||||
| SettingsRoles | src/pages/SettingsRoles.tsx | 522 | ✅ |
|
||||
| SettingsGroups | src/pages/SettingsGroups.tsx | 688 | ✅ |
|
||||
| SettingsPlugins | src/pages/SettingsPlugins.tsx | 255 | ✅ |
|
||||
| SettingsSystem | src/pages/SettingsSystem.tsx | 277 | ✅ |
|
||||
| SettingsCurrencies | src/pages/SettingsCurrencies.tsx | 142 | ✅ |
|
||||
| SettingsTaxes | src/pages/SettingsTaxes.tsx | 143 | ✅ |
|
||||
| SettingsSequences | src/pages/SettingsSequences.tsx | 138 | ✅ |
|
||||
| SettingsNotifications | src/pages/SettingsNotifications.tsx | 158 | ✅ |
|
||||
| AIAssistant | src/pages/AIAssistant.tsx | 122 | ✅ |
|
||||
| AISettings | src/pages/AISettings.tsx | 332 | ✅ |
|
||||
| ProactiveAISettings | src/pages/ProactiveAISettings.tsx | 157 | ✅ |
|
||||
|
||||
### Fehlende Seiten
|
||||
| Seite | Status | Anmerkung |
|
||||
|-------|--------|-----------|
|
||||
| Companies List | ❌ MISSING | Keine Companies.tsx, API-Hooks existieren |
|
||||
| Company Detail | ❌ MISSING | Keine CompanyDetail.tsx |
|
||||
| Contact Detail Route | ❌ MISSING | ContactDetail.tsx existiert als Komponente (372 Zeilen), aber keine Route /contacts/:id |
|
||||
|
||||
## 2. Component Library (12+2 Komponenten)
|
||||
|
||||
Alle 12 geforderten UI-Komponenten vorhanden: Button, Input, Select, Modal, Toast, Table, Card, Badge, Avatar, Pagination, EmptyState, Skeleton.
|
||||
Zusätzlich: ConfirmDialog, ResizablePanel.
|
||||
|
||||
## 3. Layout
|
||||
- AppShell ✅, Sidebar ✅ (237 Zeilen), TopBar ✅ (146 Zeilen)
|
||||
- ContentArea ⚠️ Inline in AppShell
|
||||
- PluginToolbar ✅, AISidebar ✅ (322 Zeilen), MessageSidebar ✅ (689 Zeilen)
|
||||
|
||||
## 4. Plugin UI System
|
||||
- PluginRegistry.tsx ❌ MISSING
|
||||
- PluginLoader.tsx ❌ MISSING
|
||||
- src/plugins/ Verzeichnis ❌ MISSING
|
||||
- Plugin-Routes hartkodiert in routes/index.tsx
|
||||
|
||||
## 5. State Management
|
||||
- TanStack Query: 138 Aufrufe, 60+ Hooks ✅
|
||||
- Zustand: 5 Stores (authStore, uiStore, commStore, pluginToolbarStore, calendarStore) ✅
|
||||
|
||||
## 6. Code-Splitting
|
||||
- React.lazy ❌ MISSING (0 Verwendungen)
|
||||
- Suspense ❌ MISSING
|
||||
|
||||
## 7. i18n
|
||||
- de.json: 750 Keys ✅
|
||||
- en.json: 750 Keys ✅
|
||||
- Perfekt synchron
|
||||
|
||||
## 8. Forms
|
||||
- React Hook Form + Zod installiert ✅
|
||||
- Nur in 3 Pages aktiv genutzt ⚠️
|
||||
|
||||
## 9. Feature Modules (70 Components, 13.893 Zeilen)
|
||||
- ai/ (5 Komponenten) ✅
|
||||
- calendar/ (12 Komponenten) ✅
|
||||
- comm/ (11 Block-Typen) ✅
|
||||
- contacts/ (4 Komponenten) ✅
|
||||
- dms/ (9 Komponenten) ✅
|
||||
- mail/ (13 Komponenten) ✅
|
||||
- tags/ (3 Komponenten) ✅
|
||||
- companies/ ❌ MISSING
|
||||
- dashboard/ ❌ MISSING
|
||||
|
||||
## 10. API Client (12 Module, 3.456 Zeilen)
|
||||
- client.ts, hooks.ts (Re-Export-Hub), auth.ts, users.ts, contacts.ts, roles.ts, groups.ts, audit.ts, notifications.ts, plugins.ts, settings.ts, attachments.ts, unifiedContacts.ts ✅
|
||||
- workflows.ts ❌ MISSING
|
||||
|
||||
## 11. Custom Hooks (5 Hooks)
|
||||
useAuth, useTenant, usePermission, useAIContext, useCommWebSocket ✅
|
||||
|
||||
## 12. Accessibility
|
||||
- ARIA roles ⚠️ Partial
|
||||
- sr-only ⚠️ Partial
|
||||
- prefers-reduced-motion ✅
|
||||
- 44px touch targets ✅
|
||||
- focus-ring ✅
|
||||
|
||||
## 13. Tests (38 Dateien, 3.045 Zeilen)
|
||||
- UI Components: 13 Tests ✅
|
||||
- Shell/Layout: 4 Tests ✅
|
||||
- Auth: 2 Tests ✅
|
||||
- Dashboard: 1 Test ✅
|
||||
- Contacts: 1 Test ✅
|
||||
- Settings: 3 Tests ✅
|
||||
- Calendar: 3 Tests ✅
|
||||
- Mail: 3 Tests ✅
|
||||
- DMS: 2 Tests ✅
|
||||
- Search: 2 Tests ✅
|
||||
- Tags: 2 Tests ✅
|
||||
- Permissions: 1 Test ✅
|
||||
- AuditLog: 1 Test ✅
|
||||
- i18n: 1 Test ✅
|
||||
- AI Components ❌ MISSING
|
||||
- SettingsGroups/System/Currencies/Taxes/Sequences/Notifications/Plugins ❌ MISSING
|
||||
- Calendar Page ❌ MISSING
|
||||
- DMS Sub-Components ❌ MISSING
|
||||
- Contact Sub-Components ❌ MISSING
|
||||
- Comm Blocks ❌ MISSING
|
||||
- API Hooks ❌ MISSING
|
||||
- Stores ❌ MISSING
|
||||
|
||||
## 14. E2E Tests (Playwright)
|
||||
- Playwright ❌ MISSING (komplett)
|
||||
|
||||
## 15. Frontend Build
|
||||
- vite.config.ts ✅
|
||||
- tsconfig.json ✅
|
||||
- package.json ✅ (26 deps, 15 devDeps)
|
||||
- tailwind.config.js ✅
|
||||
- postcss.config.js ✅
|
||||
|
||||
## 16. Frontend Dockerfile
|
||||
- ❌ MISSING (Multi-Stage Build im Haupt-Dockerfile)
|
||||
|
||||
## 17. Nginx Config
|
||||
- ❌ MISSING
|
||||
|
||||
## 18. TipTap (Rich Text Editor)
|
||||
- RichTextEditor.tsx (231 Zeilen) ✅
|
||||
- 8 TipTap-Extensions ✅
|
||||
|
||||
## 19. PDF.js
|
||||
- Nicht nötig — FilePreviewModal nutzt iframe mit Browser-PDF-Viewer ✅
|
||||
|
||||
## 20. TanStack Table
|
||||
- DataGrid.tsx (160 Zeilen) ✅
|
||||
- AuditLog ColumnDef ✅
|
||||
- Virtual Scrolling ❌ MISSING
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Kategorie | Dateien | Zeilen |
|
||||
|-----------|---------|--------|
|
||||
| Pages | 27 | 7.826 |
|
||||
| Components | 70 | 13.893 |
|
||||
| API Modules | 12+ | 3.456 |
|
||||
| Stores | 5 | 461 |
|
||||
| Hooks | 5 | 247 |
|
||||
| Routes | 2 | ~100 |
|
||||
| i18n | 3 | 1.749 |
|
||||
| Tests | 38 | 3.045 |
|
||||
| Config | 5 | ~200 |
|
||||
| Utils | 1 | ~100 |
|
||||
| **Total** | **~167** | **~30.000** |
|
||||
|
||||
**Frontend ist zu ~70% vollständig.** Kritische Lücken: Plugin-UI-System, Code-Splitting, E2E Tests, Contact-Detail-Route. Alle im MASTER-PLAN.md eingeplant.
|
||||
@@ -1,172 +0,0 @@
|
||||
# Quality Gate Review — Phase 1 → Phase 2 (Re-Review)
|
||||
|
||||
**Datum:** 2026-06-28
|
||||
**Dateien:** requirements.md (2142 Zeilen), extracted-architecture-details.md (1006 Zeilen)
|
||||
**Vorherige Findings:** 6 Fixes angewendet
|
||||
|
||||
---
|
||||
|
||||
## Prüfkriterien & Ergebnisse
|
||||
|
||||
### 1. Vollständigkeit: 143 Features (73 Core + 70 Plugin)
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- Active feature headings (exkl. historisch): 143
|
||||
- `[v1]`-Features (Core): 73
|
||||
- `[v2-Plugin]`-Features (Plugin): 70
|
||||
- `[v1-Plugin]`-Features: 0 (alle konvertiert)
|
||||
- Summary-Tabelle: 73 Core + 70 Plugin = 143
|
||||
- DISCOVERY_CHECK_FINAL: `features_with_ids=143/143`
|
||||
|
||||
### 2. Konsistenz: Plugin vs Core Trennung
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- F-FILE-01–04: alle `[v2-Plugin]` (vorher `[v1-Plugin]`)
|
||||
- F-DMS-01–07: alle `[v2-Plugin]`
|
||||
- F-LINK-01–06: alle `[v2-Plugin]`
|
||||
- F-TAG-01–04: alle `[v2-Plugin]`
|
||||
- F-PERM-01–06: alle `[v2-Plugin]`
|
||||
- F-FILEUI-01–06: alle `[v2-Plugin]`
|
||||
- F-CAL-01–18: alle `[v2-Plugin]` (F-CAL-10 = `[v2-Plugin — später]`)
|
||||
- F-MAIL-01–19: alle `[v2-Plugin]`
|
||||
- F-PLUGIN-01/02: `[v1]` (Plugin-System ist Core)
|
||||
- F-CORE-04 (UI-Plugin-Framework): `[v1]` (Core-Infrastruktur)
|
||||
- Summary-Header: `### Core-Features (v1)` und `### Plugin-Features (v2-Plugin)`
|
||||
|
||||
### 3. Keine Implementierungs-Details in requirements.md
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- Keine HTML-Tags (`<div>`, `<span>`, `<button>`, `<input>` etc.) in Feature-Definitions
|
||||
- Keine React/JSX-Syntax (`className=`, `useState`, `<React`)
|
||||
- Keine CSS-Property-Spezifikationen (`min-height: 44px`, `::after`, `@media` etc.) — bereinigt in F-A11Y
|
||||
- F-A11Y-01: Keine ARIA-Attribut-Spezifikationen, keine `.sr-only` CSS-Klassen-Erwähnung
|
||||
- F-A11Y-02: Keine konkreten CSS-Property-Namen in Akzeptanzkriterium
|
||||
- F-A11Y-03: Keine konkreten CSS-Regeln (`min-height`, `min-width`, `::after`)
|
||||
- Implementierungs-Details sind in extracted-architecture-details.md
|
||||
|
||||
### 4. Test-Szenarien für alle Features
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- 143/143 Features haben `Test Scenarios` oder `Test Scenarios (Pflicht)`
|
||||
- F-COMP-01: Test Scenarios bei Zeile 172 (3 Szenarien) — verifiziert
|
||||
- F-CONT-01: Test Scenarios bei Zeile 295 (3 Szenarien) — verifiziert
|
||||
- F-A11Y-01–03: jeweils 3 Test Scenarios — verifiziert
|
||||
- DISCOVERY_CHECK_FINAL: `test_scenarios=143/143`
|
||||
- Alle Test-Szenarien haben konkretes erwartetes Ergebnis
|
||||
|
||||
### 5. Non-Goals aktuell
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- 28 Non-Goals dokumentiert (Zeilen 1940–1968)
|
||||
- Multi-Tenant nicht mehr als Non-Goal (ist v1-Feature)
|
||||
- AI Lead-Scoring / Auto-Enrichment als Non-Goal (KI-Copilot ist v1)
|
||||
- Nummernkreise/Sequenzen, State Machine, Document Versioning als Non-Goals
|
||||
- S/MIME, Mail-Server-Hosting, Mailinglisten, Newsletter als Non-Goals
|
||||
- Changelog dokumentiert Non-Goal-Updates (Zeile 2127)
|
||||
|
||||
### 6. Annahmen aktuell
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- 13 Annahmen dokumentiert (Zeilen 1918–1936)
|
||||
- Annahme 1: Multi-Tenant (Multi-Company) — aktualisiert
|
||||
- Annahme 4: Max 10 concurrent Users pro Tenant
|
||||
- Annahme 11: Plugin-System als v1-Feature
|
||||
- Annahme 13: KI-Copilot ist v1-Feature
|
||||
- Keine Single-Tenant-Annahme mehr vorhanden
|
||||
|
||||
### 7. DISCOVERY_CHECK_FINAL: 143/143
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- `DISCOVERY_CHECK_FINAL: categories=21/21, features_with_ids=143/143, test_scenarios=143/143, constraints=Y, non_goals=Y, domain=Y, ready_for_ui=Y`
|
||||
- Changelog-Zeile 2128: `143 Features (73 Core + 70 Plugin)` — aktualisiert
|
||||
|
||||
### 8. extracted-architecture-details.md: vollständig, keine Single-Tenant-Kontradiktionen
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- Zeile 380: `Multi-Tenant (Multi-Company)` — korrigiert
|
||||
- Zeile 888: `Multi-Tenant (Multi-Company)` — korrigiert
|
||||
- Zeile 961: `~~Multi-Tenant (Single-Tenant in v1)~~ — Multi-Tenant (Multi-Company) ist v1-Feature` — durchgestrichen (historisch)
|
||||
- Keine aktiven Single-Tenant-Referenzen verbleibend
|
||||
- Alle 3 Vorkommen von 'Single-Tenant' sind in Durchstreichung (~~...~~) oder korrigiert
|
||||
|
||||
### 9. Changelog vorhanden
|
||||
**Status: ✅ PASS**
|
||||
|
||||
Verifikation:
|
||||
- `## Changelog (Bereinigung 2026-06-28)` bei Zeile 2119
|
||||
- 10 Änderungen dokumentiert
|
||||
- DISCOVERY_CHECK-Zeile aktualisiert: 143 Features (73 Core + 70 Plugin)
|
||||
- Verschiebung von Implementierungs-Details nach extracted-architecture-details.md dokumentiert
|
||||
|
||||
---
|
||||
|
||||
## Summary-Ranges Verifikation
|
||||
|
||||
| Bereich | Range in Summary | Body-Features | Status |
|
||||
|---------|-----------------|---------------|--------|
|
||||
| Auth | F-AUTH-01–F-AUTH-08 | 8 (01-08) | ✅ |
|
||||
| Companies | F-COMP-01–F-COMP-08 | 8 (01-08) | ✅ |
|
||||
| Contacts | F-CONT-01–F-CONT-07 | 7 (01-07) | ✅ |
|
||||
| Data | F-DATA-01–F-DATA-04, F-DATA-06 | 5 (01-04, 06) | ✅ (gap: kein F-DATA-05) |
|
||||
| UI | F-UI-01–F-UI-06, F-UI-08 | 7 (01-06, 08) | ✅ (gap: kein F-UI-07) |
|
||||
| Accessibility | F-A11Y-01–F-A11Y-03 | 3 (01-03) | ✅ |
|
||||
| Security | F-SEC-01–F-SEC-03 | 3 (01-03) | ✅ |
|
||||
| Infrastruktur | F-INFRA-01–F-INFRA-04 | 4 (01-04) | ✅ |
|
||||
| Migration | F-MIG-01 | 1 (01) | ✅ |
|
||||
| Integration | F-INT-01–F-INT-02 | 2 (01-02) | ✅ |
|
||||
| Testing | F-TEST-01 | 1 (01) | ✅ |
|
||||
| Environments | F-ENV-01 | 1 (01) | ✅ |
|
||||
| Dokumentation | F-DOC-01 | 1 (01) | ✅ |
|
||||
| Performance | F-PERF-01 | 1 (01) | ✅ |
|
||||
| Scheduling | F-SCHED-01 | 1 (01) | ✅ |
|
||||
| AI | F-AI-01 | 1 (01) | ✅ |
|
||||
| Workflow | F-WF-01 | 1 (01) | ✅ |
|
||||
| Search | F-SEARCH-01 | 1 (01) | ✅ |
|
||||
| Navigation | F-NAV-01 | 1 (01) | ✅ |
|
||||
| Settings | F-SET-01 | 1 (01) | ✅ |
|
||||
| Core-Infrastructure | F-CORE-01–F-CORE-13 | 13 (01-13) | ✅ |
|
||||
| Plugin-System | F-PLUGIN-01–F-PLUGIN-02 | 2 (01-02) | ✅ |
|
||||
| File | F-FILE-01–F-FILE-04 | 4 (01-04) | ✅ |
|
||||
| DMS | F-DMS-01–F-DMS-07 | 7 (01-07) | ✅ |
|
||||
| Links | F-LINK-01–F-LINK-06 | 6 (01-06) | ✅ |
|
||||
| Tags | F-TAG-01–F-TAG-04 | 4 (01-04) | ✅ |
|
||||
| Permissions | F-PERM-01–F-PERM-06 | 6 (01-06) | ✅ |
|
||||
| File-UI | F-FILEUI-01–F-FILEUI-06 | 6 (01-06) | ✅ |
|
||||
| Kalender | F-CAL-01–F-CAL-18 | 18 (01-18) | ✅ |
|
||||
| Mail | F-MAIL-01–F-MAIL-19 | 19 (01-19) | ✅ |
|
||||
|
||||
**Core Total: 73 ✓**
|
||||
**Plugin Total: 70 ✓**
|
||||
**Grand Total: 143 ✓**
|
||||
|
||||
---
|
||||
|
||||
## Gesamturteil
|
||||
|
||||
| # | Kriterium | Status |
|
||||
|---|-----------|--------|
|
||||
| 1 | Vollständigkeit: 143 Features | ✅ PASS |
|
||||
| 2 | Konsistenz: Plugin vs Core | ✅ PASS |
|
||||
| 3 | Keine Implementierungs-Details | ✅ PASS |
|
||||
| 4 | Test-Szenarien für alle | ✅ PASS |
|
||||
| 5 | Non-Goals aktuell | ✅ PASS |
|
||||
| 6 | Annahmen aktuell | ✅ PASS |
|
||||
| 7 | DISCOVERY_CHECK_FINAL 143/143 | ✅ PASS |
|
||||
| 8 | extracted: keine Single-Tenant-Kontradiktionen | ✅ PASS |
|
||||
| 9 | Changelog vorhanden | ✅ PASS |
|
||||
|
||||
### **Gesamt: 9/9 PASS — Quality Gate PASSED ✅**
|
||||
|
||||
**Bereit für Phase 2 (UI Design / Architecture): YES**
|
||||
|
||||
---
|
||||
|
||||
*Review durchgeführt am 2026-06-28. Alle 6 vorherigen Findings wurden erfolgreich behoben und verifiziert.*
|
||||
@@ -1,314 +0,0 @@
|
||||
# LeoCRM — Quality Gate Phase 2 (Architecture) Re-Review (Round 2)
|
||||
|
||||
**Reviewer:** Quality Reviewer (Agent Zero)
|
||||
**Datum:** 2026-06-28
|
||||
**Phase:** Phase 2 — Architecture + Task Graph + AGENTS.md
|
||||
**Previous Review:** quality-gate-phase2.md (Round 1, BLOCKED — 3 Critical, 5 Major, 3 Minor)
|
||||
**Verdict:** ⚠️ **APPROVED_WITH_SUGGESTIONS** — 0 Critical, 1 Major, 2 Minor
|
||||
|
||||
---
|
||||
|
||||
## Fix Verification Summary (11 Issues from Round 1)
|
||||
|
||||
| # | Issue | Severity (R1) | Fix Status | Evidence |
|
||||
|---|-------|---------------|------------|----------|
|
||||
| 1 | F-AI-01 (KI-Copilot) missing | CRITICAL | ✅ FIXED | Architecture §8b (lines 1483-1530): API endpoints, RBAC enforcement, DB table `ai_conversations`, frontend integration. Task T09 covers F-AI-01 with 22 acceptance criteria + test_spec. |
|
||||
| 2 | F-WF-01 (Hybrid-Workflow-Engine) missing | CRITICAL | ✅ FIXED | Architecture §8c (lines 1538-1620): Hybrid approach (code-engine + configurable), API endpoints, DB tables `workflows`/`workflow_instances`/`workflow_step_history`. Task T09 covers F-WF-01 with 22 acceptance criteria + test_spec. |
|
||||
| 3 | Session-Storage contradiction | CRITICAL | ✅ FIXED | §6 (line 200-210): `sessions` table labeled "Audit Trail — primary session store is Redis" with clear note. ADR-05 (line 1871): "Server-side sessions in Redis (primary store), PostgreSQL `sessions` table retains session records as an audit trail." Session creation flow (line 1242-1243): Redis key + PostgreSQL audit record. Consistent across all three locations. |
|
||||
| 4 | 19 v1 Features without traceability | MAJOR | ✅ FIXED | All 19 features verified in task_graph requirement_ids: F-NAV-01, F-SET-01, F-UI-01-06, F-UI-08, F-SEC-02, F-SEC-03, F-SCHED-01, F-DATA-03, F-DATA-04, F-DATA-06, F-ENV-01, F-INFRA-02, F-INFRA-03, F-TEST-01 — all present (grep count ≥1). |
|
||||
| 5 | F-DOC-01 (Dokumentation) missing | MAJOR | ✅ FIXED | Task T10 covers F-DOC-01: README.md, docs/admin-guide.md, docs/api-overview.md with 3 acceptance criteria + test command. |
|
||||
| 6 | F-INFRA-04 (Monitoring & Alerting) missing | MAJOR | ✅ FIXED | Architecture §8d (lines 1662-1710): Health endpoint, Prometheus metrics, alerting rules, structured logging. Task T10 covers F-INFRA-04 with 5 acceptance criteria. |
|
||||
| 7 | F-PERF-01 (Performance) missing | MAJOR | ✅ FIXED | Architecture §8e (lines 1714-1770): DB indexing strategy, query optimization, frontend performance, performance tests. Task T10 covers F-PERF-01 with 6 acceptance criteria. |
|
||||
| 8 | F-CONT-08 phantom in task_graph | MAJOR | ✅ FIXED | `grep -n 'F-CONT-08' task_graph.json` returns 0 results. Phantom removed. |
|
||||
| 9 | api_tokens table not in DB schema | MINOR | ✅ FIXED | DB Schema §2 (line 366-380): `api_tokens` table defined with id, tenant_id, user_id, token_hash, name, scopes (JSONB), expires_at, last_used_at, created_at, revoked_at. Index on (tenant_id, user_id) and (token_hash). |
|
||||
| 10 | CSP-Header not mentioned | MINOR | ✅ FIXED | Architecture §6 (lines 1284-1291): Full CSP header in Nginx config, plus X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, HSTS, Referrer-Policy, DOMPurify sanitization. |
|
||||
| 11 | Typo line 1033 (```n) | MINOR | ✅ FIXED | `grep -n '```n' architecture.md` returns 0 results. No malformed code fences found. |
|
||||
|
||||
**Fix Score: 11/11 resolved.** All critical and major issues from Round 1 are fixed.
|
||||
|
||||
---
|
||||
|
||||
## Original 10 Criteria Re-Check
|
||||
|
||||
| # | Kriterium | R1 Result | R2 Result | Change |
|
||||
|---|----------|-----------|----------|--------|
|
||||
| 1 | Architecture.md deckt alle 10 Bereiche ab | ✅ PASS | ✅ PASS | No change — all 10 areas present, plus 4 new sub-sections (§8b-§8e) |
|
||||
| 2 | Task Graph: 6-8 substantielle Tasks | ✅ PASS | ✅ PASS | 10 tasks (was 8). T09 (700 lines) and T10 (500 lines) are substantielle. Acceptable expansion. |
|
||||
| 3 | 143/143 Features abgedeckt | ❌ FAIL | ✅ PASS | All 134 v1 features covered (140 total - 6 v2). 0 phantom. Coverage summary count is incorrect (see MINOR-2). |
|
||||
| 4 | AGENTS.md: Commands, Forbidden Patterns, Task-Zuweisung | ✅ PASS | ⚠️ PARTIAL | Commands ✅, Forbidden patterns ✅, but Task-Zuweisung table and Phasen-Plan missing T09/T10 (see MAJOR-1). |
|
||||
| 5 | Keine Widersprüche arch.md ↔ requirements.md | ⚠️ PARTIAL | ✅ PASS | All gaps closed: F-AI-01 §8b, F-WF-01 §8c, F-INFRA-04 §8d, F-PERF-01 §8e, F-SEC-02 CSP. |
|
||||
| 6 | Keine Widersprüche task_graph.json ↔ arch.md | ⚠️ PARTIAL | ✅ PASS | F-CONT-08 phantom removed. api_tokens table in DB schema. Task endpoints match architecture API design. |
|
||||
| 7 | Multi-Tenant (tenant_id) konsistent | ✅ PASS | ✅ PASS | No change. All tables have tenant_id. New tables (ai_conversations, workflows, workflow_instances, workflow_step_history) also have tenant_id. |
|
||||
| 8 | Plugin-System als v1-Core-Feature | ✅ PASS | ✅ PASS | No change. |
|
||||
| 9 | PostgreSQL 16, React 18 SPA, FastAPI | ✅ PASS | ✅ PASS | No change. |
|
||||
| 10 | Session-Auth + API-Token separat | ⚠️ PARTIAL | ✅ PASS | Session storage contradiction resolved. Redis primary + PostgreSQL audit trail consistent across §6, ADR-05, and session creation flow. api_tokens table in DB schema. |
|
||||
|
||||
**Gesamt: 8 PASS, 1 PARTIAL, 0 FAIL → APPROVED_WITH_SUGGESTIONS**
|
||||
|
||||
---
|
||||
|
||||
## New Findings (Round 2)
|
||||
|
||||
### MAJOR-1: AGENTS.md not updated for T09 and T10
|
||||
|
||||
- **Artifact:** AGENTS.md
|
||||
- **Location:** Lines 377-398 (Phasen-Plan + Task Assignment table), Line 484 (Release Gate)
|
||||
- **Issue:** AGENTS.md still references only 8 tasks (T01-T08) in 5 phases. The task_graph.json has been expanded to 10 tasks (T01-T10) in 6 phases. The following sections are out of sync:
|
||||
- **Phasen-Plan table** (line 381-385): Missing Phase 6 (T10) and T09 in Phase 3
|
||||
- **Task Assignment table** (line 391-398): Missing rows for T09 and T10
|
||||
- **Release Gate** (line 484): Says "All 8 tasks complete" — should say "All 10 tasks complete"
|
||||
- **Block Rules** (line 412): Says "After 3 blocks (9 tasks)" — should reference 10 tasks
|
||||
- **Recommendation:**
|
||||
1. Add T09 to Phasen-Plan Phase 3 (parallel with T04, T05, T06)
|
||||
2. Add Phase 6 with T10 to Phasen-Plan
|
||||
3. Add T09 and T10 rows to Task Assignment table
|
||||
4. Update Release Gate to "All 10 tasks complete"
|
||||
5. Update Block Rules to reference 10 tasks (4 blocks)
|
||||
- **Block transition:** NEIN — does not block implementation start, but must be fixed before Phase 3 execution
|
||||
|
||||
### MINOR-1: Architecture section numbering (§8b-§8e under §8)
|
||||
|
||||
- **Artifact:** architecture.md
|
||||
- **Location:** Lines 1483, 1538, 1662, 1714
|
||||
- **Issue:** New sections §8b (KI-Integration), §8c (Workflow Engine), §8d (Monitoring), §8e (Performance) are sub-sections of §8 (Deployment Architecture). Topically, KI-Integration and Workflow Engine are architecture concerns, not deployment concerns.
|
||||
- **Recommendation:** Consider renumbering as §11 (KI-Integration), §12 (Workflow Engine), or as sub-sections of §1 (System Architecture). Not blocking — content is correct and well-structured.
|
||||
|
||||
### MINOR-2: feature_coverage_summary count incorrect
|
||||
|
||||
- **Artifact:** task_graph.json
|
||||
- **Location:** Line 556 (`"total_features": 143`)
|
||||
- **Issue:** The summary states 143 total features, but requirements.md contains 140 unique feature IDs. Of these, 6 are [v2-Plugin] (F-FILE-01-04, F-FILEUI-05-06), leaving 134 v1 features — all covered by tasks. The note says "6 v2-Plugin features excluded" but the total count is wrong (143 should be 140).
|
||||
- **Recommendation:** Change `"total_features": 143` to `"total_features": 140` and update the note to say "134 v1 features covered, 6 v2-Plugin features excluded".
|
||||
|
||||
---
|
||||
|
||||
## Severity Summary
|
||||
|
||||
| Severity | Count | Details |
|
||||
|----------|-------|--------|
|
||||
| **CRITICAL** | 0 | — |
|
||||
| **MAJOR** | 1 | AGENTS.md not updated for T09/T10 |
|
||||
| **MINOR** | 2 | Section numbering, feature_coverage_summary count |
|
||||
| **SUGGESTION** | 0 | — |
|
||||
|
||||
---
|
||||
|
||||
## Detailed Fix Verification
|
||||
|
||||
### ✅ CRITICAL-1 (FIXED): F-AI-01 — KI-Copilot
|
||||
|
||||
**Architecture §8b (lines 1483-1530):**
|
||||
- API-First-Design als Grundlage documented ✅
|
||||
- 3 API Endpoints: `/api/v1/ai/copilot/query`, `/api/v1/ai/copilot/history`, `/api/v1/ai/copilot/execute` ✅
|
||||
- RBAC-Durchsetzung: 5 points (Auth via session, RBAC middleware, field-level permissions, tenant isolation, audit log) ✅
|
||||
- Implementation-Modell v1: Query/Execute/History + LLM config via env vars ✅
|
||||
- Frontend-Integration: Sidebar entry, chat interface, confirmation dialog ✅
|
||||
- DB table `ai_conversations` with tenant_id, user_id, role, content, proposed_actions (JSONB) ✅
|
||||
- Test file `test_ai_copilot.py` listed in test tree ✅
|
||||
|
||||
**Task T09 (lines 426-467):**
|
||||
- F-AI-01 in requirement_ids ✅
|
||||
- 7 acceptance criteria for Copilot (query, execute, RBAC, history, audit, tenant, field-level) ✅
|
||||
- test_spec with 3 commands + 2 test files ✅
|
||||
- Coverage target: 80% ✅
|
||||
|
||||
### ✅ CRITICAL-2 (FIXED): F-WF-01 — Hybrid-Workflow-Engine
|
||||
|
||||
**Architecture §8c (lines 1538-1620):**
|
||||
- Hybrid-Ansatz: Code-Engine (hardcoded Python workflows) + Configurable Engine (user-defined via Admin-UI) ✅
|
||||
- 10 API Endpoints for workflow management ✅
|
||||
- 3 DB tables: `workflows` (definition with JSONB steps), `workflow_instances` (running), `workflow_step_history` (audit trail) ✅
|
||||
- Step JSONB structure documented with example (action, approval, notification types) ✅
|
||||
- All tables have tenant_id ✅
|
||||
|
||||
**Task T09 (lines 426-467):**
|
||||
- F-WF-01 in requirement_ids ✅
|
||||
- 14 acceptance criteria for Workflow (CRUD definitions, instances, advance/approve/reject/cancel, event trigger, step history, code-engine, timeout) ✅
|
||||
- test_spec includes `test_workflows.py` ✅
|
||||
|
||||
### ✅ CRITICAL-3 (FIXED): Session-Storage Contradiction
|
||||
|
||||
- **§6 sessions table (line 200):** Labeled "Audit Trail — primary session store is Redis" ✅
|
||||
- **Note (line 210):** "Session lookup at runtime uses Redis (`session:{id}` with TTL=8h). This PostgreSQL table is an immutable audit trail." ✅
|
||||
- **Session creation flow (lines 1242-1243):** "Create session in Redis (key: `session:{session_id}`, TTL=8h)" + "Write session record to PostgreSQL `sessions` table for audit trail" ✅
|
||||
- **ADR-05 (line 1871):** "Server-side sessions in Redis (primary store for fast lookup), session ID in HttpOnly+Secure+SameSite=Strict cookie. PostgreSQL `sessions` table retains session records as an audit trail" ✅
|
||||
- **All three locations are now consistent:** Redis = primary session store (fast lookup, TTL), PostgreSQL = immutable audit trail ✅
|
||||
|
||||
### ✅ MAJOR-1 (FIXED): 19 v1 Features without traceability
|
||||
|
||||
All 19 previously-missing features verified present in task_graph requirement_ids:
|
||||
|
||||
| Feature | Task | Verified |
|
||||
|---------|------|----------|
|
||||
| F-NAV-01 | T07 | ✅ (grep count: 1) |
|
||||
| F-SET-01 | T07 | ✅ (grep count: 1) |
|
||||
| F-UI-01 | T07 | ✅ (grep count: 1) |
|
||||
| F-UI-02 | T07 | ✅ (grep count: 1) |
|
||||
| F-UI-03 | T07 | ✅ (grep count: 1) |
|
||||
| F-UI-04 | T07 | ✅ (grep count: 1) |
|
||||
| F-UI-05 | T07 | ✅ (grep count: 1) |
|
||||
| F-UI-06 | T07 | ✅ (grep count: 1) |
|
||||
| F-UI-08 | T07 | ✅ (grep count: 1) |
|
||||
| F-SEC-02 | T01 | ✅ (grep count: 1) |
|
||||
| F-SEC-03 | T01 | ✅ (grep count: 1) |
|
||||
| F-SCHED-01 | T01 | ✅ (grep count: 1) |
|
||||
| F-DATA-03 | T02 | ✅ (grep count: 1) |
|
||||
| F-DATA-04 | T02 | ✅ (grep count: 1) |
|
||||
| F-DATA-06 | T07 | ✅ (grep count: 1) |
|
||||
| F-ENV-01 | T08 | ✅ (grep count: 1) |
|
||||
| F-INFRA-02 | T08 | ✅ (grep count: 2) |
|
||||
| F-INFRA-03 | T01 | ✅ (grep count: 2) |
|
||||
| F-TEST-01 | All tasks | ✅ (grep count: 10) |
|
||||
|
||||
### ✅ MAJOR-2 (FIXED): F-DOC-01 — Dokumentation
|
||||
|
||||
- Task T10 covers F-DOC-01 with 3 acceptance criteria: README.md, Swagger UI, admin-guide.md, api-overview.md ✅
|
||||
- Test command: `test -f README.md && test -f docs/admin-guide.md && test -f docs/api-overview.md` ✅
|
||||
|
||||
### ✅ MAJOR-3 (FIXED): F-INFRA-04 — Monitoring & Alerting
|
||||
|
||||
- Architecture §8d: Health endpoint, Prometheus metrics, alerting, structured logging ✅
|
||||
- Task T10: 5 acceptance criteria + test_spec with `test_monitoring.py` ✅
|
||||
|
||||
### ✅ MAJOR-4 (FIXED): F-PERF-01 — Performance
|
||||
|
||||
- Architecture §8e: Indexing strategy, query optimization, frontend performance, performance tests ✅
|
||||
- Task T10: 6 acceptance criteria + test_spec with `test_performance.py` ✅
|
||||
|
||||
### ✅ MAJOR-5 (FIXED): F-CONT-08 Phantom
|
||||
|
||||
- `grep -n 'F-CONT-08' task_graph.json` → 0 results ✅
|
||||
- F-CONT-08 completely removed from all requirement_ids arrays ✅
|
||||
|
||||
### ✅ MINOR-1 (FIXED): api_tokens table in DB Schema
|
||||
|
||||
- DB Schema §2 (line 366-380): Full table definition with 9 columns + 2 indexes ✅
|
||||
- Labeled "post-MVP, architecture ready" ✅
|
||||
- Has tenant_id ✅
|
||||
|
||||
### ✅ MINOR-2 (FIXED): CSP-Header
|
||||
|
||||
- Architecture §6 (lines 1284-1291): Full CSP header + 5 additional security headers ✅
|
||||
- DOMPurify/escaped rendering mentioned ✅
|
||||
- Linked to F-SEC-02 ✅
|
||||
|
||||
### ✅ MINOR-3 (FIXED): Typo line 1033
|
||||
|
||||
- `grep -n '```n' architecture.md` → 0 results ✅
|
||||
- No malformed code fences found anywhere in the file ✅
|
||||
|
||||
---
|
||||
|
||||
## Feature Coverage Cross-Check (Programmatic)
|
||||
|
||||
**Method:** Regex extraction of all `F-[A-Z]+-[0-9]+` patterns from requirements.md and task_graph.json, set difference.
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Unique features in requirements.md | 140 |
|
||||
| Unique features in task_graph.json | 136 |
|
||||
| Missing from task_graph (v2-Plugin, legitimately excluded) | 4 (F-FILE-02, F-FILE-03, F-FILE-04, F-FILEUI-06) |
|
||||
| Phantom features (in task_graph but not in requirements) | 0 |
|
||||
| v1 features covered | 134/134 (100%) |
|
||||
| v2 features excluded | 6/6 (F-FILE-01-04, F-FILEUI-05-06) |
|
||||
|
||||
**Note:** F-FILE-01 and F-FILEUI-05 appear in requirements.md as [v2-Plugin] but are NOT in any task's requirement_ids array — they only appear in the coverage summary note text. This is correct behavior.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Section Inventory
|
||||
|
||||
| Section | Lines | Status |
|
||||
|---------|-------|--------|
|
||||
| §1 System Architecture | 10-141 | ✅ Complete (diagram, services, backend/frontend structure) |
|
||||
| §2 DB Schema | 142-822 | ✅ Complete (Core + Plugin + AI + Workflow tables, FTS, api_tokens) |
|
||||
| §3 API Design | 823-1094 | ✅ Complete (all endpoints with Feature IDs, Copilot + Workflow endpoints added) |
|
||||
| §4 Plugin Architecture | 1095-1206 | ✅ Complete (Manifest, Lifecycle, Event Bus, DI, UI Framework) |
|
||||
| §5 Multi-Tenant | 1207-1233 | ✅ Complete (Session Context, ORM Auto-Filter, TenantMixin) |
|
||||
| §6 Auth Architecture | 1234-1311 | ✅ Complete (Session, RBAC, API Tokens, CSRF, CSP, Password Reset) |
|
||||
| §7 Frontend Architecture | 1312-1384 | ✅ Complete (Stack, Routing, State, i18n, A11Y, Design System) |
|
||||
| §8 Deployment Architecture | 1385-1482 | ✅ Complete (Docker Compose, .env, Backup) |
|
||||
| §8b KI-Integration | 1483-1537 | ✅ NEW — Complete (API endpoints, RBAC, DB table, frontend, impl model) |
|
||||
| §8c Workflow Engine | 1538-1661 | ✅ NEW — Complete (Hybrid approach, API, DB tables, step JSONB structure) |
|
||||
| §8d Monitoring & Alerting | 1662-1713 | ✅ NEW — Complete (Health endpoint, Prometheus, alerting, structured logging) |
|
||||
| §8e Performance | 1714-1776 | ✅ NEW — Complete (Indexing strategy, query optimization, frontend perf, tests) |
|
||||
| §9 Test Strategy | 1777-1824 | ✅ Complete (Backend, Frontend, E2E, test file tree updated) |
|
||||
| §10 ADRs | 1825-1888 | ✅ Complete (6 ADRs, ADR-05 updated for Redis+PostgreSQL session) |
|
||||
| Open Questions | 1889-1897 | ✅ Present |
|
||||
| Handoff | 1898-1904 | ✅ Present |
|
||||
|
||||
**Total: 1904 lines (was 1468 in Round 1) — 436 lines added for new sections.**
|
||||
|
||||
---
|
||||
|
||||
## Task Graph Inventory
|
||||
|
||||
| Task | Title | Est. Lines | Dependencies | Test Spec | Acceptance Criteria |
|
||||
|------|-------|------------|--------------|------------|---------------------|
|
||||
| T01 | Core Infrastructure + Multi-Tenant + Auth | 500 | — | ✅ 3 commands | ✅ 25 criteria |
|
||||
| T02 | Company + Contact + Import/Export | 600 | T01 | ✅ 3 commands | ✅ 24 criteria |
|
||||
| T03 | Plugin System Framework | 500 | T01 | ✅ 3 commands | ✅ 14 criteria |
|
||||
| T04 | DMS Plugin + Tags Plugin | 700 | T01, T03 | ✅ 4 commands | ✅ 32 criteria |
|
||||
| T05 | Calendar Plugin | 700 | T01, T03 | ✅ 3 commands | ✅ 29 criteria |
|
||||
| T06 | Mail Plugin | 800 | T01, T03 | ✅ 3 commands | ✅ 40 criteria |
|
||||
| T07 | Frontend Core SPA | 600 | T01, T02 | ✅ 4 commands | ✅ 32 criteria |
|
||||
| T08 | Frontend Plugins + Search + Deployment | 700 | T03-T07 | ✅ 6 commands | ✅ 47 criteria |
|
||||
| T09 | KI-Copilot API + Hybrid Workflow Engine | 700 | T01, T02 | ✅ 3 commands | ✅ 22 criteria |
|
||||
| T10 | Monitoring, Performance Testing, Documentation | 500 | T01, T02, T08 | ✅ 5 commands | ✅ 16 criteria |
|
||||
|
||||
- All tasks in 500-800 lines range ✅
|
||||
- Every task has test_spec with commands, test_files, coverage_target ✅
|
||||
- Every task has substantielle acceptance_criteria ✅
|
||||
- 6-phase execution plan with parallelization ✅
|
||||
- No micro-tasks ✅
|
||||
|
||||
---
|
||||
|
||||
## AGENTS.md Verification
|
||||
|
||||
### Build/Test Commands ✅
|
||||
- Backend: venv setup, uvicorn, alembic, pytest, pytest-cov, mypy, ruff ✅
|
||||
- Frontend: npm install, dev, build, vitest, tsc, eslint ✅
|
||||
- Docker Compose: build, up, logs, down, config validate ✅
|
||||
- E2E: Playwright install + test ✅
|
||||
|
||||
### Forbidden Patterns ✅
|
||||
- Backend: 14 patterns (SQLite, Jinja2, Cross-Tenant, Plaintext Passwords, JWT, Naive Datetime, Integer IDs, Hard-Delete ohne GDPR, Manual Tenant Filter, Sync I/O, Raw SQL, Secrets in Code, Unvalidated Input, Missing Audit Log, Plugin Tables ohne tenant_id) ✅
|
||||
- Frontend: 10 patterns (Class Components, Inline Styles, Hardcoded Strings, Manual Fetch, Server Data in Zustand, `any` Types, Missing ARIA, Touch Targets <44px, Direct DOM, Unsafe HTML) ✅
|
||||
- Deployment: 5 patterns (Root in Container, Exposed DB Port, No Health Check, No Volume, Secrets in compose) ✅
|
||||
|
||||
### Task-Zuweisung ⚠️ PARTIAL
|
||||
- Phasen-Plan table: Only 5 phases / 8 tasks — **missing T09 (Phase 3) and T10 (Phase 6)** ❌
|
||||
- Task Assignment table: Only T01-T08 — **missing T09 and T10** ❌
|
||||
- Release Gate: "All 8 tasks complete" — **should be 10** ❌
|
||||
- Block Rules: "After 3 blocks (9 tasks)" — should reference 10 tasks ⚠️
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **[MAJOR]** Update AGENTS.md Phasen-Plan table to include T09 in Phase 3 and add Phase 6 with T10
|
||||
2. **[MAJOR]** Add T09 and T10 rows to Task Assignment table in AGENTS.md
|
||||
3. **[MAJOR]** Update Release Gate in AGENTS.md to "All 10 tasks complete"
|
||||
4. **[MINOR]** Update Block Rules in AGENTS.md to reference 10 tasks (4 blocks)
|
||||
5. **[MINOR]** Fix feature_coverage_summary in task_graph.json: `total_features` should be 140, not 143
|
||||
6. **[MINOR]** Consider renumbering §8b-§8e as top-level sections (not blocking)
|
||||
|
||||
---
|
||||
|
||||
## Review Metadata
|
||||
|
||||
- **Files reviewed:** architecture.md (1904 lines), task_graph.json (571 lines), AGENTS.md (542 lines), requirements.md (2142 lines, reference), quality-gate-phase2.md (383 lines, previous review)
|
||||
- **Cross-check method:** Programmatic Feature-ID extraction + set difference (Python regex/grep), targeted section reads
|
||||
- **Review method:** Full verification of all 11 Round-1 issues + re-check of 10 original criteria + new issue detection
|
||||
- **Tools used:** text_editor (read), code_execution_tool (grep/sed/python cross-check)
|
||||
|
||||
---
|
||||
|
||||
## Verdict
|
||||
|
||||
**✅ APPROVED_WITH_SUGGESTIONS**
|
||||
|
||||
All 11 issues from Round 1 are resolved. All 10 original criteria pass (8 PASS, 1 PARTIAL due to AGENTS.md gap). No critical issues remain. One major issue (AGENTS.md not updated for T09/T10) is non-blocking for implementation start but must be fixed before Phase 3 execution.
|
||||
|
||||
**Phase transition: APPROVED** — Implementation may begin. AGENTS.md update should be done in parallel with T01 implementation.
|
||||
@@ -1,312 +0,0 @@
|
||||
# Quality Gate Review — Phase 2 Architecture (Round 3)
|
||||
|
||||
**Project:** leocrm
|
||||
**Date:** 2026-06-28
|
||||
**Reviewer:** Quality Reviewer (automated)
|
||||
**Scope:** v1/v2 separation fix verification after Round 2
|
||||
|
||||
---
|
||||
|
||||
## VERDICT: APPROVED_WITH_SUGGESTIONS
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| Critical | 0 |
|
||||
| Major | 1 |
|
||||
| Minor | 3 |
|
||||
| Suggestion | 2 |
|
||||
|
||||
---
|
||||
|
||||
## 1. V1/V2 SEPARATION — ✅ PASS
|
||||
|
||||
**Methodology:** Extracted all `requirement_ids` from each v1 task (T01, T02, T03, T07, T09, T10) and cross-checked against v2 feature prefixes (F-CAL-*, F-DMS-, F-FILE*, F-FILEUI-*, F-LINK-*, F-MAIL-*, F-PERM-*, F-TAG-*).
|
||||
|
||||
**Result:** No v2 feature IDs appear in any v1 task.
|
||||
|
||||
| Task | Scope | V2 Features Found |
|
||||
|------|-------|-------------------|
|
||||
| T01 | v1 | ✓ None |
|
||||
| T02 | v1 | ✓ None |
|
||||
| T03 | v1 | ✓ None |
|
||||
| T07 | v1 | ✓ None |
|
||||
| T09 | v1 | ✓ None |
|
||||
| T10 | v1 | ✓ None |
|
||||
|
||||
**Feature ID extraction per v1 task:**
|
||||
- T01: F-CORE-*, F-AUTH-*, F-SEC-*, F-INFRA-*, F-INT-*, F-SCHED-*, F-TEST-01 (25 reqs)
|
||||
- T02: F-COMP-*, F-CONT-*, F-DATA-*, F-MIG-*, F-CORE-*, F-SEARCH-*, F-TEST-01 (25 reqs)
|
||||
- T03: F-PLUGIN-*, F-CORE-*, F-TEST-01 (7 reqs)
|
||||
- T07: F-AUTH-*, F-COMP-*, F-CONT-*, F-CORE-*, F-SEARCH-*, F-A11Y-*, F-INT-*, F-NAV-*, F-SET-*, F-UI-*, F-DATA-*, F-TEST-01 (38 reqs)
|
||||
- T09: F-AI-01, F-WF-01, F-CORE-*, F-TEST-01 (5 reqs)
|
||||
- T10: F-INFRA-*, F-PERF-01, F-DOC-01, F-ENV-01, F-TEST-01 (8 reqs)
|
||||
|
||||
---
|
||||
|
||||
## 2. V1 FEATURE COVERAGE — ✅ PASS
|
||||
|
||||
**Methodology:** Extracted 73 v1 features from requirements.md (70 with `[v1]` header markers + 3 F-A11Y features marked `[v1]`). Cross-checked against architecture.md and task_graph.json.
|
||||
|
||||
### Architecture Coverage
|
||||
- **V1 features in architecture.md:** 73/73 ✅
|
||||
- All v1 features are referenced in the architecture document.
|
||||
|
||||
### Task Graph Coverage
|
||||
- **V1 features assigned to v1 tasks:** 73/73 ✅
|
||||
- Zero v1 features missing from task assignments.
|
||||
- The task_graph contains 3 additional IDs (F-A11Y-01, F-A11Y-02, F-A11Y-03) in T07 that are correctly marked `[v1]` in requirements.md.
|
||||
|
||||
**Feature count reconciliation:**
|
||||
- Requirements.md: 73 v1 features (marked `[v1]`) + 70 v2 features (marked `[v2-Plugin]` or unmarked) = 143 total
|
||||
- Wait — our regex found 140 unique header features (73 v1 + 67 v2 headers). However, 3 v2 features (F-A11Y-01/02/03) are actually v1. The `[v2-Plugin]` marker on some features was not caught by the initial `[v1]`/`[v2]` regex because it uses `[v2-Plugin]` format. This does not affect the review outcome — all 73 v1 features are accounted for.
|
||||
|
||||
---
|
||||
|
||||
## 3. DEPENDENCY CHAIN — ✅ PASS
|
||||
|
||||
**Methodology:** Extracted `dependencies` field from all v1 tasks and verified no v2 task appears as a dependency.
|
||||
|
||||
| V1 Task | Dependencies | All V1? |
|
||||
|---------|-------------|---------|
|
||||
| T01 | [] | ✓ (no deps) |
|
||||
| T02 | [T01] | ✓ |
|
||||
| T03 | [T01] | ✓ |
|
||||
| T07 | [T01, T02] | ✓ |
|
||||
| T09 | [T01, T02] | ✓ |
|
||||
| T10 | [T01, T02] | ✓ |
|
||||
|
||||
**Critical check:** T10 does NOT depend on T08, T08a, T08b, T08c, or any v2 task. ✅
|
||||
|
||||
**V2 task dependencies (for reference):**
|
||||
- T04: [T01, T03] — v1 deps only ✅
|
||||
- T05: [T01, T03] — v1 deps only ✅
|
||||
- T06: [T01, T03] — v1 deps only ✅
|
||||
- T08a: [T04, T07] — v2+v1 deps (expected) ✅
|
||||
- T08b: [T05, T07] — v2+v1 deps (expected) ✅
|
||||
- T08c: [T06, T07] — v2+v1 deps (expected) ✅
|
||||
- T11: [T01, T03] — v1 deps only ✅
|
||||
|
||||
**Conclusion:** V1 tasks can execute independently without any v2 task. The dependency chain is clean.
|
||||
|
||||
---
|
||||
|
||||
## 4. TASK SIZING — ✅ PASS
|
||||
|
||||
**Methodology:** Counted `requirement_ids` and `acceptance_criteria` per task. Threshold: ≤40 each.
|
||||
|
||||
| Task | Reqs | ACs | Overloaded? |
|
||||
|------|------|-----|-------------|
|
||||
| T01 | 25 | 26 | ✅ No |
|
||||
| T02 | 25 | 24 | ✅ No |
|
||||
| T03 | 7 | 14 | ✅ No |
|
||||
| T04 | 11 | 26 | ✅ No |
|
||||
| T05 | 19 | 30 | ✅ No |
|
||||
| T06 | 20 | 40 | ✅ No (at limit) |
|
||||
| T07 | 38 | 32 | ✅ No |
|
||||
| T08a | 23 | 12 | ✅ No |
|
||||
| T08b | 13 | 11 | ✅ No |
|
||||
| T08c | 16 | 18 | ✅ No |
|
||||
| T09 | 5 | 22 | ✅ No |
|
||||
| T10 | 8 | 18 | ✅ No |
|
||||
| T11 | 16 | 14 | ✅ No |
|
||||
|
||||
**Note:** T06 has exactly 40 ACs (at the limit) and T07 has 38 reqs (close to limit). Recommend monitoring during implementation but not blocking.
|
||||
|
||||
---
|
||||
|
||||
## 5. ARCHITECTURE V1/V2 MARKERS — ⚠️ PARTIAL
|
||||
|
||||
**Methodology:** Searched architecture.md for section headers containing v2 domain names with v2/Plugin Phase markers.
|
||||
|
||||
### V2 Section Markers Found
|
||||
| Domain | V2-Marked Sections | Status |
|
||||
|--------|-------------------|--------|
|
||||
| DMS | 2 | ✅ `### DMS Plugin Tables (v2 — Plugin Phase)`, `### DMS Plugin Endpoints (v2 — Plugin Phase)` |
|
||||
| Calendar | 1 | ✅ `### Calendar Plugin Tables (v2 — Plugin Phase)` |
|
||||
| Mail | 1 | ✅ `### Mail Plugin Tables (v2 — Plugin Phase)` |
|
||||
| Tag | 0 | ⚠️ Missing v2 marker |
|
||||
| Permission | 0 | ⚠️ Missing v2 marker |
|
||||
| File | 0 | ⚠️ Missing v2 marker |
|
||||
|
||||
### V1 Feature References in Architecture
|
||||
- **73/73 v1 features referenced** ✅
|
||||
- All v1 feature IDs (including F-A11Y-01/02/03) appear in architecture.md.
|
||||
|
||||
### V2 Feature References in Architecture
|
||||
- **48/70 v2 features explicitly referenced** (22 missing)
|
||||
- Missing v2 feature IDs in architecture.md:
|
||||
- F-CAL-06, F-CAL-07, F-CAL-08, F-CAL-12, F-CAL-14, F-CAL-15
|
||||
- F-FILE-01, F-FILE-02, F-FILE-03, F-FILE-04
|
||||
- F-FILEUI-02, F-FILEUI-03, F-FILEUI-05, F-FILEUI-06
|
||||
- F-LINK-02, F-LINK-03, F-LINK-04, F-LINK-06
|
||||
- F-PERM-01, F-PERM-02, F-PERM-04
|
||||
- F-TAG-03
|
||||
|
||||
**Assessment:** The missing v2 feature references in architecture.md are a MINOR issue. The architecture document covers the plugin system architecture generically (plugin manifest, plugin DB migrations, UI plugin framework). Individual v2 feature IDs are more relevant at the task/implementation level. However, the missing v2 section markers for Tag, Permission, and File domains should be added for completeness.
|
||||
|
||||
---
|
||||
|
||||
## 6. AGENTS.md COMPLETENESS — ✅ PASS
|
||||
|
||||
**Methodology:** Verified all 13 task IDs (T01-T11, T08a, T08b, T08c) are referenced in AGENTS.md.
|
||||
|
||||
| Task ID | In AGENTS.md |
|
||||
|---------|-------------|
|
||||
| T01 | ✅ |
|
||||
| T02 | ✅ |
|
||||
| T03 | ✅ |
|
||||
| T04 | ✅ |
|
||||
| T05 | ✅ |
|
||||
| T06 | ✅ |
|
||||
| T07 | ✅ |
|
||||
| T08a | ✅ |
|
||||
| T08b | ✅ |
|
||||
| T08c | ✅ |
|
||||
| T09 | ✅ |
|
||||
| T10 | ✅ |
|
||||
| T11 | ✅ |
|
||||
|
||||
- V1 phase mentioned: ✅ (18 occurrences of 'v1')
|
||||
- V2 phase mentioned: ✅ (16 occurrences of 'v2')
|
||||
- Phase plan shows v1 and v2 phases separately: ✅
|
||||
|
||||
---
|
||||
|
||||
## 7. ORIGINAL 10 CRITERIA (from Round 1/2) — ✅ ALL PASS
|
||||
|
||||
| # | Criterion | Status | Evidence |
|
||||
|---|-----------|--------|----------|
|
||||
| a | F-AI-01 referenced in architecture | ✅ PASS | Found in architecture.md |
|
||||
| b | F-WF-01 referenced in architecture | ✅ PASS | Found in architecture.md |
|
||||
| c | Redis session storage (ADR-05) consistent | ✅ PASS | ADR-05 present: "Server-side sessions in Redis (primary store for fast lookup)" |
|
||||
| d | All 19 traceability IDs present in task_graph | ✅ PASS | 19/19 found, zero missing |
|
||||
| e | F-DOC-01 covered | ✅ PASS | In T10 requirement_ids |
|
||||
| f | F-INFRA-04 covered | ✅ PASS | In T10 requirement_ids |
|
||||
| g | F-PERF-01 covered | ✅ PASS | In T10 requirement_ids |
|
||||
| h | F-CONT-08 (phantom) NOT in task_graph | ✅ PASS | Confirmed absent — F-CONT-08 does not exist in requirements.md or task_graph |
|
||||
| i | CSP header mentioned | ✅ PASS | Content-Security-Policy / CSP found in architecture.md |
|
||||
| j | api_tokens mentioned | ✅ PASS | api_tokens / API token found in architecture.md |
|
||||
|
||||
---
|
||||
|
||||
## DETAILED FINDINGS
|
||||
|
||||
### Finding 1 — MAJOR: 6 V2 Features Unassigned to Any Task
|
||||
|
||||
**Severity:** Major
|
||||
**Artifact:** task_graph.json
|
||||
**Location:** T04 (DMS), T08a (DMS UI)
|
||||
**Issue:** The following 6 v2 features have no `requirement_ids` entry in any task:
|
||||
- F-FILE-01: Datei-Explorer (DMS plugin)
|
||||
- F-FILE-02: Datei-Sharing (DMS plugin)
|
||||
- F-FILE-03: PDF-Preview (DMS plugin)
|
||||
- F-FILE-04: OnlyOffice-Integration (DMS plugin)
|
||||
- F-FILEUI-05: Upload-Progress-Anzeige (DMS plugin)
|
||||
- F-FILEUI-06: Drag & Drop zwischen Ordnern (DMS plugin)
|
||||
|
||||
**Impact:** These requirements exist in requirements.md but have no task ownership. They may be implicitly covered by T04 (DMS backend) and T08a (DMS UI), but without explicit `requirement_ids` entries, there is no traceability and risk of implementation gaps.
|
||||
|
||||
**Recommendation:** Add F-FILE-01 through F-FILE-04 to T04's `requirement_ids` and F-FILEUI-05, F-FILEUI-06 to T08a's `requirement_ids`. Alternatively, create a dedicated T08d task for the file-explorer UI features if T08a is already at capacity (23 reqs).
|
||||
|
||||
---
|
||||
|
||||
### Finding 2 — MINOR: Missing V2 Section Markers for Tag, Permission, and File Domains
|
||||
|
||||
**Severity:** Minor
|
||||
**Artifact:** architecture.md
|
||||
**Location:** Section headers
|
||||
**Issue:** Three v2 domains lack explicit "v2 — Plugin Phase" section markers:
|
||||
- Tag: No v2-marked section header found
|
||||
- Permission: No v2-marked section header found
|
||||
- File (DMS File Explorer): No v2-marked section header found
|
||||
|
||||
The architecture document has v2 markers for DMS, Calendar, and Mail tables/endpoints, but not for Tag, Permission, or File-specific sections.
|
||||
|
||||
**Recommendation:** Add v2 section markers for:
|
||||
- `### Tag Plugin Tables (v2 — Plugin Phase)`
|
||||
- `### Tag Plugin Endpoints (v2 — Plugin Phase)`
|
||||
- `### Permission Plugin Tables (v2 — Plugin Phase)`
|
||||
- `### Permission Plugin Endpoints (v2 — Plugin Phase)`
|
||||
- `### File Explorer (DMS) (v2 — Plugin Phase)`
|
||||
|
||||
---
|
||||
|
||||
### Finding 3 — MINOR: 22 V2 Features Not Explicitly Referenced in Architecture.md
|
||||
|
||||
**Severity:** Minor
|
||||
**Artifact:** architecture.md
|
||||
**Location:** Feature ID references
|
||||
**Issue:** 22 of 70 v2 features are not explicitly referenced by feature ID in architecture.md. While the architectural concepts (plugin system, DMS tables, etc.) are described, individual feature-level traceability is missing for these 22 features.
|
||||
|
||||
**Impact:** Low — v2 is the plugin phase and the architecture covers the plugin system generically. Individual feature IDs are more relevant at implementation time. However, adding references improves traceability.
|
||||
|
||||
**Recommendation:** Add feature ID references to the relevant architecture sections for the 22 missing v2 features listed in Section 5 above.
|
||||
|
||||
---
|
||||
|
||||
### Finding 4 — MINOR: T06 at AC Limit (40/40)
|
||||
|
||||
**Severity:** Minor
|
||||
**Artifact:** task_graph.json
|
||||
**Location:** T06 (Mail Plugin)
|
||||
**Issue:** T06 has exactly 40 acceptance criteria, hitting the threshold limit. While not exceeding, this is a large task that may be difficult to implement and test in a single block.
|
||||
|
||||
**Recommendation:** Consider splitting T06 into T06a (core mail: F-MAIL-01 through F-MAIL-10) and T06b (advanced mail: F-MAIL-11 through F-MAIL-19) if implementation proves unwieldy.
|
||||
|
||||
---
|
||||
|
||||
### Finding 5 — SUGGESTION: T07 Has 38 Requirements (Near Limit)
|
||||
|
||||
**Severity:** Suggestion
|
||||
**Artifact:** task_graph.json
|
||||
**Location:** T07 (Frontend SPA)
|
||||
**Issue:** T07 has 38 requirements, close to the 40 limit. This is the frontend SPA task covering all v1 UI features.
|
||||
|
||||
**Recommendation:** Monitor during implementation. If T07 becomes too large, consider splitting into T07a (layout, navigation, core UI) and T07b (company/contact UI, search, data tables).
|
||||
|
||||
---
|
||||
|
||||
### Finding 6 — SUGGESTION: V2 Feature Marker Format Inconsistency
|
||||
|
||||
**Severity:** Suggestion
|
||||
**Artifact:** requirements.md
|
||||
**Location:** Feature headers
|
||||
**Issue:** V1 features use `[v1]` marker format, but v2 features use `[v2-Plugin]` format. The regex `\[v2\]` does not match `[v2-Plugin]`, which initially caused 0 v2 features to be detected. This is a formatting inconsistency.
|
||||
|
||||
**Recommendation:** Standardize marker format — either all use `[v1]`/`[v2]` or all use `[v1-Core]`/`[v2-Plugin]`. This improves automated parsing reliability.
|
||||
|
||||
---
|
||||
|
||||
## SUMMARY TABLE
|
||||
|
||||
| Verification Item | Result |
|
||||
|-------------------|--------|
|
||||
| 1. V1/V2 Separation (no v2 in v1 tasks) | ✅ PASS |
|
||||
| 2. V1 Feature Coverage (73/73 in arch + tasks) | ✅ PASS |
|
||||
| 3. Dependency Chain (v1 independent from v2) | ✅ PASS |
|
||||
| 4. Task Sizing (≤40 reqs, ≤40 ACs) | ✅ PASS |
|
||||
| 5. Architecture V1/V2 Markers | ⚠️ PARTIAL (3 domains missing v2 markers) |
|
||||
| 6. AGENTS.md Completeness (13/13 tasks) | ✅ PASS |
|
||||
| 7a. F-AI-01 in architecture | ✅ PASS |
|
||||
| 7b. F-WF-01 in architecture | ✅ PASS |
|
||||
| 7c. Redis session storage (ADR-05) | ✅ PASS |
|
||||
| 7d. 19 traceability IDs in task_graph | ✅ PASS (19/19) |
|
||||
| 7e. F-DOC-01 covered | ✅ PASS |
|
||||
| 7f. F-INFRA-04 covered | ✅ PASS |
|
||||
| 7g. F-PERF-01 covered | ✅ PASS |
|
||||
| 7h. F-CONT-08 phantom absent | ✅ PASS |
|
||||
| 7i. CSP header mentioned | ✅ PASS |
|
||||
| 7j. api_tokens mentioned | ✅ PASS |
|
||||
|
||||
---
|
||||
|
||||
## NEXT STEPS
|
||||
|
||||
1. **MAJOR — Fix before v2 implementation:** Add F-FILE-01 through F-FILE-04 and F-FILEUI-05, F-FILEUI-06 to appropriate v2 task `requirement_ids` in task_graph.json.
|
||||
2. **MINOR — Improve before v2 implementation:** Add v2 section markers for Tag, Permission, and File domains in architecture.md.
|
||||
3. **MINOR — Improve traceability:** Add the 22 missing v2 feature ID references to architecture.md sections.
|
||||
4. **Non-blocking:** T06 at AC limit and T07 near req limit — monitor during implementation.
|
||||
|
||||
**Phase Gate Decision:** The v1 scope is clean, complete, and correctly separated from v2. The dependency chain allows independent v1 execution. All 10 original criteria pass. The single MAJOR issue (6 unassigned v2 features) does not block v1 implementation but should be resolved before v2 work begins.
|
||||
|
||||
**This phase gate is APPROVED WITH SUGGESTIONS. V1 implementation may proceed.**
|
||||
@@ -1,383 +0,0 @@
|
||||
# LeoCRM — Quality Gate Phase 2 (Architecture) Review
|
||||
|
||||
**Reviewer:** Quality Reviewer (Agent Zero)
|
||||
**Datum:** 2026-06-28
|
||||
**Phase:** Phase 2 — Architecture + Task Graph + AGENTS.md
|
||||
**Verdict:** ❌ **BLOCKED** — 3 Critical Issues, 5 Major Issues
|
||||
|
||||
---
|
||||
|
||||
## Prüfkriterien-Übersicht
|
||||
|
||||
| # | Kriterium | Ergebnis | Severity |
|
||||
|---|----------|----------|----------|
|
||||
| 1 | Architecture.md deckt alle 10 Bereiche ab | ✅ PASS | — |
|
||||
| 2 | Task Graph: 6-8 substantielle Tasks | ✅ PASS | — |
|
||||
| 3 | 143/143 Features abgedeckt | ❌ **FAIL** | CRITICAL |
|
||||
| 4 | AGENTS.md: Commands, Forbidden Patterns, Task-Zuweisung | ✅ PASS | — |
|
||||
| 5 | Keine Widersprüche arch.md ↔ requirements.md | ⚠️ PARTIAL | MAJOR |
|
||||
| 6 | Keine Widersprüche task_graph.json ↔ arch.md | ⚠️ PARTIAL | MINOR |
|
||||
| 7 | Multi-Tenant (tenant_id) konsistent | ✅ PASS | — |
|
||||
| 8 | Plugin-System als v1-Core-Feature | ✅ PASS | — |
|
||||
| 9 | PostgreSQL 16, React 18 SPA, FastAPI | ✅ PASS | — |
|
||||
| 10 | Session-based Auth + API-Token separat | ⚠️ PARTIAL | CRITICAL |
|
||||
|
||||
**Gesamt:** 6 PASS, 2 PARTIAL, 1 FAIL, 1 CRITICAL PARTIAL → **BLOCKED**
|
||||
|
||||
---
|
||||
|
||||
## Detaillierte Befunde
|
||||
|
||||
### ✅ Kriterium 1: Architecture.md — 10 Bereiche (PASS)
|
||||
|
||||
Alle 10 Bereiche sind vorhanden und substantiell ausgearbeitet:
|
||||
|
||||
| Bereich | Section | Zeilen | Status |
|
||||
|---------|---------|--------|-------|
|
||||
| System Architecture | §1 | 1-131 | ✅ Vollständig (Diagramm, Services, Backend/Frontend Struktur) |
|
||||
| DB Schema | §2 | 134-725 | ✅ Vollständig (Core + Plugin Tables, FTS) |
|
||||
| API Design | §3 | 728-967 | ✅ Vollständig (alle Endpoints mit Feature-IDs) |
|
||||
| Plugin Architecture | §4 | 970-1078 | ✅ Vollständig (Manifest, Lifecycle, Event Bus, DI, UI Framework) |
|
||||
| Multi-Tenant | §5 | 1081-1106 | ✅ Vollständig (Session Context, ORM Auto-Filter, TenantMixin) |
|
||||
| Auth | §6 | 1108-1171 | ✅ Vollständig (Session, RBAC, API Tokens, CSRF, Password Reset) |
|
||||
| Frontend | §7 | 1174-1244 | ✅ Vollständig (Stack, Routing, State, i18n, A11Y, Design System) |
|
||||
| Deployment | §8 | 1247-1343 | ✅ Vollständig (Docker Compose, .env, Backup) |
|
||||
| Test Strategy | §9 | 1345-1386 | ✅ Vollständig (Backend, Frontend, E2E) |
|
||||
| ADRs | §10 | 1389-1450 | ✅ Vollständig (6 ADRs mit Context/Decision/Rationale/Alternatives) |
|
||||
|
||||
---
|
||||
|
||||
### ✅ Kriterium 2: Task Graph — 8 substantielle Tasks (PASS)
|
||||
|
||||
| Task | Titel | Est. Lines | Dependencies | Test Spec | Acceptance Criteria |
|
||||
|------|-------|------------|--------------|------------|---------------------|
|
||||
| T01 | Core Infrastructure + Multi-Tenant + Auth | 500 | — | ✅ 3 commands | ✅ 25 criteria |
|
||||
| T02 | Company + Contact + Import/Export | 600 | T01 | ✅ 3 commands | ✅ 24 criteria |
|
||||
| T03 | Plugin System Framework | 500 | T01 | ✅ 3 commands | ✅ 14 criteria |
|
||||
| T04 | DMS Plugin + Tags Plugin | 700 | T01, T03 | ✅ 4 commands | ✅ 32 criteria |
|
||||
| T05 | Calendar Plugin | 700 | T01, T03 | ✅ 3 commands | ✅ 29 criteria |
|
||||
| T06 | Mail Plugin | 800 | T01, T03 | ✅ 3 commands | ✅ 27 criteria |
|
||||
| T07 | Frontend Core SPA | 600 | T01, T02 | ✅ 4 commands | ✅ 31 criteria |
|
||||
| T08 | Frontend Plugins + Search + Deployment | 700 | T03-T07 | ✅ 6 commands | ✅ 42 criteria |
|
||||
|
||||
- Alle Tasks im 200-800 Zeilen-Bereich ✅
|
||||
- Jeder Task hat test_spec mit commands, test_files, coverage_target ✅
|
||||
- Jeder Task hat substantielle acceptance_criteria ✅
|
||||
- 5-Phasen-Execution-Plan mit Parallelisierung ✅
|
||||
- Keine Micro-Tasks ✅
|
||||
|
||||
---
|
||||
|
||||
### ❌ Kriterium 3: Feature Coverage 143/143 (FAIL — CRITICAL)
|
||||
|
||||
**Cross-Check-Ergebnis (programmatisch durchgeführt):**
|
||||
|
||||
- **Requirements.md:** 143 eindeutige Feature-IDs
|
||||
- **Task Graph:** 114 eindeutige Feature-IDs in requirement_ids arrays
|
||||
- **Fehlend:** 30 Features in requirements.md aber NICHT in task_graph.json
|
||||
- **Phantom:** 1 Feature in task_graph.json aber NICHT in requirements.md (F-CONT-08)
|
||||
|
||||
#### Klassifikation der 30 fehlenden Features:
|
||||
|
||||
**Kategorie A: v2-Scope (6 Features — legitimerweise ausgeschlossen)**
|
||||
|
||||
| Feature | Beschreibung | Tag |
|
||||
|---------|-------------|-----|
|
||||
| F-FILE-01 | Datei-Explorer | [v2-Plugin] |
|
||||
| F-FILE-02 | Datei-Sharing | [v2-Plugin] |
|
||||
| F-FILE-03 | PDF-Preview | [v2-Plugin] |
|
||||
| F-FILE-04 | OnlyOffice-Integration | [v2-Plugin] |
|
||||
| F-FILEUI-05 | Upload-Progress-Anzeige | [v2-Plugin] |
|
||||
| F-FILEUI-06 | Drag & Drop zwischen Ordnern | [v2-Plugin] |
|
||||
|
||||
→ Diese 6 Features sind als [v2-Plugin] markiert und korrekterweise nicht in v1-Tasks enthalten. Funktionalität ist teilweise durch F-DMS-XX und F-FILEUI-01-04 abgedeckt.
|
||||
|
||||
**Kategorie B: Implizit abgedeckt, aber Feature-ID fehlt in task_graph (19 Features — Traceability-Gap)**
|
||||
|
||||
| Feature | Beschreibung | Implizit gedeckt durch | Severity |
|
||||
|---------|-------------|----------------------|----------|
|
||||
| F-DATA-03 | Daten-Validierung | Pydantic schemas in allen Tasks | MAJOR |
|
||||
| F-DATA-04 | PostgreSQL als Datenbank | ADR-01, gesamte DB Schema | MAJOR |
|
||||
| F-DATA-06 | ARIA-Rollen auf DataTable | F-A11Y-01/02/03 in T07 | MAJOR |
|
||||
| F-ENV-01 | Environments & Secrets | .env.example in Architecture §8 | MAJOR |
|
||||
| F-INFRA-02 | Backup & Restore | Architecture §8 Backup section | MAJOR |
|
||||
| F-INFRA-03 | Logging | LOG_LEVEL in .env.example | MAJOR |
|
||||
| F-NAV-01 | Navigation Sidebar | T07 Layout Shell (Sidebar) | MAJOR |
|
||||
| F-SCHED-01 | Background-Jobs | T01 ARQ Job Queue | MAJOR |
|
||||
| F-SEC-02 | XSS-Schutz & Input-Sanitization | DOMPurify, Pydantic validation | MAJOR |
|
||||
| F-SEC-03 | Session-Timeout | T01 Auth (8h timeout) | MAJOR |
|
||||
| F-SET-01 | Einstellungen als Baum-Menü | T07 Settings Feature (SettingsTree) | MAJOR |
|
||||
| F-TEST-01 | Testing-Strategie | Architecture §9 + AGENTS.md | MAJOR |
|
||||
| F-UI-01 | Responsive Design | T07 Tailwind responsive breakpoints | MAJOR |
|
||||
| F-UI-02 | Internationalisierung | T07 i18n setup (de/en) | MAJOR |
|
||||
| F-UI-03 | Error-Handling & Toast | T07 Toast component | MAJOR |
|
||||
| F-UI-04 | Loading-States | T07 Skeleton component | MAJOR |
|
||||
| F-UI-05 | Empty-States | T07 EmptyState component | MAJOR |
|
||||
| F-UI-06 | Confirmation-Dialogs | T07 ConfirmDialog | MAJOR |
|
||||
| F-UI-08 | Datenansichten Tabelle/Karten/Liste | T07 TanStack Table | MAJOR |
|
||||
|
||||
→ Diese 19 Features sind funktional durch die Tasks abgedeckt, aber ihre Feature-IDs sind NICHT in den `requirement_ids` Arrays der Tasks gelistet. **Die Traceability ist broken.** Jede Anforderung muss explizit einem Task zugeordnet sein.
|
||||
|
||||
**Kategorie C: Völlig unabgedeckt — v1 Features ohne Task und ohne Architecture (5 Features — CRITICAL)**
|
||||
|
||||
| Feature | Beschreibung | Status | Severity |
|
||||
|---------|-------------|--------|----------|
|
||||
| **F-AI-01** | KI-Copilot mit voller API-Kontrolle [v1] | ❌ KEIN Task, KEINE Architecture | **CRITICAL** |
|
||||
| **F-WF-01** | Hybrid-Workflow-Engine [v1] | ❌ KEIN Task, KEINE Architecture | **CRITICAL** |
|
||||
| F-DOC-01 | Dokumentation [v1] | ❌ KEIN Task | MAJOR |
|
||||
| F-INFRA-04 | Monitoring & Alerting [v1] | ❌ Nicht abgedeckt | MAJOR |
|
||||
| F-PERF-01 | Performance [v1] | ❌ Nicht abgedeckt | MAJOR |
|
||||
|
||||
**Detailanalyse F-AI-01 (KI-Copilot):**
|
||||
- Requirements sagen: "KI-Copilot von Anfang an einplanen" + "Architektur muss KI-Integration von vornherein unterstützen"
|
||||
- Architecture.md erwähnt nur API-First-Design (F-CORE-06), aber hat KEINE Sektion für KI-Integration
|
||||
- Task Graph hat keinen Task für KI-Copilot
|
||||
- Der Copilot benötigt API-Zugriff mit RBAC-Respektierung — die API existiert, aber kein Task implementiert die Copilot-Integration
|
||||
- **Erforderliche Aktion:** Architektur um KI-Integration-Sektion erweitern + Task für KI-Copilot API-Endpunkt hinzufügen (oder als Teil von T01/T02 als API-First-Design-Nachweis)
|
||||
|
||||
**Detailanalyse F-WF-01 (Hybrid-Workflow-Engine):**
|
||||
- Requirements sagen: "Hybrid-Ansatz: Code-Engine für Kern-Workflows + konfigurierbare Workflow-Regeln"
|
||||
- Architecture.md hat Event Bus, aber keine Workflow-Engine
|
||||
- Task Graph hat keinen Task für Workflow-Engine
|
||||
- **Erforderliche Aktion:** Architektur um Workflow-Engine-Sektion erweitern + Task hinzufügen (oder bestehenden Task erweitern)
|
||||
|
||||
#### Phantom Feature
|
||||
|
||||
| Feature | In task_graph | In requirements.md | Issue |
|
||||
|---------|--------------|-------------------|-------|
|
||||
| F-CONT-08 | ✅ T02 requirement_ids | ❌ Nicht vorhanden | Phantom — vermutlich GDPR-Delete für Contacts, das eigentlich F-COMP-08 ist (bereits gelistet) |
|
||||
|
||||
---
|
||||
|
||||
### ✅ Kriterium 4: AGENTS.md (PASS)
|
||||
|
||||
**Build/Test Commands:**
|
||||
- Backend: venv setup, uvicorn, alembic, pytest, pytest-cov, mypy, ruff ✅
|
||||
- Frontend: npm install, dev, build, vitest, tsc, eslint ✅
|
||||
- Docker Compose: build, up, logs, down, config validate ✅
|
||||
- E2E: Playwright install + test ✅
|
||||
|
||||
**Forbidden Patterns:**
|
||||
- Backend: 14 Forbidden Patterns (SQLite, Jinja2, Cross-Tenant, Plaintext Passwords, JWT, Naive Datetime, Integer IDs, Hard-Delete ohne GDPR, Manual Tenant Filter, Sync I/O, Raw SQL, Secrets in Code, Unvalidated Input, Missing Audit Log, Plugin Tables ohne tenant_id) ✅
|
||||
- Frontend: 10 Forbidden Patterns (Class Components, Inline Styles, Hardcoded Strings, Manual Fetch, Server Data in Zustand, `any` Types, Missing ARIA, Touch Targets <44px, Direct DOM, Unsafe HTML) ✅
|
||||
- Deployment: 5 Forbidden Patterns (Root in Container, Exposed DB Port, No Health Check, No Volume, Secrets in compose) ✅
|
||||
|
||||
**Task-Zuweisung:**
|
||||
- 5-Phasen-Plan mit Parallelisierung ✅
|
||||
- Task-to-Subagent Mapping (alle implementation_engineer) ✅
|
||||
- Block Rules (max 3 Tasks/Block, quality_reviewer nach Block) ✅
|
||||
- Quality Gates (Per-Task, Phase, Release) ✅
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Kriterium 5: Widersprüche architecture.md ↔ requirements.md (PARTIAL — MAJOR)
|
||||
|
||||
**Keine direkten Widersprüche** in Technologie-Entscheidungen:
|
||||
- PostgreSQL 16 ↔ F-DATA-04 ✓
|
||||
- React 18 SPA ↔ Requirements ✓
|
||||
- FastAPI Backend ↔ Requirements ✓
|
||||
- Session-based Auth ↔ F-AUTH-01/F-INT-02 ✓
|
||||
- Plugin System ↔ F-PLUGIN-01/02 ✓
|
||||
|
||||
**Aber: Gaps (Requirements fordern, Architecture schweigt):**
|
||||
- F-AI-01 fordert KI-Integration → Architecture hat keine KI-Sektion (CRITICAL)
|
||||
- F-WF-01 fordert Workflow-Engine → Architecture hat keine Workflow-Sektion (CRITICAL)
|
||||
- F-SEC-02 fordert CSP-Header → Architecture erwähnt keinen CSP-Header (MINOR)
|
||||
- F-INFRA-04 fordert Monitoring & Alerting → Architecture hat keins (MAJOR)
|
||||
- F-PERF-01 fordert Performance-Requirements → Architecture hat keine Performance-Sektion (MAJOR)
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Kriterium 6: Widersprüche task_graph.json ↔ architecture.md (PARTIAL — MINOR)
|
||||
|
||||
- Task-API-Endpoints ↔ Architecture API Design: Konsistent ✅
|
||||
- Task-DB-Models ↔ Architecture DB Schema: Konsistent ✅
|
||||
- Task-Plugin-Architecture ↔ Architecture Plugin Section: Konsistent ✅
|
||||
- Task-Dependencies ↔ Architecture Service Dependencies: Konsistent ✅
|
||||
- **F-CONT-08** in T02 requirement_ids existiert nicht in requirements.md (Phantom) — MINOR
|
||||
- `api_tokens` table in Architecture §6 erwähnt, aber nicht in DB Schema §2 — MINOR
|
||||
|
||||
---
|
||||
|
||||
### ✅ Kriterium 7: Multi-Tenant konsistent (PASS)
|
||||
|
||||
- DB Schema: Jede Tabelle hat `tenant_id UUID FK→tenants.id` ✅
|
||||
- Core: tenants, users, user_tenants, roles, sessions, companies, contacts, company_contacts, audit_log, deletion_log, notifications, password_reset_tokens, plugins ✅
|
||||
- DMS: dms_folders, dms_files, file_links, folder_permissions, file_shares, share_links ✅
|
||||
- Calendar: calendars, calendar_entries, calendar_entry_links, calendar_shares, user_calendar_visibility, subtasks, resources, resource_bookings ✅
|
||||
- Mail: mail_accounts, mail_folders, mails, mail_attachments, mail_labels, mail_label_assignments, mail_rules, mail_templates, mail_signatures, vacation_sent_log, pgp_keys, contact_pgp_keys ✅
|
||||
- Tags: tags, tag_assignments ✅
|
||||
- Architecture §5: ORM Auto-Filter via `before_query` event listener ✅
|
||||
- TenantMixin base class ✅
|
||||
- Cross-Tenant Protection: 404 (not 403) ✅
|
||||
- Plugin Tables MUST include tenant_id — migration validator enforces ✅
|
||||
- AGENTS.md Forbidden: "Plugin Tables without tenant_id" ✅
|
||||
- Task T01 acceptance criteria: "Cross-tenant access auf company → 404" ✅
|
||||
|
||||
---
|
||||
|
||||
### ✅ Kriterium 8: Plugin-System als v1-Core-Feature (PASS)
|
||||
|
||||
- Architecture §4: Vollständige Plugin-Architektur (Manifest, Lifecycle, Event Bus, DI, UI Framework) ✅
|
||||
- ADR-03: Built-in plugins with manifest-driven registration ✅
|
||||
- Task T03: Plugin System Framework (install/activate/deactivate/uninstall, migrations, UI registry) ✅
|
||||
- Tasks T04-T06: Plugin-Implementierungen (DMS+Tags, Calendar, Mail) ✅
|
||||
- Plugin Endpoints in API Design ✅
|
||||
- AGENTS.md: Plugin structure in conventions ✅
|
||||
- Nicht als Non-Goal markiert ✅
|
||||
|
||||
---
|
||||
|
||||
### ✅ Kriterium 9: PostgreSQL 16, React 18 SPA, FastAPI (PASS)
|
||||
|
||||
- Docker Compose: `postgres:16-alpine` ✅
|
||||
- Frontend Stack: React 18, Vite, React Router v6 ✅
|
||||
- Backend: FastAPI + Uvicorn ✅
|
||||
- ADR-01: PostgreSQL 16 instead of SQLite ✅
|
||||
- AGENTS.md Forbidden: ❌ SQLite, ❌ Jinja2 ✅
|
||||
- Keine SQLite-Referenzen in gesamter Architektur ✅
|
||||
- Keine Jinja2-Referenzen in gesamter Architektur ✅
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Kriterium 10: Session-based Auth + API-Token separat (PARTIAL — CRITICAL)
|
||||
|
||||
**Session-based Auth:**
|
||||
- Architecture §6: Session in `sessions` table, HttpOnly+Secure+SameSite=Strict cookie ✅
|
||||
- ADR-05: Session-based Auth instead of JWT ✅
|
||||
- AGENTS.md Forbidden: ❌ JWT Tokens ✅
|
||||
- Password hashing: bcrypt cost=12 ✅
|
||||
- CSRF: SameSite=Strict + Origin-Header-Validierung ✅
|
||||
|
||||
**⚠️ CRITICAL CONTRADICTION — Session Storage:**
|
||||
- Architecture §6 (line 1116): "Create session in `sessions` table" → PostgreSQL
|
||||
- ADR-05 (line 1437): "Server-side sessions in Redis" + "Session data in Redis for fast lookup"
|
||||
- DB Schema (lines 193-200): `sessions` table definiert mit id, user_id, tenant_id, csrf_token, expires_at
|
||||
- **Widerspruch:** Section 6 sagt PostgreSQL `sessions` table, ADR-05 sagt Redis. Es ist unklar, ob Sessions in Redis ODER PostgreSQL ODER beiden gespeichert werden.
|
||||
- **Erforderliche Aktion:** Klären und konsistent dokumentieren: Redis für Session-Lookup (fast) + PostgreSQL für Persistenz (survival)? Oder nur PostgreSQL? ADR-05 muss mit Section 6 übereinstimmen.
|
||||
|
||||
**API Tokens:**
|
||||
- Architecture §6 erwähnt `api_tokens` table (user_id, token_hash, name, scopes, expires_at) ✅
|
||||
- Markiert als "post-MVP, but architecture supports it" ✅
|
||||
- `api_tokens` table NICHT in DB Schema §2 definiert — MINOR
|
||||
- Token auth via `Authorization: Bearer <token>` ✅
|
||||
- Token respektiert RBAC und tenant isolation ✅
|
||||
|
||||
---
|
||||
|
||||
## Severity Summary
|
||||
|
||||
| Severity | Count | Details |
|
||||
|----------|-------|--------|
|
||||
| **CRITICAL** | 3 | F-AI-01 fehlt, F-WF-01 fehlt, Session-Storage-Widerspruch |
|
||||
| **MAJOR** | 5 | 19 Features ohne Traceability, F-DOC-01 fehlt, F-INFRA-04 fehlt, F-PERF-01 fehlt, F-CONT-08 Phantom |
|
||||
| **MINOR** | 3 | api_tokens table nicht in DB Schema, CSP-Header nicht erwähnt, Architecture line 1033 typo (```n) |
|
||||
| **SUGGESTION** | 1 | Feature-IDs der implizit abgedeckten Features zu task_graph hinzufügen |
|
||||
|
||||
---
|
||||
|
||||
## Findings (Strukturiert)
|
||||
|
||||
### CRITICAL-1: F-AI-01 (KI-Copilot) — V1 Feature komplett fehlt
|
||||
- **Artifact:** architecture.md, task_graph.json
|
||||
- **Location:** F-AI-01 in requirements.md ist [v1], aber kein Task und keine Architecture-Sektion
|
||||
- **Issue:** Requirements fordern KI-Copilot mit API-Kontrolle und RBAC-Respektierung. Weder Architecture.md noch Task Graph enthalten einen Task oder eine Sektion dafür.
|
||||
- **Recommendation:**
|
||||
1. Architecture.md um Sektion "KI-Integration" erweitern: API-First-Design als Grundlage, KI-Copilot API-Endpoint (`/api/v1/ai/copilot`), RBAC-Durchsetzung via bestehende Middleware
|
||||
2. Task Graph: Neuen Task T09 hinzufügen ODER T01/T02 erweitern um KI-Copilot API-Endpoint
|
||||
3. F-AI-01 zu requirement_ids des entsprechenden Tasks hinzufügen
|
||||
- **Block transition:** JA
|
||||
|
||||
### CRITICAL-2: F-WF-01 (Hybrid-Workflow-Engine) — V1 Feature komplett fehlt
|
||||
- **Artifact:** architecture.md, task_graph.json
|
||||
- **Location:** F-WF-01 in requirements.md ist [v1], aber kein Task und keine Architecture-Sektion
|
||||
- **Issue:** Requirements fordern Hybrid-Workflow-Engine (Code-Engine + konfigurierbare Regeln). Architecture hat nur Event Bus, keine Workflow-Engine.
|
||||
- **Recommendation:**
|
||||
1. Architecture.md um Sektion "Workflow Engine" erweitern: Code-basierte Kern-Workflows + konfigurierbare User-Workflows
|
||||
2. DB Schema: `workflows`, `workflow_steps`, `workflow_instances` Tabellen
|
||||
3. Task Graph: Neuen Task hinzufügen ODER bestehenden Task erweitern
|
||||
4. F-WF-01 zu requirement_ids hinzufügen
|
||||
- **Block transition:** JA
|
||||
|
||||
### CRITICAL-3: Session-Storage-Widerspruch (PostgreSQL vs Redis)
|
||||
- **Artifact:** architecture.md
|
||||
- **Location:** Section 6 (line 1116) vs ADR-05 (line 1437)
|
||||
- **Issue:** Section 6 sagt "Create session in `sessions` table" (PostgreSQL). ADR-05 sagt "Server-side sessions in Redis". DB Schema definiert `sessions` table. Unklar, wo Sessions gespeichert werden.
|
||||
- **Recommendation:**
|
||||
1. Entscheidung treffen: Redis für Session-Store (fast, mit TTL) ODER PostgreSQL `sessions` table (persistent) ODER beides (Redis für Lookup + PostgreSQL für Audit)
|
||||
2. Architecture §6 und ADR-5 konsistent machen
|
||||
3. Wenn Redis-only: `sessions` table aus DB Schema entfernen oder als Audit-Trail behalten
|
||||
4. Wenn PostgreSQL-only: ADR-05 Rationale anpassen
|
||||
- **Block transition:** JA
|
||||
|
||||
### MAJOR-1: 19 v1 Features ohne Traceability in task_graph
|
||||
- **Artifact:** task_graph.json
|
||||
- **Location:** requirement_ids arrays in allen Tasks
|
||||
- **Issue:** 19 Features sind funktional durch Tasks abgedeckt, aber ihre IDs fehlen in den requirement_ids Arrays. Traceability ist broken.
|
||||
- **Recommendation:** Füge folgende Feature-IDs zu den entsprechenden Tasks hinzu:
|
||||
- T01: F-SCHED-01, F-SEC-02, F-SEC-03, F-INFRA-03
|
||||
- T02: F-DATA-03, F-DATA-04
|
||||
- T07: F-NAV-01, F-SET-01, F-UI-01, F-UI-02, F-UI-03, F-UI-04, F-UI-05, F-UI-06, F-UI-08, F-DATA-06
|
||||
- T08: F-ENV-01, F-INFRA-02
|
||||
- Alle Tasks / übergreifend: F-TEST-01
|
||||
- **Block transition:** NEIN, aber vor Implementation beheben
|
||||
|
||||
### MAJOR-2: F-DOC-01 (Dokumentation) — V1 Feature ohne Task
|
||||
- **Artifact:** task_graph.json
|
||||
- **Issue:** F-DOC-01 fordert Dokumentation. Kein Task hat diesen Feature-ID. Architektur erwähnt `docs/admin-guide.md` aber kein Task erstellt Dokumentation.
|
||||
- **Recommendation:** T08 um Dokumentations-Task erweitern oder separaten Mini-Task für Admin-Guide + API-Docs hinzufügen
|
||||
|
||||
### MAJOR-3: F-INFRA-04 (Monitoring & Alerting) — V1 Feature nicht abgedeckt
|
||||
- **Artifact:** architecture.md, task_graph.json
|
||||
- **Issue:** Requirements fordern Monitoring & Alerting. Architecture und Task Graph enthalten keins.
|
||||
- **Recommendation:** Architecture §8 um Monitoring-Sektion erweitern (z.B. /health endpoint erweitert, Prometheus metrics, Alerting). Task T01 oder T08 um Monitoring erweitern.
|
||||
|
||||
### MAJOR-4: F-PERF-01 (Performance) — V1 Feature nicht abgedeckt
|
||||
- **Artifact:** architecture.md, task_graph.json
|
||||
- **Issue:** Requirements fordern Performance (200k Records, FTS, <2s Response). Architecture hat keine Performance-Sektion oder -Tests.
|
||||
- **Recommendation:** Architecture um Performance-Sektion erweitern (DB Indexing Strategy, Query Optimization, Pagination Limits). Task T02 um Performance-Test erweitern (200k seed + list <2s).
|
||||
|
||||
### MAJOR-5: F-CONT-08 Phantom in task_graph.json
|
||||
- **Artifact:** task_graph.json
|
||||
- **Location:** T02 requirement_ids array
|
||||
- **Issue:** F-CONT-08 existiert nicht in requirements.md. Vermutlich für GDPR-Delete von Contacts gedacht, was bereits durch F-COMP-08 abgedeckt ist.
|
||||
- **Recommendation:** F-CONT-08 aus T02 requirement_ids entfernen. Funktionalität ist bereits durch F-COMP-08 abgedeckt.
|
||||
|
||||
### MINOR-1: api_tokens table nicht in DB Schema definiert
|
||||
- **Artifact:** architecture.md
|
||||
- **Location:** Section 6 erwähnt api_tokens table, aber Section 2 (DB Schema) definiert sie nicht
|
||||
- **Recommendation:** api_tokens table in DB Schema aufnehmen (selbst wenn post-MVP)
|
||||
|
||||
### MINOR-2: CSP-Header nicht erwähnt
|
||||
- **Artifact:** architecture.md
|
||||
- **Location:** F-SEC-02 fordert CSP-Header, Architecture erwähnt keins
|
||||
- **Recommendation:** Nginx config um Content-Security-Policy Header erweitern
|
||||
|
||||
### MINOR-3: Architecture line 1033 — Typo in code fence
|
||||
- **Artifact:** architecture.md
|
||||
- **Location:** Line 1033: ````n` statt ```` `
|
||||
- **Recommendation:** `n` entfernen
|
||||
|
||||
---
|
||||
|
||||
## Next Steps (vor Phase-Übergang erforderlich)
|
||||
|
||||
1. **[CRITICAL]** Architecture.md um KI-Integration-Sektion erweitern (F-AI-01)
|
||||
2. **[CRITICAL]** Architecture.md um Workflow-Engine-Sektion erweitern (F-WF-01) + entsprechende DB-Tabellen
|
||||
3. **[CRITICAL]** Session-Storage-Widerspruch auflösen (Redis vs PostgreSQL) und Architecture §6 + ADR-05 konsistent machen
|
||||
4. **[MAJOR]** 19 implizit abgedeckte Feature-IDs zu task_graph.json requirement_ids hinzufügen
|
||||
5. **[MAJOR]** F-DOC-01, F-INFRA-04, F-PERF-01 Tasks oder Task-Erweiterungen definieren
|
||||
6. **[MAJOR]** F-CONT-08 aus task_graph.json entfernen (Phantom)
|
||||
7. **[MINOR]** api_tokens table in DB Schema aufnehmen
|
||||
8. **[MINOR]** CSP-Header in Nginx config dokumentieren
|
||||
9. **[MINOR]** Typo in architecture.md line 1033 korrigieren
|
||||
10. **[SUGGESTION]** feature_coverage_summary in task_graph.json aktualisieren nach Hinzufügen der fehlenden IDs
|
||||
|
||||
---
|
||||
|
||||
## Review-Metadata
|
||||
|
||||
- **Files reviewed:** architecture.md (1468 lines), task_graph.json (480 lines), AGENTS.md (538 lines), requirements.md (2142 lines, Referenz)
|
||||
- **Cross-check method:** Programmatische Feature-ID-Extraktion + Set-Differenz (Python regex)
|
||||
- **Review duration:** Vollständige Lektüre aller 4 Dateien
|
||||
- **Tool used:** text_editor (read), code_execution_tool (python cross-check)
|
||||
@@ -1,375 +0,0 @@
|
||||
# Requirements Review: requirements.md
|
||||
|
||||
**Datum:** 2026-06-28
|
||||
**Reviewer:** Requirements Analyst (automatisiert)
|
||||
**Datei:** `/a0/usr/workdir/dev-projects/leocrm/requirements.md`
|
||||
**Zeilen:** 2131
|
||||
**Feature-IDs:** ~141 aktive + 16 archivierte = ~157 total
|
||||
**Status der Datei:** Finalisiert — ready_for_ui (laut Header)
|
||||
|
||||
---
|
||||
|
||||
## Section 1: Konsistenz-Issues
|
||||
|
||||
### 1.1 Plugin-System vs. Core-Feature Widerspruch (CRITICAL)
|
||||
|
||||
**Der zentrale Widerspruch der Datei.**
|
||||
|
||||
**F-PLUGIN-01 (Zeile 848-851)** deklariert:
|
||||
> „Die Module sollen als Plugins realisiert sein, sodass das CRM später durch Plugins erweitert werden kann. Module (Mail, Kalender, Dateien, Tags) sind Plugins."
|
||||
|
||||
**F-PLUGIN-02 (Zeile 857-860)** definiert Plugin-Schnittstelle, Lifecycle-Hooks, Plugin-Manifest.
|
||||
|
||||
**Gleichzeitig** werden genau diese Module als detaillierte Core-Features mit konkreten HTTP-Endpunkten, DB-Schemas und Test-Szenarien spezifiziert:
|
||||
- **F-DMS-01 bis F-DMS-07 (Zeilen 991-1083):** DMS mit `POST /api/dms/folders`, `PATCH /api/dms/files/{id}`, etc.
|
||||
- **F-CAL-01 bis F-CAL-18 (Zeilen 1397-1662):** Kalender mit `POST /api/calendar/entries`, `GET /api/calendar/kanban`, etc.
|
||||
- **F-MAIL-01 bis F-MAIL-19 (Zeilen 1668-1951):** Mail mit `POST /api/mail/send`, IMAP IDLE, SMTP, PGP, etc.
|
||||
- **F-TAG-01 bis F-TAG-04 (Zeilen 1173-1223):** Tags mit `POST /api/tags/assign`, etc.
|
||||
|
||||
**Widerspruch:** Wenn Module Plugins sind, dann gehören ihre detaillierten Feature-Spezifikationen (Endpunkte, DB-Schemas, Test-Szenarien) NICHT in die Core-Requirements. Der Core definiert die Plugin-Schnittstelle; das Plugin definiert seine eigenen Features. So wie es jetzt ist, wird das Plugin-System deklariert, aber dann werden die „Plugin-Module" im Core-Requirements-Dokument detailliert spezifiziert — als wären sie Core-Features.
|
||||
|
||||
**F-CORE-01 bis F-CORE-13 (Zeilen 864-953)** definieren Core-Infrastruktur (Event Bus, Tenant-Isolation, Plugin-Migration, Service Container, API-First, Async Queue, Caching, Storage, Import/Export, PDF-Gen, Notification Service). Diese sind allesamt Architekturentscheidungen, keine Requirements.
|
||||
|
||||
**Fazit:** Die Datei versucht gleichzeitig zu sagen „ diese Module sind Plugins" UND „ diese Module sind Core-Features mit konkreten Implementierungsdetails". Das ist ein architektonischer Widerspruch, der in der Architektur-Phase aufgelöst werden muss — nicht in den Requirements.
|
||||
|
||||
### 1.2 Multi-Tenant (F-AUTH-07) vs. ältere Requirements ohne Tenant-Kontext (WARNING)
|
||||
|
||||
**F-AUTH-07 (Zeile 135-138)** deklariert Multi-Tenant als v1-Feature:
|
||||
> „Das System ist Multi-Tenant-fähig. Mehrere Firmen (Tenants) können im System verwaltet werden. Daten sind pro Tenant isoliert."
|
||||
|
||||
**F-CORE-02 (Zeile 871-874)** spezifiziert `tenant_id` auf allen Tabellen, ORM-Middleware für automatisches Query-Scoping.
|
||||
|
||||
**Annahme 1 (Zeile 1978):** „v1 ist Multi-Tenant (Multi-Company) — mehrere Firmen (Tenants) im System."
|
||||
|
||||
**Aber:** Die früher geschriebenen Requirements (F-AUTH-01 bis F-CONT-07, Zeilen 57-410) erwähnen Tenant-Kontext an keiner Stelle:
|
||||
- F-AUTH-01 (Login): kein Tenant-Bezug
|
||||
- F-AUTH-03 (User-Verwaltung): kein Tenant-Bezug — aber in Multi-Tenant muss ein User einem Tenant zugeordnet sein
|
||||
- F-COMP-01 (Firma anlegen): kein `tenant_id` in Feld-Tabelle (Zeile 156-186)
|
||||
- F-CONT-01 (Kontakt anlegen): kein `tenant_id` in Feld-Tabelle (Zeile 300-333)
|
||||
- F-COMP-05 (Pagination): kein Tenant-Filter erwähnt
|
||||
- F-COMP-06 (Suche): kein Tenant-Scoping erwähnt
|
||||
|
||||
**Fazit:** Multi-Tenant wurde später hinzugefügt und die frühen Requirements wurden nicht nachträglich aktualisiert. Das führt zu einer Lücke: Wie verhält sich F-COMP-01 (Firma anlegen) in Multi-Tenant-Kontext? Wird die Firma automatisch dem aktiven Tenant zugeordnet? Kann ein User Firmen in mehreren Tenants anlegen? Diese Fragen sind in den Requirements nicht beantwortet.
|
||||
|
||||
### 1.3 KI-Copilot (F-AI-01) mit voller API-Kontrolle vs. ältere UI-only-Flow-Requirements (WARNING)
|
||||
|
||||
**F-AI-01 (Zeile 798-806)** deklariert:
|
||||
> „Der Copilot hat Zugriff auf die volle API und soll alles steuern können — Daten abfragen, erstellen, bearbeiten, löschen, Aktionen auslösen, Workflows triggern."
|
||||
|
||||
**F-CORE-06 (Zeile 899-902)** deklariert API-First:
|
||||
> „Alle Core-Features und Plugin-Features sind primär über die API nutzbar. Die UI ist ein API-Client."
|
||||
|
||||
**Aber:** Mehrere Requirements beschreiben nur UI-Flows ohne API-Bezug:
|
||||
- F-UI-01 (Responsive Design, Zeile 495-503): nur CSS-Breakpoints, kein API-Bezug
|
||||
- F-UI-02 (i18n, Zeile 509-517): nur Frontend-Library, kein API-Bezug
|
||||
- F-UI-03 (Toast-Notifications, Zeile 523-531): nur Frontend-Komponente
|
||||
- F-UI-04 (Loading-States, Zeile 537-545): nur Frontend-State
|
||||
- F-UI-05 (Empty-States, Zeile 551-559): nur Frontend-Komponente
|
||||
- F-UI-06 (Confirmation-Dialogs, Zeile 565-573): nur Frontend-Modal
|
||||
- F-UI-08 (Datenansichten, Zeile 579-582): nur Frontend-Toggle
|
||||
|
||||
**Einschränkung:** Diese UI-Requirements sind legitimerweise UI-only — sie beschreiben Präsentationslogik, keine Datenoperationen. F-CORE-06 sollte explizit ausschließen, dass reine UI-Präsentations-Features keine API-Entpunkte benötigen. Aktuell ist die Formulierung „alle Features über API nutzbar" zu breit und suggeriert, dass auch Toast-Notifications einen API-Endpunkt haben müssten.
|
||||
|
||||
**Zusätzlicher Befund:** F-AI-01 und F-CORE-06 wurden retroaktiv hinzugefügt. Die ursprünglichen Requirements (v0.1, archiviert in Appendix A, Zeile 2089-2128) beschreiben Jinja2-Templates und SQLite — eine völlig andere Architektur. Die Datei hat also mindestens drei Evolutionsschichten:
|
||||
1. v0.1: Single-Tenant, Jinja2, SQLite (archiviert)
|
||||
2. v0.3: React SPA, PostgreSQL, RBAC (Hauptteil)
|
||||
3. v0.5+: Multi-Tenant, Plugin-System, API-First, KI-Copilot, Mail/Kalender/DMS (hinzugefügt)
|
||||
|
||||
Die Schichten wurden nicht vollständig integriert — Rückbezüge fehlen.
|
||||
|
||||
### 1.4 Auth-Mechanismus-Unschärfe (WARNING)
|
||||
|
||||
**F-AUTH-01 (Zeile 58):** „Session-basierte Auth mit HttpOnly+Secure+SameSite=Strict Cookie"
|
||||
|
||||
**F-AUTH-02 (Zeile 72-78):** Test-Szenario sagt „Token wird entfernt" und Akzeptanzkriterium sagt „Server-Token-Blacklist optional für v1" — das suggeriert Token-basierte Auth (JWT?), nicht Session-basierte Auth.
|
||||
|
||||
**F-INT-02 (Zeile 714-722):** „API-Endpunkte sind via Session-Cookie authentifiziert" aber erwähnt auch „Optional: API-Key für externe Integrationen".
|
||||
|
||||
**F-SEC-03 (Zeile 616-624):** „Session läuft nach 8h ab" — aber „Token gültig <8h" und „Token nach 8h → API gibt 401" — wieder Token-Sprache.
|
||||
|
||||
**Fazit:** Die Datei wechselt inkonsistent zwischen „Session" und „Token". Entweder es ist Session-basiert (Cookie + Server-Side Session Store) oder Token-basiert (JWT Stateless). Das muss entschieden und einheitlich formuliert werden.
|
||||
|
||||
---
|
||||
|
||||
## Section 2: Requirements vs. Bauanleitung Assessment
|
||||
|
||||
### 2.1 Enthaltene Implementierungsdetails
|
||||
|
||||
Die Datei enthält massiv Implementierungsdetails, die in eine Requirements-Spec nicht gehören:
|
||||
|
||||
#### HTTP-Endpunkte (Architektur, nicht Requirement)
|
||||
Jedes einzelne Akzeptanzkriterium spezifiziert konkrete HTTP-Endpunkte mit Pfaden, HTTP-Methoden, Query-Parametern und Response-Codes:
|
||||
- `POST /api/auth/login` (Zeile 65)
|
||||
- `GET /api/companies/{id}` (Zeile 207)
|
||||
- `DELETE /api/companies/{id}?cascade=true|false` (Zeile 235)
|
||||
- `GET /api/contacts?page=1&page_size=25&sort_by=last_name&sort_order=asc` (Zeile 396)
|
||||
- `POST /api/dms/files/upload` (Zeile 1013)
|
||||
- `GET /api/dms/files/{id}/preview` (Zeile 1041)
|
||||
- `POST /api/calendar/entries` (Zeile 1439)
|
||||
- `GET /api/calendar/kanban?period=this_week` (Zeile 1419)
|
||||
- `POST /api/mail/send` (Zeile 1693)
|
||||
- `GET /api/mail/search?q=angebot&folder=inbox` (Zeile 1709)
|
||||
- ...und dutzende weitere
|
||||
|
||||
**Problem:** Der Endpunkt-Pfad ist eine Architekturentscheidung. Ein Requirement sagt „User kann sich einloggen" — der Pfad `/api/auth/login` ist Implementierung.
|
||||
|
||||
#### DB-Schema-Definitionen (Architektur, nicht Requirement)
|
||||
- **F-COMP-01 (Zeilen 156-186):** Vollständige Feld-Tabelle mit Typen: `String(100)`, `Integer`, `Decimal`, `Picklist`, `FK→Company`, `Text(32000)`, etc. — das ist ein DB-Schema
|
||||
- **F-CONT-01 (Zeilen 300-333):** Vollständige Feld-Tabelle für Kontakte mit Typen
|
||||
- **F-COMP-07 (Zeile 277):** `audit_log` Tabellenname
|
||||
- **F-COMP-08 (Zeile 291):** `deletion_log` Tabellenname
|
||||
- **F-CONT-07 (Zeile 424):** `company_contacts` N:M-Tabellenname
|
||||
- **F-CORE-02 (Zeile 872):** `tenant_id` Feld auf allen Tabellen
|
||||
- **F-MAIL-03 (Zeile 1709):** `tsvector`-Index, `mail_body_tsv`, `mail_subject_tsv`
|
||||
- **F-CAL-12 (Zeile 1572):** `user_calendar_visibility` Tabellenname
|
||||
- **F-CAL-15 (Zeile 1614):** `assigned_to: user_id` Feldname
|
||||
|
||||
**Problem:** Feldnamen, -typen und Tabellennamen sind Implementierungsdetails, die in das DB-Schema der Architektur gehören.
|
||||
|
||||
#### Technologie-Entscheidungen (Architektur, nicht Requirement)
|
||||
- **F-CORE-07 (Zeile 907):** „Celery + Redis oder RQ + Redis" — Technologie-Wahl
|
||||
- **F-CORE-08 (Zeile 914):** „Redis als Cache-Backend" — Technologie-Wahl
|
||||
- **F-CORE-10 (Zeile 928):** „S3-kompatibles Storage (z.B. MinIO)" — Technologie-Wahl
|
||||
- **F-MAIL-02 (Zeile 1693):** „DOMPurify" — Library-Wahl
|
||||
- **F-MAIL-12 (Zeile 1846):** „python-gnupg" — Library-Wahl
|
||||
- **F-UI-02 (Zeile 517):** „react-i18next" — Library-Wahl
|
||||
- **F-DMS-04 (Zeile 1034):** „PDF.js" — Library-Wahl
|
||||
- **F-DATA-03 (Zeile 459):** „Pydantic-Schemas" — Library-Wahl
|
||||
- **F-INFRA-03 (Zeile 666):** „Python logging mit JSON-Formatter" — Library-Wahl
|
||||
|
||||
#### Protokoll-Details (Architektur, nicht Requirement)
|
||||
- **F-MAIL-01 (Zeile 1670):** „IMAP4rev1 (RFC 3501)", „IMAP IDLE (RFC 2177)"
|
||||
- **F-MAIL-02 (Zeile 1693):** „multipart/mixed", „SMTP-Versand"
|
||||
- **F-MAIL-05 (Zeile 1733):** „References- und In-Reply-To-Header (RFC 5322)"
|
||||
- **F-MAIL-18 (Zeile 1929):** „AES-256, Key via Env-Var"
|
||||
- **F-CAL-08 (Zeile 1516):** „RRULE (RFC 5545)"
|
||||
- **F-CAL-09 (Zeile 1530):** „RFC 5545 konform"
|
||||
- **F-MAIL-18 (Zeile 1929):** „IMAP MOVE (RFC 6851)"
|
||||
|
||||
#### Frontend-Komponenten-Namen (Architektur, nicht Requirement)
|
||||
- **F-CAL-01 (Zeile 1405):** `CalendarView` Komponente
|
||||
- **F-CAL-02 (Zeile 1419):** `KanbanCalendar` Komponente
|
||||
- **F-FILEUI-01 (Zeile 1321):** `FileBrowser`, `SidebarTree`, `MainView` Komponenten
|
||||
- **F-FILEUI-02 (Zeile 1335):** `Breadcrumb` Komponente
|
||||
- **F-FILEUI-03 (Zeile 1349):** `ContextMenu` Komponente
|
||||
- **F-FILEUI-04 (Zeile 1363):** Multi-Select-State in `FileBrowser`
|
||||
- **F-MAIL-05 (Zeile 1741):** `ThreadView` Komponente
|
||||
|
||||
#### Farbcodes und UI-Implementierung (Architektur, nicht Requirement)
|
||||
- **F-CAL-06 (Zeile 1485):** `{appointment+normal: "#3B82F6", task+normal: "#F59E0B", *+follow_up: "#F97316", *+private: "#9CA3AF"}` — konkrete Hex-Codes
|
||||
- **F-COMP-04 (Zeile 235):** `deleted_at = NOW` — SQL-Ausdruck
|
||||
- **F-FILEUI-02 (Zeile 1335):** „Materialized Path oder rekursive Abfrage" — DB-Pattern
|
||||
- **F-FILEUI-06 (Zeile 1391):** „HTML5 Drag & Drop API" — Browser-API
|
||||
- **F-FILEUI-05 (Zeile 1377):** „XMLHttpRequest (für Progress-Events) oder WebSocket" — Technologie
|
||||
|
||||
#### Algorithmus- und Logik-Details (Architektur, nicht Requirement)
|
||||
- **F-MAIL-07 (Zeilen 1762-1771):** Regelauswertungs-Reihenfolge, Background-Worker-Trigger
|
||||
- **F-MAIL-08 (Zeile 1786):** `vacation_sent_log`, No-Reply-Erkennung: „noreply", „no-reply", „donotreply"
|
||||
- **F-CAL-08 (Zeile 1516):** Recurrence-Instanz-Generierung, Exception-Handling
|
||||
- **F-CAL-15 (Zeile 1614):** Notification-Versand bei Zuweisung
|
||||
|
||||
### 2.2 Schätzung des Anteils
|
||||
|
||||
| Kategorie | Zeilen (geschätzt) | Anteil |
|
||||
|-----------|--------------------|--------|
|
||||
| **Genuine Requirements (das WAS)** | ~700-750 | ~35% |
|
||||
| — Projektbeschreibung, Domain Knowledge | ~25 | |
|
||||
| — Feature-Anforderung-Texte („User kann...") | ~250 | |
|
||||
| — Test-Szenarien (Verhalten, nicht Implementation) | ~300 | |
|
||||
| — Non-funktionale Anforderungen | ~20 | |
|
||||
| — Annahmen, Non-Goals, Checkliste, Open Questions | ~155 | |
|
||||
| **Architektur/Implementierung (das HOW)** | ~1380-1430 | ~65% |
|
||||
| — HTTP-Endpunkte in Akzeptanzkriterien | ~400 | |
|
||||
| — DB-Schema-Definitionen (Feld-Tabellen, Typen) | ~150 | |
|
||||
| — F-CORE-01 bis F-CORE-13 (Architekturentscheidungen) | ~100 | |
|
||||
| — F-PLUGIN-01/02 (Plugin-System-Architektur) | ~20 | |
|
||||
| — F-WF-01 (Workflow-Engine-Architektur) | ~10 | |
|
||||
| — Protokoll-Details (RFCs, IMAP, SMTP) | ~80 | |
|
||||
| — Technologie-/Library-Wahlen | ~60 | |
|
||||
| — Frontend-Komponenten-Namen | ~40 | |
|
||||
| — Farbcodes, SQL-Ausdrücke, Algorithmus-Details | ~50 | |
|
||||
| — Redundanzen (F-FILE vs F-DMS, F-SCHED vs F-CORE-07) | ~100 | |
|
||||
| — Historische/archivierte Requirements (Appendix A) | ~40 | |
|
||||
| — Formatierung, Leerzeilen, Trennlinien | ~370 | |
|
||||
|
||||
**Fazit:** Die Datei ist zu ~35% eine Requirements-Spec und zu ~65% eine Architektur-/Implementierungs-Dokumentation. Sie hat den Charakter einer Bauanleitung angenommen, nicht den einer Anforderungsspezifikation.
|
||||
|
||||
---
|
||||
|
||||
## Section 3: Empfehlung
|
||||
|
||||
### 3.1 Was in requirements.md bleiben sollte
|
||||
|
||||
**Genuine Requirements — das WAS:**
|
||||
|
||||
1. **Projektbeschreibung** (Zeilen 10-14) — Was ist das Projekt?
|
||||
2. **Domain Knowledge** (Zeilen 17-31) — Fachliche Begriffe und Referenzen
|
||||
3. **Tech-Stack-Entscheidungen** (Zeilen 34-52) — Hohe-Level-Entscheidungen (Backend, DB, Frontend, Deployment)
|
||||
4. **Feature-Anforderungstexte** — Die „Anforderung:"-Absätze jedes Features, bereinigt um Implementierungsdetails:
|
||||
- F-AUTH-01 bis F-AUTH-08: Was muss die Auth können?
|
||||
- F-COMP-01 bis F-COMP-08: Was muss Firmen-Management können?
|
||||
- F-CONT-01 bis F-CONT-07: Was muss Kontakt-Management können?
|
||||
- F-DATA-01 bis F-DATA-06: Was muss Daten-Management können?
|
||||
- F-UI-01 bis F-UI-08: Was muss die UI bieten?
|
||||
- F-SEC-01 bis F-SEC-03: Welche Sicherheitsanforderungen?
|
||||
- F-INFRA-01 bis F-INFRA-04: Welche Infrastrukturanforderungen?
|
||||
- F-MIG-01: Was muss Migration/Import können?
|
||||
- F-INT-01: Welche Integrationsanforderung?
|
||||
- F-TEST-01: Welche Test-Strategie?
|
||||
- F-ENV-01: Welche Environment-Anforderung?
|
||||
- F-DOC-01: Welche Doku-Anforderung?
|
||||
- F-PERF-01: Welche Performance-Anforderung?
|
||||
- F-SEARCH-01: Was muss die globale Suche können?
|
||||
- F-NAV-01: Welche Navigation?
|
||||
- F-SET-01: Welche Einstellungen?
|
||||
- F-DMS-01 bis F-DMS-07: Was muss DMS können? (ohne Endpunkte)
|
||||
- F-LINK-01 bis F-LINK-06: Was muss Verknüpfung können? (ohne Endpunkte)
|
||||
- F-TAG-01 bis F-TAG-04: Was muss Tagging können? (ohne Endpunkte)
|
||||
- F-PERM-01 bis F-PERM-06: Welche Berechtigungs-Requirements? (ohne Endpunkte)
|
||||
- F-FILEUI-01 bis F-FILEUI-06: Welche UI-Requirements für Datei-Browser? (ohne Komponentennamen)
|
||||
- F-CAL-01 bis F-CAL-18: Was muss Kalender können? (ohne Endpunkte, ohne Farbcodes)
|
||||
- F-MAIL-01 bis F-MAIL-19: Was muss Mail können? (ohne Protokoll-Details)
|
||||
- F-AI-01: Was muss der KI-Copilot können?
|
||||
- F-SCHED-01: Welche Background-Job-Anforderung?
|
||||
5. **Test-Szenarien** — Aber bereinigt: nur Verhalten beschreiben („User klickt X → Y passiert"), keine Implementierung („`deleted_at = NOW` gesetzt", „`tsvector`-Index")
|
||||
6. **Non-funktionale Anforderungen** (Zeilen 1957-1973) — Bleiben, aber Metriken ohne Library-Namen
|
||||
7. **Annahmen** (Zeilen 1976-1999) — Bleiben
|
||||
8. **Non-Goals** (Zeilen 2001-2046) — Bleiben
|
||||
9. **Discovery-Checkliste** (Zeilen 2049-2073) — Bleibt
|
||||
10. **Open Questions** (Zeilen 2077-2085) — Bleibt
|
||||
|
||||
### 3.2 Was nach architecture.md verschoben werden sollte
|
||||
|
||||
**Architektur/Implementierung — das HOW:**
|
||||
|
||||
1. **F-CORE-01 bis F-CORE-13 (Zeilen 864-953):** Komplett in architecture.md
|
||||
- Event Bus, Tenant-Isolation (`tenant_id`), Plugin-Migration, UI-Plugin-Framework, Service Container/DI, API-First (Endpunkt-Versionierung `/api/v1/`), Async Job Queue (Celery/Redis), Caching (Redis), Storage-Backend (S3/MinIO), Import/Export Service, PDF-Gen, Notification Service
|
||||
|
||||
2. **F-PLUGIN-01, F-PLUGIN-02 (Zeilen 848-860):** Plugin-System-Architektur → architecture.md
|
||||
- Plugin-Schnittstelle, Manifest-Format, Lifecycle-Hooks, Abhängigkeiten
|
||||
|
||||
3. **F-WF-01 (Zeile 812-815):** Workflow-Engine-Architektur → architecture.md
|
||||
- Hybrid-Ansatz, Code-Engine vs. konfigurierbare Regeln
|
||||
|
||||
4. **Alle HTTP-Endpunkt-Spezifikationen:** → architecture.md (API-Contract-Sektion)
|
||||
- `POST /api/auth/login`, `GET /api/companies/{id}`, etc.
|
||||
- Request/Response-Body-Formate
|
||||
- Query-Parameter-Spezifikationen
|
||||
- HTTP-Status-Codes
|
||||
|
||||
5. **Alle DB-Schema-Definitionen:** → architecture.md (DB-Schema-Sektion)
|
||||
- Feld-Tabellen mit Typen (F-COMP-01 Zeilen 156-186, F-CONT-01 Zeilen 300-333)
|
||||
- Tabellennamen (`audit_log`, `deletion_log`, `company_contacts`, `user_calendar_visibility`)
|
||||
- `tenant_id`-Feld-Spezifikation
|
||||
- `tsvector`-Index-Spezifikation
|
||||
|
||||
6. **Protokoll-Details:** → architecture.md
|
||||
- IMAP4rev1, IMAP IDLE, IMAP MOVE, SMTP-Auth
|
||||
- RFC 5545 (RRULE), RFC 5322 (Threading)
|
||||
- PGP-Verschlüsselung (python-gnupg)
|
||||
- DOMPurify-Sanitization
|
||||
- AES-256-Verschlüsselung für Passwörter
|
||||
|
||||
7. **Frontend-Komponenten-Architektur:** → architecture.md (Frontend-Architektur-Sektion)
|
||||
- Komponenten-Namen (`CalendarView`, `KanbanCalendar`, `FileBrowser`, `Breadcrumb`, `ContextMenu`, `ThreadView`)
|
||||
- State-Management (`Multi-Select-State`, `user_calendar_visibility`)
|
||||
- HTML5 Drag & Drop API, XMLHttpRequest
|
||||
- Materialized Path Pattern
|
||||
|
||||
8. **Farbcodes und UI-Mappings:** → architecture.md oder design-system.md
|
||||
- Hex-Codes für Kalender-Typen
|
||||
- Farb-Mapping-Logik
|
||||
|
||||
9. **Algorithmus-Details:** → architecture.md
|
||||
- Mail-Regel-Auswertung
|
||||
- Auto-Reply-Logik (No-Reply-Erkennung, `vacation_sent_log`)
|
||||
- Recurrence-Instanz-Generierung
|
||||
- Thread-Gruppierung
|
||||
|
||||
10. **F-FILE-01 bis F-FILE-04 (Zeilen 955-985):** Duplikate von F-DMS/F-PERM — entfernen oder konsolidieren
|
||||
11. **F-SCHED-01 (Zeile 784-792):** Duplikat von F-CORE-07 — konsolidieren
|
||||
12. **Appendix A: Historische Anforderungen (Zeilen 2089-2128):** In separates `changelog.md` oder entfernen
|
||||
|
||||
### 3.3 Wie die Widersprüche (Plugin vs. Core-Feature) aufgelöst werden können
|
||||
|
||||
**Option A: Module sind Core-Features (empfohlen für v1/v2)**
|
||||
- Entferne F-PLUGIN-01, F-PLUGIN-02, F-CORE-01 bis F-CORE-13 aus requirements.md
|
||||
- Module (Mail, Kalender, DMS, Tags) sind Core-Features mit Requirements
|
||||
- Plugin-System ist ein Non-Goal für v1/v2 („Plugin-System für spätere Versionen")
|
||||
- Vorteil: Konsistent, weniger Komplexität, schneller implementierbar
|
||||
- Nachteil: Weniger Erweiterbarkeit
|
||||
|
||||
**Option B: Module sind Plugins**
|
||||
- Core-Requirements definieren nur Plugin-Schnittstelle und Core-Infrastruktur
|
||||
- Plugin-Requirements (Mail, Kalender, DMS) werden in separate Plugin-Specs ausgelagert
|
||||
- Core-Requirements sagen: „Das System unterstützt Plugins. Plugin 'Mail' muss X können. Plugin 'Kalender' muss Y können."
|
||||
- Die detaillierten Feature-Spezifikationen (F-MAIL-*, F-CAL-*, F-DMS-*) wandern in Plugin-Requirements
|
||||
- Vorteil: Saubere Trennung, Erweiterbarkeit
|
||||
- Nachteil: Mehr Dokumentation, mehr Komplexität, Over-Engineering für ein Mini-CRM
|
||||
|
||||
**Empfehlung: Option A für v1/v2.**
|
||||
Ein Mini-CRM mit 10 concurrent Users braucht kein Plugin-System. Das Plugin-System ist ein Architektur-Non-Goal für v1/v2. Die Module werden als Core-Features implementiert. Wenn Erweiterbarkeit später benötigt wird, kann ein Plugin-System in v3+ hinzugefügt werden. F-PLUGIN-01, F-PLUGIN-02, F-CORE-01 bis F-CORE-13 werden zu Non-Goals.
|
||||
|
||||
---
|
||||
|
||||
## Section 4: Spezifische Konflikte (Tabelle)
|
||||
|
||||
| ID/Zeile | Issue | Severity | Vorschlag |
|
||||
|----------|-------|----------|-----------|
|
||||
| F-PLUGIN-01 (848) vs F-DMS/F-CAL/F-MAIL | Module als Plugins deklariert, aber als Core-Features mit Endpunkten/DB-Schemas spezifiziert | **critical** | Plugin-System als Non-Goal für v1/v2; Module als Core-Features deklarieren |
|
||||
| F-FILE-01-04 (955-985) vs F-DMS-01-07 (991-1083) | F-FILE und F-DMS beschreiben dasselbe Modul mit unterschiedlichen IDs. F-FILE-01 (Datei-Explorer) = F-DMS-01 (Ordner-Struktur), F-FILE-03 (PDF-Preview) = F-DMS-04, F-FILE-04 (OnlyOffice) = F-DMS-05 | **critical** | F-FILE-01 bis F-FILE-04 entfernen; durch F-DMS-Referenzen ersetzen |
|
||||
| F-FILE-03 (973) vs F-DMS-04 (1033) | Beide spezifizieren PDF-Preview im Browser — Duplikat | **critical** | F-FILE-03 entfernen; F-DMS-04 behalten (detaillierter) |
|
||||
| F-FILE-04 (982) vs F-DMS-05 (1047) | Beide spezifizieren OnlyOffice-Integration — Duplikat | **critical** | F-FILE-04 entfernen; F-DMS-05 behalten (detaillierter) |
|
||||
| F-FILE-02 (964) vs F-PERM-03/04 (1257-1279) | F-FILE-02 (Datei-Sharing) ist vereinfachte Version von F-PERM-03/04 — Redundanz | **warning** | F-FILE-02 entfernen; F-PERM-03/04 als maßgeblich deklarieren |
|
||||
| F-SCHED-01 (784) vs F-CORE-07 (906) | Beide beschreiben Background-Jobs/Async-Queue — F-SCHED-01 ist vereinfachte Version von F-CORE-07 | **warning** | F-SCHED-01 entfernen; F-CORE-07 in architecture.md verschieben; Requirement „lange Operationen als Background-Job" in requirements.md behalten |
|
||||
| F-DATA-01/02 (430-452) vs F-CORE-11 (934) | CSV/Excel-Export (F-DATA) überlappt mit Generic Import/Export Service (F-CORE-11) | **warning** | F-CORE-11 in architecture.md; F-DATA-01/02 in requirements.md behalten (das WAS); F-CORE-11 beschreibt das HOW |
|
||||
| F-AUTH-07 (135) vs F-AUTH-01-F-CONT-07 (57-410) | Multi-Tenant deklariert, aber frühe Requirements erwähnen Tenant-Kontext nicht | **warning** | Frühe Requirements um Tenant-Bezug ergänzen: „Firma wird dem aktiven Tenant zugeordnet", „Suche ist Tenant-gefiltert" |
|
||||
| F-AUTH-01 (58) vs F-AUTH-02 (72-78) | F-AUTH-01: „Session-basiert", F-AUTH-02: „Token wird entfernt", „Server-Token-Blacklist" — inkonsistente Terminologie | **warning** | Einheitlich „Session" verwenden; Token-Blacklist entfernen oder klar als Session-Invalidierung benennen |
|
||||
| F-SEC-03 (616) vs F-AUTH-01 (58) | F-SEC-03 spricht von „Token" („Token gültig <8h", „Token nach 8h → 401"), F-AUTH-01 von „Session-Cookie" | **warning** | Einheitlich Session-basiert formulieren; „Session läuft nach 8h ab" |
|
||||
| F-CORE-06 (899) vs F-UI-01-06 (495-573) | API-First („alle Features über API") vs. reinen UI-Features ohne API-Bezug (Toast, Loading-States, Empty-States) | **warning** | F-CORE-06 einschränken: „Alle Daten- und Funktions-Features über API nutzbar; reine UI-Präsentations-Features (Loading-States, Toasts) ausgenommen" |
|
||||
| F-AUTH-06 (126) vs F-AUTH-04 (98) | F-AUTH-06 (Multi-User mit Rollen) überlappt mit F-AUTH-04 (RBAC) — F-AUTH-06 ist detailliertere Version | **warning** | Zusammenführen oder F-AUTH-06 als Erweiterung von F-AUTH-04 kennzeichnen |
|
||||
| F-AUTH-08 (144) vs F-AUTH-04/06 (98-129) | F-AUTH-08 (Feld-Ebene-Granularität) erweitert F-AUTH-04/06, wird aber nicht kreuzreferenziert | **warning** | F-AUTH-08 als Unterpunkt von F-AUTH-04/06 integrieren oder explizit referenzieren |
|
||||
| F-SEARCH-01 (821) vs F-COMP-06 (255)/F-CONT-06 (402) | Globale Suche überlappt mit Firmen-/Kontakt-Suche — keine klare Abgrenzung | **warning** | F-SEARCH-01 als übergeordnete Suche deklarieren; F-COMP-06/F-CONT-06 als Modul-Suche mit Querverweis |
|
||||
| F-INT-01 (700) vs F-MAIL-02 (1683) | E-Mail-Integration für Passwort-Reset (F-INT-01) ist Subset des vollen Mail-Moduls (F-MAIL-02) | **info** | F-INT-01 als v1-Requirement behalten; F-MAIL-02 als v2-Erweiterung kennzeichnen; F-INT-01 bei F-MAIL-02 referenzieren |
|
||||
| F-CAL-10 (1536) vs Non-Goals (2028) | F-CAL-10 (Ressourcen-Booking) als „Optional für später (post-v2)" markiert, hat aber volle Test-Szenarien und Akzeptanzkriterien | **warning** | Entweder zu Non-Goals verschieben oder als v2-Feature belassen mit klarer Markierung „post-v2" |
|
||||
| F-COMP-01 Feldtabelle (156-186) | DB-Schema mit Typen (String(100), Integer, Decimal) in Requirements | **info** | Feldliste als „Felder, die erfasst werden" in requirements.md; Typen und Constraints in architecture.md |
|
||||
| F-CONT-01 Feldtabelle (300-333) | DB-Schema mit Typen in Requirements | **info** | Analog zu F-COMP-01 |
|
||||
| F-COMP-04 (235) | `deleted_at = NOW` (SQL-Ausdruck) in Akzeptanzkriterium | **info** | „Firma wird als gelöscht markiert (Soft-Delete)" — ohne SQL |
|
||||
| F-CONT-07 (424) | `company_contacts` Tabellenname in Akzeptanzkriterium | **info** | „N:M-Verknüpfung wird erstellt" — ohne Tabellennamen |
|
||||
| F-CAL-06 (1485) | Hex-Farbcodes in Akzeptanzkriterium | **info** | „Farbe wird basierend auf Typ zugeordnet" — Farbwerte in design-system.md |
|
||||
| F-CAL-08 (1516) | RRULE (RFC 5545) in Akzeptanzkriterium | **info** | „Wiederholungsmuster werden unterstützt" — RFC-Referenz in architecture.md |
|
||||
| F-MAIL-03 (1709) | `tsvector`-Index in Akzeptanzkriterium | **info** | „Volltext-Suche über alle Mails" — Index-Strategie in architecture.md |
|
||||
| F-MAIL-01 (1677) | „IMAP IDLE-Listener läuft als Background-Task" in Akzeptanzkriterium | **info** | „Neue Mails werden innerhalb von 5 Sekunden angezeigt" — Implementierung in architecture.md |
|
||||
| F-MAIL-02 (1693) | „DOMPurify" in Akzeptanzkriterium | **info** | „HTML wird sanitisiert" — Library in architecture.md |
|
||||
| F-MAIL-12 (1846) | „python-gnupg" in Akzeptanzkriterium | **info** | „PGP-Verschlüsselung wird unterstützt" — Library in architecture.md |
|
||||
| F-FILEUI-01 (1321) | `FileBrowser`, `SidebarTree`, `MainView` Komponentennamen | **info** | „Datei-Browser mit Baum-Ansicht und Hauptbereich" — Komponentennamen in architecture.md |
|
||||
| F-FILEUI-02 (1335) | „Materialized Path oder rekursive Abfrage" in Akzeptanzkriterium | **info** | „Pfad wird aus Ordner-Hierarchie generiert" — Pattern in architecture.md |
|
||||
| F-FILEUI-06 (1391) | „HTML5 Drag & Drop API" in Akzeptanzkriterium | **info** | „Drag & Drop wird unterstützt" — API in architecture.md |
|
||||
| F-FILEUI-05 (1377) | „XMLHttpRequest oder WebSocket" in Akzeptanzkriterium | **info** | „Upload-Progress wird angezeigt" — Technologie in architecture.md |
|
||||
| F-CORE-07 (907) | „Celery + Redis oder RQ + Redis" — Technologie-Wahl in Requirements | **info** | Komplett in architecture.md |
|
||||
| F-CORE-08 (914) | „Redis als Cache-Backend" — Technologie-Wahl in Requirements | **info** | Komplett in architecture.md |
|
||||
| F-CORE-10 (928) | „S3-kompatibles Storage (z.B. MinIO)" — Technologie-Wahl in Requirements | **info** | Komplett in architecture.md |
|
||||
| F-CORE-02 (872) | `tenant_id`-Feld-Spezifikation in Requirements | **info** | „Daten sind pro Tenant isoliert" — `tenant_id` in architecture.md |
|
||||
| DISCOVERY_CHECK (2131) | Behauptet `features_with_ids=127/127` — tatsächlich sind es ~141 aktive Feature-IDs | **warning** | Zählung korrigieren oder klären, welche Features gezählt wurden |
|
||||
| F-DATA-05 fehlt | Springt von F-DATA-04 (Zeile 472) zu F-DATA-06 (Zeile 481) — F-DATA-05 existiert nicht | **info** | Entweder F-DATA-05 nachtragen oder Nummerierung korrigieren |
|
||||
| F-UI-07 fehlt | Springt von F-UI-06 (Zeile 565) zu F-UI-08 (Zeile 579) — F-UI-07 existiert nicht | **info** | Entweder F-UI-07 nachtragen oder Nummerierung korrigieren |
|
||||
| F-COMP-07 (269) vs F-COMP-08 (283) | Audit-Log und DSGVO-Löschung haben überlappende Belange (beide behandeln Logging von Löschungen), Interaktion nicht dokumentiert | **info** | Klarstellen: Audit-Log = schreibende Aktionen; DSGVO-Löschung = harte Löschung inkl. Audit-Log-Einträgen, separate `deletion_log` |
|
||||
| NF-06 (1966) | Code-Struktur (`api/`, `models/`, `schemas/`, `services/`, `tests/`) in nicht-funktionaler Anforderung | **info** | In architecture.md verschieben; in requirements.md: „Code-Struktur ist klar getrennt" |
|
||||
| Appendix A (2089-2128) | Historische v0.1-Requirements mit veralteten Tech-Stack (Jinja2, SQLite, Python 3.11) | **info** | In `changelog.md` verschieben oder entfernen; verwirrend in requirements.md |
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| Gesamtzeilen | 2131 |
|
||||
| Aktive Feature-IDs | ~141 |
|
||||
| Genuine Requirements-Anteil | ~35% |
|
||||
| Architektur/Implementierungs-Anteil | ~65% |
|
||||
| Critical Issues | 4 |
|
||||
| Warning Issues | 14 |
|
||||
| Info Issues | 21 |
|
||||
| Empfehlung | Requirements bereinigen, ~65% nach architecture.md verschieben, Plugin-System als Non-Goal für v1/v2 |
|
||||
|
||||
**Urteil:** Die Datei ist eine Mischung aus Requirements-Spec und Architektur-Dokument. Sie hat den Charakter einer Bauanleitung angenommen. Für eine saubere Trennung sollten ~65% des Inhalts in architecture.md verschoben werden. Die verbleibende requirements.md sollte nur das WAS beschreiben — nicht das HOW.
|
||||
-2142
File diff suppressed because it is too large
Load Diff
@@ -1,446 +0,0 @@
|
||||
# LeoCRM Phase 2 — Security & Data Risk Review
|
||||
|
||||
**Reviewer:** Security Data Engineer (A0 Orchestrator)
|
||||
**Date:** 2026-06-28
|
||||
**Project:** leocrm
|
||||
**Phase:** Pre-Implementation (Phase 2 to Phase 3)
|
||||
**Files reviewed:** architecture.md (1939 lines), task_graph.json (v2.0.0, 13 tasks), requirements.md (2142 lines, 143 features)
|
||||
**Scope:** Security architecture, multi-tenant isolation, auth, data persistence, migration, backup/restore, plugin security, dependency risks
|
||||
|
||||
---
|
||||
|
||||
## VERDICT: APPROVED_WITH_CONCERNS
|
||||
|
||||
> **Update 2026-07-23:** Several risks have been resolved in the codebase since this review.
|
||||
> See "Resolution Status" markers below each risk.
|
||||
|
||||
### Resolution Summary (2026-07-23)
|
||||
|
||||
| Risk | Severity | Status | How resolved |
|
||||
|------|----------|--------|-------------|
|
||||
| M-01 (Brute-Force) | Major | ✅ RESOLVED | `rate_limit.py` implemented with Redis INCR+EXPIRE. Login, password-reset, general API rate limiting. |
|
||||
| M-02 (RLS) | Major | ✅ RESOLVED | Migration 0015 implements PostgreSQL RLS policies on all tenant-scoped tables. ORM filter + RLS as defense-in-depth. |
|
||||
| M-03 (Worker Tenant) | Major | ✅ RESOLVED | `worker.py` (71 lines) with tenant context propagation. ARQ jobs carry tenant_id. |
|
||||
| M-04 (Secret Rotation) | Major | ⬜ OPEN | No rotation policy documented. Planned in Phase 0.1. |
|
||||
| M-05 (CORS) | Major | ✅ RESOLVED | Explicit origin list in config.py, no wildcards. |
|
||||
| M-06 (Plugin Security) | Major | ⬜ PARTIAL | Plugin migration runner validates tenant_id. Plugin install validation planned in Phase 3.10b. |
|
||||
| M-07 (Dependency Risk) | Major | ⬜ OPEN | PyMuPDF (AGPL) → pypdf replacement planned in Phase 0.20. |
|
||||
| m-01 (Secret Key) | Minor | ⬜ OPEN | Purpose documentation planned in Phase 0.1. |
|
||||
| m-02 (2FA) | Minor | ⬜ OPEN | Post-MVP, acceptable for v1. |
|
||||
| m-03 (Tenant Switch) | Minor | ✅ RESOLVED | Tenant switch validates user_tenants membership. |
|
||||
| m-04 (CSRF Token) | Minor | ⬜ OPEN | SameSite+Origin is sufficient. Token unused. Planned cleanup in Phase 0.18. |
|
||||
| m-05 (SQL Injection) | Minor | ⬜ PARTIAL | SQLAlchemy ORM used everywhere. Unified search has f-string table names (controlled). Guideline planned in Phase 3.8. |
|
||||
| m-06 (Mail Encryption) | Minor | ⬜ OPEN | Hardcoded salt `b"leocrm-mail-salt"` — fix planned in Phase 0.19. |
|
||||
| m-07 (Backup) | Minor | ⬜ OPEN | Backup system planned in Phase 5.15. |
|
||||
| m-08 (Log Injection) | Minor | ✅ RESOLVED | structlog with JSON formatting. |
|
||||
|
||||
The architecture is well-structured with strong fundamentals (session-based auth, CSRF protection, CSP headers, RBAC with field-level permissions, audit trail). However, **7 major risks** and **8 minor risks** must be addressed before or during implementation. No critical blocking issues found, but 3 major risks (RLS gap, rate limiting, CORS) should be resolved before Phase 3 start.
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| Critical | 0 |
|
||||
| Major | 7 |
|
||||
| Minor | 8 |
|
||||
|
||||
---
|
||||
|
||||
## 1. AUTH SECURITY — Session-Based Auth + API Tokens
|
||||
|
||||
### Design Summary
|
||||
- **Session store:** Redis (`session:{id}`, TTL=8h) — primary runtime store
|
||||
- **Audit trail:** PostgreSQL `sessions` table (immutable, retains all sessions ever created)
|
||||
- **Cookie:** `leocrm_session=<id>; HttpOnly; Secure; SameSite=Strict; Path=/`
|
||||
- **Password hashing:** bcrypt cost=12
|
||||
- **API tokens:** `api_tokens` table (SHA-256 hashed, scoped, expiring) — post-MVP but architecture-ready
|
||||
- **Password reset:** Token-based, 24h expiry, hashed storage, no user enumeration, session invalidation on reset
|
||||
|
||||
### Assessment: GOOD with concerns
|
||||
|
||||
**Positive:**
|
||||
- ADR-05 decision is sound: server-side sessions avoid JWT pitfalls (token leakage, no revocation)
|
||||
- Immediate session invalidation via Redis key deletion
|
||||
- Forensic session audit trail in PostgreSQL (session validation flow checks Redis then PG audit then deny)
|
||||
- No user enumeration on login or password reset
|
||||
- Cookie flags correctly set (HttpOnly, Secure, SameSite=Strict)
|
||||
|
||||
**MAJOR RISK M-01: No brute-force protection on auth endpoints**
|
||||
- **Finding:** No account lockout, failed-attempt tracking, or rate limiting on `POST /api/v1/auth/login` or `POST /api/v1/auth/password-reset/request`
|
||||
- **Impact:** An attacker can perform unlimited password guessing attempts. bcrypt cost=12 slows each attempt (~250ms) but does not prevent distributed attacks.
|
||||
- **Requirements reference:** F-AUTH-01 has no lockout test scenario. Non-Goals section 18 explicitly excludes rate limiting from v1.
|
||||
- **Recommendation:** Add a minimal failed-attempt counter in Redis (`login_failures:{email}`, TTL=15min, threshold=10, lockout 15min) before Phase 3. This is distinct from general rate limiting and is scoped to auth only.
|
||||
|
||||
**MINOR RISK m-01: LEOCRM_SECRET_KEY purpose and rotation undefined**
|
||||
- **Finding:** `LEOCRM_SECRET_KEY=<min-32-chars>` is listed in env vars but its usage is not specified (session ID generation? cookie signing? CSRF token generation?). No rotation policy documented.
|
||||
- **Recommendation:** Document the secret's purpose in `.env.example` and define a rotation procedure in the admin guide. If used for signing session IDs, rotation invalidates all sessions (acceptable, document it).
|
||||
|
||||
**MINOR RISK m-02: No 2FA in v1**
|
||||
- **Finding:** Non-Goals section 13 explicitly excludes 2FA. Acceptable for v1 internal CRM, but should be prioritized post-MVP if exposed to internet.
|
||||
- **Recommendation:** Document as post-MVP roadmap item with priority based on exposure.
|
||||
|
||||
---
|
||||
|
||||
## 2. MULTI-TENANT ISOLATION — Row-Level Security
|
||||
|
||||
### Design Summary
|
||||
- Every table has `tenant_id` (UUID, NOT NULL on core tables)
|
||||
- ORM auto-filtering via SQLAlchemy `before_query` event listener (`do_orm_execute`)
|
||||
- `TenantMixin` base class enforces `tenant_id` column on all models
|
||||
- Cross-tenant access returns 404 (not 403) to prevent information leakage
|
||||
- Plugin tables must include `tenant_id` (validator checks)
|
||||
- Tenant switch via `POST /api/v1/auth/switch-tenant`
|
||||
|
||||
### Assessment: MODERATE RISK — needs DB-level enforcement
|
||||
|
||||
**MAJOR RISK M-02: RLS claimed but only ORM-level filtering implemented**
|
||||
- **Finding:** Architecture line 154 states "PostgreSQL 16 with Row-Level Security for tenant isolation" but the implementation (lines 1232-1240) is **exclusively ORM-level filtering** via SQLAlchemy event listener. No `CREATE POLICY`, `ENABLE ROW LEVEL SECURITY`, or `SET app.current_tenant` session variables are defined.
|
||||
- **Impact:** Any query that bypasses the ORM (raw SQL, `session.execute(text(...))`, stored procedures, Alembic migrations, ARQ worker jobs that don't set tenant context) will NOT be tenant-filtered. A single missed `_tenant_filter_disabled` flag or raw query can leak cross-tenant data.
|
||||
- **Evidence:** The `before_query` listener checks `if not _tenant_filter_disabled` — this flag must be managed carefully. Any code path that sets it without restoring is a data leak vector.
|
||||
- **Recommendation:**
|
||||
1. Implement PostgreSQL RLS policies as a **defense-in-depth** layer: `CREATE POLICY tenant_isolation ON <table> USING (tenant_id = current_setting('app.current_tenant')::uuid)`
|
||||
2. Set `app.current_tenant` at the beginning of each DB session/transaction from the authenticated session context
|
||||
3. Keep ORM filtering as the primary layer; RLS as the safety net
|
||||
4. This is a design change — should be approved before Phase 3 implementation
|
||||
|
||||
**MAJOR RISK M-03: Tenant context propagation to ARQ workers not defined**
|
||||
- **Finding:** Background jobs (exports, mail-sync, reminders, backups) run in a separate worker process. The architecture does not specify how `current_tenant_id()` is set in worker context. If a worker job operates on tenant-scoped data without setting the tenant context, the ORM filter may not apply or may apply incorrectly.
|
||||
- **Impact:** Cross-tenant data exposure in background job results (e.g., export contains data from all tenants).
|
||||
- **Recommendation:** Define and document tenant context propagation for ARQ workers: each job must carry `tenant_id` in its job context, and the worker must set `current_tenant_id()` before executing any DB queries.
|
||||
|
||||
**MINOR RISK m-03: Tenant switch does not validate user-tenant membership**
|
||||
- **Finding:** `POST /api/v1/auth/switch-tenant` updates the session's `tenant_id`. The architecture does not explicitly state that the endpoint validates the user's membership in the target tenant via `user_tenants` table.
|
||||
- **Recommendation:** Ensure the switch endpoint checks `user_tenants` membership before updating the session. Add a test case: user attempts to switch to a tenant they don't belong to then 403.
|
||||
|
||||
---
|
||||
|
||||
## 3. CSRF PROTECTION
|
||||
|
||||
### Design Summary
|
||||
- SameSite=Strict cookie (browser-level protection)
|
||||
- Origin-Header-Validierung middleware (server-side check)
|
||||
- Only GET/HEAD/OPTIONS exempt from CSRF check
|
||||
- CSRF token stored per session in Redis and PostgreSQL audit table
|
||||
|
||||
### Assessment: GOOD
|
||||
|
||||
**Positive:**
|
||||
- Two-layer CSRF protection (SameSite + Origin validation) is a solid approach
|
||||
- SameSite=Strict is the strongest browser-level CSRF defense
|
||||
- Origin validation is server-side and not bypassable by client tweaks
|
||||
- Test scenarios defined in task_graph.json: "CSRF: POST without Origin header then 403"
|
||||
|
||||
**MINOR RISK m-04: CSRF token stored but not validated in requests**
|
||||
- **Finding:** A `csrf_token` is generated and stored per session, but the architecture does not describe a mechanism where the frontend sends the token back (e.g., in a `X-CSRF-Token` header) and the backend validates it. The protection relies entirely on SameSite + Origin.
|
||||
- **Impact:** SameSite=Strict + Origin validation is sufficient for v1. The stored CSRF token appears unused.
|
||||
- **Recommendation:** Either (a) remove the csrf_token from the session model if SameSite+Origin is the chosen strategy, or (b) implement double-submit cookie pattern for defense-in-depth. Clarify in architecture.
|
||||
|
||||
---
|
||||
|
||||
## 4. INPUT VALIDATION
|
||||
|
||||
### Design Summary
|
||||
- Pydantic schemas validate all API inputs (F-DATA-03)
|
||||
- XSS protection: server-side Pydantic validation + frontend DOMPurify/escaped rendering (F-SEC-02)
|
||||
- CSP headers prevent inline script execution
|
||||
- HTML in user inputs is escaped, not rendered
|
||||
|
||||
### Assessment: GOOD
|
||||
|
||||
**Positive:**
|
||||
- Pydantic on all API inputs is FastAPI best practice
|
||||
- Server-side + client-side sanitization (defense in depth)
|
||||
- CSP header is well-configured: `script-src 'self'`, `object-src 'none'`, `base-uri 'self'`
|
||||
- XSS test scenarios defined in requirements
|
||||
|
||||
**MINOR RISK m-05: No SQL injection prevention explicitly documented**
|
||||
- **Finding:** While SQLAlchemy ORM with parameterized queries is the default, the architecture does not explicitly state a prohibition on raw SQL or string interpolation in queries.
|
||||
- **Recommendation:** Add an explicit coding guideline: no raw SQL with string interpolation; all raw queries must use parameterized `text()` with bind parameters.
|
||||
|
||||
---
|
||||
|
||||
## 5. SECRETS MANAGEMENT (F-ENV-01)
|
||||
|
||||
### Design Summary
|
||||
- `.env.example` documents all environment variables with `<secret>` placeholders
|
||||
- Secrets never in Git repo (`.gitignore` includes `.env`)
|
||||
- Missing secret env var then app fails to start with clear error (F-ENV-01 test scenario 3)
|
||||
- Secrets: POSTGRES_PASSWORD, LEOCRM_SECRET_KEY, SMTP_PASS, MAIL_ENCRYPTION_KEY, S3_SECRET_KEY, AI_API_KEY
|
||||
- Pydantic Settings for env var loading (config.py)
|
||||
|
||||
### Assessment: ADEQUATE for v1 with gaps
|
||||
|
||||
**MAJOR RISK M-04: No secret rotation policy**
|
||||
- **Finding:** No rotation procedure is defined for any secret (LEOCRM_SECRET_KEY, MAIL_ENCRYPTION_KEY, POSTGRES_PASSWORD, SMTP_PASS). F-ENV-01 only covers initial setup, not lifecycle.
|
||||
- **Impact:** If a secret is compromised, there is no documented procedure to rotate it. MAIL_ENCRYPTION_KEY rotation is especially critical — changing it without a re-encryption plan would make existing encrypted mail credentials unreadable.
|
||||
- **Recommendation:**
|
||||
1. Document rotation procedures for each secret in admin guide
|
||||
2. For MAIL_ENCRYPTION_KEY: implement key versioning (store key_id with encrypted data, support old + new key during rotation)
|
||||
3. For LEOCRM_SECRET_KEY: document that rotation invalidates all sessions (acceptable)
|
||||
4. For POSTGRES_PASSWORD: document procedure (change password, update env, restart)
|
||||
|
||||
**MINOR RISK m-06: .env file approach for production**
|
||||
- **Finding:** Docker Compose uses `env_file: .env` for all services including production on Coolify. This means secrets are stored in a plaintext file on the server.
|
||||
- **Impact:** If the host filesystem is compromised, all secrets are readable. Docker env vars are also visible via `docker inspect`.
|
||||
- **Recommendation:** For production on Coolify, use Coolify's secret/environment variable management (injects as container env vars without a file on disk). The `.env` file approach is fine for dev only. Document this split in the deployment guide.
|
||||
|
||||
---
|
||||
|
||||
## 6. DATA MIGRATION RISK (F-MIG-01)
|
||||
|
||||
### Design Summary
|
||||
- F-MIG-01: CSV import with field mapping, per-row error reporting, auto-company-detection for contact imports
|
||||
- No legacy system data migration (no ETL from external CRM systems)
|
||||
- No schema migration risk (greenfield project with Alembic)
|
||||
|
||||
### Assessment: LOW RISK — properly scoped
|
||||
|
||||
**Positive:**
|
||||
- CSV import is well-defined with field mapping and error handling
|
||||
- No complex legacy migration in v1 (correct scope decision)
|
||||
- Alembic for schema migrations is standard and reliable
|
||||
|
||||
**MAJOR RISK M-05: CSV import has no file size limit or row count validation**
|
||||
- **Finding:** F-MIG-01 test scenario imports 50 companies. No mention of maximum file size, maximum row count, or memory protection for large CSV files. A 500MB CSV with 1M rows could cause OOM or timeout.
|
||||
- **Impact:** Denial of service via large CSV upload; potential memory exhaustion.
|
||||
- **Recommendation:**
|
||||
1. Define max upload size (e.g., 10MB for CSV)
|
||||
2. Process CSV in streaming mode (not loading entire file into memory)
|
||||
3. Add row count limit (e.g., 50,000 rows per import)
|
||||
4. Run import as background job (ARQ) for files >1000 rows
|
||||
|
||||
---
|
||||
|
||||
## 7. BACKUP/RESTORE (F-INFRA-02)
|
||||
|
||||
### Design Summary
|
||||
- `pg_dump` daily cron job to backup volume or S3
|
||||
- Storage volume backup (files)
|
||||
- Restore documented in `docs/admin-guide.md`
|
||||
- Backup failure triggers alert to admin
|
||||
|
||||
### Assessment: MAJOR RISK — inadequate for multi-tenant production
|
||||
|
||||
**MAJOR RISK M-06: Backup strategy insufficient for multi-tenant PostgreSQL**
|
||||
- **Finding:** The backup design has multiple gaps:
|
||||
1. **No backup encryption:** `pg_dump` output is plaintext. Tenant data (companies, contacts, emails) is stored unencrypted in the backup volume/S3.
|
||||
2. **No retention policy:** No definition of how many backups to keep (7 days? 30 days?). Unlimited backups cause storage exhaustion; too few cause data loss.
|
||||
3. **No tested restore procedure:** F-INFRA-02 acceptance criterion says "Restore-Dokumentation vorhanden" but there is no test scenario that verifies an actual restore works.
|
||||
4. **No point-in-time recovery:** Only daily `pg_dump` snapshots. If a tenant accidentally deletes data at 14:00 and notices at 17:00, all data created between 00:00 and 14:00 that day is lost.
|
||||
5. **Multi-tenant restore granularity:** `pg_dump` is all-or-nothing. If one tenant needs restore, all tenants are affected. No mention of tenant-level export/restore.
|
||||
6. **Redis not backed up:** Session data is in Redis with TTL=8h. Redis is not included in backup strategy. If Redis is lost, all active sessions are invalidated (users must re-login). This is acceptable but should be documented.
|
||||
- **Recommendation:**
|
||||
1. Encrypt pg_dump output (gpg or S3 SSE-KMS)
|
||||
2. Define retention: 7 daily + 4 weekly + 12 monthly
|
||||
3. Add a restore test to the test suite (backup, restore, verify row count)
|
||||
4. Enable PostgreSQL WAL archiving for point-in-time recovery (PITR)
|
||||
5. Document that restore is all-tenant; consider tenant-level CSV export as a quick-recovery alternative
|
||||
6. Document Redis session loss behavior (acceptable: users re-login)
|
||||
|
||||
---
|
||||
|
||||
## 8. PLUGIN SECURITY (T03 Plugin Framework)
|
||||
|
||||
### Design Summary
|
||||
- Built-in plugins only (no dynamic external loading in v1) — ADR-03
|
||||
- Plugin manifest schema (Pydantic)
|
||||
- Lifecycle hooks: install/activate/deactivate/uninstall
|
||||
- Plugin DB migration runner with `plugin_migrations` tracking
|
||||
- Migration validator checks `tenant_id` on all plugin tables
|
||||
- Service Container DI: plugins receive db, cache, event_bus, storage, notifications
|
||||
- Event Bus integration: plugins register/unregister event listeners
|
||||
|
||||
### Assessment: MODERATE RISK — tenant isolation enforced, but no permission scoping
|
||||
|
||||
**MAJOR RISK M-07: No plugin API permission scoping**
|
||||
- **Finding:** Plugins receive injected services (db, cache, event_bus, storage, notifications) but there is no permission model restricting what a plugin can do. A plugin with access to the `db` session can query any table within the current tenant context. There is no "plugin A can only read companies, plugin B can only write to its own tables" model.
|
||||
- **Impact:** A malicious or buggy built-in plugin could access/modify data from other modules within the same tenant. Since all v1 plugins are built-in (shipped with code), this is lower risk, but the architecture should define the permission model for when external plugins are added post-MVP.
|
||||
- **Recommendation:**
|
||||
1. For v1: document that plugins are trusted (built-in only) and have full tenant-scoped access
|
||||
2. For post-MVP: define plugin permission scopes in the manifest (e.g., `permissions: ["companies:read", "contacts:write"]`)
|
||||
3. Add a test: plugin cannot access data from a different tenant (already covered by tenant_id validator)
|
||||
|
||||
**MINOR RISK m-07: Plugin event bus has no namespacing**
|
||||
- **Finding:** Plugins register event listeners on a shared event bus. There is no mention of event namespacing to prevent event name collisions between plugins.
|
||||
- **Recommendation:** Use prefixed event names (e.g., `dms.file.uploaded`, `calendar.event.created`) to avoid collisions.
|
||||
|
||||
**MINOR RISK m-08: Plugin uninstall with data removal has no confirmation audit**
|
||||
- **Finding:** `DELETE /api/v1/plugins/{name}?remove_data=true` drops plugin tables. The architecture does not mention that this destructive action is logged in the audit log.
|
||||
- **Recommendation:** Log plugin uninstall with data removal to `audit_log` with actor, timestamp, plugin name, and table list.
|
||||
|
||||
---
|
||||
|
||||
## 9. DEPENDENCY RISKS
|
||||
|
||||
### Design Summary
|
||||
- **Backend:** FastAPI, SQLAlchemy, Pydantic, ARQ, Redis-py, asyncpg/psycopg
|
||||
- **Frontend:** React 18, TanStack Query v5, Vite, Tailwind CSS
|
||||
- **Database:** PostgreSQL 16-alpine
|
||||
- **Cache/Queue:** Redis 7-alpine
|
||||
- **Document editing:** OnlyOffice Document Server
|
||||
|
||||
### Assessment: LOW-MODERATE RISK
|
||||
|
||||
**OnlyOffice `:latest` tag**
|
||||
- **Finding:** Docker Compose uses `onlyoffice/documentserver:latest`. This tag is mutable and can introduce breaking changes or security vulnerabilities without notice.
|
||||
- **Impact:** Unpredictable updates; potential breaking changes; supply chain risk.
|
||||
- **Recommendation:** Pin to a specific version tag (e.g., `onlyoffice/documentserver:8.2.2`). Update deliberately after testing.
|
||||
|
||||
**Other dependency notes:**
|
||||
- FastAPI, React 18, PostgreSQL 16, Redis 7 are all current stable major versions with active security maintenance
|
||||
- No known critical CVEs in these major versions as of 2026-06
|
||||
- **Recommendation:** Pin all dependencies in `requirements.txt` / `package.json` with exact versions or minimum patches. Add `pip-audit` and `npm audit` to CI pipeline.
|
||||
- **Recommendation:** Use `postgres:16-alpine` and `redis:7-alpine` (already specified — good). Pin minor versions for reproducibility.
|
||||
|
||||
---
|
||||
|
||||
## 10. RATE LIMITING
|
||||
|
||||
### Assessment: MAJOR RISK — explicitly excluded from v1
|
||||
|
||||
**Finding:** Non-Goals section 18: "Kein zentrales Rate-Limiting in v1." This means:
|
||||
- `POST /api/v1/auth/login` — no rate limit (brute-force possible, see M-01)
|
||||
- `POST /api/v1/auth/password-reset/request` — no rate limit (email bombing possible)
|
||||
- All API endpoints — no rate limit (DoS via excessive requests)
|
||||
- API tokens (post-MVP) — no rate limit per token
|
||||
|
||||
**Impact:**
|
||||
- Auth endpoints are brute-force vulnerable (mitigated partially by bcrypt cost=12, but not for distributed attacks)
|
||||
- Password reset endpoint can be abused to send unlimited emails (SMTP abuse, email bombing)
|
||||
- General API abuse (data scraping, DoS)
|
||||
|
||||
**Recommendation:**
|
||||
- Implement **auth-scoped rate limiting** (not general rate limiting) before Phase 3:
|
||||
- Login: 10 attempts per email per 15 min (Redis counter)
|
||||
- Password reset: 3 requests per email per hour
|
||||
- This is minimal effort and high security value
|
||||
- General API rate limiting can remain post-MVP if the app is internal-only, but document the decision
|
||||
|
||||
---
|
||||
|
||||
## 11. CORS
|
||||
|
||||
**MAJOR RISK M-08: CORS configuration not specified**
|
||||
- **Finding:** The frontend is served on port 80 (Nginx) and the API on port 8000 (FastAPI). In production, they may share a domain (reverse proxy) or be on separate ports. The architecture does not specify CORS headers.
|
||||
- **Impact:** If frontend and API are on different origins (e.g., dev environment: `localhost:80` to `localhost:8000`), the browser will block requests without proper CORS headers. If CORS is set to wildcard, credentials (cookies) will not work.
|
||||
- **Recommendation:**
|
||||
- In production: serve frontend + API behind the same reverse proxy (same origin, no CORS needed)
|
||||
- In development: configure FastAPI CORS middleware with `allow_origins=["http://localhost:80"]`, `allow_credentials=True`, `allow_methods=["*"]`, `allow_headers=["*"]`
|
||||
- Never use `allow_origins=["*"]` with `allow_credentials=True` (browser rejects this)
|
||||
- Document CORS configuration in architecture.md
|
||||
|
||||
---
|
||||
|
||||
## 12. DOCKER/COMPOSE SECURITY
|
||||
|
||||
### Findings
|
||||
|
||||
| Issue | Severity | Detail |
|
||||
|-------|----------|--------|
|
||||
| No non-root user | Minor | All containers run as root by default. Add `user:` directive or use images with non-root users. |
|
||||
| No `cap_drop: ALL` | Minor | Containers retain all Linux capabilities. Drop all and add only needed ones. |
|
||||
| No read-only filesystem | Minor | Add `read_only: true` with `tmpfs` for writable paths. |
|
||||
| Redis without auth | Major | `redis:7-alpine` has no `requirepass` or ACL configured. Any container on the network can access Redis. |
|
||||
| OnlyOffice `:latest` | Minor | Mutable tag; pin to specific version. |
|
||||
| All ports exposed | Minor | `ports: ["8000:8000"]`, `["80:80"]`, `["8080:80"]` expose to host. In production, use internal network + reverse proxy only. |
|
||||
| No health checks on all services | Minor | Only `backend` has a healthcheck. Add for `postgres`, `redis`, `worker`. |
|
||||
| No resource limits | Minor | No `mem_limit`, `cpus` limits. A runaway process can consume all host resources. |
|
||||
|
||||
**Recommendation for Redis auth:**
|
||||
- Add `REDIS_PASSWORD` env var
|
||||
- Configure Redis with `requirepass` or use ACL users
|
||||
- Update `REDIS_URL` to include password: `redis://:<password>@redis:6379/0`
|
||||
|
||||
---
|
||||
|
||||
## 13. FILE UPLOADS
|
||||
|
||||
### Finding
|
||||
DMS plugin (T05) handles file uploads via `POST /api/v1/dms/files/upload (multipart)`. The architecture does not specify:
|
||||
- Maximum file size limit
|
||||
- Allowed file types / MIME type validation
|
||||
- File content verification (magic bytes, not just extension)
|
||||
- Malware / virus scanning
|
||||
- Filename sanitization (path traversal prevention)
|
||||
|
||||
### Assessment: MAJOR RISK (deferred to plugin implementation)
|
||||
- **Impact:** Path traversal via malicious filenames, disk exhaustion via large files, stored XSS via uploaded HTML/SVG files, potential malware storage.
|
||||
- **Recommendation:** Define upload security in T05 task specification:
|
||||
1. Max file size: 50MB (configurable)
|
||||
2. Allowed MIME types whitelist (exclude `text/html`, `image/svg+xml`, `application/javascript`)
|
||||
3. Filename sanitization: strip path components, use UUID-based storage names
|
||||
4. Store files outside web root (already handled by storage service)
|
||||
5. Post-MVP: ClamAV integration for malware scanning
|
||||
|
||||
---
|
||||
|
||||
## 14. LOGGING SENSITIVE DATA
|
||||
|
||||
### Assessment: GOOD
|
||||
- Structured JSON logs (F-INFRA-03): timestamp, level, event, method, path, status, duration, tenant_id, user_id
|
||||
- No password, token, or secret values in log format
|
||||
- Log level configurable via `LOG_LEVEL` env var
|
||||
- **Recommendation:** Add explicit log sanitization in the logging middleware: filter out `password`, `new_password`, `token`, `Authorization` header fields from request body logging if request body is ever logged.
|
||||
|
||||
---
|
||||
|
||||
## 15. DATA PERSISTENCE AND DATA LOSS RISK
|
||||
|
||||
### Assessment: MODERATE RISK
|
||||
- Soft-delete with `deleted_at` column — good for accidental deletion recovery
|
||||
- DSGVO hard-delete with `deletion_log` — good for compliance
|
||||
- `deletion_log` mentioned in architecture but table schema not fully defined in the reviewed sections
|
||||
- **Risk:** Soft-deleted data is still in the database. If a tenant requests GDPR deletion, the hard-delete must also remove soft-deleted records.
|
||||
- **Recommendation:** Verify `deletion_log` table schema includes: tenant_id, entity_type, entity_id, deleted_by, deleted_at, data_summary (for audit).
|
||||
|
||||
---
|
||||
|
||||
## SUMMARY TABLE
|
||||
|
||||
| # | Risk | Severity | Domain | Action Before Phase 3? |
|
||||
|---|------|----------|--------|----------------------|
|
||||
| M-01 | No brute-force protection on auth | Major | Auth | YES — add Redis-based attempt counter |
|
||||
| M-02 | RLS claimed but ORM-only filtering | Major | Multi-Tenant | YES — add DB-level RLS as defense-in-depth |
|
||||
| M-03 | ARQ worker tenant context undefined | Major | Multi-Tenant | YES — define in architecture |
|
||||
| M-04 | No secret rotation policy | Major | Secrets | NO — document before deployment |
|
||||
| M-05 | CSV import no size/row limit | Major | Migration | NO — add in T05/T07 implementation |
|
||||
| M-06 | Backup insufficient for multi-tenant | Major | Backup | NO — resolve before deployment |
|
||||
| M-07 | No plugin API permission scoping | Major | Plugins | NO — acceptable for v1 (built-in only) |
|
||||
| M-08 | CORS not specified | Major | Network | YES — configure and document |
|
||||
| m-01 | LEOCRM_SECRET_KEY purpose/rotation undefined | Minor | Secrets | NO |
|
||||
| m-02 | No 2FA in v1 | Minor | Auth | NO (post-MVP) |
|
||||
| m-03 | Tenant switch membership validation | Minor | Multi-Tenant | YES — add test case |
|
||||
| m-04 | CSRF token stored but unused | Minor | CSRF | NO — clarify architecture |
|
||||
| m-05 | No SQL injection prevention guideline | Minor | Validation | NO — add coding guideline |
|
||||
| m-06 | .env file for production | Minor | Secrets | NO — use Coolify env management |
|
||||
| m-07 | Plugin event bus no namespacing | Minor | Plugins | NO |
|
||||
| m-08 | Plugin uninstall no audit log | Minor | Plugins | NO |
|
||||
|
||||
---
|
||||
|
||||
## TOP 3 RISKS
|
||||
|
||||
1. **M-02: RLS gap** — Architecture claims PostgreSQL RLS but implements only ORM-level tenant filtering. Raw SQL, worker jobs, or filter bypass bugs can leak cross-tenant data. **Must add DB-level RLS policies as defense-in-depth before implementation.**
|
||||
|
||||
2. **M-01: No brute-force protection** — Auth endpoints (login, password reset) have no rate limiting, lockout, or failed-attempt tracking. Combined with M-08 (no CORS config), the attack surface for credential attacks is significant. **Must add minimal Redis-based auth rate limiting before Phase 3.**
|
||||
|
||||
3. **M-06: Backup strategy inadequate** — No encryption, no retention policy, no tested restore, no PITR for multi-tenant PostgreSQL. Data loss risk for production tenants. **Must resolve before deployment phase.**
|
||||
|
||||
---
|
||||
|
||||
## RECOMMENDATION FOR PHASE 3 START
|
||||
|
||||
**APPROVED_WITH_CONCERNS — Phase 3 may start after addressing the 3 pre-implementation items:**
|
||||
|
||||
1. **M-02:** Add PostgreSQL RLS policy definitions to architecture.md (defense-in-depth alongside ORM filtering)
|
||||
2. **M-01 + Rate Limiting:** Add auth-scoped rate limiting (login attempt counter + password reset throttle) to T01 task specification
|
||||
3. **M-08:** Add CORS configuration to architecture.md (same-origin in prod, explicit origins in dev)
|
||||
|
||||
Additionally, update T01 task to include:
|
||||
- ARQ worker tenant context propagation (M-03)
|
||||
- Tenant switch membership validation test (m-03)
|
||||
- Redis auth configuration (Docker Compose)
|
||||
|
||||
The remaining major risks (M-04, M-05, M-06, M-07) can be addressed during implementation or before deployment.
|
||||
|
||||
---
|
||||
|
||||
*Review complete. No secrets, credentials, or live values were inspected. All findings based on architecture.md, task_graph.json, and requirements.md content only.*
|
||||
@@ -1,47 +0,0 @@
|
||||
# Test Report — Phase 5: Outbox DLQ, Monitoring, Consumer-Registry
|
||||
|
||||
## Date: 2026-08-02
|
||||
|
||||
## Test Execution
|
||||
|
||||
```
|
||||
cd /a0/usr/workdir/leocrm-fix && python -m pytest tests/test_outbox.py tests/test_outbox_phase5.py -v
|
||||
```
|
||||
|
||||
## Results: 18 passed, 0 failed
|
||||
|
||||
### Existing Tests (test_outbox.py) — 6/6 passed
|
||||
- test_enqueue_outbox_event_inserts_pending_row ✅
|
||||
- test_process_outbox_batch_publishes_events ✅
|
||||
- test_process_outbox_batch_empty_returns_zero ✅
|
||||
- test_process_outbox_batch_retry_on_failure ✅
|
||||
- test_process_outbox_batch_max_attempts_marks_failed ✅
|
||||
- test_enqueue_multiple_events_and_batch_size ✅
|
||||
|
||||
### Phase 5 Tests (test_outbox_phase5.py) — 12/12 passed
|
||||
- test_failed_event_has_error_message ✅ (DLQ: error_message + failed_at set)
|
||||
- test_replay_failed_event ✅ (single replay: failed→pending)
|
||||
- test_replay_failed_event_not_found ✅ (404 case)
|
||||
- test_replay_all_failed_events ✅ (bulk replay: 3 events reset)
|
||||
- test_get_outbox_stats ✅ (counts per status, total, oldest pending age)
|
||||
- test_get_outbox_stats_empty ✅ (empty tenant returns zeros)
|
||||
- test_get_failed_events ✅ (failed events with error details)
|
||||
- test_get_failed_events_pagination ✅ (limit/offset pagination)
|
||||
- test_get_consumer_registry ✅ (event_name→handler_names mapping)
|
||||
- test_outbox_deliveries_written_on_success ✅ (status='delivered')
|
||||
- test_outbox_deliveries_written_on_failure ✅ (status='failed', last_error set)
|
||||
- test_route_import ✅ (all 5 endpoints registered)
|
||||
|
||||
## Syntax Check
|
||||
```
|
||||
python -c 'import app.core.outbox; import app.routes.outbox; import app.models.outbox; import app.models.consumer_inbox; import app.models.outbox_delivery'
|
||||
→ All imports OK
|
||||
```
|
||||
|
||||
## Smoke Test
|
||||
- All 5 API endpoints registered under `/api/v1/outbox/`
|
||||
- DLQ columns (error_message, failed_at) functional in event_outbox
|
||||
- Replay functions reset failed events to pending correctly
|
||||
- outbox_deliveries entries written per-consumer during processing
|
||||
- Consumer registry reads from event_bus._handlers at runtime
|
||||
- RLS: tenant context required for all monitoring queries
|
||||
Reference in New Issue
Block a user