155 lines
7.4 KiB
Markdown
155 lines
7.4 KiB
Markdown
# T11 Briefing — Tags Plugin + Permissions Plugin + Entity Links Backend
|
|
|
|
## Project Root
|
|
/a0/usr/workdir/dev-projects/leocrm
|
|
|
|
## Task
|
|
Implement 3 builtin plugins: Tags, Permissions, Entity Links.
|
|
|
|
## Plugin Framework (existing — read these files first)
|
|
- `app/plugins/base.py` — BasePlugin abstract class with lifecycle hooks
|
|
- `app/plugins/manifest.py` — PluginManifest, PluginRouteDef schemas
|
|
- `app/plugins/registry.py` — PluginRegistry (discovers builtins, manages lifecycle)
|
|
- `app/plugins/builtins/test_sample.py` — Example plugin (reference pattern)
|
|
- `app/plugins/builtins/migrations/` — Migration SQL files go here
|
|
- `app/core/event_bus.py` — EventBus for pub/sub
|
|
- `app/core/service_container.py` — DI container
|
|
- `app/core/db.py` — Base, TenantMixin, TimestampMixin
|
|
- `app/models/company.py` — Company model (reference for model patterns)
|
|
- `app/models/plugin.py` — Plugin + PluginMigration models
|
|
|
|
## Architecture Rules
|
|
- Plugins live in `app/plugins/builtins/` as subdirectories (e.g. `app/plugins/builtins/tags/`)
|
|
- Each plugin has: `__init__.py` (exports plugin class), `plugin.py` (BasePlugin subclass), `routes.py` (APIRouter), `models.py` (SQLAlchemy models), `schemas.py` (Pydantic schemas), `migrations/` (SQL files)
|
|
- Migrations are plain SQL files in `app/plugins/builtins/<plugin>/migrations/`
|
|
- Models use SQLAlchemy 2.0 style (Mapped, mapped_column) with PGUUID, TenantMixin
|
|
- Routes use FastAPI APIRouter, registered via manifest routes list
|
|
- Events: subscribe in on_activate, handlers named `on_<event_name>`
|
|
|
|
## 1. Tags Plugin (`app/plugins/builtins/tags/`)
|
|
|
|
### Requirements (F-TAG-01 through F-TAG-04)
|
|
- Tags can be applied to files, folders, companies, contacts
|
|
- Tags are global (not per-user), centrally managed
|
|
- Multiple tags per entity (N:M)
|
|
- Tag CRUD with color support
|
|
- Tag filtering in lists (AND/OR combination)
|
|
- Tag cloud/sidebar with entity counts
|
|
|
|
### Endpoints
|
|
```
|
|
GET /api/v1/tags → 200, list tags with entity counts
|
|
POST /api/v1/tags → 201, create tag (name, color)
|
|
PATCH /api/v1/tags/{id} → 200, update tag
|
|
DELETE /api/v1/tags/{id} → 204, cascade delete assignments
|
|
POST /api/v1/tags/assign → 200, assign tag to entity (tag_id, entity_type, entity_id)
|
|
DELETE /api/v1/tags/assign → 204, remove tag assignment
|
|
POST /api/v1/tags/bulk-assign → 200, assign multiple tags to entity
|
|
GET /api/v1/tags/{id}/entities → 200, list entities with this tag
|
|
```
|
|
|
|
### Models
|
|
- `Tag`: id (UUID), name (str, unique per tenant), color (str, hex), tenant_id
|
|
- `TagAssignment`: id, tag_id (FK), entity_type (str: company/contact/file/folder), entity_id (UUID), tenant_id
|
|
|
|
### Migration
|
|
- `0001_initial.sql`: Create `tags` and `tag_assignments` tables with tenant_id columns
|
|
|
|
## 2. Permissions Plugin (`app/plugins/builtins/permissions/`)
|
|
|
|
### Requirements (F-PERM-01 through F-PERM-06)
|
|
- Personal root folder per user ("Mein Bereich")
|
|
- Shared root folders for teams/departments
|
|
- Share files/folders with individual users (read/write)
|
|
- Share files/folders with user groups (read/write)
|
|
- Public share links (with password, expiry, download-only or preview+download)
|
|
- Permission display (who has access?)
|
|
|
|
### Endpoints
|
|
```
|
|
GET /api/v1/dms/files/{id}/permissions → 200, permission list
|
|
POST /api/v1/dms/files/{id}/permissions → 201, grant permission
|
|
DELETE /api/v1/dms/files/{id}/permissions/{user_id} → 204, revoke
|
|
POST /api/v1/dms/files/{id}/share-link → 200, create share link (returns public token URL)
|
|
GET /api/public/share/{token} → 200 (file) or 410 (expired)
|
|
DELETE /api/v1/dms/share-links/{id} → 204, revoke share link
|
|
```
|
|
|
|
### Models
|
|
- `Permission`: id, file_id (UUID), user_id (UUID), group_id (UUID nullable), access_level (read/write), tenant_id
|
|
- `ShareLink`: id, file_id (UUID), token (str, unique), password_hash (nullable), expires_at (nullable), access_level (download/preview), tenant_id
|
|
|
|
### Migration
|
|
- `0001_initial.sql`: Create `permissions` and `share_links` tables
|
|
|
|
### Special
|
|
- Public share endpoint `/api/public/share/{token}` must NOT require auth
|
|
- Expired links return 410 Gone
|
|
- Password-protected links verify password before serving
|
|
|
|
## 3. Entity Links Backend (`app/plugins/builtins/entity_links/`)
|
|
|
|
### Requirements (F-LINK-01 through F-LINK-06)
|
|
- Link files/folders to companies (N:M)
|
|
- Link files/folders to contacts (N:M)
|
|
- Reverse links (file shows linked entities)
|
|
- Multi-links (one file → many entities)
|
|
- Event cleanup: on company.deleted/contact.deleted → remove links
|
|
|
|
### Endpoints
|
|
```
|
|
POST /api/v1/dms/files/{id}/link → 200, link file to entity (entity_type, entity_id)
|
|
DELETE /api/v1/dms/files/{id}/link → 204, remove link (entity_type, entity_id in body)
|
|
GET /api/v1/dms/files/{id}/links → 200, list all linked entities for file
|
|
GET /api/v1/companies/{id}/files → 200, list linked files for company
|
|
GET /api/v1/contacts/{id}/files → 200, list linked files for contact
|
|
```
|
|
|
|
### Models
|
|
- `EntityLink`: id, file_id (UUID), entity_type (str: company/contact), entity_id (UUID), tenant_id, created_by (UUID)
|
|
|
|
### Migration
|
|
- `0001_initial.sql`: Create `entity_links` table
|
|
|
|
### Event Handling
|
|
- Subscribe to `company.deleted` → delete all EntityLink rows where entity_type='company' AND entity_id=deleted_id
|
|
- Subscribe to `contact.deleted` → delete all EntityLink rows where entity_type='contact' AND entity_id=deleted_id
|
|
|
|
## Acceptance Criteria (14 total — ALL must pass)
|
|
1. GET /api/v1/dms/files/{id}/permissions → 200 + permission list
|
|
2. POST /api/v1/dms/files/{id}/link → 200, file linked to entity
|
|
3. DELETE /api/v1/dms/files/{id}/link → 204, link removed
|
|
4. POST /api/v1/dms/files/{id}/share-link → 200 + public token URL
|
|
5. GET /api/public/share/{token} with expired link → 410
|
|
6. GET /api/v1/tags → 200 + tags with counts
|
|
7. POST /api/v1/tags → 201, tag created
|
|
8. PATCH /api/v1/tags/{id} → 200
|
|
9. DELETE /api/v1/tags/{id} → 204, cascade delete assignments
|
|
10. POST /api/v1/tags/assign → 200, tag assigned to entity
|
|
11. DELETE /api/v1/tags/assign → 204, tag removed
|
|
12. POST /api/v1/tags/bulk-assign → 200, multiple tags assigned
|
|
13. DMS plugin listens to company.deleted event → linked files cleanup
|
|
14. Folder permissions enforced: user without read → 403
|
|
|
|
## Test Files (create in `tests/`)
|
|
- `tests/test_tags.py` — Tag CRUD, assignment, bulk assign, cascade delete, counts
|
|
- `tests/test_permissions.py` — Personal root, shared root, share with users/groups, share links (password, expiry), permission display, 403 enforcement
|
|
- `tests/test_entity_links.py` — Link file to company, link to contact, reverse links, multi-links, event cleanup on deletion
|
|
|
|
## Verification Commands
|
|
```bash
|
|
cd /a0/usr/workdir/dev-projects/leocrm
|
|
python -m pytest tests/test_tags.py tests/test_permissions.py tests/test_entity_links.py -v --tb=short
|
|
python -m pytest tests/test_tags.py tests/test_permissions.py tests/test_entity_links.py --cov=app/plugins/builtins --cov-report=term-missing
|
|
```
|
|
|
|
## Rules
|
|
- Use text_editor:write for new files, text_editor:patch for updates
|
|
- Read existing files before modifying
|
|
- No Lorem Ipsum, no placeholder code
|
|
- Follow existing patterns (SQLAlchemy 2.0, Pydantic v2, FastAPI APIRouter)
|
|
- Each plugin must have manifest, plugin class, routes, models, schemas, migrations
|
|
- Register plugins in `app/plugins/builtins/__init__.py`
|
|
- Keep response under 50 lines
|
|
- Report: files created, test count + pass/fail, coverage %
|