7.4 KiB
7.4 KiB
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 hooksapp/plugins/manifest.py— PluginManifest, PluginRouteDef schemasapp/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 hereapp/core/event_bus.py— EventBus for pub/subapp/core/service_container.py— DI containerapp/core/db.py— Base, TenantMixin, TimestampMixinapp/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_idTagAssignment: id, tag_id (FK), entity_type (str: company/contact/file/folder), entity_id (UUID), tenant_id
Migration
0001_initial.sql: Createtagsandtag_assignmentstables 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_idShareLink: id, file_id (UUID), token (str, unique), password_hash (nullable), expires_at (nullable), access_level (download/preview), tenant_id
Migration
0001_initial.sql: Createpermissionsandshare_linkstables
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: Createentity_linkstable
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)
- GET /api/v1/dms/files/{id}/permissions → 200 + permission list
- POST /api/v1/dms/files/{id}/link → 200, file linked to entity
- DELETE /api/v1/dms/files/{id}/link → 204, link removed
- POST /api/v1/dms/files/{id}/share-link → 200 + public token URL
- GET /api/public/share/{token} with expired link → 410
- GET /api/v1/tags → 200 + tags with counts
- POST /api/v1/tags → 201, tag created
- PATCH /api/v1/tags/{id} → 200
- DELETE /api/v1/tags/{id} → 204, cascade delete assignments
- POST /api/v1/tags/assign → 200, tag assigned to entity
- DELETE /api/v1/tags/assign → 204, tag removed
- POST /api/v1/tags/bulk-assign → 200, multiple tags assigned
- DMS plugin listens to company.deleted event → linked files cleanup
- Folder permissions enforced: user without read → 403
Test Files (create in tests/)
tests/test_tags.py— Tag CRUD, assignment, bulk assign, cascade delete, countstests/test_permissions.py— Personal root, shared root, share with users/groups, share links (password, expiry), permission display, 403 enforcementtests/test_entity_links.py— Link file to company, link to contact, reverse links, multi-links, event cleanup on deletion
Verification Commands
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 %