Files
kite/.planning/phases/04-folders-sharing-quotas-document-ux/04-03-SUMMARY.md
T
curo1305 c6feb5faf2 docs(phase-4): complete 04-03-PLAN.md — Folders API + audit helper
- Create 04-03-SUMMARY.md with full frontmatter, decisions, threat surface scan
- Update STATE.md: plan 3/9, new decisions, session continuity
- Update ROADMAP.md: mark 04-01, 04-02, 04-03 plans complete (3/9)
- Update REQUIREMENTS.md: mark FOLD-01..FOLD-05 complete
2026-05-25 18:40:33 +02:00

7.0 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions requirements-completed duration completed
04-folders-sharing-quotas-document-ux 03 api
fastapi
sqlalchemy
postgresql
sqlite
folders
audit-log
fts
quota
phase provides
03-document-migration-multi-user-isolation Document ORM model with folder_id FK, Quota model with CASE WHEN decrement pattern, get_regular_user dep, ownership-assertion-404 pattern
phase provides
04-01 AuditLog ORM model with metadata_ attribute, Folder ORM model with UniqueConstraint, Share ORM model
write_audit_log() async helper in backend/services/audit.py (flush-not-commit, never-raises)
POST /api/folders — create folder with parent_id, IntegrityError → 409
GET /api/folders — list top-level folders (parent_id IS NULL)
GET /api/folders/{id} — folder metadata + breadcrumb array (iterative Python walk)
PATCH /api/folders/{id} — rename folder
DELETE /api/folders/{id} — cascade-delete with WITH RECURSIVE CTE + atomic quota decrement
PATCH /api/documents/{id}/folder — move document to folder (or root)
GET /api/documents extended with sort, order, folder_id, q params and is_shared field
04-04
04-05
04-06
04-07
added patterns
write_audit_log: flush-not-commit (session.flush only); never-raises (bare except + warning log)
Folder IDOR prevention: all ownership failures return 404 not 403
IntegrityError → 409 for duplicate folder name under same parent
WITH RECURSIVE CTE for subtree collection + OperationalError fallback for SQLite tests
Atomic quota decrement: CASE WHEN used_bytes > delta THEN ... ELSE 0 END (SQLite compat)
MinIO deletion best-effort: per-object try/except, never aborts primary operation
Breadcrumb: iterative Python parent-walk (not SQL recursive CTE) for SQLite compat
document_move_router: separate APIRouter with /api/documents prefix on folders module
FTS via plainto_tsquery wrapped in try/except for SQLite unit test compat
created modified
backend/services/audit.py
backend/api/folders.py
backend/api/documents.py
backend/main.py
write_audit_log uses session.flush() not session.commit() — D-14 architectural constraint: caller owns the transaction
Breadcrumb built by iterative Python walk (not WITH RECURSIVE) so unit tests on SQLite pass without special handling
document_move_router (PATCH /api/documents/{id}/folder) placed in folders.py not documents.py as a separate APIRouter with /api/documents prefix — logically a folder operation
FTS query (plainto_tsquery) wrapped in try/except so SQLite unit tests are not broken — silently degrades to unfiltered results on SQLite
Backward-compat fast path in list_documents: when no new params (sort=date, order=desc, no folder_id, no q), delegates to storage.list_metadata() for full topic-filter compat
FOLD-01
FOLD-02
FOLD-03
FOLD-04
FOLD-05
5min 2026-05-25

Phase 4 Plan 03: Folders API (FOLD-01..05) Summary

Folder CRUD REST API with breadcrumb navigation, document move, FTS + sort extensions, and a flush-not-commit audit helper usable by all Phase 4 plans

Performance

  • Duration: 5 min
  • Started: 2026-05-25T15:53:05Z
  • Completed: 2026-05-25T15:58:00Z
  • Tasks: 2
  • Files modified: 4

Accomplishments

  • Created backend/services/audit.py with write_audit_log() — fire-and-forget audit helper that flushes within the caller's transaction (never commits), catches all exceptions, logs warnings, never re-raises
  • Created backend/api/folders.py with 5 FOLD endpoints (create, list, get+breadcrumb, rename, cascade-delete) and the document-move endpoint — all guarded by get_regular_user, all returning 404 (not 403) for IDOR prevention
  • Extended backend/api/documents.py list endpoint with sort (name/date/size), order (asc/desc), folder_id filter, full-text search via plainto_tsquery, and is_shared field per document

Task Commits

Each task was committed atomically:

  1. Task 1: Create backend/services/audit.py - 259a154 (feat)
  2. Task 2: Create backend/api/folders.py + extend documents.py + register in main.py - 33a6f9a (feat)

Plan metadata: (included in SUMMARY commit)

Files Created/Modified

  • /Users/nik/Documents/Progamming/document_scanner/backend/services/audit.py - write_audit_log() async helper; flush-not-commit; never-raises
  • /Users/nik/Documents/Progamming/document_scanner/backend/api/folders.py - FOLD-01..05 endpoints + document move endpoint; all with 404-not-403 IDOR protection
  • /Users/nik/Documents/Progamming/document_scanner/backend/api/documents.py - Extended list_documents with sort/order/folder_id/q/is_shared; backward-compat fast path preserved
  • /Users/nik/Documents/Progamming/document_scanner/backend/main.py - Registered folders_router and document_move_router

Decisions Made

  • write_audit_log uses session.flush() — the caller commits; flush queues the entry in the same unit of work without committing
  • Breadcrumb uses iterative Python walk (not WITH RECURSIVE in Python/SQLAlchemy) — ensures SQLite unit tests pass
  • PATCH /api/documents/{id}/folder placed in folders.py as a separate document_move_router with the /api/documents prefix — avoids circular import between folders and documents modules
  • FTS (plainto_tsquery) wrapped in try/except Exception in list_documents — SQLite silently degrades to unfiltered results; PostgreSQL works fully

Deviations from Plan

None — plan executed exactly as written.

Issues Encountered

  • Pre-existing test failure test_extractor.py::test_extract_docx (missing python-docx package) was present before this plan and is out of scope. Not introduced by this plan.

Threat Surface Scan

No new trust boundaries introduced beyond what is documented in the plan's <threat_model>. All mitigations applied:

Threat Mitigation Confirmed
T-04-03-01: Elevation via admin on folder endpoints get_regular_user on all 6 endpoints
T-04-03-02: FTS cross-user data leak Document.user_id == current_user.id always in WHERE
T-04-03-03: Quota decrement race Atomic CASE WHEN UPDATE; best-effort MinIO delete
T-04-03-04: Folder IDOR All ownership failures return 404 (not 403)
T-04-03-05: Cross-user folder assignment Both doc and target folder ownership checked separately
T-04-03-06: IntegrityError 500 Caught → 409 Conflict with descriptive message

Known Stubs

None — all endpoints are fully wired and functional.

Next Phase Readiness

  • write_audit_log() available for Plans 04-04 (shares), 04-05 (proxy), 04-06 (admin audit viewer)
  • Folder CRUD complete; Plans 04-04 and later can create folders as test fixtures
  • Document list extended with folder_id and FTS — frontend integration can proceed

Phase: 04-folders-sharing-quotas-document-ux Completed: 2026-05-25