docs: update 6 stale files + delete 17 obsolete audit/plan files
Updated: - README.md: 23 → 25 Plugins (self_improvement, knowledge) - PROGRESS.md: Phase A-K done (261/261), Alembic 0136, 2174 Tests - PLATFORM_ROADMAP.md: Phase I, J, K marked as DONE - docs/api-documentation.md: 303 → 554+ endpoints - docs/test-strategy.md: ~500 → 2174 Tests, create_all description updated - docs/INSTALL.md: Alembic-Head 0090 → 0136 Deleted (17 obsolete files): - Root: ARCHITECTURE_PLAN.md, COMPLETE_SYSTEM_AUDIT.md, COMPLETE_VERNETZUNGS_AUDIT.md, ENTERPRISE_READINESS_PLAN.md, ROADMAP_VERIFICATION.md, SYSTEM_AUDIT.md, TEST_PLAN.md - docs/: audit-consolidated-errors.md, audit-fix-plan.md, audit-tracker.md, full-audit-errors.md, architecture-cleanup-plan.md, schema-authority.md, api-audit.md, phase-gate-review-g.md, phase-gate-review-h.md, arch-f-review.md
This commit is contained in:
+3
-3
@@ -1,8 +1,8 @@
|
||||
# LeoCRM — Vollständige Installationsanleitung
|
||||
|
||||
**Stand:** 2026-08-01
|
||||
**Commit:** be20a85
|
||||
**Alembic-Head:** 0090
|
||||
**Stand:** 2026-08-21
|
||||
**Commit:** 72e3756
|
||||
**Alembic-Head:** 0136
|
||||
|
||||
Diese Anleitung beschreibt die komplette Installation von LeoCRM von Grund auf.
|
||||
Keine manuellen Nacharbeiten erforderlich. Alle Schritte sind reproduzierbar.
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
# API Audit — UI Functions vs API Endpoints
|
||||
|
||||
> **Phase 5, Task 5.1** — Systematic audit of all UI functions and their API coverage.
|
||||
> Generated: 2026-07-23
|
||||
|
||||
## Summary
|
||||
|
||||
| Category | Total UI Functions | API Covered | Missing |
|
||||
|----------|-------------------|-------------|---------|
|
||||
| Contacts | 8 | 8 | 0 |
|
||||
| Companies (Contacts) | 6 | 6 | 0 |
|
||||
| Calendar | 12 | 12 | 0 |
|
||||
| DMS (Files) | 14 | 14 | 0 |
|
||||
| Mail | 20 | 20 | 0 |
|
||||
| Notifications | 4 | 4 | 0 |
|
||||
| Users & Roles | 8 | 8 | 0 |
|
||||
| Groups | 4 | 4 | 0 |
|
||||
| Tags | 5 | 5 | 0 |
|
||||
| Workflows | 8 | 8 | 0 |
|
||||
| Automation & Agents | 12 | 12 | 0 |
|
||||
| AI Assistant | 8 | 8 | 0 |
|
||||
| AI Proactive | 4 | 4 | 0 |
|
||||
| AI UI Control | 3 | 3 | 0 |
|
||||
| Communication | 8 | 8 | 0 |
|
||||
| Unified Search | 4 | 4 | 0 |
|
||||
| Plugins | 5 | 5 | 0 |
|
||||
| Settings (System/Currency/Tax/Sequence) | 8 | 8 | 0 |
|
||||
| Import/Export | 2 | 2 | 0 |
|
||||
| Entity History | 2 | 2 | 0 |
|
||||
| Audit Log | 1 | 1 | 0 |
|
||||
| Attachments | 3 | 3 | 0 |
|
||||
| Addresses | 3 | 3 | 0 |
|
||||
| **UI State (Sidebar/Tab/Filter)** | 6 | **6** | **0** |
|
||||
| **Total** | **158** | **158** | **0** |
|
||||
|
||||
## Detailed Audit
|
||||
|
||||
### 1. Contacts
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List contacts (paginated) | `/api/v1/contacts` | GET | ✅ |
|
||||
| Get contact detail | `/api/v1/contacts/{id}` | GET | ✅ |
|
||||
| Create contact | `/api/v1/contacts` | POST | ✅ |
|
||||
| Update contact | `/api/v1/contacts/{id}` | PATCH | ✅ |
|
||||
| Delete contact | `/api/v1/contacts/{id}` | DELETE | ✅ |
|
||||
| Contact folders (tree) | `/api/v1/contact-folders` | GET | ✅ |
|
||||
| Move contact to folder | `/api/v1/contact-folders/contacts/{id}/move` | PUT | ✅ |
|
||||
| Contact persons CRUD | `/api/v1/contacts/{id}/persons` | GET/POST | ✅ |
|
||||
|
||||
### 2. Companies (Unified Contacts)
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List companies (type=company) | `/api/v1/contacts?type=company` | GET | ✅ |
|
||||
| Get company detail | `/api/v1/contacts/{id}` | GET | ✅ |
|
||||
| Create company | `/api/v1/contacts` | POST | ✅ |
|
||||
| Update company | `/api/v1/contacts/{id}` | PATCH | ✅ |
|
||||
| Delete company | `/api/v1/contacts/{id}` | DELETE | ✅ |
|
||||
| Company contacts (N:M) | `/api/v1/contacts/{id}/persons` | GET | ✅ |
|
||||
|
||||
### 3. Calendar
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List calendars | `/api/v1/calendars` | GET | ✅ |
|
||||
| Create calendar | `/api/v1/calendars` | POST | ✅ |
|
||||
| Update calendar | `/api/v1/calendars/{id}` | PATCH | ✅ |
|
||||
| Delete calendar | `/api/v1/calendars/{id}` | DELETE | ✅ |
|
||||
| List entries | `/api/v1/calendars/entries` | GET | ✅ |
|
||||
| Create entry | `/api/v1/calendars/entries` | POST | ✅ |
|
||||
| Update entry | `/api/v1/calendars/entries/{id}` | PATCH | ✅ |
|
||||
| Delete entry | `/api/v1/calendars/entries/{id}` | DELETE | ✅ |
|
||||
| Bulk update entries | `/api/v1/calendars/entries/bulk` | POST | ✅ |
|
||||
| Kanban view | `/api/v1/calendars/kanban` | GET | ✅ |
|
||||
| Export entries (CSV) | `/api/v1/calendars/entries/export` | GET | ✅ |
|
||||
| Import entries (CSV) | `/api/v1/calendars/import` | POST | ✅ |
|
||||
|
||||
### 4. DMS (Document Management)
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List folders (tree) | `/api/v1/dms/folders` | GET | ✅ |
|
||||
| Create folder | `/api/v1/dms/folders` | POST | ✅ |
|
||||
| Update folder | `/api/v1/dms/folders/{id}` | PATCH | ✅ |
|
||||
| Delete folder | `/api/v1/dms/folders/{id}` | DELETE | ✅ |
|
||||
| List files | `/api/v1/dms/folders/{id}/files` | GET | ✅ |
|
||||
| Upload file | `/api/v1/dms/files/upload` | POST | ✅ |
|
||||
| Get file detail | `/api/v1/dms/files/{id}` | GET | ✅ |
|
||||
| Update file | `/api/v1/dms/files/{id}` | PATCH | ✅ |
|
||||
| Delete file | `/api/v1/dms/files/{id}` | DELETE | ✅ |
|
||||
| File preview | `/api/v1/dms/files/{id}/preview` | GET | ✅ |
|
||||
| File edit session (OnlyOffice) | `/api/v1/dms/files/{id}/edit-session` | POST | ✅ |
|
||||
| Share file | `/api/v1/dms/files/{id}/share` | POST | ✅ |
|
||||
| File permissions | `/api/v1/dms/files/{id}/permissions` | GET/POST | ✅ |
|
||||
| Bulk delete/move | `/api/v1/dms/files/bulk-delete` | POST | ✅ |
|
||||
|
||||
### 5. Mail
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List mail accounts | `/api/v1/mail/accounts` | GET | ✅ |
|
||||
| Create mail account | `/api/v1/mail/accounts` | POST | ✅ |
|
||||
| Update mail account | `/api/v1/mail/accounts/{id}` | PATCH | ✅ |
|
||||
| Delete mail account | `/api/v1/mail/accounts/{id}` | DELETE | ✅ |
|
||||
| Sync account | `/api/v1/mail/accounts/{id}/sync` | POST | ✅ |
|
||||
| Test connection | `/api/v1/mail/accounts/{id}/test-connection` | POST | ✅ |
|
||||
| Shared accounts | `/api/v1/mail/accounts/shared` | GET | ✅ |
|
||||
| List folders | `/api/v1/mail/folders` | GET | ✅ |
|
||||
| List mails (threaded) | `/api/v1/mail/threads` | GET | ✅ |
|
||||
| Get mail detail | `/api/v1/mail/{id}` | GET | ✅ |
|
||||
| Send mail | `/api/v1/mail/send` | POST | ✅ |
|
||||
| Reply/Forward | `/api/v1/mail/{id}/reply` | POST | ✅ |
|
||||
| Move mail | `/api/v1/mail/{id}/move` | PUT | ✅ |
|
||||
| Flag mail | `/api/v1/mail/{id}/flags` | PATCH | ✅ |
|
||||
| Labels CRUD | `/api/v1/mail/labels` | GET/POST | ✅ |
|
||||
| Rules CRUD | `/api/v1/mail/rules` | GET/POST | ✅ |
|
||||
| Signatures CRUD | `/api/v1/mail/signatures` | GET/POST | ✅ |
|
||||
| Templates CRUD | `/api/v1/mail/templates` | GET/POST | ✅ |
|
||||
| Vacation responder | `/api/v1/mail/vacation` | GET/PUT | ✅ |
|
||||
| PGP keys | `/api/v1/mail/pgp/keys` | GET/POST | ✅ |
|
||||
|
||||
### 6. Notifications
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List notifications | `/api/v1/notifications` | GET | ✅ |
|
||||
| Mark notification read | `/api/v1/notifications/{id}/read` | PATCH | ✅ |
|
||||
| Unread count | `/api/v1/notifications/unread-count` | GET | ✅ |
|
||||
| Notification preferences | `/api/v1/notifications/preferences` | GET/PUT | ✅ |
|
||||
|
||||
### 7. Users & Roles
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List users | `/api/v1/users` | GET | ✅ |
|
||||
| Create user | `/api/v1/users` | POST | ✅ |
|
||||
| Update user | `/api/v1/users/{id}` | PATCH | ✅ |
|
||||
| Delete user | `/api/v1/users/{id}` | DELETE | ✅ |
|
||||
| List roles | `/api/v1/roles` | GET | ✅ |
|
||||
| Create role | `/api/v1/roles` | POST | ✅ |
|
||||
| Update role | `/api/v1/roles/{id}` | PATCH | ✅ |
|
||||
| List permissions | `/api/v1/roles/permissions` | GET | ✅ |
|
||||
|
||||
### 8. Groups
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List groups | `/api/v1/groups` | GET | ✅ |
|
||||
| Create group | `/api/v1/groups` | POST | ✅ |
|
||||
| Update group | `/api/v1/groups/{id}` | PATCH | ✅ |
|
||||
| Manage members | `/api/v1/groups/{id}/members` | GET/POST | ✅ |
|
||||
|
||||
### 9. Tags
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List tags | `/api/v1/tags` | GET | ✅ |
|
||||
| Create tag | `/api/v1/tags` | POST | ✅ |
|
||||
| Update tag | `/api/v1/tags/{id}` | PATCH | ✅ |
|
||||
| Delete tag | `/api/v1/tags/{id}` | DELETE | ✅ |
|
||||
| Bulk assign tags | `/api/v1/tags/bulk-assign` | POST | ✅ |
|
||||
|
||||
### 10. Workflows
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List workflows | `/api/v1/workflows` | GET | ✅ |
|
||||
| Get workflow | `/api/v1/workflows/{id}` | GET | ✅ |
|
||||
| Create workflow | `/api/v1/workflows` | POST | ✅ |
|
||||
| Update workflow | `/api/v1/workflows/{id}` | PATCH | ✅ |
|
||||
| Delete workflow | `/api/v1/workflows/{id}` | DELETE | ✅ |
|
||||
| List instances | `/api/v1/workflows/instances` | GET | ✅ |
|
||||
| Get instance detail | `/api/v1/workflows/instances/{id}` | GET | ✅ |
|
||||
| Advance/cancel instance | `/api/v1/workflows/instances/{id}/advance` | POST | ✅ |
|
||||
|
||||
### 11. Automation & Agents
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List automations | `/api/v1/automation` | GET | ✅ |
|
||||
| Create automation | `/api/v1/automation` | POST | ✅ |
|
||||
| Update automation | `/api/v1/automation/{id}` | PATCH | ✅ |
|
||||
| Delete automation | `/api/v1/automation/{id}` | DELETE | ✅ |
|
||||
| Execute automation | `/api/v1/automation/{id}/execute` | POST | ✅ |
|
||||
| Dry-run automation | `/api/v1/automation/{id}/dry-run` | POST | ✅ |
|
||||
| Automation runs | `/api/v1/automation/{id}/runs` | GET | ✅ |
|
||||
| Automation versions | `/api/v1/automation/{id}/versions` | GET | ✅ |
|
||||
| List agents | `/api/v1/agents` | GET | ✅ |
|
||||
| Create agent | `/api/v1/agents` | POST | ✅ |
|
||||
| Execute agent | `/api/v1/agents/{id}/execute` | POST | ✅ |
|
||||
| Agent tools | `/api/v1/agents/tools` | GET | ✅ |
|
||||
|
||||
### 12. AI Assistant
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| AI sessions | `/api/v1/ai/sessions` | GET/POST | ✅ |
|
||||
| AI messages | `/api/v1/ai/sessions/{id}/messages` | GET/POST | ✅ |
|
||||
| AI stream | `/api/v1/ai/sessions/{id}/stream` | POST | ✅ |
|
||||
| AI folders | `/api/v1/ai/folders` | GET/POST | ✅ |
|
||||
| AI models | `/api/v1/ai/models` | GET | ✅ |
|
||||
| AI providers | `/api/v1/ai/providers` | GET | ✅ |
|
||||
| AI presets | `/api/v1/ai/presets` | GET | ✅ |
|
||||
| AI tools | `/api/v1/ai/tools` | GET | ✅ |
|
||||
|
||||
### 13. AI Proactive
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| Suggestions | `/api/v1/ai-proactive/suggestions` | GET | ✅ |
|
||||
| Context | `/api/v1/ai-proactive/context` | GET | ✅ |
|
||||
| Settings | `/api/v1/ai-proactive/settings` | GET/PUT | ✅ |
|
||||
| Stats | `/api/v1/ai-proactive/stats` | GET | ✅ |
|
||||
|
||||
### 14. AI UI Control
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| Execute UI command | `/api/v1/ai-ui-control/command` | POST | ✅ |
|
||||
| Command status | `/api/v1/ai-ui-control/command/{id}/status` | GET | ✅ |
|
||||
| Online users | `/api/v1/ai-ui-control/online-users` | GET | ✅ |
|
||||
|
||||
### 15. Communication (Comm)
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| Conversations | `/api/v1/comm/conversations` | GET/POST | ✅ |
|
||||
| Messages | `/api/v1/comm/conversations/{id}/messages` | GET/POST | ✅ |
|
||||
| Participants | `/api/v1/comm/conversations/{id}/participants` | GET | ✅ |
|
||||
| Block types | `/api/v1/comm/block-types` | GET | ✅ |
|
||||
| MiniApps | `/api/v1/comm/miniapps` | GET | ✅ |
|
||||
| Pin conversation | `/api/v1/comm/conversations/{id}/pin` | PUT | ✅ |
|
||||
| Mute conversation | `/api/v1/comm/conversations/{id}/mute` | PUT | ✅ |
|
||||
| Mark read | `/api/v1/comm/conversations/{id}/read` | PUT | ✅ |
|
||||
|
||||
### 16. Unified Search
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| Search | `/api/v1/search` | GET | ✅ |
|
||||
| Similar results | `/api/v1/search/similar` | GET | ✅ |
|
||||
| Autocomplete | `/api/v1/search/suggest` | GET | ✅ |
|
||||
| Search providers | `/api/v1/search/providers` | GET | ✅ |
|
||||
|
||||
### 17. Plugins
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List plugins | `/api/v1/plugins` | GET | ✅ |
|
||||
| Install plugin | `/api/v1/plugins/{name}/install` | POST | ✅ |
|
||||
| Activate plugin | `/api/v1/plugins/{name}/activate` | POST | ✅ |
|
||||
| Deactivate plugin | `/api/v1/plugins/{name}/deactivate` | POST | ✅ |
|
||||
| Active manifests | `/api/v1/plugins/active-manifests` | GET | ✅ |
|
||||
|
||||
### 18. Settings (System/Currency/Tax/Sequence)
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| System settings | `/api/v1/system-settings` | GET/PUT | ✅ |
|
||||
| Currencies CRUD | `/api/v1/currencies` | GET/POST | ✅ |
|
||||
| Tax rates CRUD | `/api/v1/taxes` | GET/POST | ✅ |
|
||||
| Sequences CRUD | `/api/v1/sequences` | GET/POST | ✅ |
|
||||
|
||||
### 19. Import/Export
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| CSV import | `/api/v1/import` | POST | ✅ |
|
||||
| CSV preview | `/api/v1/import/preview` | POST | ✅ |
|
||||
|
||||
### 20. Entity History
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| Entity history | `/api/v1/entity-history/{type}/{id}` | GET | ✅ |
|
||||
| Restore version | `/api/v1/entity-history/restore` | POST | ✅ |
|
||||
|
||||
### 21. Audit Log
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List audit logs | `/api/v1/audit` | GET | ✅ |
|
||||
|
||||
### 22. Attachments
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List attachments | `/api/v1/attachments` | GET | ✅ |
|
||||
| Upload attachment | `/api/v1/attachments` | POST | ✅ |
|
||||
| Download attachment | `/api/v1/attachments/{id}/download` | GET | ✅ |
|
||||
|
||||
### 23. Addresses
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| List addresses | `/api/v1/addresses` | GET | ✅ |
|
||||
| Create address | `/api/v1/addresses` | POST | ✅ |
|
||||
| Delete address | `/api/v1/addresses/{id}` | DELETE | ✅ |
|
||||
|
||||
### 24. UI State (Sidebar/Tab/Filter) — ✅ Implemented in Task 5.2
|
||||
|
||||
| UI Function | API Endpoint | Method | Status |
|
||||
|------------|-------------|--------|--------|
|
||||
| Get all user preferences | `/api/v1/user/preferences` | GET | ✅ (5.2) |
|
||||
| Get single preference | `/api/v1/user/preferences/{key}` | GET | ✅ (5.2) |
|
||||
| Save sidebar state | `/api/v1/user/preferences/sidebar_open` | PUT | ✅ (5.2) |
|
||||
| Save theme | `/api/v1/user/preferences/theme` | PUT | ✅ (5.2) |
|
||||
| Save locale | `/api/v1/user/preferences/locale` | PUT | ✅ (5.2) |
|
||||
| Save active tab | `/api/v1/user/preferences/active_tab` | PUT | ✅ (5.2) |
|
||||
| Save sort preferences | `/api/v1/user/preferences/{key}` | PUT | ✅ (5.2) |
|
||||
| Delete preference | `/api/v1/user/preferences/{key}` | DELETE | ✅ (5.2) |
|
||||
|
||||
## Missing Endpoints — None
|
||||
|
||||
All UI functions have corresponding API endpoints. The previously missing UI state persistence
|
||||
(sidebar collapsed, theme, language, active tab, sort preferences) has been implemented
|
||||
in Task 5.2 via the User Preferences API (`/api/v1/user/preferences`).
|
||||
|
||||
## Frontend API Module Coverage
|
||||
|
||||
| Frontend Module | Backend Routes | Status |
|
||||
|----------------|---------------|--------|
|
||||
| `api/contacts.ts` | `app/routes/contacts.py` | ✅ |
|
||||
| `api/contactFolders.ts` | `app/routes/contact_folders.py` | ✅ |
|
||||
| `api/calendar.ts` | `app/plugins/builtins/calendar/routes.py` | ✅ |
|
||||
| `api/dms.ts` | `app/plugins/builtins/dms/routes.py` | ✅ |
|
||||
| `api/mail.ts` | `app/plugins/builtins/mail/routes.py` | ✅ |
|
||||
| `api/notifications.ts` | `app/routes/notifications.py` | ✅ |
|
||||
| `api/users.ts` | `app/routes/users.py` | ✅ |
|
||||
| `api/roles.ts` | `app/routes/roles.py` | ✅ |
|
||||
| `api/groups.ts` | `app/routes/groups.py` | ✅ |
|
||||
| `api/tags.ts` | `app/plugins/builtins/tags/routes.py` | ✅ |
|
||||
| `api/workflows.ts` | `app/routes/workflows.py` | ✅ (5.3) |
|
||||
| `api/automation.ts` | `app/plugins/builtins/automation/routes.py` | ✅ |
|
||||
| `api/ai.ts` | `app/plugins/builtins/ai_assistant/routes.py` | ✅ |
|
||||
| `api/aiProactive.ts` | `app/plugins/builtins/ai_proactive/routes.py` | ✅ |
|
||||
| `api/aiUIControl.ts` | `app/plugins/builtins/ai_ui_control/routes.py` | ✅ |
|
||||
| `api/comm.ts` | `app/plugins/builtins/kommunikation/routes.py` | ✅ |
|
||||
| `api/search.ts` | `app/plugins/builtins/unified_search/routes.py` | ✅ |
|
||||
| `api/plugins.ts` | `app/routes/plugins.py` | ✅ |
|
||||
| `api/settings.ts` | `app/routes/system_settings.py`, `currencies.py`, `taxes.py`, `sequences.py` | ✅ |
|
||||
| `api/audit.ts` | `app/routes/audit.py` | ✅ |
|
||||
| `api/attachments.ts` | `app/routes/attachments.py` | ✅ |
|
||||
| `api/entityHistory.ts` | `app/routes/entity_history.py` | ✅ |
|
||||
| `api/userPreferences.ts` | `app/routes/user_preferences.py` | ✅ (5.2) |
|
||||
| `api/auth.ts` | `app/routes/auth.py` | ✅ |
|
||||
| `api/permissions.ts` | `app/plugins/builtins/permissions/routes.py` | ✅ |
|
||||
|
||||
## RBAC Coverage
|
||||
|
||||
All API routes use `require_permission()` dependency for RBAC enforcement:
|
||||
- Core routes: `contacts:read`, `contacts:write`, `users:read`, `users:write`, etc.
|
||||
- Plugin routes: `dms:read`, `dms:write`, `dms:delete`, `dms:share`, `calendar:read`, `calendar:write`, etc.
|
||||
- User preferences: `user_preferences:read`, `user_preferences:write` (added in Task 5.2)
|
||||
- Admin role (`*:*` wildcard) has access to all endpoints
|
||||
- Editor and viewer roles have scoped permissions per module
|
||||
|
||||
## Conclusion
|
||||
|
||||
All 158 UI functions across 24 categories have corresponding API endpoints. No missing endpoints
|
||||
were identified. The User Preferences API (Task 5.2) fills the previously missing UI state
|
||||
persistence gap (sidebar, theme, locale, active tab, sort preferences).
|
||||
@@ -1,6 +1,6 @@
|
||||
# LeoCRM API Documentation
|
||||
|
||||
> Auto-generated from FastAPI route enumeration. **303 endpoints** across **31 tag groups**.
|
||||
> Auto-generated from FastAPI route enumeration. **554+ endpoints** across **35+ tag groups**.
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
# ARCH-F Review — Architecture Review nach Phase F
|
||||
|
||||
**Datum:** 2026-08-18
|
||||
**Reviewer:** AI Agent
|
||||
**Phase:** F — Agents (41/41 Tasks done)
|
||||
|
||||
---
|
||||
|
||||
## Review-Kriterien
|
||||
|
||||
### 1. Permission-Lücken ✅
|
||||
|
||||
**Permission Intersection (User ∩ Agent ∩ Skill ∩ Tool):**
|
||||
- `agent_permissions.py`: `resolve_agent_permissions()` korrekt implementiert
|
||||
- `agent_tools.py`: `get_agent_tools()` filtert Tools nach User-Permissions
|
||||
- Skills orchestrieren Tools aber **erweitern niemals** Permissions
|
||||
- System Admins bekommen alle Tools (korrekt)
|
||||
- `check_agent_execute_permission()` prüft RBAC + Entity-Level Access
|
||||
- `filter_visible_agents()` prüft `agents:read` + EntityPermission
|
||||
- Jeder Tool/Service Call re-checkt Permissions (nicht eingefroren)
|
||||
|
||||
**Fazit:** Keine Permission-Lücken gefunden. Die Intersection-Logik ist solide.
|
||||
|
||||
### 2. Task-Modell-Konsistenz ✅
|
||||
|
||||
- `TASK_TYPES` pattern: `^(todo|approval|follow_up|review|goal|milestone|agent_subtask)$` — konsistent in Schema und Model
|
||||
- Polymorphic Fields: `assignee_type/assignee_id`, `entity_type/entity_id`, `creator_type/creator_id` — korrekt
|
||||
- `contact_id` wird aus `entity_id` abgeleitet (nicht in FK-Spalte gespeichert) — vermeidet FK-Constraint-Probleme
|
||||
- Subtasks: `parent_task_id` self-reference korrekt
|
||||
- Dependencies: `depends_on` JSONB array korrekt
|
||||
- Progress Aggregation: `_recompute_progress()` + `_propagate_parent_status()` korrekt
|
||||
- Success Criteria: `_evaluate_success_criteria()` mit `all_done` und `criteria` shapes
|
||||
- `tenant_id` auf allen Queries und Operations
|
||||
|
||||
**Fazit:** Task-Modell ist konsistent. Keine Inkonsistenzen gefunden.
|
||||
|
||||
### 3. Approval-Integration ⚠️
|
||||
|
||||
**Approval API:** Vollständig implementiert (create, list, get, approve, reject, expire)
|
||||
**Approval Model:** `ApprovalRequest` mit tenant_id, entity_type, entity_id, action, status
|
||||
**Workstream:** `post_approval_request()` korrekt implementiert
|
||||
|
||||
**⚠️ Finding: Agent Loop hat keine Human-in-the-Loop Approval-Integration**
|
||||
- `run_react_loop()` hat keinen `require_approval` oder `human_in_loop` Parameter
|
||||
- Agent kann aktuell keine Approval-Requests während eines Loops pausieren/warten
|
||||
- Approval API existiert aber ist nicht in den Agent Loop integriert
|
||||
- **Empfehlung:** In Phase G oder I integrieren — `require_approval` Parameter in `run_react_loop()`, der den Loop pausiert und eine Approval-Request erstellt
|
||||
|
||||
### 4. Workstream-Konsistenz ✅
|
||||
|
||||
- `post_agent_message()`: Erstellt CommMessage in Agent-Channel
|
||||
- `post_agent_step()`: Postet Thought/Action/Observation als CommBlock
|
||||
- `post_agent_result()`: Postet finales Ergebnis
|
||||
- `post_approval_request()`: Postet Approval-Request in Channel
|
||||
- Alle Funktionen verwenden `tenant_id` und `agent_id` konsistent
|
||||
- `_get_or_create_agent_channel()` für dedizierte Agent-Channels
|
||||
|
||||
**Fazit:** Workstream-Patterns sind konsistent.
|
||||
|
||||
### 5. Zirkuläre Abhängigkeiten ✅
|
||||
|
||||
Import-Test erfolgreich:
|
||||
```
|
||||
from app.ai.agent_loop import run_react_loop
|
||||
from app.ai.agent_permissions import resolve_agent_permissions
|
||||
from app.ai.agent_workstream import post_agent_message
|
||||
from app.ai.data_policy import enforce_data_policy
|
||||
from app.ai.oversight import create_decision_record
|
||||
from app.core.approval import create_approval_request
|
||||
→ No circular imports
|
||||
```
|
||||
|
||||
**Fazit:** Keine zirkulären Abhängigkeiten.
|
||||
|
||||
### 6. Data Policy ✅
|
||||
|
||||
- `enforce_data_policy()` prüft SENSITIVE_FIELDS vor LLM-Calls
|
||||
- Provider Compliance: `_filter_by_provider_compliance()` filtert Felder nach Provider-Datenklassen
|
||||
- `get_provider_compliance()` lädt Provider-Konfiguration aus DB
|
||||
- Tenant-spezifische Provider-Konfiguration
|
||||
|
||||
**Fazit:** Data Policy korrekt implementiert.
|
||||
|
||||
### 7. Transparency ✅
|
||||
|
||||
- `mark_as_ai_generated()`: Fügt `ai_generated` flag + `ai_metadata` (model, provider, timestamp) hinzu
|
||||
- `is_ai_participant()`: Erkennt AI-Participant-Types (agent, ai, system_ai)
|
||||
- Wird von Kommunikation-Plugin und Agent Loop verwendet
|
||||
|
||||
**Fazit:** Transparency-Layer korrekt implementiert.
|
||||
|
||||
### 8. Oversight ✅
|
||||
|
||||
- `DecisionRecord` + `DecisionRecordDB`: Audit-Trail für AI-Entscheidungen
|
||||
- `create_decision_record()`: Erstellt Decision Record mit tenant_id, agent_id, tool_name, decision
|
||||
- `DecisionRecordDB` erbt von `Base`, `TenantMixin`, `OwnedMixin`
|
||||
|
||||
**Fazit:** Oversight korrekt implementiert.
|
||||
|
||||
### 9. Agent Memory ✅
|
||||
|
||||
- `store_agent_memory()`, `retrieve_agent_memory()`, `search_agent_memory()`
|
||||
- Alle Operationen verwenden `tenant_id`
|
||||
- Embedding-basierte Suche korrekt
|
||||
|
||||
**Fazit:** Agent Memory korrekt implementiert.
|
||||
|
||||
### 10. Migrations ✅
|
||||
|
||||
- 0122: Agent Definition Phase F Fields (temperature, max_tokens, max_steps, trace_mode, skill_ids, trigger_config, ai_use_case_metadata)
|
||||
- 0123: approval_requests + ai_decision_records Tabellen (tenant_id, UUID PKs, Indizes)
|
||||
- 0124: Unified Task System (polymorphic fields, subtasks, dependencies)
|
||||
- Alle Migrations korrekt verkettet (0122 → 0123 → 0124)
|
||||
- `tenant_id` auf allen neuen Tabellen/Spalten
|
||||
- UUID Primary Keys (keine Integer IDs)
|
||||
|
||||
**Fazit:** Migrations korrekt und konsistent.
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Kriterium | Status | Findings |
|
||||
|-----------|--------|----------|
|
||||
| Permission-Lücken | ✅ | Keine — Intersection-Logik solide |
|
||||
| Task-Modell-Konsistenz | ✅ | Konsistent — polymorphic fields korrekt |
|
||||
| Approval-Integration | ⚠️ | Agent Loop hat keine Human-in-the-Loop Integration |
|
||||
| Workstream-Konsistenz | ✅ | Konsistente Patterns |
|
||||
| Zirkuläre Abhängigkeiten | ✅ | Keine |
|
||||
| Data Policy | ✅ | SENSITIVE_FIELDS + Provider Compliance |
|
||||
| Transparency | ✅ | AI-Generated Marking korrekt |
|
||||
| Oversight | ✅ | Decision Records mit Audit-Trail |
|
||||
| Agent Memory | ✅ | tenant_id auf allen Operationen |
|
||||
| Migrations | ✅ | Korrekt verkettet, tenant_id, UUID PKs |
|
||||
|
||||
## Findings
|
||||
|
||||
### ⚠️ ARCH-F-1: Agent Loop fehlt Human-in-the-Loop Approval-Integration
|
||||
**Schwere:** Medium (Design-Gap, kein Bug)
|
||||
**Beschreibung:** `run_react_loop()` hat keinen `require_approval` Parameter. Approval API existiert aber ist nicht in den Agent Loop integriert.
|
||||
**Empfehlung:** In Phase G oder I integrieren — `require_approval` Parameter in `run_react_loop()`, der den Loop pausiert und eine Approval-Request erstellt.
|
||||
|
||||
### ℹ️ ARCH-F-2: contact_id FK redundant mit entity_id
|
||||
**Schwere:** Low (bereits im Code behoben)
|
||||
**Beschreibung:** Task Model hat sowohl `contact_id` (FK zu contacts) als auch `entity_type/entity_id` (polymorphic). Code leitet `contact_id` aus `entity_id` ab, aber die FK-Spalte existiert noch in der DB.
|
||||
**Empfehlung:** In einer zukünftigen Migration die `contact_id` FK-Constraint droppen und die Spalte als nullable belassen (oder entfernen).
|
||||
|
||||
## Fazit
|
||||
|
||||
**ARCH-F Review: ✅ BESTANDEN**
|
||||
|
||||
Phase F Architektur ist solide. Keine Permission-Lücken, keine zirkulären Abhängigkeiten, konsistente Patterns. Ein Design-Gap (Agent Loop Approval-Integration) wurde identifiziert und für Phase G/I empfohlen.
|
||||
|
||||
**Freigabe für Phase G: ✅ erteilt**
|
||||
@@ -1,489 +0,0 @@
|
||||
# Architecture Cleanup Plan — LeoCRM
|
||||
|
||||
**Erstellt:** 2026-08-14
|
||||
**Basis:** Adversarial Architecture Audit (P0-1 bis P0-10, P1-9 bis P1-24)
|
||||
**Ziel:** Plugin-System funktionsfähig machen, Hartcodierungen entfernen, Core/Plugin-Grenze etablieren
|
||||
|
||||
---
|
||||
|
||||
## Prinzipien
|
||||
|
||||
1. **Jeder Fix nutzt existierende Interfaces** — kein Neubau, nur Verdrahtung
|
||||
2. **Jeder Fix ist testbar** — Plugin aktivieren/deaktivieren ohne Neustart muss funktionieren
|
||||
3. **Keine neuen Hartcodierungen** — jede neue Entität/Permission/Job kommt aus Plugin-Manifesten
|
||||
4. **Minimal-invasiv** — Core-Routes bleiben statisch (nicht migrieren), nur Plugin-Teile dynamisieren
|
||||
5. **Phase für Phase verifizierbar** — jede Phase hat klare Acceptance Criteria
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Plugin-Lifecycle zur Laufzeit funktionsfähig machen
|
||||
|
||||
**Priorität:** P0-Kritisch | **Aufwand:** ~4h | **Abhängigkeiten:** keine
|
||||
|
||||
### Problem
|
||||
Plugin-Aktivierung/Deaktivierung zur Laufzeit funktioniert nicht (P0-10, P0-9, P0-1).
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 1.1 Permission-Registry bei Runtime-Aktivierung aktualisieren (P0-10)
|
||||
**Datei:** `app/services/plugin_service.py`
|
||||
**Änderung:**
|
||||
- In `activate_plugin()`: Nach `registry.activate()` → `register_plugin_permissions(name, plugin.manifest.permissions)` aufrufen
|
||||
- In `deactivate_plugin()`: Nach `registry.deactivate()` → `unregister_plugin_permissions(name)` aufrufen
|
||||
- `from app.core.permission_registry import register_plugin_permissions, unregister_plugin_permissions`
|
||||
|
||||
**Verifikation:**
|
||||
1. Plugin via API aktivieren → Permission-Check für Plugin-Route gibt kein 403 mehr
|
||||
2. Plugin via API deaktivieren → Permission-Registry enthält Plugin nicht mehr
|
||||
3. Test: `test_plugin_lifecycle.py` — aktivieren, Route callen, deaktivieren, Route gibt 403
|
||||
|
||||
#### 1.2 Route-Registrierung aus Discovery-Ergebnis (P0-1)
|
||||
**Datei:** `app/main.py:583-605`
|
||||
**Änderung:**
|
||||
- Ersetze hartcodierte `plugin_modules`-Liste mit `registry.list_discovered()`
|
||||
- Für jeden discovered Plugin: Lese Manifest-Routes, registriere mit `require_active_plugin()`
|
||||
- Behalte `try/except` für robustness
|
||||
|
||||
```python
|
||||
# ALT:
|
||||
plugin_modules = ["app.plugins.builtins.tags", ...]
|
||||
|
||||
# NEU:
|
||||
registry = get_registry()
|
||||
for plugin_name in registry.list_discovered():
|
||||
plugin = registry.get_plugin(plugin_name)
|
||||
if plugin and plugin.manifest.routes:
|
||||
for route_def in plugin.manifest.routes:
|
||||
# ... gleiche Logik wie bisher, aber dynamisch
|
||||
```
|
||||
|
||||
**Verifikation:**
|
||||
1. Neues Plugin-Verzeichnis in `app/plugins/builtins/` erstellen → Routes erscheinen ohne main.py-Änderung
|
||||
2. Alle existierenden Plugin-Routes noch vorhanden (OpenAPI check)
|
||||
3. `builtins/__init__.py`-Imports entfernen → Discovery findet Plugins trotzdem
|
||||
|
||||
#### 1.3 `_mounted_routes` befüllen oder Route-Removal dokumentieren (P0-9)
|
||||
**Datei:** `app/plugins/registry.py:36,690-692`
|
||||
**Änderung (Option A — empfohlen):**
|
||||
- In `activate()`: Nach `app.include_router()` → `_mounted_routes[name].append(router)`
|
||||
- Das erfordert, dass `activate()` Zugriff auf `app` hat (bereits via `self._app`)
|
||||
- Route-Removal in `deactivate()` funktioniert dann tatsächlich
|
||||
|
||||
**ODER Option B — einfacher:**
|
||||
- Entferne Route-Removal-Logik aus `deactivate()`
|
||||
- Dokumentiere: Routes bleiben registriert, `require_active_plugin()` ist die einzige Verteidigung
|
||||
- Das ist die aktuelle Realität — nur ehrlich dokumentiert
|
||||
|
||||
**Verifikation:**
|
||||
- Option A: Plugin deaktivieren → Route gibt 404 (nicht 403)
|
||||
- Option B: Plugin deaktivieren → Route gibt 403 (dokumentiert)
|
||||
|
||||
#### 1.4 `builtins/__init__.py`-Imports entfernen (P0-6)
|
||||
**Datei:** `app/plugins/builtins/__init__.py`
|
||||
**Änderung:**
|
||||
- Entferne alle 10 hartcodierten Plugin-Imports
|
||||
- `discover_builtins()` findet Plugins via `pkgutil` — die `__init__.py`-Imports sind redundant
|
||||
- Behalte nur den Docstring
|
||||
|
||||
**Verifikation:**
|
||||
1. App startet ohne Fehler
|
||||
2. `registry.list_discovered()` enthält alle 21 Plugins
|
||||
3. Alle Plugin-Routes registriert
|
||||
|
||||
### Acceptance Criteria Phase 1
|
||||
- [ ] Plugin via API aktivieren → Routes funktionieren ohne Neustart
|
||||
- [ ] Plugin via API deaktivieren → Routes geben 403/404
|
||||
- [ ] Neues Plugin in `builtins/` ablegen → Routes erscheinen ohne Core-Änderung
|
||||
- [ ] `builtins/__init__.py` hat keine Plugin-Imports mehr
|
||||
- [ ] Test: `test_plugin_lifecycle.py` existiert und ist grün
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Plugin-Selbstregistrierung statt Core-Hartcodierung
|
||||
|
||||
**Priorität:** P0-Hoch | **Aufwand:** ~6h | **Abhängigkeiten:** Phase 1
|
||||
|
||||
### Problem
|
||||
Core registriert Plugin-Entities, Hooks, Restore-Configs, Worker-Jobs hartkodiert (P0-7, P0-8, P0-5).
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 2.1 Restore-Registry: Plugins registrieren selbst (P0-7)
|
||||
**Dateien:**
|
||||
- `app/core/restore_registry.py:113-195` — entferne `register_default_entities()` für Plugin-Entities
|
||||
- `app/plugins/builtins/tasks/plugin.py` — in `on_activate()`: `reg.register(RestoreConfig(entity_type="task", ...))`
|
||||
- `app/plugins/builtins/calendar/plugin.py` — in `on_activate()`: `reg.register(RestoreConfig(entity_type="calendar_entry", ...))`
|
||||
- `app/plugins/builtins/dms/plugin.py` — in `on_activate()`: `reg.register(RestoreConfig(entity_type="dms_file", ...))`
|
||||
- `app/plugins/builtins/mail/plugin.py` — in `on_activate()`: `reg.register(RestoreConfig(entity_type="mail", ..., special_handler=_mail_restore_handler))`
|
||||
- `app/plugins/builtins/mail/plugin.py` — `_mail_restore_handler` nach Mail-Plugin verschieben
|
||||
|
||||
**Core behält nur:** Contact-Registrierung (Contact ist Core)
|
||||
|
||||
**Verifikation:**
|
||||
1. Plugin aktivieren → Restore für Plugin-Entity funktioniert
|
||||
2. Plugin deaktivieren → Restore-Config für Plugin-Entity entfernt
|
||||
3. `register_default_entities()` registriert nur noch Contact
|
||||
|
||||
#### 2.2 History-Hooks: Plugins registrieren selbst (P0-8)
|
||||
**Dateien:**
|
||||
- `app/core/history_hooks.py:135-173` — entferne Plugin-Entity-Hooks aus `register_default_history_hooks()`
|
||||
- `app/plugins/builtins/tasks/plugin.py` — in `on_activate()`: `register_history_hooks(reg, "task", "task.after_create", ...)`
|
||||
- `app/plugins/builtins/calendar/plugin.py` — gleiche für `calendar_entry`
|
||||
- `app/plugins/builtins/dms/plugin.py` — gleiche für `dms_file`
|
||||
- `app/plugins/builtins/mail/plugin.py` — gleiche für `mail`
|
||||
|
||||
**Core behält nur:** Contact-Hooks
|
||||
|
||||
**Verifikation:**
|
||||
1. Plugin aktivieren → History wird für Plugin-Entities aufgezeichnet
|
||||
2. Plugin deaktivieren → Hooks werden entfernt (`on_deactivate` muss `reg.unregister_action()` aufrufen)
|
||||
3. `register_default_history_hooks()` registriert nur noch Contact
|
||||
|
||||
#### 2.3 Worker-Jobs: Plugins registrieren selbst (P0-5)
|
||||
**Dateien:**
|
||||
- `app/core/worker.py:221-227` — entferne hartcodierte `plugin_job_modules`-Liste
|
||||
- `app/plugins/base.py` — füge `get_job_modules() -> list[str]` hinzu (default: `[]`)
|
||||
- Jedes Plugin mit Jobs: überschreibe `get_job_modules()` → return `["app.plugins.builtins.<name>.jobs"]`
|
||||
- `worker.py` — iteriere `registry.list_discovered()`, rufe `plugin.get_job_modules()` auf, importiere dynamisch
|
||||
|
||||
**Verifikation:**
|
||||
1. Plugin mit Jobs aktivieren → Jobs laufen
|
||||
2. Plugin deaktivieren → Jobs werden nicht mehr geladen
|
||||
3. Neues Plugin mit Jobs → funktioniert ohne worker.py-Änderung
|
||||
|
||||
#### 2.4 OpenAPI-Tags dynamisch aus Manifesten (P1-18)
|
||||
**Datei:** `app/main.py:300-370`
|
||||
**Änderung:**
|
||||
- Entferne Plugin-spezifische OpenAPI-Tags (`dms`, `mail`, `calendar`, `search`, etc.)
|
||||
- Behalte nur Core-Tags (`health`, `auth`, `users`, `contacts`, etc.)
|
||||
- Nach Plugin-Route-Registrierung: füge Tags aus Plugin-Manifest hinzu
|
||||
|
||||
**Verifikation:**
|
||||
1. OpenAPI-Schema enthält alle Plugin-Tags mit Beschreibungen
|
||||
2. Plugin deaktivieren → Tag verschwindet aus OpenAPI
|
||||
|
||||
### Acceptance Criteria Phase 2
|
||||
- [ ] `register_default_entities()` registriert nur Contact
|
||||
- [ ] `register_default_history_hooks()` registriert nur Contact
|
||||
- [ ] `worker.py` hat keine hartkodierte Plugin-Modulliste
|
||||
- [ ] `main.py` OpenAPI-Tags enthalten keine Plugin-Tags mehr
|
||||
- [ ] Plugin deaktivieren entfernt Restore-Config, History-Hooks, Worker-Jobs
|
||||
- [ ] Plugin aktivieren registriert alles neu
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Generische Services erweiterbar machen
|
||||
|
||||
**Priorität:** P1-Hoch | **Aufwand:** ~8h | **Abhängigkeiten:** Phase 2
|
||||
|
||||
### Problem
|
||||
ENTITY_MODELS, CORE_PERMISSIONS, saved_views, tags haben hartcodierte Entity-Types (P0-3, P0-4, P1-12, P1-13).
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 3.1 ENTITY_MODELS: Plugin-Registrierungs-Interface (P0-3)
|
||||
**Dateien:**
|
||||
- `app/services/entity_permission_service.py:70-190` — entferne alle `try/except` Plugin-Import-Blöcke
|
||||
- `app/plugins/base.py` — füge `get_entity_models() -> dict[str, type]` hinzu (default: `{}`)
|
||||
- Jedes Plugin: überschreibe `get_entity_models()` → return `{"file": DmsFile, "folder": DmsFolder}`
|
||||
- `entity_permission_service.py` — neue Funktion `register_entity_model(entity_type, model_class)`
|
||||
- `main.py:lifespan()` — nach Plugin-Aktivierung: iteriere Plugins, rufe `get_entity_models()`, registriere
|
||||
- `registry.activate()` — rufe `register_entity_model()` für aktive Plugins
|
||||
- `registry.deactivate()` — entferne Entity-Models für deaktivierte Plugins
|
||||
|
||||
**Core behält:** Contact, Address, Attachment, BankAccount, Workflow, Sequence, SavedFilter, SavedView, Webhook, CustomFieldDefinition, ContactFolder, EntityAttachment, EntityHistory
|
||||
|
||||
**Verifikation:**
|
||||
1. Plugin aktivieren → ENTITY_MODELS enthält Plugin-Entities
|
||||
2. Plugin deaktivieren → ENTITY_MODELS enthält Plugin-Entities nicht mehr
|
||||
3. Permission-Resolution für Plugin-Entity funktioniert
|
||||
4. Neues Plugin mit neuer Entität → funktioniert ohne entity_permission_service.py-Änderung
|
||||
|
||||
#### 3.2 CORE_PERMISSIONS: Plugin-Permissions entfernen (P0-4)
|
||||
**Datei:** `app/core/permission_registry.py:21-128`
|
||||
**Änderung:**
|
||||
- Entferne alle Plugin-Permissions aus `CORE_PERMISSIONS` (calendar, dms, mail, tasks, comm, automation, ai, tags, entity_links, reports, search, mcp, permissions, agents, dashboard)
|
||||
- Behalte nur echte Core-Permissions: contacts, users, roles, groups, audit, settings, plugins, tenants, notifications, attachments, workflows, user_preferences, sequences, addresses, taxes, currencies, import_export, workspaces, system
|
||||
- Plugin-Permissions kommen bereits via `register_plugin_permissions()` aus Manifesten — das ist die dynamische Quelle
|
||||
- Entferne Kommentar "Plugin permissions (registered at startup, but also listed here for completeness)"
|
||||
|
||||
**Verifikation:**
|
||||
1. Plugin aktivieren → Plugin-Permissions in Registry
|
||||
2. Plugin deaktivieren → Plugin-Permissions nicht in Registry
|
||||
3. Permission-UI zeigt nur aktive Plugin-Permissions
|
||||
4. Core-Permissions weiterhin verfügbar
|
||||
|
||||
#### 3.3 CORE_FIELD_DEFINITIONS: Contact-Felder als Plugin oder Core-Deklaration (P1-16)
|
||||
**Datei:** `app/core/permission_registry.py:135-176`
|
||||
**Änderung:**
|
||||
- Contact-Felddefinitionen bleiben in Core (Contact ist Core)
|
||||
- User-Felddefinitionen bleiben in Core (User ist Core)
|
||||
- Das ist akzeptabel — Core darf Core-Felder deklarieren
|
||||
- **Kein Fix nötig** — nur Dokumentation dass dies Core-spezifisch ist
|
||||
|
||||
#### 3.4 saved_views/saved_filters: Entity-Types dynamisch (P1-12)
|
||||
**Dateien:** `app/routes/saved_views.py:19`, `app/routes/saved_filters.py:19`
|
||||
**Änderung:**
|
||||
- Entferne `VALID_ENTITY_TYPES = {"contacts", "mail", "calendar", "dms"}`
|
||||
- Entferne Pydantic `pattern="^(contacts|mail|calendar|dms)$"`
|
||||
- Stattdessen: Validiere gegen `ENTITY_MODELS.keys()` oder eine neue `get_valid_entity_types()` Funktion
|
||||
- Akzeptiere jeden String, validiere zur Laufzeit gegen registrierte Entity-Types
|
||||
|
||||
**Verifikation:**
|
||||
1. Saved View für `task` erstellen → funktioniert
|
||||
2. Saved View für `nonexistent` erstellen → 422
|
||||
3. Plugin deaktivieren → Saved Views für Plugin-Entity noch abrufbar aber nicht neu erstellbar
|
||||
|
||||
#### 3.5 tags/entity_links: VALID_ENTITY_TYPES dynamisch (P1-13)
|
||||
**Dateien:** `app/plugins/builtins/tags/routes.py:25`, `app/plugins/builtins/entity_links/routes.py:22`
|
||||
**Änderung:**
|
||||
- Entferne hartcodierte Sets
|
||||
- Tags: Validiere gegen `ENTITY_MODELS.keys()` (jede registrierte Entität kann getaggt werden)
|
||||
- Entity-Links: Validiere gegen `ENTITY_MODELS.keys()` (jede registrierte Entität kann verlinkt werden)
|
||||
- Frontend `tags.ts:12` — `EntityType` dynamisch aus API laden oder als `string` deklarieren
|
||||
|
||||
**Verifikation:**
|
||||
1. Tag für `task` erstellen → funktioniert
|
||||
2. Tag für `nonexistent` → 422
|
||||
3. Frontend zeigt alle verfügbaren Entity-Types an
|
||||
|
||||
#### 3.6 Dashboard-Counts dynamisch (P1-21)
|
||||
**Datei:** `app/routes/dashboard.py:57-110`
|
||||
**Änderung:**
|
||||
- Behalte Contact/Company/Person als Core-Counts
|
||||
- Füge Plugin-Counts-Interface hinzu: `BasePlugin.get_dashboard_counts(db, tenant_id, user_id) -> list[dict]`
|
||||
- `/counts`-Endpoint iteriert aktive Plugins, sammelt Counts
|
||||
- Plugins können eigene Counts beitragen (z.B. Tasks: offene Tasks, Mail: ungelesene Mails)
|
||||
|
||||
**Verifikation:**
|
||||
1. Dashboard zeigt Plugin-Counts an
|
||||
2. Plugin deaktivieren → Plugin-Counts verschwinden
|
||||
|
||||
### Acceptance Criteria Phase 3
|
||||
- [ ] `ENTITY_MODELS` enthält keine `try/except` Plugin-Import-Blöcke mehr
|
||||
- [ ] `CORE_PERMISSIONS` enthält keine Plugin-Permissions mehr
|
||||
- [ ] saved_views/saved_filters akzeptieren alle registrierten Entity-Types
|
||||
- [ ] tags/entity_links akzeptieren alle registrierten Entity-Types
|
||||
- [ ] Dashboard-Counts enthalten Plugin-Beiträge
|
||||
- [ ] Plugin deaktivieren entfernt Permissions, Entity-Models aus Registries
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Core/Plugin-Abhängigkeiten reduzieren
|
||||
|
||||
**Priorität:** P1-Mittel | **Aufwand:** ~6h | **Abhängigkeiten:** Phase 3
|
||||
|
||||
### Problem
|
||||
Core-Dateien importieren direkt Plugin-Modelle (P1-9, P1-10, P1-19, P1-20).
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 4.1 Core→Plugin-Imports durch Contracts ersetzen (P1-9, P1-10)
|
||||
**Dateien (41 Core→Plugin-Imports):**
|
||||
- `app/core/notifications.py:43` → nutze `get_contract("kommunikation")` statt direktem Import
|
||||
- `app/core/restore_registry.py:132-237` → nach Phase 2.1 erledigt (Plugins registrieren selbst)
|
||||
- `app/core/trigger_dispatcher.py:116,174` → nutze `get_contract("automation")`
|
||||
- `app/core/worker.py:169,221-227,273` → nach Phase 2.3 erledigt (dynamische Job-Discovery)
|
||||
- `app/services/entity_permission_service.py:86-187` → nach Phase 3.1 erledigt (dynamische ENTITY_MODELS)
|
||||
- `app/services/attachment_service.py:25` → nutze `get_contract("dms")` für File-Modell
|
||||
- `app/commands/mail_commands.py:16,36,109,147` → nutze `get_contract("mail")`
|
||||
- `app/commands/calendar_commands.py:38,104,153` → nutze `get_contract("calendar")`
|
||||
- `app/commands/dms_commands.py:50,130` → nutze `get_contract("dms")`
|
||||
- `app/ai/llm_client.py:292,319` → nutze `get_contract("ai_assistant")`
|
||||
- `app/routes/errors.py:124` → nutze `get_contract("forgejo_error_reporter")` oder mache Error-Reporting generisch
|
||||
- `app/main.py:151,171` → gleiche wie errors.py
|
||||
|
||||
**Verifikation:**
|
||||
1. `grep -rn 'from app.plugins.builtins' app/core/ app/services/ app/routes/ app/commands/ app/ai/` → 0 Treffer (außer contracts)
|
||||
2. Plugin deaktivieren → Core funktioniert ohne Fehler (graceful degradation)
|
||||
3. `check_cross_plugin_imports.py` erweitert auf Core-Verzeichnisse
|
||||
|
||||
#### 4.2 Cross-Plugin-Import-Checker auf Core erweitern (P1-20)
|
||||
**Datei:** `scripts/check_cross_plugin_imports.py`
|
||||
**Änderung:**
|
||||
- `find_python_files()` default search_path: auch `app/core/`, `app/services/`, `app/routes/`, `app/commands/`, `app/ai/` scannen
|
||||
- Neue EXEMPT_PATHS für legitime Core-Imports (z.B. `main.py` für Route-Registrierung)
|
||||
- CI-Pipeline prüft nun Core→Plugin-Imports auch
|
||||
|
||||
**Verifikation:**
|
||||
1. `python scripts/check_cross_plugin_imports.py` findet 0 Verstöße
|
||||
2. CI-Pipeline grün
|
||||
|
||||
#### 4.3 Cross-Plugin-Imports in Plugins auf Contracts umstellen (P1-19)
|
||||
**Aufwand:** Hoch (226 Imports), aber mechanisch
|
||||
**Priorisierung:**
|
||||
- Start mit Plugins, die am häufigsten importiert werden (kommunikation, ai_assistant, unified_search)
|
||||
- Jeder `from app.plugins.builtins.<plugin>.<module> import X` → `get_contract("<plugin>")` mit None-Check
|
||||
- Contracts müssen alle aktuell direkt importierten Symbole exponieren
|
||||
|
||||
**Verifikation:**
|
||||
1. `grep -rn 'from app.plugins.builtins' app/plugins/builtins/ | grep -v contracts | grep -v __init__` → 0
|
||||
2. Alle Plugin-Tests grün
|
||||
3. Plugin deaktivieren → abhängige Plugins degradieren gracefully
|
||||
|
||||
### Acceptance Criteria Phase 4
|
||||
- [ ] 0 Core→Plugin-Imports (außer contracts)
|
||||
- [ ] Cross-Plugin-Checker prüft Core-Verzeichnisse
|
||||
- [ ] Cross-Plugin-Imports in Plugins reduziert um >80%
|
||||
- [ ] Plugin deaktivieren → keine Import-Fehler in Core oder anderen Plugins
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Test-Infrastruktur und Qualität
|
||||
|
||||
**Priorität:** P1-Mittel | **Aufwand:** ~4h | **Abhängigkeiten:** Phase 1-4
|
||||
|
||||
### Problem
|
||||
Tests mocken Permissions weg, conftest importiert alle Plugins hartkodiert, keine E2E-Tests für Plugin-Lifecycle (P1-14, P1-15).
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 5.1 conftest.py: Plugin-Modelle dynamisch laden (P1-14)
|
||||
**Datei:** `tests/conftest.py:50-80`
|
||||
**Änderung:**
|
||||
- Entferne alle hartcodierten Plugin-Model-Imports
|
||||
- Stattdessen: iteriere `registry.list_discovered()`, rufe `plugin.get_entity_models()` auf, importiere Modelle dynamisch
|
||||
- `Base.metadata.create_all()` findet alle Tabellen weil Modelle importiert wurden
|
||||
|
||||
```python
|
||||
# ALT: 15 hartcodierte Imports
|
||||
from app.plugins.builtins.calendar.models import Calendar, CalendarEntry, ...
|
||||
|
||||
# NEU:
|
||||
registry = get_registry()
|
||||
registry.discover_builtins()
|
||||
for name in registry.list_discovered():
|
||||
plugin = registry.get_plugin(name)
|
||||
if plugin:
|
||||
models = plugin.get_entity_models()
|
||||
# Import module to register models with Base.metadata
|
||||
for entity_type, model_class in models.items():
|
||||
# Model class is already imported via get_entity_models()
|
||||
pass
|
||||
```
|
||||
|
||||
**Verifikation:**
|
||||
1. Tests laufen ohne hartcodierte Plugin-Imports
|
||||
2. Plugin entfernen → Tests für Plugin laufen nicht, aber andere Tests grün
|
||||
3. Neues Plugin → Tests finden Modelle automatisch
|
||||
|
||||
#### 5.2 Plugin-Lifecycle E2E-Test (neu)
|
||||
**Datei:** `tests/test_plugin_lifecycle.py` (neu)
|
||||
**Inhalt:**
|
||||
1. Test-Plugin erstellen (minimal, mit Route, Permission, Entity-Model, Job)
|
||||
2. Plugin aktivieren via API → Route erreichbar, Permission verfügbar, Job registriert
|
||||
3. Plugin deaktivieren via API → Route gibt 403/404, Permission entfernt, Job entfernt
|
||||
4. Plugin wieder aktivieren → alles wieder da
|
||||
5. Plugin mit Dependency aktivieren → funktioniert nur wenn Dependency aktiv
|
||||
6. Plugin mit Dependency deaktivieren → wird blockiert wenn Dependency aktiv
|
||||
|
||||
**Verifikation:**
|
||||
1. Test ist grün
|
||||
2. Test läuft ohne Mocks für Permission/Visibility/Tenant
|
||||
|
||||
#### 5.3 Permission-Mock-Tests umstellen (P1-15)
|
||||
**Dateien:** `tests/test_graph_rag.py:42`, `test_agent_memory.py:42`, `test_marketplace.py:48`, `test_external_agent_api.py:38`
|
||||
**Änderung:**
|
||||
- Entferne `patch("app.core.permissions.check_permission", return_value=True)`
|
||||
- Stattdessen: Test-User mit echten Permissions erstellen
|
||||
- `conftest.py` hat bereits `create_test_user` mit Role → nutze echte Permissions
|
||||
- Für `set_tenant_context` Mocks: nutze echte DB-Session mit Tenant-Kontext
|
||||
|
||||
**Verifikation:**
|
||||
1. Tests laufen ohne Permission-Mocks
|
||||
2. Tests testen echte Permission-Enforcement
|
||||
3. Test mit unzureichenden Permissions → 403 (nicht 200)
|
||||
|
||||
### Acceptance Criteria Phase 5
|
||||
- [ ] `conftest.py` hat keine hartcodierten Plugin-Imports
|
||||
- [ ] `test_plugin_lifecycle.py` existiert und ist grün
|
||||
- [ ] Keine `patch("app.core.permissions.check_permission")` mehr in Tests
|
||||
- [ ] Plugin-Lifecycle E2E-Test testet echte Permission/Visibility/Tenant-Isolation
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Doppelarchitekturen auflösen
|
||||
|
||||
**Priorität:** P1-Niedrig | **Aufwand:** ~4h | **Abhängigkeiten:** Phase 4
|
||||
|
||||
### Problem
|
||||
Notification-Doppelarchitektur, Dedup/Import-Export Contact-spezifisch (P1-11, P1-22, P1-23).
|
||||
|
||||
### Tasks
|
||||
|
||||
#### 6.1 Notification-Doppelarchitektur dokumentieren oder auflösen (P1-11)
|
||||
**Datei:** `app/core/notifications.py`
|
||||
**Änderung:**
|
||||
- `create_notification()` als deprecated markieren (bereits getan)
|
||||
- Frontend `NotificationDropdown` auf Communication-API umstellen
|
||||
- `app/routes/notifications.py` als deprecated markieren oder auf Communication redirect
|
||||
- Langfristig: `notifications`-Tabelle entfernen, alles über Communication-Plugin
|
||||
|
||||
**Verifikation:**
|
||||
1. Frontend nutzt Communication-API für Notifications
|
||||
2. `notifications`-Route gibt Deprecation-Warning
|
||||
|
||||
#### 6.2 Dedup-Service: Plugin-Interface oder als Contact-Service deklarieren (P1-22)
|
||||
**Datei:** `app/services/dedup_service.py`
|
||||
**Änderung (Option A — Plugin-Interface):**
|
||||
- `BasePlugin.get_dedup_config() -> DedupConfig | None` hinzufügen
|
||||
- Plugins deklarieren Dedup-Felder und Match-Logik
|
||||
- Dedup-Service iteriert aktive Plugins
|
||||
- **Aufwand:** Hoch — generische Dedup-Engine
|
||||
|
||||
**ODER Option B — ehrlich deklarieren:**
|
||||
- `dedup_service.py` → `contact_dedup_service.py` umbenennen
|
||||
- Dokumentieren: Dedup ist Contact-spezifisch, nicht generisch
|
||||
- **Aufwand:** Klein — nur Umbenennung und Doku
|
||||
|
||||
**Empfehlung:** Option B — Dedup ist CRM-spezifisch, muss nicht generisch sein.
|
||||
|
||||
#### 6.3 Import/Export: Plugin-Interface oder als Contact-Service deklarieren (P1-23)
|
||||
**Datei:** `app/services/import_export_service.py`
|
||||
**Gleiche Entscheidung wie 6.2:**
|
||||
- Option A: Generisches Import/Export-Interface für Plugins
|
||||
- Option B: Als `contact_import_export_service.py` deklarieren
|
||||
|
||||
**Empfehlung:** Option B für jetzt, Option A wenn ein Plugin Import/Export braucht.
|
||||
|
||||
### Acceptance Criteria Phase 6
|
||||
- [ ] Notification-Doppelarchitektur aufgelöst oder dokumentiert
|
||||
- [ ] Dedup/Import-Export als Contact-spezifisch deklariert oder generisch gemacht
|
||||
|
||||
---
|
||||
|
||||
## Gesamtaufwand
|
||||
|
||||
| Phase | Aufwand | Priorität | Abhängigkeit |
|
||||
|-------|---------|-----------|-------------|
|
||||
| 1: Plugin-Lifecycle | ~4h | P0-Kritisch | keine |
|
||||
| 2: Selbstregistrierung | ~6h | P0-Hoch | Phase 1 |
|
||||
| 3: Generische Services | ~8h | P1-Hoch | Phase 2 |
|
||||
| 4: Core/Plugin-Abhängigkeiten | ~6h | P1-Mittel | Phase 3 |
|
||||
| 5: Test-Infrastruktur | ~4h | P1-Mittel | Phase 1-4 |
|
||||
| 6: Doppelarchitekturen | ~4h | P1-Niedrig | Phase 4 |
|
||||
| **Total** | **~32h** | | |
|
||||
|
||||
Bei 8h/Tag: **4 Arbeitstage** für alle Phasen.
|
||||
Phase 1 allein: **einen halben Tag**.
|
||||
|
||||
---
|
||||
|
||||
## Risiken
|
||||
|
||||
1. **Phase 1 kann versteckte Abhängigkeiten aufdecken** — wenn Plugin-Aktivierung zur Laufzeit zum ersten Mal richtig getestet wird, können neue Bugs sichtbar werden
|
||||
2. **Phase 3.1 (ENTITY_MODELS)** ist der komplexeste Fix — das Permission-System hängt davon ab
|
||||
3. **Phase 4.3 (226 Cross-Imports)** ist mechanisch aber fehleranfällig — jeder Contract muss alle Symbole exponieren
|
||||
4. **Tests können brechen** — wenn Permission-Mocks entfernt werden, können Tests failen die vorher grün waren (was gut ist, aber Aufwand bedeutet)
|
||||
|
||||
## Erfolgsmessung
|
||||
|
||||
Nach Abschluss aller Phasen:
|
||||
1. **Plugin hinzufügen:** 0 Core-Dateien ändern → Plugin in `builtins/` ablegen, aktivieren
|
||||
2. **Plugin deaktivieren:** Alle Routes, Permissions, Jobs, Hooks, Entity-Models entfernt
|
||||
3. **Plugin entfernen:** `uninstall` → alle Spuren gelöscht
|
||||
4. **Neue Entität:** Plugin deklariert Entität in Manifest → Permissions, Tags, Links, Saved Views funktionieren
|
||||
5. **Cross-Plugin-Checker:** 0 Verstöße in Core und Plugins
|
||||
6. **E2E-Test:** Plugin-Lifecycle-Test grün ohne Mocks
|
||||
|
||||
Das ist das Ziel: **Ein Plugin-System, das wirklich modular ist.**
|
||||
@@ -1,143 +0,0 @@
|
||||
# Konsolidierte Fehlerliste — LeoCRM Architektur-Audit
|
||||
|
||||
**Ursprüngliches Audit-Datum:** 2026-08-15
|
||||
**Verifiziert und aktualisiert:** 2026-08-17
|
||||
**Dateien geprüft:** 1052 (vollständig)
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung (verifiziert 2026-08-17)
|
||||
|
||||
| Schwere | Auditiert | Behoben | Intentional Design | Noch offen |
|
||||
|---------|-----------|---------|-------------------|-----------|
|
||||
| P0 | 8 | **8** | 0 | **0** |
|
||||
| P1 | 37 | **~32** | ~5 | **~5** (low priority) |
|
||||
| P2 | 71 | **~35** | ~15 | **~21** (frontend `any` types reduziert 181→61) |
|
||||
| P3 | 58 | unbekannt | unbekannt | unbekannt (low priority) |
|
||||
| **Total** | **174** | **~75** | **~20** | **~26** |
|
||||
|
||||
---
|
||||
|
||||
## P0 — Runtime Crashes / Security (8) — ✅ ALLE BEHOBEN
|
||||
|
||||
### P0-1: hooks.py:83 — unregister() _filters 2-tuple CRASH ✅
|
||||
**Datei:** `app/core/hooks.py`
|
||||
**Status:** Behoben — `unregister()` nutzt jetzt 3-tuple `(p, c, o)` unpacking.
|
||||
**Verifikation:** `grep -c '(p, c, o)' app/core/hooks.py` → 3
|
||||
|
||||
### P0-2: trigger_dispatcher.py:127 — AutomationDefinition nicht importiert ✅
|
||||
**Datei:** `app/core/trigger_dispatcher.py`
|
||||
**Status:** Behoben — `AutomationDefinition = automation_contract.Automation` via Contract import.
|
||||
**Verifikation:** `grep -c 'AutomationDefinition.*=.*automation_contract' app/core/trigger_dispatcher.py` → 1
|
||||
|
||||
### P0-3: contacts/plugin.py:88-90 — clear_actions statt unregister_actions_by_owner ✅
|
||||
**Datei:** `app/plugins/builtins/contacts/plugin.py`
|
||||
**Status:** Behoben — nutzt `unregister_actions_by_owner("contacts")`.
|
||||
**Verifikation:** `grep -c 'unregister_actions_by_owner' app/plugins/builtins/contacts/plugin.py` → 3
|
||||
|
||||
### P0-T1 bis P0-T5: Test check_permission mocks ✅
|
||||
**Dateien:** `tests/test_external_agent_api.py`, `tests/test_graph_rag.py`, `tests/test_agent_memory.py`, `tests/test_marketplace.py`, `tests/test_cross_tenant_standalone.py`
|
||||
**Status:** Behoben — keine `check_permission` mocks mehr in Source-Dateien (nur stale `.pyc` Bytecode gefunden).
|
||||
**Verifikation:** `grep -rl 'patch.*check_permission.*return_value.*True' tests/*.py` → 0
|
||||
|
||||
---
|
||||
|
||||
## P1 — Funktionale Fehler (37) — ~32 behoben, ~5 offen
|
||||
|
||||
### P1-1: attachment_service.py:48 — DmsFile type hint not imported ✅
|
||||
**Datei:** `app/services/attachment_service.py`
|
||||
**Status:** Behoben — `DmsFile` Referenz nicht mehr vorhanden (0 Treffer).
|
||||
|
||||
### P1-2 bis P1-10: Backend P1s
|
||||
Die meisten P1 Backend-Issues wurden während Phase B und F behoben. Stichproben zeigen keine offenen P1-Backend-Issues mehr.
|
||||
|
||||
### P1 Frontend (1): Hardcoded Plugin-Routes
|
||||
**Datei:** `frontend/src/routes/index.tsx`
|
||||
**Status:** Intentional Design — Plugin-Routes sind hardcoded für Code-Splitting und lazy loading. `PluginRouteRenderer` dient als dynamischer catch-all. Kein Fix nötig.
|
||||
|
||||
### P1 Tests (26): Test-spezifische Issues
|
||||
Die meisten P1 Test-Issues (hardcoded DB-URLs, fehlende Mocks) wurden während Phase B und F behoben.
|
||||
|
||||
---
|
||||
|
||||
## P2 — Code-Qualität (71) — ~35 behoben, ~15 intentional, ~21 offen
|
||||
|
||||
### P2-9: hooks.py:52-53 — Type-Annotationen falsch ✅
|
||||
**Datei:** `app/core/hooks.py`
|
||||
**Status:** Behoben — `_actions` und `_filters` nutzen jetzt `tuple[int, Callable, str]` statt `tuple[int, Callable]`.
|
||||
**Commit:** `45ebbee`
|
||||
|
||||
### P2-27, P2-34, P2-35: Deprecated Notification imports ✅
|
||||
**Dateien:** `app/services/workflow_service.py`, `app/routes/users.py`, `app/workflows/engine.py`
|
||||
**Status:** Behoben — keine `Notification` oder `create_notification` imports mehr vorhanden.
|
||||
|
||||
### P2-23: report_generator/plugin.py:9 — top-level import ✅
|
||||
**Datei:** `app/plugins/builtins/report_generator/plugin.py`
|
||||
**Status:** Behoben — kein top-level `jobs` import mehr.
|
||||
|
||||
### P2-24: base.py:81 — __self__ Heuristik ✅
|
||||
**Datei:** `app/plugins/base.py`
|
||||
**Status:** Behoben — `__self__` nicht mehr vorhanden.
|
||||
|
||||
### P2-51: address.py:9 — Hardcoded pattern ✅
|
||||
**Datei:** `app/schemas/address.py`
|
||||
**Status:** Behoben — `pattern="^contact$"` nicht mehr vorhanden.
|
||||
|
||||
### P2 Frontend: `any` types ✅ (teilweise)
|
||||
**Status:** Behoben — `any` types von 181 auf 61 reduziert (source), 61 in tests übrig.
|
||||
**Commit:** `45ebbee`
|
||||
**Verbleibend:** 61 `any` in source (meist in API-Clients mit dynamischen Backend-Typen), 61 in tests.
|
||||
|
||||
### P2 Frontend: heroicons → lucide-react ✅
|
||||
**Dateien:** `AgentChat.tsx`, `AgentMonitor.tsx`, `AgentRunLog.tsx`
|
||||
**Status:** Behoben — `@heroicons/react/24/outline` (nicht installiert) durch `lucide-react` ersetzt.
|
||||
|
||||
### P2 Frontend: Fehlende Type-Exports ✅
|
||||
**Datei:** `frontend/src/types/automation.ts`
|
||||
**Status:** Behoben — `AgentToolInfo`, `AgentSkillInfo`, `AgentRunFull`, `AgentDefinitionFull` hinzugefügt.
|
||||
|
||||
### P2 Frontend: toast API ✅
|
||||
**Datei:** `AgentEditor.tsx`
|
||||
**Status:** Behoben — `toast({})` durch `toast.success()` etc. ersetzt.
|
||||
|
||||
### P2 Frontend: Select options ✅
|
||||
**Datei:** `TaskDetail.tsx`
|
||||
**Status:** Behoben — `Select` mit `options` prop statt children `<option>`.
|
||||
|
||||
### P2 Frontend: TaskStatus types ✅
|
||||
**Dateien:** `TaskBoard.tsx`, `TaskDetail.tsx`, `Tasks.tsx`
|
||||
**Status:** Behoben — `string` durch `TaskStatus` / `AssigneeType` ersetzt.
|
||||
|
||||
### P2 Intentional Design (kein Fix nötig)
|
||||
|
||||
| Issue | Beschreibung | Warum intentional |
|
||||
|-------|-------------|-------------------|
|
||||
| P2-7 | Contact in ENTITY_MODELS | Core-Entity, muss hardcoded sein |
|
||||
| P2-12, P2-13 | saved_views/filters `contacts:read` | Core-Feature, Permission hardcoded |
|
||||
| P2-14, P2-15, P2-16 | SENSITIVE_FIELDS, CORE_FIELD_DEFINITIONS | Core-Security-Config, muss hardcoded sein |
|
||||
| P2-26 | _WRITE_PERMISSIONS in deps.py | Core-Permission-Liste, hardcoded |
|
||||
| P2-28 | Dashboard Contact counts | Core-Dashboard, hardcoded |
|
||||
| P2-29 | import_export entity_type default | Core-Feature, `Form(...)` required |
|
||||
| P2-33 | SYSTEM_PERMISSIONS in roles.py | Core-Permission-Liste, hardcoded |
|
||||
| P2-38 | webhooks require_permission | Core-Feature, Permission hardcoded |
|
||||
| P2-54, P2-F1 | Hardcoded Plugin-Routes | Code-Splitting, lazy loading, intentional |
|
||||
| P2-F2 | Settings nav items | Core-Settings, hardcoded |
|
||||
| P2-F3 | ENTITY_URL_MAP | Core-Entity-Map, hardcoded |
|
||||
| P2-F10, F11, F12 | Contact field defs | Core-Entity-Fields, hardcoded |
|
||||
|
||||
---
|
||||
|
||||
## P3 — Low Priority (58) — nicht verifiziert
|
||||
|
||||
P3-Issues sind low priority (Style, Dokumentation, Minor-Optimierungen) und wurden nicht einzeln verifiziert.
|
||||
|
||||
---
|
||||
|
||||
## Verifikation (2026-08-17)
|
||||
|
||||
- **tsc --noEmit:** ✅ clean (0 errors)
|
||||
- **vite build:** ✅ erfolgreich
|
||||
- **Deploy:** ✅ erfolgreich (103s, Health check green)
|
||||
- **Health:** ✅ DB up, Redis up, Storage up, Worker up
|
||||
- **Alembic:** ✅ 0124 (OK)
|
||||
- **RLS:** ✅ 113 tables (OK)
|
||||
@@ -1,235 +0,0 @@
|
||||
# Audit Fix-Plan — LeoCRM Architektur-Audit
|
||||
|
||||
**Basis:** `docs/audit-consolidated-errors.md` (153 eindeutige Fehler)
|
||||
**Datum:** 2026-08-15
|
||||
|
||||
---
|
||||
|
||||
## Prinzipien
|
||||
|
||||
1. **P0 zuerst** — Runtime Crashes müssen sofort behoben werden
|
||||
2. **Kein neues Architekturmodell** — vorhandene Interfaces nutzen
|
||||
3. **Deklarierte Plugin→Plugin-Abhängigkeiten sind erlaubt**
|
||||
4. **Minimal focused changes** — bestehenden Style beibehalten
|
||||
5. **Jeder Fix wird verifiziert** — Tests laufen, App startet
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: P0 Runtime Crashes (8 Fixes, ~2h)
|
||||
|
||||
### 1.1 hooks.py:83 — unregister() _filters 2-tuple CRASH
|
||||
**Datei:** `app/core/hooks.py:83`
|
||||
**Änderung:** `register_filter` speichert 3-tuple `(priority, callback, owner_tag)`, aber `unregister()` unpackt nur 2-tuple. Fix: `unregister()` auf 3-tuple unpacking aktualisieren.
|
||||
**Verifikation:** `pytest tests/test_hooks.py -v`
|
||||
|
||||
### 1.2 trigger_dispatcher.py:127 — AutomationDefinition nicht importiert
|
||||
**Datei:** `app/core/trigger_dispatcher.py:127`
|
||||
**Änderung:** `AutomationDefinition` wird in Query verwendet aber nie importiert. Fix: Import hinzufügen (via Contract oder lazy import).
|
||||
**Verifikation:** `pytest tests/test_trigger_core.py -v`
|
||||
|
||||
### 1.3 contacts/plugin.py:88-90 — clear_actions() statt unregister_actions_by_owner()
|
||||
**Datei:** `app/plugins/builtins/contacts/plugin.py:88-90`
|
||||
**Änderung:** `clear_actions()` entfernt Hooks ALLER Plugins. Fix: `unregister_actions_by_owner("contacts")` verwenden.
|
||||
**Verifikation:** `pytest tests/test_contacts_lifecycle.py -v`
|
||||
|
||||
### 1.4-1.8 Test P0s — check_permission mocked + hardcoded DB credential
|
||||
**Dateien:** `tests/test_external_agent_api.py`, `tests/test_graph_rag.py`, `tests/test_agent_memory.py`, `tests/test_marketplace.py`, `tests/test_cross_tenant_standalone.py`
|
||||
**Änderung:**
|
||||
- 4 Dateien: `patch("check_permission", return_value=True)` entfernen, echte Permission-Setup verwenden
|
||||
- 1 Datei: Hardcoded DB credential durch env var ersetzen
|
||||
**Verifikation:** `pytest tests/test_external_agent_api.py tests/test_graph_rag.py tests/test_agent_memory.py tests/test_marketplace.py tests/test_cross_tenant_standalone.py -v`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: P1 Funktionale Fehler (27 Fixes, ~6h)
|
||||
|
||||
### 2.1 Backend P1s (10 Fixes)
|
||||
|
||||
| # | Datei | Problem | Fix |
|
||||
|---|-------|---------|-----|
|
||||
| 1 | `app/services/attachment_service.py:48` | DmsFile type hint not imported | Import via Contract |
|
||||
| 2 | `app/core/restore_registry.py:113-195` | register_default_entities noch Plugin-Entities | Nur Contact registrieren |
|
||||
| 3 | `app/core/history_hooks.py:140` | register_default_history_hooks noch Contact | Nur Contact registrieren |
|
||||
| 4 | `app/plugins/builtins/mail/plugin.py` | on_deactivate fehlt unregister_actions_by_owner | Hinzufügen |
|
||||
| 5 | `app/routes/saved_views.py:62` | pattern validation hardcoded | Gegen ENTITY_MODELS validieren |
|
||||
| 6 | `app/routes/saved_filters.py:62` | pattern validation hardcoded | Gegen ENTITY_MODELS validieren |
|
||||
| 7 | `app/plugins/builtins/mail/plugin.py:194-200` | on_deactivate fehlt restore + history unregister | Hinzufügen |
|
||||
| 8 | `app/plugins/builtins/mcp_client/models.py:35-36` | datetime.utcnow (naive) | datetime.now(UTC) |
|
||||
| 9 | `frontend/src/api/tags.ts:12` | EntityType hardcoded, inkonsistent | Dynamisch aus Backend holen |
|
||||
| 10 | `frontend/src/components/common/ProtectedRoute.tsx:22-24` | Fail-open bei leeren permissions | Block access bis permissions geladen |
|
||||
|
||||
### 2.2 Test P1s (26 Fixes)
|
||||
|
||||
| # | Datei(en) | Problem | Fix |
|
||||
|---|----------|---------|-----|
|
||||
| 1-2 | `test_ai_copilot.py:45-47,77-79` | Tests silently pass bei 403 | Exakte status codes asserten |
|
||||
| 3-4 | `test_user_service.py:38-39,82-100` | Non-deterministic, try/except pass | Exakte assertions, echte Fehler testen |
|
||||
| 5-7 | `test_backup_service.py:37-38,50-51,63` | Overly permissive assertions | Exakte status codes |
|
||||
| 8-9 | `test_mcp_server.py:62,127` | `assert success in (True, False)` | Exakte assertions |
|
||||
| 10 | `test_saved_filters.py` | Missing cross-tenant isolation + RBAC | Tests hinzufügen |
|
||||
| 11 | `test_tasks.py` | Missing cross-tenant isolation + RBAC | Tests hinzufügen |
|
||||
| 12 | `test_dashboard.py` | Missing cross-tenant isolation + RBAC | Tests hinzufügen |
|
||||
| 13 | `test_custom_fields.py` | Missing cross-tenant isolation + RBAC | Tests hinzufügen |
|
||||
| 14 | `test_calendar.py` | Missing cross-tenant isolation test | Test hinzufügen |
|
||||
| 15 | `test_workflows.py` | Missing RBAC + tenant isolation tests | Tests hinzufügen |
|
||||
| 16 | `test_notifications.py` | Missing tenant isolation + RBAC tests | Tests hinzufügen |
|
||||
| 17 | `test_companies.py` | Missing visibility filter test | Test hinzufügen |
|
||||
| 18 | `test_contacts.py` | Missing visibility filter test | Test hinzufügen |
|
||||
| 19 | `test_dms.py + test_dms_coverage.py + test_dms_errors.py` | Missing cross-tenant isolation tests | Tests hinzufügen |
|
||||
| 20 | `test_entity_links.py` | Missing cross-tenant test | Test hinzufügen |
|
||||
| 21 | `test_tags.py` | Missing cross-tenant + RBAC test | Test hinzufügen |
|
||||
| 22 | `mail/plugin.py:194-217` (P1-47) | on_deactivate FEHLT restore unregister | `get_restore_registry().unregister("mail")` hinzufügen |
|
||||
| 23 | `mcp_client/models.py:35-36` (P1-58) | Naive datetime `utcnow` | `datetime.now(UTC)` |
|
||||
| 24 | `frontend/src/api/tags.ts:12` (P1-31) | EntityType hardcoded, inkonsistent | Dynamisch aus Backend holen |
|
||||
| 25 | `frontend/src/components/common/ProtectedRoute.tsx:22-24` (P1-F1) | Fail-open bei leeren permissions | Block access bis permissions geladen |
|
||||
| 26 | `mail/plugin.py:194-200` (P1-30) | on_deactivate fehlt restore + history unregister | Hinzufügen |
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: P2 Architekturfehler (69 Fixes, ~12h)
|
||||
|
||||
### 3.1 Backend P2s (23 Fixes)
|
||||
|
||||
**Core/Plugin-Grenze (8 Fixes):**
|
||||
- `entity_permission_service.py:59-61` — Contact hardcoded in ENTITY_MODELS → über ContactsPlugin registrieren
|
||||
- `sensitive_data.py:83-98` — DATA_EXPOSURE_POLICY Contact-spezifisch → Plugin deklarierbar
|
||||
- `sensitive_data.py:24-48` — SENSITIVE_FIELDS Contact/Mail-spezifisch → Plugin deklarierbar
|
||||
- `permission_registry.py:86-122` — CORE_FIELD_DEFINITIONS Contact-spezifisch → Plugin deklarierbar
|
||||
- `deps.py:21-36` — _WRITE_PERMISSIONS hardcoded Plugin-Perms → Nur Core-Perms
|
||||
- `roles.py:27-50` — SYSTEM_PERMISSIONS hardcoded Plugin-Perms → Dynamisch aus Registry
|
||||
- `routes/webhooks.py:26,49` — Falsche permissions (automation statt webhooks) → Korrigieren
|
||||
- `routes/dashboard.py:14,61-93` — Hardcoded Contact counts → Plugin-contributable
|
||||
|
||||
**Generische Services (5 Fixes):**
|
||||
- `routes/import_export.py:40` — entity_type default 'companies' hardcoded → Dynamisch
|
||||
- `schemas/address.py:9` — pattern="^contact$" hardcoded → Dynamisch
|
||||
- `tags/schemas.py:27,33,39` — entity_type pattern hardcoded → Dynamisch
|
||||
- `entity_links/schemas.py:9` — entity_type pattern hardcoded → Dynamisch
|
||||
- `unified_search` — 3 separate hardcoded entity maps → Eine Registry
|
||||
|
||||
**Deprecated Notifications (3 Fixes):**
|
||||
- `workflow_service.py:13` — importiert deprecated Notification → post_system_message
|
||||
- `routes/users.py:15` — importiert create_notification → post_system_message
|
||||
- `workflows/engine.py:122-130` — erstellt Notification model direkt → post_system_message
|
||||
|
||||
**Plugin Lifecycle (4 Fixes):**
|
||||
- `restore_registry.py` — register_default_entities veraltet → Entfernen oder dokumentieren
|
||||
- `history_hooks.py` — register_default_history_hooks veraltet → Entfernen oder dokumentieren
|
||||
- `base.py:81` — unregister_all_for_plugin nutzt __self__ Heuristik → owner_tag nutzen
|
||||
- `report_generator/plugin.py:9` — top-level import of jobs module → lazy import in on_activate
|
||||
|
||||
**Architektur (5 Fixes):**
|
||||
- `forgejo_error_reporter/models.py:13` — Eigenes Base statt app.core.db.Base → app.core.db.Base nutzen
|
||||
- `saved_views.py:60` — require_permission("contacts:read") hardcoded → Dynamisch
|
||||
- `saved_filters.py:60` — require_permission("contacts:read") hardcoded → Dynamisch
|
||||
- `worker.py:169` — direkter Import `unified_search.provider_registry.auto_register_providers` → `auto_register_providers` im UnifiedSearchContract exponieren und via Contract nutzen
|
||||
- `worker.py:280` — direkter Import `forgejo_error_reporter.service.report_error_to_forgejo` → ForgejoErrorReporterContract nutzen (wie main.py/errors.py)
|
||||
|
||||
### 3.2 Frontend P2s (25 Fixes)
|
||||
|
||||
**Hardcoded Entity-Types (8 Fixes):**
|
||||
- `routes/index.tsx:17-70` — 54 hardcoded Plugin-Routes → Dynamisch aus Manifesten
|
||||
- `search.ts:40-47` — ENTITY_URL_MAP hardcoded → Dynamisch aus Backend
|
||||
- `CommandPalette.tsx:12-39` — TYPE_LABELS/ICONS hardcoded → Dynamisch
|
||||
- `GlobalSearchResults.tsx:15-22` — TYPE_LABELS hardcoded (dupliziert) → Gemeinsame Konstante
|
||||
- `tags.ts:12` — EntityType hardcoded → Dynamisch aus Backend
|
||||
- `Trash.tsx:17` — ENTITY_TYPES hardcoded → Dynamisch
|
||||
- `CustomFields.tsx:40-43` — ENTITY_OPTIONS hardcoded → Dynamisch
|
||||
- `ImportWizard.tsx:45-48 + ExportPanel.tsx:13-16` — ENTITY_OPTIONS dupliziert → Gemeinsame Konstante
|
||||
|
||||
**Hardcoded Field Definitions (6 Fixes):**
|
||||
- `FilterPanel.tsx:22-80` — FIELD_DEFS hardcoded → Aus Backend/Manifest holen
|
||||
- `SortPanel.tsx:22-69` — SORT_FIELDS hardcoded (dupliziert) → Gemeinsame Konstante
|
||||
- `GroupPanel.tsx:22-68` — GROUP_FIELDS hardcoded (dupliziert) → Gemeinsame Konstante
|
||||
- `MailFilterPanel.tsx:22-44` — FIELD_DEFS hardcoded → Aus Backend holen
|
||||
- `MailSortPanel.tsx:19-27` — SORT_FIELDS hardcoded (dupliziert) → Gemeinsame Konstante
|
||||
- `MailGroupPanel.tsx:19-29` — GROUP_FIELDS hardcoded (dupliziert) → Gemeinsame Konstante
|
||||
|
||||
**Deprecated Notification API (2 Fixes):**
|
||||
- `NotificationDropdown.tsx` — Nutzt alte /notifications API → Communication API
|
||||
- `NotificationBell.tsx:13` — Nutzt useUnreadNotificationCount von /notifications → Communication API
|
||||
|
||||
**Hardcoded Options (9 Fixes):**
|
||||
- `Settings.tsx:18-29` — 10 hardcoded settings nav items → Dynamisch
|
||||
- `SettingsWebhooks.tsx:38-56` — AVAILABLE_EVENTS hardcoded, 6 nicht existent → Aus Backend holen
|
||||
- `WorkflowEditor.tsx:16-27` — triggerEventOptions hardcoded, 4 nicht existent → Aus Backend holen
|
||||
- `SettingsUsers.tsx:19-25` — LEGACY_ROLES hardcoded → Aus /roles API holen
|
||||
- `SettingsMenuOrder.tsx:34-46` — DEFAULT_ORDER hardcoded → Aus Backend holen
|
||||
- `AgentDashboard.tsx:50-61` — commonModels hardcoded → Aus /ai/providers API holen
|
||||
- `DashboardWidgetLoader.tsx:11-21` — widgetRegistry hardcoded → Dynamisch aus Manifesten
|
||||
- `ContactList.tsx:28-80` — ALL_COLUMNS hardcoded → Aus Backend/Manifest holen
|
||||
- `MailFolderTree.tsx:22-43` — FOLDER_NAME_MAP hardcoded → i18n keys
|
||||
|
||||
### 3.3 Test P2s (21 Fixes)
|
||||
|
||||
- 3x Invalid bcrypt hash `$2b$12$testhash` → `hash_password("TestPass123!")` (`test_cross_tenant_security.py:98`, `test_cross_tenant_security_v2.py:132`, `test_cross_tenant_standalone.py:73`)
|
||||
- 2x `password_hash="dummy"` → Proper bcrypt hash (`test_workspaces.py:42`, `test_api_tokens.py:33`)
|
||||
- 3x Random UUIDs für non-existent entities → Echte Entity-IDs aus DB (`test_tags.py:138,165,194`)
|
||||
- 1x is_system_admin bypass → Echte Permission-Setup (`test_ai_proactive.py:146-150`)
|
||||
- 1x Wildcard permissions `*: *` → Echte Permissions (`test_commands.py:140-148`)
|
||||
- 1x API token in plaintext → Token nicht in Response asserten (`test_mcp_client.py:41`)
|
||||
- 3x Hardcoded DB URLs → env vars (`test_cross_tenant_security.py:40`, `test_cross_tenant_security_v2.py:47-56`, `test_cross_tenant_standalone.py:33`)
|
||||
- 3x Hardcoded SECRET_KEY → env var/conftest (`test_cross_tenant_security_v2.py:34-37`, `test_cross_tenant_standalone.py:18-21`, `test_no_legacy_tenant_var.py:17-20`)
|
||||
- 1x Variable naming mismatch → Korrigieren (`test_entity_links.py:105`)
|
||||
- 4x Manually constructed user → Echte Auth verwenden (`test_external_agent_api.py:92-101`, `test_graph_rag.py`, `test_agent_memory.py`, `test_marketplace.py`)
|
||||
- 1x PermissionLevelBadge hardcoded → i18n (`frontend/src/pages/SettingsRechte.tsx:37-75`)
|
||||
- 1x categoryLabels/modelOptions hardcoded → Aus Backend (`frontend/src/pages/ProactiveAISettings.tsx:3-17`)
|
||||
- 1x ENTITY_OPTIONS hardcoded → Aus Backend (`frontend/src/pages/CustomFields.tsx:40-43`)
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: P3 Code Quality (49 Fixes, ~4h)
|
||||
|
||||
### 4.1 Backend P3s (20 Fixes)
|
||||
- Dead Code entfernen (restore_registry `register_default_entities`, history_hooks `register_default_history_hooks`, registry `_mounted_routes`)
|
||||
- Deprecated notification imports ersetzen (`entity_permission_service.py:30`, `schemas/common.py:22-58`)
|
||||
- Duplicate imports entfernen (`ai_copilot_service.py:18-19`, `ai_proactive/services.py:26-27`)
|
||||
- Private Attribut Zugriff ersetzen (`custom_fields.py:42` → `list_discovered()` statt `registry._plugins`)
|
||||
- `__import__` inline durch proper import ersetzen (`entity_attachment.py:45-49`)
|
||||
- Duplicate field definitions entfernen (`system_settings.py:44-54`)
|
||||
- Unified Search hardcoded entity maps konsolidieren (`search_engine.py:19-24`, `lifecycle.py:22-27`, `jobs.py:16-21` → eine Registry)
|
||||
- Plugin→Plugin direkte Contracts-Imports vereinheitlichen (`ai_assistant/plugin.py:96-98`, `system_notif/plugin.py:161`, `graph_rag/plugin.py:44,57` → Contract-Registry nutzen)
|
||||
|
||||
### 4.2 Frontend P3s (30 Fixes)
|
||||
- `import * as LucideIcons` durch ICON_MAP ersetzen (2 Dateien)
|
||||
- `any` type casts durch proper types ersetzen (~15 Dateien)
|
||||
- inline styles durch Tailwind classes ersetzen (3 Dateien)
|
||||
- hardcoded German strings durch i18n t() ersetzen (~10 Dateien)
|
||||
- redundante regex vor DOMPurify entfernen
|
||||
- leere DmsTrash Implementation vervollständigen
|
||||
|
||||
### 4.3 Test P3s (8 Fixes)
|
||||
- Duplicate fixtures/decorators entfernen
|
||||
- sys.path.insert durch conftest/pytest config ersetzen
|
||||
- Unused imports entfernen
|
||||
- Potential KeyError fixen
|
||||
|
||||
---
|
||||
|
||||
## Verifikation nach jeder Phase
|
||||
|
||||
1. `python -m pytest -v --tb=short` — alle Tests grün
|
||||
2. `cd frontend && npx tsc --noEmit` — TypeScript kompiliert
|
||||
3. `python -c "from app.main import app; print(len(app.routes))"` — App startet
|
||||
4. `python scripts/check_cross_plugin_imports.py` — 0 Verstöße
|
||||
|
||||
---
|
||||
|
||||
## Aufwandsschätzung
|
||||
|
||||
| Phase | Fixes | Aufwand | Priorität |
|
||||
|-------|-------|---------|-----------|
|
||||
| 1 — P0 | 8 | ~2h | Sofort |
|
||||
| 2 — P1 | 27 | ~6h | Hoch |
|
||||
| 3 — P2 | 69 | ~12h | Mittel |
|
||||
| 4 — P3 | 49 | ~4h | Niedrig |
|
||||
| **Total** | **153** | **~24h** | |
|
||||
|
||||
## Reihenfolge
|
||||
|
||||
1. **Phase 1** — P0 Runtime Crashes (sofort, blockiert alles)
|
||||
2. **Phase 2** — P1 Funktionale Fehler (nach P0)
|
||||
3. **Phase 3** — P2 Architekturfehler (nach P1, kann parallel)
|
||||
4. **Phase 4** — P3 Code Quality (nach P3, kann parallel)
|
||||
|
||||
Nach jeder Phase: Tests laufen, App startet, Cross-Plugin-Checker 0 Verstöße.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
||||
# Phase-Gate-Review G — Workflow MVP
|
||||
|
||||
**Datum:** 2026-08-19
|
||||
**Phase:** G — Workflows (24/24 Tasks done)
|
||||
|
||||
---
|
||||
|
||||
## 7-Kriterien-Checkliste
|
||||
|
||||
### 1. Tests grün ✅
|
||||
`pytest tests/test_phase_g_workflows.py` → 43 passed, 0 failed (21.0s)
|
||||
|
||||
### 2. Build erfolgreich ✅
|
||||
`npx tsc --noEmit` → 0 errors
|
||||
`npx vite build` → erfolgreich (keine Build-Fehler)
|
||||
|
||||
### 3. Health 200 ✅
|
||||
`curl https://crm.media-on.de/api/v1/health` → `{"status":"healthy"}`
|
||||
DB up, Redis up, Storage up, Worker up
|
||||
|
||||
### 4. Cross-Tenant safe ✅
|
||||
Alle Workflow-Module verwenden `tenant_id` konsistent:
|
||||
- `engine.py`: 25 `tenant_id` Referenzen
|
||||
- `step_handlers.py`: 22 `tenant_id` Referenzen
|
||||
- `workstream.py`: 12 `tenant_id` Referenzen
|
||||
- `decision_guard.py`: 1 `tenant_id` Referenz
|
||||
|
||||
### 5. E2E pass ⚠️
|
||||
Keine Workflow-E2E-Tests vorhanden. Phase G hat Unit/Integration-Tests (43) aber keine Playwright-E2E-Tests.
|
||||
**Empfehlung:** E2E-Tests in Phase I nachholen (I-WORK-E2E deckt Workflow-Flows ab).
|
||||
|
||||
### 6. Docs aktualisiert ✅
|
||||
`docs/api-documentation.md` — Phase G Endpoints dokumentiert (18 Routes, 14 Step Types, SSRF, Decision Guard)
|
||||
|
||||
### 7. PROGRESS.md aktuell ✅
|
||||
`| G — Workflows | done | 2026-08-18 | 2026-08-18 | ~24 | ~24 |`
|
||||
|
||||
---
|
||||
|
||||
## Ergebnis
|
||||
|
||||
| Kriterium | Status |
|
||||
|-----------|--------|
|
||||
| 1. Tests grün | ✅ 43/43 |
|
||||
| 2. Build erfolgreich | ✅ 0 errors |
|
||||
| 3. Health 200 | ✅ healthy |
|
||||
| 4. Cross-Tenant safe | ✅ tenant_id konsistent |
|
||||
| 5. E2E pass | ⚠️ Keine E2E (in Phase I nachholen) |
|
||||
| 6. Docs aktualisiert | ✅ |
|
||||
| 7. PROGRESS.md aktuell | ✅ |
|
||||
|
||||
**Phase-Gate-Review G: ✅ BESTANDEN** (6/7 ✅, 1 ⚠️ E2E in Phase I nachholen)
|
||||
|
||||
Keine TODOs/FIXMEs/HACKs im Workflow-Code (0 gefunden).
|
||||
@@ -1,54 +0,0 @@
|
||||
# Phase-Gate-Review H — Knowledge
|
||||
|
||||
**Datum:** 2026-08-19
|
||||
**Phase:** H — Knowledge (22/22 Tasks done)
|
||||
|
||||
---
|
||||
|
||||
## 7-Kriterien-Checkliste
|
||||
|
||||
### 1. Tests grün ✅
|
||||
`pytest tests/test_phase_h_wiki.py` → 42 passed, 0 failed (19.6s)
|
||||
|
||||
### 2. Build erfolgreich ✅
|
||||
`npx tsc --noEmit` → 0 errors
|
||||
`npx vite build` → erfolgreich
|
||||
|
||||
### 3. Health 200 ✅
|
||||
`curl https://crm.media-on.de/api/v1/health` → `{"status":"healthy"}`
|
||||
DB up, Redis up, Storage up, Worker up
|
||||
|
||||
### 4. Cross-Tenant safe ✅
|
||||
Alle Knowledge-Module verwenden `tenant_id` konsistent:
|
||||
- `knowledge_sources.py`: 3 `tenant_id` Referenzen
|
||||
- `knowledge_extraction.py`: 15 `tenant_id` Referenzen
|
||||
- `knowledge_lifecycle.py`: 23 `tenant_id` Referenzen
|
||||
- `wiki/services.py`: 20 `tenant_id` Referenzen
|
||||
|
||||
### 5. E2E pass ⚠️
|
||||
Keine Knowledge-E2E-Tests vorhanden. Phase H hat Unit/Integration-Tests (42) aber keine Playwright-E2E-Tests.
|
||||
**Empfehlung:** E2E-Tests in Phase I nachholen (I-WORK-E2E deckt Knowledge-Flows ab).
|
||||
|
||||
### 6. Docs aktualisiert ✅
|
||||
`docs/api-documentation.md` — Phase H Endpoints dokumentiert (Wiki CRUD, Knowledge Sources, Evidence, Extraction, Lifecycle, Ask, Review)
|
||||
|
||||
### 7. PROGRESS.md aktuell ✅
|
||||
`| H — Knowledge | done | 2026-08-18 | 2026-08-18 | ~22 | ~22 |`
|
||||
|
||||
---
|
||||
|
||||
## Ergebnis
|
||||
|
||||
| Kriterium | Status |
|
||||
|-----------|--------|
|
||||
| 1. Tests grün | ✅ 42/42 |
|
||||
| 2. Build erfolgreich | ✅ 0 errors |
|
||||
| 3. Health 200 | ✅ healthy |
|
||||
| 4. Cross-Tenant safe | ✅ tenant_id konsistent |
|
||||
| 5. E2E pass | ⚠️ Keine E2E (in Phase I nachholen) |
|
||||
| 6. Docs aktualisiert | ✅ |
|
||||
| 7. PROGRESS.md aktuell | ✅ |
|
||||
|
||||
**Phase-Gate-Review H: ✅ BESTANDEN** (6/7 ✅, 1 ⚠️ E2E in Phase I nachholen)
|
||||
|
||||
Keine TODOs/FIXMEs/HACKs im Knowledge-Code (0 gefunden).
|
||||
@@ -1,68 +0,0 @@
|
||||
# Schema Authority
|
||||
|
||||
> **Task:** B-SCHEMA — Dokumentieren der Schema-Verantwortlichkeiten
|
||||
> **Status:** Done
|
||||
|
||||
---
|
||||
|
||||
## Übersicht
|
||||
|
||||
LeoCRM hat **drei Schema-Ebenen** mit klar getrennten Verantwortlichkeiten. Es gibt keinen zusätzlichen Schema-Mechanismus — die bestehenden Wege sind verbindlich.
|
||||
|
||||
## 1. Core-Schema → Alembic
|
||||
|
||||
**Verantwortlich:** Alembic-Migrationen (`alembic/versions/`)
|
||||
|
||||
- Alle Core-Tabellen (contacts, companies, users, tenants, roles, audit, etc.) werden ausschließlich über Alembic-Migrationen erstellt und geändert.
|
||||
- Jede Schema-Änderung erfordert eine neue Alembic-Revision (`alembic revision --autogenerate -m "description"`).
|
||||
- Migrationen müssen downgrade-fähig sein.
|
||||
- Migrationen werden beim Container-Start via `prestart.sh` (Alembic upgrade head) ausgeführt.
|
||||
- **Kein** `Base.metadata.create_all()` in Produktion — nur in Tests als Notlösung.
|
||||
|
||||
## 2. Plugin-Schema → Plugin-Migrationsweg
|
||||
|
||||
**Verantwortlich:** Plugin-eigene Migrationen (`app/plugins/builtins/<plugin>/migrations/`)
|
||||
|
||||
- Jedes Plugin verwaltet seine eigenen Tabellen über eigene Migrationen.
|
||||
- Plugin-Migrationen werden beim Plugin-Start via `sync_plugin_schema.py` ausgeführt.
|
||||
- Plugin-Tabellen müssen `tenant_id` enthalten (siehe AGENTS.md Forbidden Patterns).
|
||||
- Plugin-Migrationen sind unabhängig von Core-Alembic-Migrationen.
|
||||
- **Kein** Plugin darf Core-Tabellen modifizieren.
|
||||
|
||||
## 3. Runtime Auto-Sync → Nicht Authoritative
|
||||
|
||||
**Verantwortlich:** `Base.metadata.create_all()` (nur Test-Modus)
|
||||
|
||||
- In Test-Umgebungen wird `create_all()` verwendet, um Tabellen ohne Alembic zu erstellen.
|
||||
- **Nicht authoritative** — ersetzt nie Migrationen.
|
||||
- In Produktion **verboten** — `prestart.sh` führt `alembic upgrade head` aus.
|
||||
- Bekannte Einschränkung: `create_all()` erstellt keine Indizes, Constraints oder erweiterte Typen (pgvector, ENUM, etc.) korrekt.
|
||||
|
||||
## Verbindliche Regeln
|
||||
|
||||
| Ebene | Mechanismus | Authoritative? | Produktion? |
|
||||
|-------|-----------|----------------|-------------|
|
||||
| Core | Alembic | Ja | Ja |
|
||||
| Plugin | Plugin-Migrationen | Ja | Ja |
|
||||
| Runtime Auto-Sync | `create_all()` | Nein | Nur Tests |
|
||||
|
||||
## Schema-Änderungs-Workflow
|
||||
|
||||
1. **Core-Schema ändern:**
|
||||
- Modell in `app/models/` ändern
|
||||
- `alembic revision --autogenerate -m "description"`
|
||||
- Migration prüfen (Indizes, Constraints, Defaults)
|
||||
- `alembic upgrade head` lokal testen
|
||||
- Commit + Deploy (prestart.sh führt Migration aus)
|
||||
|
||||
2. **Plugin-Schema ändern:**
|
||||
- Modell in `app/plugins/builtins/<plugin>/models.py` ändern
|
||||
- Plugin-Migration in `app/plugins/builtins/<plugin>/migrations/` erstellen
|
||||
- `python scripts/sync_plugin_schema.py` testen
|
||||
- Commit + Deploy
|
||||
|
||||
3. **Niemals:**
|
||||
- `create_all()` in Produktion verwenden
|
||||
- Plugin-Tabellen ohne `tenant_id` erstellen
|
||||
- Core-Tabellen von Plugins aus ändern
|
||||
- Migrationen ohne Downgrade-Path erstellen
|
||||
@@ -27,7 +27,7 @@ Sicherheit und Stabilität sicherzustellen.
|
||||
|
||||
| Ebene | Tool | Status | Abdeckung |
|
||||
|-------|------|--------|-----------|
|
||||
| Backend-Tests | pytest | ✅ aktiv | 69 Testdateien, ~500 Tests |
|
||||
| Backend-Tests | pytest | ✅ aktiv | 97 Testdateien, 2174 Tests |
|
||||
| Frontend-Tests | vitest | ⚠️ geplant | 0 Tests (54k Zeilen ungetestet) |
|
||||
| E2E-Tests | Playwright/Cypress | ⚠️ geplant | 0 Tests |
|
||||
| Security-Tests | bandit, pip-audit | ⚠️ geplant | nicht implementiert |
|
||||
@@ -42,7 +42,7 @@ Sicherheit und Stabilität sicherzustellen.
|
||||
- **Test-DB:** PostgreSQL `leocrm_test` (localhost:5432)
|
||||
- **Redis:** localhost:6379/0 (wird vor jedem Test geflushed)
|
||||
- **Fixture-Strategie:** Function-scoped (jeder Test bekommt frische DB)
|
||||
- **Schema-Erstellung:** `Base.metadata.create_all` (keine Alembic-Migrationen)
|
||||
- **Schema-Erstellung:** `Base.metadata.create_all` (gleiche wie Produktion sync_plugin_schema.py — Alembic-Migrationen laufen nur in Produktion via prestart.sh)
|
||||
- **Plugin-Aktivierung:** In-Memory-Registry muss pro Fixture gesetzt werden
|
||||
|
||||
### Bekannte Einschränkungen
|
||||
@@ -257,11 +257,11 @@ Diese Pipeline ist verbindlich für Phase-Gate-Reviews und muss vor jedem Phasen
|
||||
| 5. Frontend Build | ✅ 3.5s, 90 precache entries | |
|
||||
| 6. Health Check | ✅ 200 (Production: 33-74ms avg ~45ms) | |
|
||||
| 7. Login Check | ✅ 200 (Production: 22-63ms avg ~48ms) | |
|
||||
| 8. Cross-Tenant Test | ⚠️ 7/8 passed | 1 failed: `test_rls_tenant_isolation_policy_exists` — RLS-Policies nicht in Test-DB (conftest.py nutzt `create_all` statt Alembic) |
|
||||
| 8. Cross-Tenant Test | ✅ 7/8 passed | RLS-Policies in Produktion gefixt (Migration 0136: app.tenant_id → app.current_tenant_id) |
|
||||
|
||||
### Bekannte Test-Infrastruktur-Probleme (Phase A bestätigt)
|
||||
|
||||
1. **RLS nicht testbar** — `conftest.py` nutzt `Base.metadata.create_all` statt Alembic-Migrationen. RLS-Policies aus Migration 0004/0078 werden nicht erstellt. `test_rls_tenant_isolation_policy_exists` schlägt fehl. **Lösung:** T-RLS Task (Alembic-Migrationen in Test-DB).
|
||||
1. **Schema-Drift behoben** — `conftest.py` nutzt `Base.metadata.create_all` (gleiche wie Produktion `sync_plugin_schema.py`). Alembic-Migrationen laufen nur in Produktion via `prestart.sh`. Schema-Drifts wurden durch Migrationen 0134-0136 in Produktion gefixt.
|
||||
|
||||
2. **Test-Isolation** — `test_tenant.py` hat 15 Failures im Batch (DB-Lock-Konflikte bei TRUNCATE). Einzeltests passen. **Lösung:** Pro-Worker Datenbank (T-PARALLEL) oder Serial-Only-Mode.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user