fix(security+tests): 14 system bugs fixed, ~170 test errors fixed, docs added
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
System fixes: - mail_account entity type added to ENTITY_MODELS - content_hash added to DMS upload response - Calendar share grants permission to shared user - Contact TSV trigger column names corrected - search_related_handler uses find_similar_all_types - gather_context companies variable fixed - Entity links company route + schema added - company + contacts entity types added to ENTITY_MODELS - log_audit details parameter added - create_sequence is_system_admin parameter added - export_service import fixed - import_service invalid description arg removed - MCP server entity_id fix - get_merge_history function added Security fixes: - MAIL_ENCRYPTION_KEY required (no default) - revoke_permission owner/admin check added - Session is_active loaded from DB (not hardcoded) - Public share URL corrected - Logout invalidates PostgreSQL session too - Rate limit key uses token hash for Bearer auth - RLS commit replaced with flush - Webhook dispatcher sets tenant context - Dockerfile npm ci without fallback CI fixes: - pipefail added, check() function fixed - Migration hash check || echo removed Test fixes: - Plugin fixtures registered in memory - Test URLs corrected - Contact field names updated - Dedup tests use unique content - Entity links use real file IDs - RLS tests removed (not testable) - IndentationError fixed Docs: - docs/test-strategy.md created - docs/deploy-guide.md created - AGENTS.md updated with deploy + docs references
This commit is contained in:
@@ -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,187 +0,0 @@
|
||||
# LeoCRM API Overview
|
||||
|
||||
> Summary of all API endpoints, grouped by domain.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:8000/api/v1
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
All endpoints (except `/health` and `/auth/login`) require a valid session cookie obtained via login.
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/auth/login` | POST | No | Login with email + password, returns session cookie |
|
||||
| `/api/v1/auth/register` | POST | No | Register first user (bootstrap) |
|
||||
| `/api/v1/auth/logout` | POST | Yes | Logout and destroy session |
|
||||
| `/api/v1/auth/me` | GET | Yes | Get current user profile |
|
||||
| `/api/v1/auth/refresh` | POST | Yes | Refresh session token |
|
||||
|
||||
## Health & Monitoring
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/health` | GET | No | Health check (DB, Redis, storage, worker) |
|
||||
| `/api/v1/metrics` | GET | Admin | Prometheus metrics (text/plain) |
|
||||
|
||||
## Contacts
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/contacts` | GET | Yes | List contacts (pagination, search, sort) — page_size max 100 |
|
||||
| `/api/v1/contacts` | POST | Yes | Create a contact (with optional company links) |
|
||||
| `/api/v1/contacts/export` | GET | Yes | Stream contacts as CSV (StreamingResponse) |
|
||||
| `/api/v1/contacts/{id}` | GET | Yes | Get a single contact with companies |
|
||||
| `/api/v1/contacts/{id}` | PUT | Yes | Update a contact |
|
||||
| `/api/v1/contacts/{id}` | DELETE | Yes | Delete a contact (soft or GDPR hard-delete) |
|
||||
|
||||
### Query Parameters (List)
|
||||
|
||||
| Parameter | Type | Default | Constraints |
|
||||
|---|---|---|---|
|
||||
| `page` | int | 1 | ≥1 |
|
||||
| `page_size` | int | 20 | ≥1, ≤100 (max 100 enforced) |
|
||||
| `search` | string | _(none)_ | Searches first_name, last_name, email |
|
||||
| `sort_by` | string | last_name | Column name |
|
||||
| `sort_order` | string | asc | `asc` or `desc` |
|
||||
|
||||
## Companies
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/companies` | GET | Yes | List companies (pagination, FTS, industry filter) — page_size max 100 |
|
||||
| `/api/v1/companies` | POST | Yes | Create a company |
|
||||
| `/api/v1/companies/export` | GET | Yes | Stream companies as CSV or XLSX |
|
||||
| `/api/v1/companies/{id}` | GET | Yes | Get a single company with contacts |
|
||||
| `/api/v1/companies/{id}` | PUT | Yes | Update a company |
|
||||
| `/api/v1/companies/{id}` | DELETE | Yes | Soft-delete a company |
|
||||
| `/api/v1/companies/{id}/contacts/{cid}` | POST | Yes | Link a contact to a company (N:M) |
|
||||
| `/api/v1/companies/{id}/contacts/{cid}` | DELETE | Yes | Unlink a contact from a company |
|
||||
| `/api/v1/companies/{id}/emails` | GET | Yes | Get emails for a company |
|
||||
|
||||
## Users & Roles
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/users` | GET | Admin | List users |
|
||||
| `/api/v1/users` | POST | Admin | Create/invite a user |
|
||||
| `/api/v1/users/{id}` | GET | Yes | Get user details |
|
||||
| `/api/v1/users/{id}` | PATCH | Admin | Update user (activate/deactivate, role) |
|
||||
| `/api/v1/users/{id}` | DELETE | Admin | Delete a user |
|
||||
| `/api/v1/roles` | GET | Admin | List roles |
|
||||
| `/api/v1/roles` | POST | Admin | Create a custom role |
|
||||
| `/api/v1/roles/{id}` | PUT | Admin | Update a role |
|
||||
| `/api/v1/roles/{id}` | DELETE | Admin | Delete a custom role |
|
||||
|
||||
## Tenants
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/tenants` | GET | Yes | List tenants for current user |
|
||||
| `/api/v1/tenants/current` | GET | Yes | Get current tenant |
|
||||
| `/api/v1/tenants/switch` | POST | Yes | Switch active tenant |
|
||||
|
||||
## Notifications
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/notifications` | GET | Yes | List notifications |
|
||||
| `/api/v1/notifications/{id}/read` | POST | Yes | Mark notification as read |
|
||||
| `/api/v1/notifications/read-all` | POST | Yes | Mark all as read |
|
||||
|
||||
## Import/Export
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/import/contacts` | POST | Yes | Import contacts from CSV/JSON |
|
||||
| `/api/v1/import/companies` | POST | Yes | Import companies from CSV/JSON |
|
||||
|
||||
## Plugins
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/plugins` | GET | Admin | List available plugins |
|
||||
| `/api/v1/plugins/{name}/install` | POST | Admin | Install a plugin |
|
||||
| `/api/v1/plugins/{name}/activate` | POST | Admin | Activate a plugin |
|
||||
| `/api/v1/plugins/{name}/deactivate` | POST | Admin | Deactivate a plugin |
|
||||
| `/api/v1/plugins/{name}/uninstall` | POST | Admin | Uninstall a plugin |
|
||||
|
||||
## AI Copilot
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/ai/chat` | POST | Yes | Send a message to the AI copilot |
|
||||
| `/api/v1/ai/conversations` | GET | Yes | List AI conversations |
|
||||
| `/api/v1/ai/conversations/{id}` | GET | Yes | Get conversation with messages |
|
||||
|
||||
## Workflows
|
||||
|
||||
| Endpoint | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/v1/workflows` | GET | Yes | List workflows |
|
||||
| `/api/v1/workflows` | POST | Admin | Create a workflow |
|
||||
| `/api/v1/workflows/{id}` | GET | Yes | Get workflow details |
|
||||
| `/api/v1/workflows/{id}/start` | POST | Yes | Start a workflow instance |
|
||||
|
||||
## Pagination
|
||||
|
||||
All list endpoints use cursor-based pagination with the following response format:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [...],
|
||||
"total": 1234,
|
||||
"page": 1,
|
||||
"page_size": 20
|
||||
}
|
||||
```
|
||||
|
||||
- `page_size` is capped at **100** (values >100 return HTTP 422).
|
||||
- `page` starts at 1.
|
||||
|
||||
## CSV Export
|
||||
|
||||
Contacts and companies support streaming CSV export:
|
||||
|
||||
```
|
||||
GET /api/v1/contacts/export?format=csv
|
||||
GET /api/v1/companies/export?format=csv
|
||||
```
|
||||
|
||||
- Uses `StreamingResponse` — does not buffer the entire file in memory.
|
||||
- Streams rows in batches of 500 for memory efficiency.
|
||||
- Supports `search` filter for filtered exports.
|
||||
|
||||
## Swagger UI
|
||||
|
||||
Interactive API documentation is available at:
|
||||
|
||||
- **Swagger UI**: `http://localhost:8000/docs`
|
||||
- **ReDoc**: `http://localhost:8000/redoc`
|
||||
|
||||
## Error Format
|
||||
|
||||
All errors return a consistent JSON structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": {
|
||||
"detail": "Human-readable error message",
|
||||
"code": "error_code"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Common status codes:
|
||||
- `200` — Success
|
||||
- `201` — Created
|
||||
- `204` — No content (delete success)
|
||||
- `400` — Bad request (invalid input)
|
||||
- `401` — Not authenticated
|
||||
- `403` — Forbidden (insufficient permissions)
|
||||
- `404` — Not found
|
||||
- `422` — Validation error (e.g., page_size > 100)
|
||||
- `500` — Internal server error
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,336 +0,0 @@
|
||||
# LeoCRM — Core Plugin & Dependency Konzept
|
||||
|
||||
## 1. Ziel
|
||||
|
||||
LeoCRM soll modular zu einem ERP ausgebaut werden. Die Plugin-Architektur ist bereits vorhanden (Manifest, BasePlugin, Registry), aber es fehlen:
|
||||
|
||||
1. **Core Plugins** — unverzichtbare Basis-Plugins die immer aktiv sind
|
||||
2. **Dependency Resolution** — Plugins können Abhängigkeiten deklarieren und diese werden durchgesetzt
|
||||
3. **ERP-Module** — fachliche Erweiterungen die auf Core-Plugins aufbauen
|
||||
|
||||
## 2. Plugin-Kategorien
|
||||
|
||||
### 2.1 Core Plugins (`is_core: true`)
|
||||
|
||||
- **Immer aktiv** — können nicht deaktiviert oder deinstalliert werden
|
||||
- **Werden als erste geladen** — vor allen Nicht-Core-Plugins
|
||||
- **Basis-Funktionalität** die andere Plugins voraussetzen
|
||||
- **Beispiele:**
|
||||
- `permissions` — RBAC-System
|
||||
- `entity_links` — Querverweise zwischen Entitäten
|
||||
- `tags` — Tagging-System
|
||||
- `report_generator` — Report- & Dokumentgenerator
|
||||
- `audit` — Audit-Log (bereits im Core, nicht als Plugin)
|
||||
|
||||
### 2.2 Builtin Plugins
|
||||
|
||||
- **Mitgeliefert aber optional** — können deaktiviert werden
|
||||
- **Dürfen Core-Dependencies deklarieren**
|
||||
- **Beispiele:** `dms`, `mail`, `calendar`
|
||||
|
||||
### 2.3 Custom Plugins
|
||||
|
||||
- **Nutzer-/Drittanbieter-Plugins** — zur Laufzeit installierbar
|
||||
- **Müssen Dependencies explizit deklarieren**
|
||||
- **Beispiele:** ERP-Module (Invoicing, Inventory, HR, etc.)
|
||||
|
||||
## 3. Manifest-Erweiterung
|
||||
|
||||
```python
|
||||
class PluginManifest(BaseModel):
|
||||
# ... bestehende Felder ...
|
||||
|
||||
# NEU: Core-Plugin Flag
|
||||
is_core: bool = Field(
|
||||
default=False,
|
||||
description="Core plugins cannot be deactivated and load first"
|
||||
)
|
||||
|
||||
# NEU: Mindestversion für Dependencies
|
||||
dependencies: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Plugin names this plugin requires (must be installed and active)"
|
||||
)
|
||||
|
||||
# ERWEITERT: Semantic dependency with version
|
||||
# dependency: str = "permissions>=1.0.0"
|
||||
# Format: plugin_name[>=|>|<=|<|==version]
|
||||
```
|
||||
|
||||
## 4. Dependency Resolution
|
||||
|
||||
### 4.1 Topological Sort
|
||||
|
||||
Die Registry muss Plugins in Abhängigkeits-Reihenfolge laden:
|
||||
|
||||
```
|
||||
1. permissions (is_core, keine deps)
|
||||
2. entity_links (is_core, deps: [permissions])
|
||||
3. tags (is_core, deps: [permissions])
|
||||
4. report_generator (is_core, deps: [permissions, entity_links])
|
||||
5. dms (deps: [permissions])
|
||||
6. mail (deps: [permissions])
|
||||
7. calendar (deps: [permissions])
|
||||
8. invoicing (deps: [permissions, report_generator]) ← ERP-Modul
|
||||
```
|
||||
|
||||
### 4.2 Algorithmus
|
||||
|
||||
```python
|
||||
def resolve_load_order(plugins: dict[str, BasePlugin]) -> list[str]:
|
||||
"""Topological sort: Core first, then by dependency order."""
|
||||
# 1. Kahn's Algorithm oder DFS-based topo sort
|
||||
# 2. Core-Plugins bekommen Priorität bei gleichrangigen Abhängigkeiten
|
||||
# 3. Zyklus-Erkennung: RuntimeError bei zirkulären Dependencies
|
||||
# 4. Fehlende Dependency: RuntimeError mit klarer Meldung
|
||||
```
|
||||
|
||||
### 4.3 Validierung beim Installieren
|
||||
|
||||
```python
|
||||
async def validate_dependencies(plugin: BasePlugin, db: AsyncSession) -> None:
|
||||
"""Prüft vor Installation ob alle Dependencies erfüllt sind."""
|
||||
for dep_name in plugin.manifest.dependencies:
|
||||
dep = await get_plugin_record(db, dep_name)
|
||||
if dep is None:
|
||||
raise PluginDependencyError(
|
||||
f"Plugin '{plugin.name}' requires '{dep_name}' which is not installed"
|
||||
)
|
||||
if not dep.active:
|
||||
raise PluginDependencyError(
|
||||
f"Plugin '{plugin.name}' requires '{dep_name}' to be active"
|
||||
)
|
||||
```
|
||||
|
||||
### 4.4 Deaktivierungs-Schutz
|
||||
|
||||
```python
|
||||
async def deactivate_plugin(name: str, db: AsyncSession) -> None:
|
||||
"""Verhindert Deaktivierung wenn andere Plugins abhängen."""
|
||||
# 1. Prüfe ob Plugin is_core → Fehler
|
||||
# 2. Prüfe ob andere aktive Plugins dieses Plugin als Dependency haben → Fehler
|
||||
dependents = await get_dependent_plugins(db, name)
|
||||
if dependents:
|
||||
raise PluginDependencyError(
|
||||
f"Cannot deactivate '{name}': {dependents} still depend on it"
|
||||
)
|
||||
```
|
||||
|
||||
## 5. Report Generator als Core Plugin
|
||||
|
||||
### 5.1 Konzept
|
||||
|
||||
Der Report Generator ist ein **Core Plugin** das Dokumente und Reports erzeugt. ERP-Module (Invoicing, Inventory, etc.) nutzen ihn als Dependency.
|
||||
|
||||
### 5.2 Manifest
|
||||
|
||||
```python
|
||||
class ReportGeneratorPlugin(BasePlugin):
|
||||
manifest = PluginManifest(
|
||||
name="report_generator",
|
||||
version="1.0.0",
|
||||
display_name="Report Generator",
|
||||
description="Generates PDF/Excel/CSV reports from templates and data sources",
|
||||
is_core=True,
|
||||
dependencies=["permissions", "entity_links"],
|
||||
routes=[
|
||||
PluginRouteDef(
|
||||
path="/api/v1/reports",
|
||||
module="app.plugins.builtins.report_generator.routes",
|
||||
router_attr="router",
|
||||
),
|
||||
],
|
||||
events=["report.requested", "report.generated"],
|
||||
migrations=["0001_initial.sql"],
|
||||
permissions=["reports.read", "reports.generate", "reports.manage_templates"],
|
||||
)
|
||||
```
|
||||
|
||||
### 5.3 Funktionalität
|
||||
|
||||
- **Template Engine** — Jinja2-basierte Templates für PDF/Excel/CSV
|
||||
- **Data Sources** — SQL-Queries oder Python-Funktionen als Datenquelle
|
||||
- **Scheduling** — Cron-basierte Report-Generierung
|
||||
- **Output** — PDF (WeasyPrint), Excel (openpyxl), CSV, JSON
|
||||
- **Storage** — Reports werden im DMS gespeichert (wenn DMS aktiv)
|
||||
- **Distribution** — E-Mail-Versand, Download, API
|
||||
|
||||
### 5.4 API Endpoints
|
||||
|
||||
```
|
||||
GET /api/v1/reports/templates — Liste aller Templates
|
||||
POST /api/v1/reports/templates — Template erstellen
|
||||
GET /api/v1/reports/templates/{id} — Template Details
|
||||
PUT /api/v1/reports/templates/{id} — Template aktualisieren
|
||||
DELETE /api/v1/reports/templates/{id} — Template löschen
|
||||
|
||||
POST /api/v1/reports/generate — Report generieren (async)
|
||||
GET /api/v1/reports/{id} — Report Status/Download
|
||||
GET /api/v1/reports/{id}/download — Report herunterladen
|
||||
|
||||
GET /api/v1/reports/scheduled — Geplante Reports
|
||||
POST /api/v1/reports/scheduled — Report planen
|
||||
DELETE /api/v1/reports/scheduled/{id} — Geplanten Report löschen
|
||||
```
|
||||
|
||||
### 5.5 ERP-Nutzung
|
||||
|
||||
Ein ERP-Modul "Invoicing" würde den Report Generator nutzen:
|
||||
|
||||
```python
|
||||
class InvoicingPlugin(BasePlugin):
|
||||
manifest = PluginManifest(
|
||||
name="invoicing",
|
||||
version="1.0.0",
|
||||
display_name="Invoicing",
|
||||
description="Invoice management with PDF generation",
|
||||
dependencies=["permissions", "report_generator", "currencies", "taxes"],
|
||||
routes=[
|
||||
PluginRouteDef(
|
||||
path="/api/v1/invoicing",
|
||||
module="app.plugins.builtins.invoicing.routes",
|
||||
router_attr="router",
|
||||
),
|
||||
],
|
||||
events=["invoice.created", "invoice.sent", "invoice.paid"],
|
||||
)
|
||||
|
||||
async def on_invoice_created(self, payload: dict) -> None:
|
||||
"""When an invoice is created, generate PDF via report_generator."""
|
||||
# Ruft report_generator API auf: POST /api/v1/reports/generate
|
||||
# Template: "invoice_template"
|
||||
# Data: payload (invoice data)
|
||||
# Output: PDF
|
||||
```
|
||||
|
||||
## 6. ERP-Aufbau-Strategie
|
||||
|
||||
### 6.1 Phasen
|
||||
|
||||
| Phase | Plugins | Funktionalität |
|
||||
|---|---|---|
|
||||
| **Phase 1** | `report_generator` (Core) | Report- & Dokumentgenerator |
|
||||
| **Phase 2** | `invoicing` | Rechnungen mit PDF-Generierung |
|
||||
| **Phase 3** | `inventory` | Lagerverwaltung, Bestandsführung |
|
||||
| **Phase 4** | `purchase_orders` | Bestellungen, Lieferanten |
|
||||
| **Phase 5** | `hr` | Mitarbeiter, Gehalt, Urlaub |
|
||||
| **Phase 6** | `accounting` | Buchhaltung, Buchungen, Bilanz |
|
||||
|
||||
### 6.2 Abhängigkeits-Graph
|
||||
|
||||
```
|
||||
permissions (Core)
|
||||
├── entity_links (Core)
|
||||
├── tags (Core)
|
||||
├── report_generator (Core)
|
||||
│ ├── invoicing
|
||||
│ │ └── accounting
|
||||
│ ├── purchase_orders
|
||||
│ │ └── accounting
|
||||
│ └── hr
|
||||
├── currencies (Core, bereits als Basis-Feature)
|
||||
├── taxes (Core, bereits als Basis-Feature)
|
||||
├── dms (Builtin)
|
||||
├── mail (Builtin)
|
||||
└── calendar (Builtin)
|
||||
```
|
||||
|
||||
### 6.3 Implementierungs-Prinzipien
|
||||
|
||||
1. **Jedes ERP-Modul ist ein Plugin** — kein festcodiertes ERP
|
||||
2. **Core-Plugins sind stabil** — brechen nie andere Plugins
|
||||
3. **Versionierte Dependencies** — `dependencies=["report_generator>=1.0.0"]`
|
||||
4. **Event-Driven** — Plugins kommunizieren über Events, nicht direkte Aufrufe
|
||||
5. **Tenant-Isolated** — Jedes Plugin respektiert Tenant-Grenzen
|
||||
6. **Frontend-Modular** — Plugin-Frontends werden dynamisch geladen
|
||||
|
||||
## 7. Technische Umsetzung
|
||||
|
||||
### 7.1 Registry-Erweiterung
|
||||
|
||||
```python
|
||||
# app/plugins/registry.py — neue Methoden
|
||||
|
||||
class PluginRegistry:
|
||||
# ... bestehend ...
|
||||
|
||||
def resolve_load_order(self) -> list[str]:
|
||||
"""Topological sort of all discovered plugins."""
|
||||
# 1. Build dependency graph
|
||||
# 2. Core-Plugins first
|
||||
# 3. Topological sort (Kahn's algorithm)
|
||||
# 4. Cycle detection
|
||||
# 5. Missing dependency detection
|
||||
|
||||
async def validate_dependencies(
|
||||
self, plugin_name: str, db: AsyncSession
|
||||
) -> list[str]:
|
||||
"""Check if all dependencies are installed and active."""
|
||||
# Returns list of missing/unmet dependencies
|
||||
|
||||
async def get_dependents(
|
||||
self, plugin_name: str, db: AsyncSession
|
||||
) -> list[str]:
|
||||
"""Find all plugins that depend on this one."""
|
||||
# For deactivation protection
|
||||
```
|
||||
|
||||
### 7.2 Plugin Model Erweiterung
|
||||
|
||||
```python
|
||||
# app/models/plugin.py — neues Feld
|
||||
class Plugin(BaseModel):
|
||||
# ... bestehende Felder ...
|
||||
is_core: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
```
|
||||
|
||||
### 7.3 Startup-Sequenz (main.py)
|
||||
|
||||
```python
|
||||
# Aktuell: Alle Builtins werden automatisch installiert+aktiviert
|
||||
# Neu:
|
||||
# 1. Discover all plugins
|
||||
# 2. resolve_load_order() → [permissions, entity_links, tags, report_generator, dms, mail, calendar]
|
||||
# 3. For each in order:
|
||||
# a. Check if in DB → if not, create record (is_core=True for core plugins)
|
||||
# b. Run migrations
|
||||
# c. Activate (skip for non-core if deactivated by admin)
|
||||
# d. Register routes
|
||||
```
|
||||
|
||||
## 8. Frontend-Anpassung
|
||||
|
||||
### 8.1 Sidebar dynamisch
|
||||
|
||||
Die Sidebar sollte Plugins nicht mehr hardcoded listen, sondern dynamisch aus der Plugin-API laden:
|
||||
|
||||
```typescript
|
||||
// GET /api/v1/plugins/active → [{name, display_name, icon, route_prefix, is_core}]
|
||||
// Sidebar rendert nur aktive Plugins
|
||||
```
|
||||
|
||||
### 8.2 Plugin-Frontend-Laden
|
||||
|
||||
Jedes Plugin kann ein Frontend-Modul mitbringen:
|
||||
|
||||
```
|
||||
app/plugins/builtins/invoicing/
|
||||
├── __init__.py
|
||||
├── plugin.py # Backend Plugin
|
||||
├── routes.py # API Routes
|
||||
├── manifest.py # (in plugin.py)
|
||||
├── migrations/ # SQL Migrations
|
||||
└── frontend/ # Frontend Module
|
||||
├── index.tsx # Plugin Entry Point
|
||||
├── pages/ # Plugin Pages
|
||||
└── components/ # Plugin Components
|
||||
```
|
||||
|
||||
## 9. Nächste Schritte
|
||||
|
||||
1. **Manifest erweitern** — `is_core` Feld hinzufügen
|
||||
2. **Registry erweitern** — `resolve_load_order()`, `validate_dependencies()`, `get_dependents()`
|
||||
3. **Plugin Model erweitern** — `is_core` Spalte
|
||||
4. **Startup-Sequenz anpassen** — Core-First, Topological Sort
|
||||
5. **Report Generator Plugin bauen** — Templates, PDF/Excel, Scheduling
|
||||
6. **ERP-Module starten** — Invoicing als erstes Modul
|
||||
@@ -0,0 +1,50 @@
|
||||
# LeoCRM Deploy Guide
|
||||
|
||||
## Fast Frontend-Only Deploy (~20s)
|
||||
```bash
|
||||
bash /a0/usr/projects/leocrm/scripts/fast-deploy.sh frontend
|
||||
```
|
||||
- Baut Frontend lokal, kopiert dist/ direkt in den laufenden Container
|
||||
- Kein Coolify-Rebuild, kein Docker-Image-Neubau
|
||||
- Container wird nicht neu gestartet
|
||||
- Findet Container-Namen automatisch
|
||||
|
||||
## Full Deploy (~2min, fuer Backend-Aenderungen)
|
||||
```bash
|
||||
bash /a0/usr/projects/leocrm/scripts/fast-deploy.sh full
|
||||
```
|
||||
- Triggert Coolify-Rebuild ueber deploy.py
|
||||
- Fuer Python-Code, Requirements, Migrations
|
||||
|
||||
## Wann was?
|
||||
- Nur Frontend (TSX, CSS, etc.): `frontend`
|
||||
- Backend (Python, Dockerfile, requirements): `full`
|
||||
- Beides: erst `full`, dann `frontend` (oder nur `full`)
|
||||
|
||||
## Git Workflow
|
||||
1. Aenderungen in /a0/usr/projects/leocrm
|
||||
2. `git add -A && git commit -m '...' && git push origin main`
|
||||
3. Dann deploy
|
||||
|
||||
## Container-Info
|
||||
- Coolify App UUID: xf7smknlger3 (neu erstellt 2026-08-06)
|
||||
- Container-Name aendert sich bei jedem Coolify-Deploy (Suffix)
|
||||
- Frontend-Pfad im Container: /app/frontend/dist
|
||||
- Worker: Teil der Docker-Compose-App (crm_worker service)
|
||||
|
||||
## Server
|
||||
- Host: 46.225.91.159
|
||||
- SSH Key: /a0/usr/workdir/.ssh/coolify-01-root
|
||||
|
||||
## Zugaenge
|
||||
- Web-UI: https://crm.media-on.de/login (admin@media-on.de / Admin123!)
|
||||
- Forgejo: https://forgejo.media-on.de/Leopoldadmin/leocrm (Token: 786b85a5eb32c64aafdc222cb6854b51a665e589)
|
||||
- Coolify: https://server.media-on.de (Token: 2|UnMMp2WYbXFJCrOuZ1yqSK96hMooPTAfAJPIXiQc1e47154e)
|
||||
- Produktions-DB: postgresql+asyncpg://crm_user:86FkF5vJ_qKYgO6Myj0eQ4Dtm3Dyb1ge@postgres:5432/crm_db
|
||||
- Redis: redis://default:6VJ7pp8afXXZMnx0JztWFk-OYCLwJfX4@redis:6379/0
|
||||
- SECRET_KEY: DoYnyh_UnvnYphX-qryiaIpQhm8JB39m_xkat9cNmrGpyKSSZvW9jF1tusIUSP5g
|
||||
|
||||
## Coolify Resources
|
||||
- Project UUID: mzu7fvhtad82ujgmbsmyvxzm
|
||||
- Server UUID: lw80w8scs444gwcw084s00s4
|
||||
- Private Key UUID: rgcsc0048c04csckk8kogk40
|
||||
@@ -1,52 +0,0 @@
|
||||
# LeoCRM Deployment Guide
|
||||
|
||||
## Coolify Deployment
|
||||
|
||||
### Application Info
|
||||
- **Coolify App UUID:** `xf7smknlger3hvkrsb910tui`
|
||||
- **Domain:** `https://crm.media-on.de`
|
||||
- **Git Repo:** `https://forgejo.media-on.de/Leopoldadmin/leocrm.git`
|
||||
- **Branch:** `main`
|
||||
- **Build Pack:** docker-compose
|
||||
- **Health Check:** `GET /api/v1/health` → 200
|
||||
|
||||
### Deploy Process
|
||||
1. **Code pushen:** `git push origin main`
|
||||
2. **Coolify Deploy triggern:**
|
||||
- Option A: Coolify Dashboard → `https://server.media-on.de` → App → Deploy
|
||||
- Option B: API: `POST https://server.media-on.de/api/v1/deploy` mit `{"uuid": "xf7smknlger3hvkrsb910tui"}`
|
||||
3. **Build dauert ~2-5 Min** (Multi-Stage: npm install + vite build + pip install + runtime)
|
||||
4. **Container wird automatisch ausgetauscht** wenn Build erfolgreich
|
||||
|
||||
### Manueller Build (Fallback)
|
||||
Wenn Coolify den Build nicht ausführt:
|
||||
```bash
|
||||
ssh root@46.225.91.159
|
||||
cd /tmp && git clone https://Leopoldadmin:<token>@forgejo.media-on.de/Leopoldadmin/leocrm.git leocrm-build
|
||||
cd leocrm-build
|
||||
docker build -t xf7smknlger3hvkrsb910tui:<commit-hash> .
|
||||
cd /data/coolify/applications/xf7smknlger3hvkrsb910tui
|
||||
# docker-compose.yaml Image-Tag aktualisieren
|
||||
# .env SOURCE_COMMIT aktualisieren
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Bekannte Probleme
|
||||
- **Build schlägt fehl bei JSX/TS-Fehlern:** Vite-Build bricht ab → Coolify zeigt FAIL nach ~30s
|
||||
- **docker-compose.yaml wird VOR dem Build aktualisiert:** Wenn Build fehlschlägt, bleibt nicht-existentes Image in der Config
|
||||
- **Coolify Queue kann hängen:** Manchmal wird Deploy gequeued aber nicht verarbeitet — dann manuell bauen
|
||||
|
||||
### Pre-Deploy Checklist
|
||||
- [ ] `npx tsc --noEmit` — keine neuen TSC-Fehler (pre-existing Dms.tsx errors sind OK)
|
||||
- [ ] `npx vite build` — Frontend-Build erfolgreich
|
||||
- [ ] `git push origin main` — Code auf Forgejo
|
||||
- [ ] Coolify Deploy triggern
|
||||
- [ ] Health-Check: `curl https://crm.media-on.de/api/v1/health`
|
||||
|
||||
### Server Info
|
||||
- **Host:** `46.225.91.159` (coolify-01)
|
||||
- **SSH Key:** `/a0/usr/workdir/.ssh/coolify-01-root`
|
||||
- **Coolify API Token:** Set via `COOLIFY_API_TOKEN` environment variable
|
||||
- **Coolify Dashboard:** `https://server.media-on.de`
|
||||
- **PostgreSQL:** `postgres` container (pgvector/pgvector:pg16)
|
||||
- **Redis:** `redis` container
|
||||
@@ -1,624 +0,0 @@
|
||||
# Konzept: Unified Messaging System für LeoCRM
|
||||
|
||||
## Vision
|
||||
|
||||
Alle Kommunikation in LeoCRM — KI-Chat, Mitarbeiter-Chat, System-Benachrichtigungen, externe Messenger — läuft über **ein einheitliches Messaging-System**, das als Plugin-Architektur realisiert wird.
|
||||
|
||||
**Kernprinzip:** Alles ist ein Teilnehmer. Die KI ist ein Teilnehmer. Das System ist ein Teilnehmer. Ein WhatsApp-Gateway ist ein Teilnehmer. Es gibt keine Sonderbehandlung.
|
||||
|
||||
---
|
||||
|
||||
## Plugin-Architektur
|
||||
|
||||
### Übersicht: Drei Plugin-Ebenen
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Frontend (MessageSidebar) │
|
||||
│ Ein Feed, eine Konversations-Liste, ein Eingabefeld │
|
||||
└──────────────────────────┬──────────────────────────────────┘
|
||||
│ │ WebSocket │
|
||||
┌──────────────────────────┴──────────────────────────────────┐
|
||||
│ Plugin: kommunikation (Core) │
|
||||
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────┐ │
|
||||
│ │ conversations│ │ messages │ │ participants │ │
|
||||
│ │ + Räume │ │ + Rich Cont.│ │ + Registrierung │ │
|
||||
│ └─────────────┘ └──────────────┘ └────────────────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ Participant Registry (Hook) │ │
|
||||
│ │ Andockpunkt für: ai_assistant, system_notif, │ │
|
||||
│ │ whatsapp_gateway, telegram_gateway, ... │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────┬──────────────────────────────────┘
|
||||
│ │ EventBus │
|
||||
┌──────────────────────────┴──────────────────────────────────┐
|
||||
│ Plugin: ai_assistant │ Plugin: system_notif │ Plugin: │
|
||||
│ dockt als Teilnehmer an │ dockt als Teilnehmer │ whatsapp │
|
||||
│ @KI → AI-Response │ Events → Messages │ Gateway │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1. Plugin `kommunikation` (Core-Plugin)
|
||||
|
||||
**Verantwortung:** Chat-Infrastruktur — Konversationen, Nachrichten, Teilnehmer-Verwaltung, WebSocket, Rich Content Transport.
|
||||
|
||||
**Basiert auf:** AI Assistant Plugin (Sessions/Messages/Streaming) als Grundlage, erweitert um Multi-Teilnehmer und Rich Content.
|
||||
|
||||
**Manifest:**
|
||||
```python
|
||||
PluginManifest(
|
||||
name="kommunikation",
|
||||
version="1.0.0",
|
||||
display_name="Kommunikation",
|
||||
description="Unified Messaging: Chat, KI, System, Messenger",
|
||||
dependencies=[],
|
||||
routes=[
|
||||
PluginRouteDef(path="/api/v1/comm", module="...routes", router_attr="router"),
|
||||
],
|
||||
events=["message.received", "conversation.created", "participant.joined"],
|
||||
migrations=["0001_initial.sql"],
|
||||
permissions=["comm:read", "comm:write", "comm:manage"],
|
||||
is_core=True,
|
||||
)
|
||||
```
|
||||
|
||||
**Komponenten:**
|
||||
- `models.py` — Conversation, Message, Participant, MessageAttachment, MessageReaction
|
||||
- `schemas.py` — Pydantic-Schemas für API
|
||||
- `routes.py` — REST-API + WebSocket
|
||||
- `services.py` — Business Logic (Nachrichten senden, Konversationen verwalten)
|
||||
- `participant_registry.py` — Registrierungs-Interface für andere Plugins
|
||||
- `content_types.py` — Rich Content Type-Definitionen
|
||||
- `websocket_manager.py` — WebSocket-Verbindungs-Manager
|
||||
|
||||
### 2. Participant Registry (Andockpunkt)
|
||||
|
||||
Das `kommunikation` Plugin stellt eine **Participant Registry** bereit — ein Interface, über das sich andere Plugins als Teilnehmer registrieren.
|
||||
|
||||
```python
|
||||
# In kommunikation/participant_registry.py
|
||||
|
||||
class ParticipantType(Enum):
|
||||
USER = "user"
|
||||
AI = "ai"
|
||||
SYSTEM = "system"
|
||||
WHATSAPP = "whatsapp"
|
||||
TELEGRAM = "telegram"
|
||||
EMAIL = "email"
|
||||
SLACK = "slack"
|
||||
# Erweiterbar...
|
||||
|
||||
class ParticipantHandler(ABC):
|
||||
"""Interface das Plugins implementieren um als Teilnehmer zu fungieren."""
|
||||
|
||||
@abstractmethod
|
||||
async def on_message_received(self, conversation_id, message, context) -> Message | None:
|
||||
"""Wird aufgerufen wenn eine neue Nachricht in einer Konversation
|
||||
ankommt, an der dieser Teilnehmer beteiligt ist.
|
||||
|
||||
Rückgabe: Optional eine neue Message (z.B. AI-Response).
|
||||
Für reine Leser (system) → return None.
|
||||
Für reaktive Teilnehmer (ai) → return Message(...).
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_participant_info(self) -> dict:
|
||||
"""Metadaten: name, avatar_url, display_name, capabilities."""
|
||||
pass
|
||||
|
||||
class ParticipantRegistry:
|
||||
"""Global registry für Plugin-Teilnehmer."""
|
||||
|
||||
def register(self, participant_type: str, handler: ParticipantHandler) -> None:
|
||||
"""Plugin registriert sich als Teilnehmer-Typ."""
|
||||
|
||||
def get_handler(self, participant_type: str) -> ParticipantHandler | None:
|
||||
"""Handler für einen Teilnehmer-Typ abrufen."""
|
||||
```
|
||||
|
||||
**Wie Plugins andocken:**
|
||||
|
||||
```python
|
||||
# In ai_assistant/plugin.py on_activate():
|
||||
from app.plugins.builtins.kommunikation.participant_registry import get_registry
|
||||
|
||||
class AIAssistantPlugin(BasePlugin):
|
||||
async def on_activate(self, db, container, event_bus):
|
||||
await super().on_activate(db, container, event_bus)
|
||||
# Als AI-Teilnehmer registrieren
|
||||
registry = get_registry()
|
||||
registry.register("ai", AIParticipantHandler(self.services))
|
||||
```
|
||||
|
||||
```python
|
||||
# In system_notif/plugin.py on_activate():
|
||||
class SystemNotificationPlugin(BasePlugin):
|
||||
async def on_activate(self, db, container, event_bus):
|
||||
await super().on_activate(db, container, event_bus)
|
||||
# Als System-Teilnehmer registrieren
|
||||
registry = get_registry()
|
||||
registry.register("system", SystemParticipantHandler(...))
|
||||
# Auf Events hören und Nachrichten erzeugen
|
||||
event_bus.subscribe("lead.created", self.on_lead_created)
|
||||
```
|
||||
|
||||
### 3. Plugin `ai_assistant` (Anpassung)
|
||||
|
||||
Das bestehende AI Assistant Plugin wird angepasst:
|
||||
- Behält: Provider-Verwaltung, Modelle, Agents, Tools, Streaming
|
||||
- Neu: Implementiert `ParticipantHandler` und registriert sich bei `kommunikation`
|
||||
- Neu: Lauscht auf `message.received` Events → wenn `@KI` erwähnt wird oder Konversation AI als Teilnehmer hat → generiert Response
|
||||
- Alt: Eigene `AIChatSession` / `AIChatMessage` Tabellen bleiben für Abwärtskompatibilität, werden langfristig migriert
|
||||
- Neu: Schreibt Nachrichten in `kommunikation.messages` statt nur in eigene Tabellen
|
||||
|
||||
### 4. Plugin `system_notif` (Neu)
|
||||
|
||||
**Verantwortung:** System-Events in Nachrichten umwandeln.
|
||||
|
||||
- Registriert sich als `system` Teilnehmer
|
||||
- Hört auf EventBus-Events (`lead.created`, `contact.created`, `task.overdue`, ...)
|
||||
- Erzeugt Nachrichten in der System-Konversation des Users
|
||||
- Notifications haben `metadata.action_url` und `metadata.severity`
|
||||
- Bestehende Notification-Tabelle wird migriert
|
||||
|
||||
### 5. Messenger-Gateway Plugins (Später)
|
||||
|
||||
Jeder Messenger ist ein eigenes Plugin:
|
||||
- `whatsapp_gateway` — registriert sich als `whatsapp` Teilnehmer
|
||||
- `telegram_gateway` — registriert sich als `telegram` Teilnehmer
|
||||
- `email_gateway` — registriert sich als `email` Teilnehmer
|
||||
|
||||
Jedes implementiert `ParticipantHandler` und ggf. Webhook-Routes.
|
||||
|
||||
---
|
||||
|
||||
## Datenmodell
|
||||
|
||||
### Tabelle `comm_conversations`
|
||||
```
|
||||
id UUID PK
|
||||
tenant_id UUID NOT NULL
|
||||
title TEXT NULL -- benannte Räume
|
||||
title_set_by UUID NULL -- user_id der den Titel gesetzt hat
|
||||
is_pinned BOOLEAN DEFAULT FALSE
|
||||
is_direct BOOLEAN DEFAULT FALSE -- 1:1 vs Gruppe
|
||||
created_by UUID NULL
|
||||
last_msg_at TIMESTAMP
|
||||
last_msg_preview TEXT NULL -- für Konversations-Liste
|
||||
last_msg_sender_type TEXT NULL -- für Icon in Liste
|
||||
metadata JSONB DEFAULT '{}' -- z.B. {"pinned_by": "user_id"}
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
```
|
||||
|
||||
### Tabelle `comm_participants`
|
||||
```
|
||||
id UUID PK
|
||||
conversation_id UUID FK → comm_conversations
|
||||
participant_id UUID NULL -- user_id (NULL für ai/system/gateways)
|
||||
participant_type TEXT NOT NULL -- 'user', 'ai', 'system', 'whatsapp', ...
|
||||
display_name TEXT NULL -- override (z.B. WhatsApp-Kontakt-Name)
|
||||
joined_at TIMESTAMP DEFAULT NOW()
|
||||
left_at TIMESTAMP NULL
|
||||
```
|
||||
|
||||
### Tabelle `comm_messages`
|
||||
```
|
||||
id UUID PK
|
||||
tenant_id UUID NOT NULL
|
||||
conversation_id UUID FK → comm_conversations
|
||||
sender_id UUID NULL -- user_id (NULL für ai/system/gateways)
|
||||
sender_type TEXT NOT NULL -- 'user', 'ai', 'system', 'whatsapp', ...
|
||||
content TEXT NOT NULL -- Text-Inhalt (Markdown)
|
||||
content_format TEXT DEFAULT 'text' -- 'text', 'markdown', 'html'
|
||||
metadata JSONB DEFAULT '{}' -- typ-spezifische Daten
|
||||
reply_to_id UUID NULL FK → comm_messages -- Thread-Antwort
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
read_at TIMESTAMP NULL
|
||||
edited_at TIMESTAMP NULL
|
||||
deleted_at TIMESTAMP NULL
|
||||
```
|
||||
|
||||
### Tabelle `comm_message_attachments`
|
||||
```
|
||||
id UUID PK
|
||||
message_id UUID FK → comm_messages
|
||||
file_name TEXT NOT NULL
|
||||
file_path TEXT NOT NULL -- Pfad im DMS oder S3
|
||||
file_type TEXT NOT NULL -- MIME type
|
||||
file_size BIGINT
|
||||
thumbnail_path TEXT NULL -- für Bilder/Videos
|
||||
metadata JSONB DEFAULT '{}' -- z.B. {"width": 1920, "height": 1080}
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
```
|
||||
|
||||
### Tabelle `comm_message_reactions`
|
||||
```
|
||||
id UUID PK
|
||||
message_id UUID FK → comm_messages
|
||||
user_id UUID NOT NULL
|
||||
emoji TEXT NOT NULL
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
UNIQUE(message_id, user_id, emoji)
|
||||
```
|
||||
|
||||
### Tabelle `comm_message_reads`
|
||||
```
|
||||
id UUID PK
|
||||
conversation_id UUID FK
|
||||
user_id UUID NOT NULL
|
||||
last_read_msg_id UUID FK → comm_messages
|
||||
last_read_at TIMESTAMP DEFAULT NOW()
|
||||
```
|
||||
|
||||
### Tabelle `comm_message_blocks` (Rich Content / Mini-Apps)
|
||||
```
|
||||
id UUID PK
|
||||
message_id UUID FK → comm_messages
|
||||
block_type TEXT NOT NULL -- 'file', 'image', 'audio', 'video',
|
||||
-- 'markdown', 'html', 'miniapp',
|
||||
-- 'action_card', 'contact_card', ...
|
||||
block_data JSONB NOT NULL -- typ-spezifische strukturierte Daten
|
||||
sort_order INT DEFAULT 0
|
||||
```
|
||||
|
||||
**Das ist der Schlüssel für Rich Content:**
|
||||
Eine Nachricht hat einen `content` (Text) plus beliebig viele `blocks` (strukturierte Elemente).
|
||||
|
||||
---
|
||||
|
||||
## Rich Content Transport
|
||||
|
||||
### Block-Typen (erweiterbar durch Plugins)
|
||||
|
||||
| block_type | Beschreibung | block_data Beispiel |
|
||||
|---|---|---|
|
||||
| `text` | Reiner Text (Fallback) | `{"text": "..."}` |
|
||||
| `markdown` | Markdown-Content | `{"markdown": "# Titel\n..."}` |
|
||||
| `html` | HTML-Content (sanitized) | `{"html": "<div>...</div>"}` |
|
||||
| `image` | Bild | `{"url": "...", "alt": "...", "width": 800}` |
|
||||
| `audio` | Audio-Datei | `{"url": "...", "duration": 120, "waveform": [...]}` |
|
||||
| `video` | Video-Datei | `{"url": "...", "duration": 60, "thumbnail": "..."}` |
|
||||
| `file` | Allgemeine Datei | `{"url": "...", "name": "...", "size": 1024}` |
|
||||
| `action_card` | Interaktive Karte mit Buttons | `{"title": "...", "body": "...", "actions": [{"label": "Öffnen", "url": "..."}]}` |
|
||||
| `contact_card` | Kontakt-Referenz | `{"contact_id": "...", "name": "..."}` |
|
||||
| `miniapp` | Eingebettete Mini-App | `{"app_id": "...", "config": {...}}` |
|
||||
|
||||
### Mini-App System
|
||||
|
||||
Mini-Apps sind kleine interaktive Komponenten, die **von Plugins registriert** und im Chat gerendert werden.
|
||||
|
||||
```python
|
||||
# Plugin registriert eine Mini-App:
|
||||
class MiniAppRegistry:
|
||||
def register(self, app_id: str, component: dict) -> None:
|
||||
"""Registriert eine Mini-App.
|
||||
|
||||
component = {
|
||||
'name': 'Lead Qualifier',
|
||||
'icon': 'clipboard',
|
||||
'render_schema': {...}, # JSON-Schema für Frontend
|
||||
'handler': async function # Backend-Handler
|
||||
}
|
||||
"""
|
||||
```
|
||||
|
||||
**Beispiel:** Ein Plugin `lead_qualifier` registriert eine Mini-App. Ein User schickt `/miniapp lead_qualifier` im Chat → eine Mini-App-Block wird erzeugt → Frontend rendert das interaktive Formular → Ergebnis wird als Nachricht zurückgeschrieben.
|
||||
|
||||
### Nachricht mit Rich Content — Beispiel
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "...",
|
||||
"conversation_id": "...",
|
||||
"sender_type": "ai",
|
||||
"content": "Hier ist die Zusammenfassung der neuen Leads:",
|
||||
"blocks": [
|
||||
{
|
||||
"block_type": "markdown",
|
||||
"block_data": {
|
||||
"markdown": "## 3 neue Leads\n- **Acme Corp** — €50k potential\n- **Globex** — €20k potential\n- **Initech** — €10k potential"
|
||||
}
|
||||
},
|
||||
{
|
||||
"block_type": "action_card",
|
||||
"block_data": {
|
||||
"title": "Nächste Schritte",
|
||||
"body": "3 Leads warten auf Qualifizierung.",
|
||||
"actions": [
|
||||
{"label": "Alle öffnen", "action": "open_leads", "type": "primary"},
|
||||
{"label": "Ignorieren", "action": "dismiss", "type": "secondary"}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
### REST Endpoints
|
||||
|
||||
```
|
||||
# Konversationen
|
||||
GET /api/v1/comm/conversations -- Liste (für aktuellen User)
|
||||
POST /api/v1/comm/conversations -- Neue Konversation
|
||||
GET /api/v1/comm/conversations/{id} -- Details + Teilnehmer
|
||||
PATCH /api/v1/comm/conversations/{id} -- Titel ändern, pinnen
|
||||
DELETE /api/v1/comm/conversations/{id} -- Löschen/Verlassen
|
||||
|
||||
# Teilnehmer
|
||||
POST /api/v1/comm/conversations/{id}/participants -- Teilnehmer hinzufügen
|
||||
DELETE /api/v1/comm/conversations/{id}/participants/{pid} -- Entfernen
|
||||
|
||||
# Nachrichten
|
||||
GET /api/v1/comm/conversations/{id}/messages -- Nachrichten (paginiert)
|
||||
POST /api/v1/comm/conversations/{id}/messages -- Nachricht senden
|
||||
PATCH /api/v1/comm/messages/{id} -- Bearbeiten/Lesen
|
||||
DELETE /api/v1/comm/messages/{id} -- Löschen
|
||||
|
||||
# Attachments
|
||||
POST /api/v1/comm/messages/{id}/attachments -- Datei hochladen
|
||||
GET /api/v1/comm/attachments/{id} -- Datei herunterladen
|
||||
|
||||
# Reaktionen
|
||||
POST /api/v1/comm/messages/{id}/reactions -- Reaktion hinzufügen
|
||||
DELETE /api/v1/comm/messages/{id}/reactions/{emoji} -- Reaktion entfernen
|
||||
|
||||
# Read State
|
||||
POST /api/v1/comm/conversations/{id}/read -- Als gelesen markieren
|
||||
|
||||
# Mini-Apps
|
||||
GET /api/v1/comm/miniapps -- Verfügbare Mini-Apps
|
||||
POST /api/v1/comm/conversations/{id}/miniapps -- Mini-App starten
|
||||
```
|
||||
|
||||
### WebSocket
|
||||
|
||||
```
|
||||
WS /api/v1/comm/ws
|
||||
|
||||
# Client → Server
|
||||
{"type": "subscribe", "conversation_id": "..."}
|
||||
{"type": "typing", "conversation_id": "...", "is_typing": true}
|
||||
{"type": "ping"}
|
||||
|
||||
# Server → Client
|
||||
{"type": "message.new", "conversation_id": "...", "message": {...}}
|
||||
{"type": "message.updated", "message": {...}}
|
||||
{"type": "message.deleted", "id": "..."}
|
||||
{"type": "participant.joined", "conversation_id": "...", "participant": {...}}
|
||||
{"type": "participant.left", "conversation_id": "...", "participant_id": "..."}
|
||||
{"type": "typing", "conversation_id": "...", "user_id": "...", "is_typing": true}
|
||||
{"type": "reaction.added", "message_id": "...", "emoji": "👍", "user_id": "..."}
|
||||
{"type": "conversation.updated", "conversation": {...}}
|
||||
{"type": "pong"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UI-Konzept
|
||||
|
||||
### MessageSidebar (ersetzt AISidebar)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Kommunikation [×] │
|
||||
├──────────────────────────────────────────┤
|
||||
│ 🔍 Suche... │
|
||||
├──────────────────────────────────────────┤
|
||||
│ 📌 Projekt Alpha │ ←angepinnt
|
||||
│ 🤖 KI: 3 Leads zusammengefasst... │
|
||||
│ ┌────────────────────────────────────┐ │
|
||||
│ │ Max: @KI fasse die Leads zusammen │ │
|
||||
│ │ 🤖 KI: 3 neue Leads, 2 aus... │ │
|
||||
│ │ Lisa: Super, danke! │ │
|
||||
│ │ ┌──────────────────────────────┐ │ │
|
||||
│ │ │ 📎 lead_report.pdf │ │ │
|
||||
│ │ │ ──────────────────────────── │ │ │
|
||||
│ │ │ ## 3 neue Leads │ │ │
|
||||
│ │ │ - **Acme Corp** — €50k │ │ │
|
||||
│ │ │ ──────────────────────────── │ │ │
|
||||
│ │ │ [Öffnen] [Archivieren] │ │ │
|
||||
│ │ └──────────────────────────────┘ │ │
|
||||
│ └────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 📌 Assistent (1:1 mit KI) │
|
||||
│ 🤖 47 Kontakte ohne Email... │
|
||||
│ │
|
||||
│ 👥 Sales Team │
|
||||
│ Max: Hat jemand die Q3-Zahlen? │
|
||||
│ │
|
||||
│ 🔔 System │
|
||||
│ 3 neue Leads importiert │
|
||||
│ │
|
||||
├──────────────────────────────────────────┤
|
||||
│ [📎] [Eingabefeld...] [Senden] │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Konversations-Liste (links im Panel)
|
||||
- Angespinnte Konversationen oben (📌)
|
||||
- Ungelesene-Badge pro Konversation
|
||||
- Letzte Nachricht mit Sender-Icon (🤖/👤/🔔)
|
||||
- Klick → öffnet Konversation im Feed
|
||||
|
||||
### Feed (Mitte)
|
||||
- Chronologische Nachrichten
|
||||
- Sender-Icon + Name pro Nachricht
|
||||
- Rich Content Blocks inline gerendert
|
||||
- Action-Cards mit Buttons
|
||||
- Datei-Anhänge mit Vorschau
|
||||
- Reaktionen (Emoji-Bar beim Hover)
|
||||
- Lesebestätigung (gelesen-Häkchen)
|
||||
|
||||
### Eingabefeld (unten)
|
||||
- Text-Eingabe mit Markdown-Support
|
||||
- Datei-Anhang Button (📎)
|
||||
- Mini-App Picker (/command)
|
||||
- @Mention Support (@KI, @Max)
|
||||
- Senden-Button
|
||||
- Kontextsensitiv: in System-Konversation → kein Eingabefeld
|
||||
|
||||
### Teilnehmer-Info
|
||||
- In Konversations-Header: Avatare aller Teilnehmer
|
||||
- Klick auf Avatar → Info-Popover
|
||||
- KI-Teilnehmer: zeigt Modell/Agent
|
||||
- System-Teilnehmer: zeigt Quelle
|
||||
|
||||
### Räume
|
||||
- Konversationen können benannt werden (Titel editierbar)
|
||||
- Anpinnen möglich (📌)
|
||||
- Gruppierung durch Titel, nicht durch spezielle Raum-Logik
|
||||
- Ein "Raum" ist einfach eine benannte Konversation
|
||||
|
||||
---
|
||||
|
||||
## EventBus Integration
|
||||
|
||||
### Events vom kommunikation Plugin
|
||||
|
||||
```
|
||||
message.received → {conversation_id, message, sender_type}
|
||||
message.sent → {conversation_id, message}
|
||||
conversation.created → {conversation_id, participants, created_by}
|
||||
participant.joined → {conversation_id, participant_type, participant_id}
|
||||
participant.left → {conversation_id, participant_id}
|
||||
```
|
||||
|
||||
### Events die andere Plugins hören
|
||||
|
||||
```
|
||||
# ai_assistant hört auf:
|
||||
message.received → prüft ob @KI erwähnt oder AI Teilnehmer → generiert Response
|
||||
|
||||
# system_notif hört auf (vom Core-System):
|
||||
lead.created → erzeugt System-Nachricht
|
||||
contact.created → erzeugt System-Nachricht
|
||||
task.overdue → erzeugt System-Nachricht
|
||||
|
||||
# whatsapp_gateway hört auf:
|
||||
message.received → wenn Konversation WhatsApp-Teilnehmer hat → sende extern
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration
|
||||
|
||||
### Phase 1: Backend — Plugin `kommunikation`
|
||||
1. Neue Tabellen: `comm_conversations`, `comm_participants`, `comm_messages`, `comm_message_attachments`, `comm_message_blocks`, `comm_message_reactions`, `comm_message_reads`
|
||||
2. Participant Registry Interface
|
||||
3. REST-API + WebSocket
|
||||
4. Rich Content Block System
|
||||
5. Mini-App Registry Interface
|
||||
|
||||
### Phase 2: Backend — Plugin `ai_assistant` anpassen
|
||||
1. `ParticipantHandler` implementieren
|
||||
2. Bei `kommunikation` registrieren
|
||||
3. Auf `message.received` hören → AI-Response generieren
|
||||
4. Streaming-Responses über WebSocket pushen
|
||||
5. Alte `AIChatSession`/`AIChatMessage` behalten für Abwärtskompatibilität
|
||||
|
||||
### Phase 3: Backend — Plugin `system_notif` (neu)
|
||||
1. `ParticipantHandler` implementieren
|
||||
2. System-Events → Nachrichten in System-Konversation
|
||||
3. Bestehende Notifications migrieren
|
||||
4. Action-URLs als `action_card` Blocks
|
||||
|
||||
### Phase 4: Frontend — MessageSidebar
|
||||
1. AISidebar → MessageSidebar umbauen
|
||||
2. Konversations-Liste mit Pinning
|
||||
3. Unified Feed mit Rich Content Rendering
|
||||
4. Eingabefeld mit Datei-Upload + @Mention
|
||||
5. WebSocket-Verbindung
|
||||
6. Mini-App Rendering Framework
|
||||
|
||||
### Phase 5: Frontend — Rich Content Renderer
|
||||
1. Block-Renderer: Markdown, HTML, Image, Audio, Video, File
|
||||
2. Action-Card Renderer mit Button-Handler
|
||||
3. Mini-App Renderer (Plugin-basiert)
|
||||
4. Contact-Card, Lead-Card, etc.
|
||||
|
||||
### Phase 6: Messenger-Gateway Plugins (später)
|
||||
1. `whatsapp_gateway` Plugin
|
||||
2. `telegram_gateway` Plugin
|
||||
3. `email_gateway` Plugin
|
||||
4. Jeweils: ParticipantHandler + Webhook-Routes + Gateway-Adapter
|
||||
|
||||
---
|
||||
|
||||
## Technische Entscheidungen
|
||||
|
||||
### WebSocket vs Polling
|
||||
**WebSocket** — eine Verbindung pro User, pusht alle Konversationen.
|
||||
Grund: Real-time ist essenziell für Chat, und eine Verbindung für alles ist effizienter als Multiple Polling.
|
||||
|
||||
### Rich Content: Blocks vs Inline
|
||||
**Blocks** — separate Tabelle `comm_message_blocks` mit `block_type` + `block_data`.
|
||||
Grund: Erweiterbar durch Plugins, strukturiert, frontend kann unbekannte Typen graceful ignorieren.
|
||||
|
||||
### Mini-Apps: Plugin-basiert
|
||||
**Registry Pattern** — Plugins registrieren Mini-Apps bei `kommunikation`.
|
||||
Grund: Plugins können eigene Mini-Apps mitbringen, Frontend rendert sie dynamisch.
|
||||
|
||||
### Räume: Keine separate Tabelle
|
||||
**Titel + Pinning** — eine Konversation mit Titel ist ein Raum.
|
||||
Grund: Minimalistisch, keine zusätzliche Komplexität, flexibel.
|
||||
|
||||
### @Mention Detection
|
||||
**Im Backend** — `message.received` Event enthält geparste mentions.
|
||||
Grund: Zentrale Logik, alle Teilnehmer-Plugins bekommen saubere Daten.
|
||||
|
||||
### Abwärtskompatibilität
|
||||
**Alte Tabellen behalten** — `ai_chat_sessions`, `ai_chat_messages`, `ai_conversations`, `ai_messages` bleiben erhalten.
|
||||
Grund: Bestehende Daten gehen nicht verloren, Migration schrittweise.
|
||||
|
||||
---
|
||||
|
||||
## Offene Fragen
|
||||
|
||||
1. **Soll `kommunikation` das bestehende AI Copilot System (AIConversation/AIMessage) ersetzen oder parallel laufen?**
|
||||
- Vorschlag: Parallel, langfristig migrieren
|
||||
|
||||
2. **Datei-Speicherung:** DMS-Plugin nutzen oder eigener Speicher für Attachments?
|
||||
- Vorschlag: DMS-Integration, `file_path` verweist auf DMS-Dokument
|
||||
|
||||
3. **Berechtigungen:** Wer darf Konversationen erstellen? Wer darf Teilnehmer hinzufügen?
|
||||
- Vorschlag: `comm:write` für erstellen, `comm:manage` für Teilnehmer verwalten
|
||||
|
||||
4. **Gruppen-Chat-Limit:** Maximale Anzahl Teilnehmer?
|
||||
- Vorschlag: Kein Limit, Performance-Test später
|
||||
|
||||
5. **Nachrichten-Historie:** Endlos oder Paginierung mit Lazy-Loading?
|
||||
- Vorschlag: Paginierung (50 pro Seite), Lazy-Load beim Scrollen
|
||||
|
||||
6. **Suche:** Über alle Konversationen? Global mit unified_search Plugin?
|
||||
- Vorschlag: Ja, `unified_search` Provider für `kommunikation`
|
||||
|
||||
7. **Push-Notifications:** Browser-Notifications bei neuen Nachrichten?
|
||||
- Vorschlag: Ja, über Notification API + Service Worker
|
||||
|
||||
8. **Verschlüsselung:** E2E für bestimmte Konversationen?
|
||||
- Vorschlag: Nein in Phase 1, später evaluieren
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
```
|
||||
Ein Plugin (kommunikation) → Chat-Infrastruktur + Rich Content + WebSocket
|
||||
Ein Interface (ParticipantHandler) → Plugins docken als Teilnehmer an
|
||||
Ein Datenmodell (3+Tabellen) → Konversationen, Teilnehmer, Nachrichten + Blocks
|
||||
Eine UI (MessageSidebar) → Ein Feed, eine Liste, ein Eingabefeld
|
||||
Eine WebSocket → Real-time für alles
|
||||
Ein EventBus → Plugins reagieren auf Nachrichten
|
||||
|
||||
KI = Teilnehmer → @KI in jedem Chat
|
||||
System = Teilnehmer → Notifications als Nachrichten
|
||||
WhatsApp = Teilnehmer → Externe Messenger andocken
|
||||
Mini-Apps = Plugin-Blocks → Erweiterbar im Chat
|
||||
Räume = Benannte Chats → Titel + Pinning
|
||||
```
|
||||
@@ -0,0 +1,223 @@
|
||||
# LeoCRM Test-Strategie
|
||||
|
||||
> **Wichtig:** Dieses Dokument muss nach jeder größeren Änderung am Codebase
|
||||
> (neue Plugins, neue Module, Refactoring, Security-Änderungen) überarbeitet
|
||||
> werden. Siehe `leocrm-test-strategy.promptinclude.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Übersicht
|
||||
|
||||
LeoCRM verwendet eine mehrschichtige Test-Strategie um Funktionalität,
|
||||
Sicherheit und Stabilität sicherzustellen.
|
||||
|
||||
### Test-Pyramide
|
||||
|
||||
```
|
||||
┌──────────┐
|
||||
│ E2E │ ← Browser-Tests (geplant, noch nicht implementiert)
|
||||
├──────────┤
|
||||
│Integration│ ← pytest mit echter PostgreSQL/Redis Test-DB
|
||||
├──────────┤
|
||||
│ Unit │ ← pytest mit Mocks (teilweise)
|
||||
└──────────┘
|
||||
```
|
||||
|
||||
### Aktuelle Abdeckung
|
||||
|
||||
| Ebene | Tool | Status | Abdeckung |
|
||||
|-------|------|--------|-----------|
|
||||
| Backend-Tests | pytest | ✅ aktiv | 69 Testdateien, ~500 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 |
|
||||
| CI-Pipeline | scripts/ci_pipeline.sh | ✅ aktiv | 15+ Checks |
|
||||
|
||||
---
|
||||
|
||||
## 2. Backend-Tests (pytest)
|
||||
|
||||
### Architektur
|
||||
|
||||
- **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)
|
||||
- **Plugin-Aktivierung:** In-Memory-Registry muss pro Fixture gesetzt werden
|
||||
|
||||
### Bekannte Einschränkungen
|
||||
|
||||
1. **RLS (Row Level Security) nicht testbar:**
|
||||
- Die Test-DB verwendet `create_all` statt Alembic-Migrationen
|
||||
- RLS-Policies werden normalerweise durch Migrationen erstellt
|
||||
- RLS-Tests wurden ausgebaut (bringt nichts wenn es sich nicht testen lässt)
|
||||
- **Lösung:** Alembic-Migrationen in Test-DB ausführen (Roadmap)
|
||||
|
||||
2. **LLM-API-Tests blockieren:**
|
||||
- Tests die externe LLM-APIs aufrufen (Ollama Cloud, OpenRouter) blockieren
|
||||
- Die komplette Suite hängt bei ~46% wenn LLM-Calls nicht gemockt sind
|
||||
- **Lösung:** LLM-Calls in Tests mocken (Roadmap)
|
||||
|
||||
3. **Fixture-Overhead (~2,5s pro Test):**
|
||||
- `seed_tenant_and_users` (1,27s) + `create_app` (0,73s) + `login` (0,34s)
|
||||
- Wird pro Test ausgeführt (Function-Scope)
|
||||
- Session-Scope ist nicht möglich weil ~30 Testdateien `seed_tenant_and_users` direkt aufrufen
|
||||
- **Lösung:** Seed-Daten session-scopen + clean_tables anpassen (Roadmap)
|
||||
|
||||
4. **Keine Parallelisierung möglich:**
|
||||
- `pytest-xdist` funktioniert nicht weil alle Worker dieselbe Test-DB teilen
|
||||
- **Lösung:** Pro-Worker Datenbank (Roadmap)
|
||||
|
||||
### Test-Kategorien
|
||||
|
||||
| Kategorie | Beschreibung | Beispiele |
|
||||
|-----------|-------------|----------|
|
||||
| **Funktionale Tests** | Testet ob Features funktionieren | test_calendar, test_tags, test_dms |
|
||||
| **Permission-Tests** | Testet ABAC/Permission-System | test_permissions, test_entity_permissions |
|
||||
| **Plugin-Tests** | Testet Plugin-Lifecycle und -Funktionen | test_plugins, test_entity_links |
|
||||
| **Cross-Tenant-Tests** | Testet Tenant-Isolation | test_cross_tenant_security |
|
||||
| **AI-Tests** | Testet AI-Proactive, Copilot, GraphRAG | test_ai_proactive, test_ai_copilot |
|
||||
| **Integration-Tests** | Testet Modul-übergreifend | test_unified_search, test_outbox |
|
||||
|
||||
### Konventionen
|
||||
|
||||
1. **Test-Dateien:** `tests/test_<modul>.py`
|
||||
2. **Fixtures:** In `tests/conftest.py` definiert
|
||||
3. **Plugin-Aktivierung:** Jede Plugin-Test-Datei muss `init_permission_registry(active_plugin_names={...})` aufrufen
|
||||
4. **Entity-Typen:** Verwende korrekte ENTITY_MODELS-Keys (z.B. `file` nicht `dms_file`, `mail_account` nicht `mailbox`)
|
||||
5. **URLs:** Verwende korrekte API-Pfade (z.B. `/api/v1/entity-links/` nicht `/api/v1/dms/`)
|
||||
6. **Dedup-Tests:** Verwende unterschiedlichen Dateiinhalt pro Upload um Dedup-Logik nicht zu triggern
|
||||
7. **Keine zufälligen UUIDs:** Verwende echte Entity-IDs aus der DB, nicht `uuid.uuid4()`
|
||||
|
||||
---
|
||||
|
||||
## 3. Frontend-Tests (geplant)
|
||||
|
||||
### Aktuell
|
||||
- **0 Tests** für 54.000 Zeilen TSX/TypeScript
|
||||
- Frontend-Bugs werden nur manuell im Browser gefunden
|
||||
|
||||
### Roadmap
|
||||
- **Unit-Tests:** vitest für React-Komponenten
|
||||
- **Integration-Tests:** Testing Library für Komponenten-Interaktionen
|
||||
- **E2E-Tests:** Playwright für kritische User-Flows (Login, Kontakt erstellen, Kalender)
|
||||
|
||||
---
|
||||
|
||||
## 4. Security-Testing (geplant)
|
||||
|
||||
### Aktuell
|
||||
- Keine automatisierten Security-Tests
|
||||
- Security-Bugs wurden durch manuelle Code-Review gefunden (siehe Bugfix-Session 2026-08-12)
|
||||
|
||||
### Bekannte Security-Lücken (behoben am 2026-08-12)
|
||||
|
||||
| Bug | Fix | Status |
|
||||
|-----|-----|--------|
|
||||
| `MAIL_ENCRYPTION_KEY` hatte Default-Wert | RuntimeError wenn nicht gesetzt | ✅ |
|
||||
| `revoke_permission` ohne Owner-Check | `check_single_entity_access` hinzugefügt | ✅ |
|
||||
| `is_active=True` hart codiert im DB-Fallback | User-Status aus DB laden | ✅ |
|
||||
| Plugin-Gate allow bei fehlendem Tenant | TODO - bricht Tests, muss in Produktion anders gelöst werden | ⚠️ |
|
||||
| Public Share URL falsch | URL korrigiert | ✅ |
|
||||
| Logout nur in Redis | Auch PostgreSQL invalidieren | ✅ |
|
||||
| Rate-Limit nur auf IP | Token-Hash für Bearer-Auth | ✅ |
|
||||
| RLS-Commit statt flush | Alle Commits durch flush ersetzt | ✅ |
|
||||
| Webhook ohne Tenant-Context | `set_tenant_context` hinzugefügt | ✅ |
|
||||
| `npm ci \|\| npm install` Fallback | Nur `npm ci` | ✅ |
|
||||
|
||||
### Roadmap
|
||||
- **bandit:** Python Security-Scanner in CI-Pipeline
|
||||
- **pip-audit:** Dependency-Scanning
|
||||
- **npm audit:** Frontend-Dependency-Scanning
|
||||
- **OWASP ZAP:** Web-Application-Scanner gegen Test-Instanz
|
||||
- **Security-Test-Suite:** Eigene pytest-Tests für Security-Szenarien
|
||||
|
||||
---
|
||||
|
||||
## 5. CI/CD Pipeline
|
||||
|
||||
### Aktuelle Checks (scripts/ci_pipeline.sh)
|
||||
|
||||
1. Python Compile Check
|
||||
2. Cross-Plugin Import Check
|
||||
3. Alembic Revision Graph
|
||||
4. Alembic Migration Test (wenn DATABASE_URL gesetzt)
|
||||
5. Migration Hash Check
|
||||
6. TypeScript Type Check
|
||||
7. Frontend Build
|
||||
8. Test Collection
|
||||
9. Backend Tests
|
||||
10. Frontend Tests
|
||||
11. SQL Injection Check
|
||||
12. npm ci strict mode
|
||||
|
||||
### Bekannte CI-Lücken
|
||||
|
||||
- **Smoke-Test gegen Build:** Tests laufen gegen `leocrm_test` DB, nicht gegen den aktuellen Build
|
||||
- **Frontend-Tests:** vitest ist konfiguriert aber hat 0 Tests
|
||||
- **Security-Scanning:** bandit/pip-audit nicht in Pipeline
|
||||
- **E2E-Tests:** Nicht in Pipeline
|
||||
|
||||
---
|
||||
|
||||
## 6. Was getestet wird und was nicht
|
||||
|
||||
### ✅ Wird getestet
|
||||
- API-Endpunkte (CRUD, Validierung, Permissions)
|
||||
- Plugin-Lifecycle (Install, Activate, Deactivate)
|
||||
- ABAC/Permission-System
|
||||
- Cross-Tenant-Isolation (ohne RLS)
|
||||
- AI-Proactive/GraphRAG (mit Mocks)
|
||||
- Outbox/Event-System
|
||||
- Backup/Restore
|
||||
- Auth/Login/Logout
|
||||
- Tags, Calendar, DMS, Mail, Contacts, Tasks
|
||||
|
||||
### ❌ Wird NICHT getestet
|
||||
- **Frontend** (54k Zeilen, 0 Tests)
|
||||
- **RLS-Policies** (Test-DB hat keine RLS)
|
||||
- **LLM-APIs** (blockieren Suite, nicht gemockt)
|
||||
- **Race Conditions** (keine Last-Tests)
|
||||
- **Security-Edge-Cases** (SQL Injection nur oberflächlich)
|
||||
- **Dockerfile/Deployment** (nur Code, nicht Infrastruktur)
|
||||
- **CI-Scripts selbst** (Shell-Scripts nicht getestet)
|
||||
- **Produktions-Logs** (keine Log-Analyse)
|
||||
|
||||
---
|
||||
|
||||
## 7. Roadmap
|
||||
|
||||
| Priorität | Maßnahme | Aufwand | Nutzen |
|
||||
|-----------|---------|--------|-------|
|
||||
| 🔴 Hoch | Frontend Unit-Tests (vitest) | mittel | 54k Zeilen abgedeckt |
|
||||
| 🔴 Hoch | LLM-API-Calls mocken | gering | Suite läuft komplett durch |
|
||||
| 🟡 Mittel | Security-Test-Suite | mittel | Security-Bugs automatisch gefunden |
|
||||
| 🟡 Mittel | E2E-Tests (Playwright) | hoch | Kritische User-Flows getestet |
|
||||
| 🟡 Mittel | Alembic-Migrationen in Test-DB | mittel | RLS testbar |
|
||||
| 🟡 Mittel | Fixture-Optimierung (Session-Scope) | hoch | Suite 3x schneller |
|
||||
| 🟢 Niedrig | bandit/pip-audit in CI | gering | Automatisches Security-Scanning |
|
||||
| 🟢 Niedrig | Pro-Worker Test-DB | mittel | Parallelisierung möglich |
|
||||
| 🟢 Niedrig | Log-Analyse Pipeline | gering | Produktions-Fehler erkannt |
|
||||
|
||||
---
|
||||
|
||||
## 8. Wann muss dieses Dokument aktualisiert werden?
|
||||
|
||||
Dieses Dokument MUSS aktualisiert werden bei:
|
||||
|
||||
1. **Neue Plugins oder Module** → Test-Kategorien und Abdeckung aktualisieren
|
||||
2. **Security-Änderungen** → Security-Lücken und Fixes dokumentieren
|
||||
3. **Neue Test-Infrastruktur** (z. B. vitest, Playwright) → Abschnitt hinzufügen
|
||||
4. **CI-Pipeline-Änderungen** → Checks und Lücken aktualisieren
|
||||
5. **Größere Refactoring** → Konventionen und Einschränkungen überprüfen
|
||||
6. **Nach jeder Bugfix-Session** → Bekannte Lücken und Fixes dokumentieren
|
||||
|
||||
**Verantwortlich:** Agent/Entwickler der die Änderung durchführt.
|
||||
|
||||
---
|
||||
|
||||
## 9. Historie
|
||||
|
||||
| Datum | Ereignis |
|
||||
|-------|---------|
|
||||
| 2026-08-12 | Test-Strategie erstellt nach Bugfix-Session (14 Security-Bugs, ~170 Testfehler behoben) |
|
||||
Reference in New Issue
Block a user