366 lines
11 KiB
Markdown
366 lines
11 KiB
Markdown
# PLAN.md
|
|
|
|
Notitie: `project_docs/SPARRING_GPT_PROJECT_PROMPT.md` is niet gevonden in deze repository op 10 maart 2026. Dit plan is opgesteld op basis van de overige documenten in `project_docs/`.
|
|
|
|
## 1. Doel van de applicatie
|
|
|
|
De applicatie is een webgebaseerde storage manager voor self-hosted omgevingen, met een veilige browserinterface om bestanden te beheren binnen strikt whitelisted root directories.
|
|
|
|
Kernwaarde van v1:
|
|
- veilige en voorspelbare filesystem-operaties binnen whitelist
|
|
- transparant taakmodel voor langlopende copy/move acties
|
|
- stabiele API-contracten die met golden tests bewaakt worden
|
|
|
|
## 2. V1 scope en out-of-scope
|
|
|
|
In scope voor v1:
|
|
- directory browsing binnen whitelist roots
|
|
- file-operaties: `rename`, `delete`, `mkdir`
|
|
- task-based operaties: `copy`, `move`
|
|
- task status/progress polling
|
|
- bookmarks CRUD (minimaal: list/create/delete)
|
|
- history logging van uitgevoerde operaties
|
|
- security path controls: canonicalisatie, traversal-blocking, symlink escape blocking
|
|
|
|
Out-of-scope voor v1:
|
|
- user management en authenticatie/autorisatie
|
|
- fijnmazig permissions management
|
|
- cloud storage integraties
|
|
- distributed/multi-node storage
|
|
- geavanceerde job scheduling (prioriteiten, parallel queue tuning, retry policy)
|
|
- recycle bin/undo functionaliteit
|
|
|
|
## 3. Voorgestelde architectuur voor eerste versie
|
|
|
|
Technische stack:
|
|
- Backend: Python + FastAPI
|
|
- Frontend: lichte JavaScript UI (zonder zwaar framework)
|
|
- Opslag: SQLite voor tasks, bookmarks, history
|
|
|
|
Architectuur v1 (monolithisch, modulair):
|
|
- `API-laag` (FastAPI routes): validatie, response-shaping, HTTP foutcodes
|
|
- `Service-laag`: use-case logica (browse, file ops, tasks, bookmarks)
|
|
- `Filesystem-laag`: gecapsuleerde filesystem calls
|
|
- `Security/path-guard`: centrale pad-resolutie en whitelist enforcement
|
|
- `Task-runner`: background worker voor copy/move met persistente status
|
|
- `Repository-laag`: SQLite toegang voor tasks/bookmarks/history
|
|
|
|
Datastroom copy/move:
|
|
1. API ontvangt request en valideert payload.
|
|
2. `path_guard` valideert source/destination tegen whitelist en security regels.
|
|
3. Service maakt task-record aan met status `queued`.
|
|
4. Worker pakt task op, zet status op `running`, voert operatie uit, werkt progress bij.
|
|
5. Bij afronding: status `completed` of `failed`; resultaat en fouten worden opgeslagen.
|
|
|
|
## 4. Voorgestelde backend projectstructuur
|
|
|
|
```text
|
|
backend/
|
|
app/
|
|
main.py
|
|
config.py
|
|
logging.py
|
|
api/
|
|
schemas.py
|
|
errors.py
|
|
routes_browse.py
|
|
routes_files.py
|
|
routes_tasks.py
|
|
routes_bookmarks.py
|
|
services/
|
|
browse_service.py
|
|
file_ops_service.py
|
|
task_service.py
|
|
bookmark_service.py
|
|
history_service.py
|
|
security/
|
|
path_guard.py
|
|
fs/
|
|
filesystem_adapter.py
|
|
tasks/
|
|
worker.py
|
|
progress.py
|
|
transitions.py
|
|
db/
|
|
sqlite.py
|
|
models.py
|
|
migrations/
|
|
001_init.sql
|
|
repositories/
|
|
task_repo.py
|
|
bookmark_repo.py
|
|
history_repo.py
|
|
tests/
|
|
unit/
|
|
test_path_guard.py
|
|
test_task_transitions.py
|
|
feature/
|
|
test_browse_flow.py
|
|
test_file_ops_flow.py
|
|
test_task_flow.py
|
|
regression/
|
|
test_path_traversal_blocked.py
|
|
test_unicode_filenames.py
|
|
test_large_directory_listing.py
|
|
golden/
|
|
test_api_browse_golden.py
|
|
test_api_errors_golden.py
|
|
```
|
|
|
|
Richtlijnen:
|
|
- security checks alleen via `path_guard.py` (geen ad-hoc checks in routes)
|
|
- filesystem calls alleen via `filesystem_adapter.py`
|
|
- API shapes blijven stabiel en worden bewaakt met golden tests
|
|
|
|
## 5. Belangrijkste API endpoints voor versie 1
|
|
|
|
Browse:
|
|
- `GET /api/browse?path=<root-relative>`
|
|
|
|
File-operaties:
|
|
- `POST /api/files/rename`
|
|
- `POST /api/files/delete`
|
|
- `POST /api/files/mkdir`
|
|
- `POST /api/files/copy` (task-based)
|
|
- `POST /api/files/move` (task-based)
|
|
|
|
Tasks:
|
|
- `GET /api/tasks/{task_id}`
|
|
- `GET /api/tasks`
|
|
|
|
Bookmarks:
|
|
- `GET /api/bookmarks`
|
|
- `POST /api/bookmarks`
|
|
- `DELETE /api/bookmarks/{id}`
|
|
|
|
## 6. Browse contract (v1 aangescherpt)
|
|
|
|
Padmodel:
|
|
- API accepteert in v1 alleen `root-relative` paden met expliciete root alias.
|
|
- Voorstel requestvorm: `GET /api/browse?path=<root>/<subpath>`
|
|
- Voorbeelden: `storage1/`, `storage1/series`, `archive/docs/2026`
|
|
- Absolute host paths worden niet geaccepteerd in publieke API om ambiguiteit te vermijden.
|
|
|
|
Succesresponse:
|
|
```json
|
|
{
|
|
"path": "storage1/series",
|
|
"directories": [
|
|
{
|
|
"name": "ShowA",
|
|
"path": "storage1/series/ShowA",
|
|
"modified": "2026-03-10T12:00:00Z"
|
|
}
|
|
],
|
|
"files": [
|
|
{
|
|
"name": "episode.mkv",
|
|
"path": "storage1/series/episode.mkv",
|
|
"size": 734003200,
|
|
"modified": "2026-03-10T11:30:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Metadata velden (v1):
|
|
- voor directories: `name`, `path`, `modified`
|
|
- voor files: `name`, `path`, `size`, `modified`
|
|
- `modified` als UTC ISO-8601 string
|
|
- geen checksum/mime in v1
|
|
|
|
Hidden files beleid:
|
|
- default: hidden entries (`.`-prefixed) niet tonen.
|
|
- optioneel query-flag: `show_hidden=true` voor expliciete opname.
|
|
- `.` en `..` worden nooit teruggegeven.
|
|
|
|
Foutresponses:
|
|
- `400` bij ongeldige `path` parameter of malformed input
|
|
- `403` bij pad buiten whitelist / security-blokkade
|
|
- `404` als pad niet bestaat
|
|
- `409` als padtype mismatch (bijv. file i.p.v. directory)
|
|
- `500` bij onverwachte I/O fout
|
|
|
|
## 7. Delete gedrag (veiligheidsuitwerking v1)
|
|
|
|
Endpoint:
|
|
- `POST /api/files/delete`
|
|
- request: `{ "path": "<root-relative>", "recursive": false }`
|
|
|
|
Gedrag files vs directories:
|
|
- file delete: direct verwijderen indien pad valide is
|
|
- directory delete:
|
|
- standaard alleen lege directory (`recursive=false`)
|
|
- non-empty directory geeft `409 directory_not_empty`
|
|
- recursive delete alleen bij expliciet `recursive=true`
|
|
|
|
Non-empty directories:
|
|
- zonder recursive: blokkeren met heldere foutmelding
|
|
- met recursive: toegestaan binnen whitelist, met strikte guard tegen symlink escapes tijdens traversal
|
|
|
|
Foutafhandeling:
|
|
- `404` target bestaat niet
|
|
- `403` security/path violations
|
|
- `409` directory non-empty zonder recursive of operation conflict
|
|
- `500` onverwachte filesystem fout
|
|
|
|
Bevestigingsaannames:
|
|
- backend voert geen interactieve confirm uit
|
|
- frontend moet destructive acties bevestigen voordat endpoint wordt aangeroepen
|
|
- voorstel frontend: extra bevestigingstekst voor recursive delete
|
|
|
|
## 8. Task/progress model (concreet v1)
|
|
|
|
Progress model:
|
|
- primaire metriek: bytes (`done_bytes`, `total_bytes`) voor copy/move van files
|
|
- secundaire metriek voor directory-operaties: `done_items`, `total_items`
|
|
- API retourneert beide waar beschikbaar
|
|
|
|
Als totaal onbekend is:
|
|
- `total_bytes` en/of `total_items` blijven `null`
|
|
- client toont indeterminate progress state
|
|
- `done_*` kan wel oplopen
|
|
|
|
Partial failure gedrag:
|
|
- v1 policy: fail-fast per task
|
|
- eerste fatale fout zet task op `failed`
|
|
- reeds verplaatste/gekopieerde items blijven staan (geen rollback in v1)
|
|
- response bevat `failed_item` en foutdetails
|
|
|
|
Eindstatussen v1:
|
|
- `completed`
|
|
- `failed`
|
|
- `queued` en `running` als tussenstatussen
|
|
- `canceled` nog niet in v1
|
|
|
|
## 9. Voorstel SQLite schema (v1)
|
|
|
|
Tabel `tasks`:
|
|
- `id TEXT PRIMARY KEY`
|
|
- `operation TEXT NOT NULL` (`copy`/`move`)
|
|
- `source_path TEXT NOT NULL`
|
|
- `destination_path TEXT NOT NULL`
|
|
- `status TEXT NOT NULL` (`queued`/`running`/`completed`/`failed`)
|
|
- `done_bytes INTEGER NULL`
|
|
- `total_bytes INTEGER NULL`
|
|
- `done_items INTEGER NULL`
|
|
- `total_items INTEGER NULL`
|
|
- `current_item TEXT NULL`
|
|
- `error_code TEXT NULL`
|
|
- `error_message TEXT NULL`
|
|
- `failed_item TEXT NULL`
|
|
- `created_at TEXT NOT NULL`
|
|
- `started_at TEXT NULL`
|
|
- `finished_at TEXT NULL`
|
|
|
|
Indexen:
|
|
- `idx_tasks_status_created_at(status, created_at DESC)`
|
|
- `idx_tasks_created_at(created_at DESC)`
|
|
|
|
Tabel `bookmarks`:
|
|
- `id INTEGER PRIMARY KEY AUTOINCREMENT`
|
|
- `label TEXT NOT NULL`
|
|
- `path TEXT NOT NULL UNIQUE`
|
|
- `created_at TEXT NOT NULL`
|
|
- `updated_at TEXT NOT NULL`
|
|
|
|
Tabel `history`:
|
|
- `id INTEGER PRIMARY KEY AUTOINCREMENT`
|
|
- `operation TEXT NOT NULL`
|
|
- `path TEXT NULL`
|
|
- `source_path TEXT NULL`
|
|
- `destination_path TEXT NULL`
|
|
- `status TEXT NOT NULL`
|
|
- `task_id TEXT NULL`
|
|
- `error_code TEXT NULL`
|
|
- `error_message TEXT NULL`
|
|
- `created_at TEXT NOT NULL`
|
|
|
|
Indexen:
|
|
- `idx_history_created_at(created_at DESC)`
|
|
- `idx_history_operation_created_at(operation, created_at DESC)`
|
|
- `idx_history_task_id(task_id)`
|
|
|
|
## 10. API error model (v1)
|
|
|
|
Standaard error shape:
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "path_outside_whitelist",
|
|
"message": "Requested path is outside allowed roots",
|
|
"details": {
|
|
"path": "storage1/../../etc"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Errorvelden:
|
|
- `code`: machine-readable, stabiel voor clientlogica
|
|
- `message`: mens-leesbare samenvatting
|
|
- `details`: optioneel object met context (geen gevoelige hostpaths)
|
|
|
|
Voorgestelde error codes:
|
|
- security/path:
|
|
- `path_outside_whitelist`
|
|
- `path_traversal_detected`
|
|
- `symlink_escape_detected`
|
|
- `invalid_root_alias`
|
|
- not found/conflict/validation:
|
|
- `path_not_found`
|
|
- `already_exists`
|
|
- `directory_not_empty`
|
|
- `invalid_request`
|
|
- `validation_error`
|
|
- operationeel:
|
|
- `io_error`
|
|
- `internal_error`
|
|
|
|
HTTP mapping:
|
|
- `400`: `invalid_request`, `validation_error`
|
|
- `403`: security/path errors
|
|
- `404`: `path_not_found`, `task_not_found`, `bookmark_not_found`
|
|
- `409`: `already_exists`, `directory_not_empty`, type conflicts
|
|
- `500`: `io_error`, `internal_error`
|
|
|
|
## 11. Minimale frontend-notitie voor v1
|
|
|
|
Hoofdschermen:
|
|
- `Browser view`: directory listing + acties (rename/delete/mkdir/copy/move)
|
|
- `Tasks view`: lijst met actieve/recente taken en detailstatus
|
|
- `Bookmarks`: snelle navigatie naar opgeslagen paden
|
|
|
|
Task polling:
|
|
- bij actieve taken elke 1-2 seconden `GET /api/tasks/{id}` of batch `GET /api/tasks`
|
|
- polling stopt automatisch bij eindstatus (`completed`/`failed`)
|
|
- UI toont determinate progress bij bekende totalen, anders indeterminate indicator
|
|
|
|
Foutweergave:
|
|
- fouten tonen met `error.message`
|
|
- client-logica kan op `error.code` beslissen (bijv. specifieke melding voor `directory_not_empty`)
|
|
- destructive acties (delete, vooral recursive) krijgen expliciete confirm-dialog
|
|
|
|
## 12. Implementatieplan in kleine stappen
|
|
|
|
1. Backend skeleton opzetten (FastAPI app, routers, config, logging).
|
|
2. `path_guard` implementeren met root-alias model en centrale security checks.
|
|
3. Unit tests toevoegen voor whitelist/traversal/symlink scenario's.
|
|
4. Browse endpoint bouwen volgens aangescherpt contract incl. hidden files beleid.
|
|
5. Golden tests toevoegen voor browse success + browse error responses.
|
|
6. `rename`, `mkdir`, `delete` implementeren met veilig delete-gedrag (recursive policy).
|
|
7. Feature tests toevoegen voor file-operaties incl. non-empty directory fouten.
|
|
8. SQLite schema (`tasks`, `bookmarks`, `history`) toevoegen met repositories.
|
|
9. Task statusmachine en transitions implementeren + unit tests.
|
|
10. Worker voor copy/move implementeren met bytes/items progress.
|
|
11. Task endpoints implementeren (`create`, `get`, `list`) met eindstatussen.
|
|
12. Feature tests voor copy/move + partial failure gedrag + polling flow.
|
|
13. Bookmarks endpoints implementeren + basis tests.
|
|
14. Regression tests voor path traversal, unicode, grote/nested directories.
|
|
15. Frontend v1 basis flows koppelen (browse, acties, tasks polling, foutweergave).
|
|
16. Eindcontrole: volledige test run + golden contract verificatie.
|
|
|
|
## 13. Governance-notitie
|
|
|
|
Volgens `CHANGE_POLICY.md` en `SAFE_FILES.md` vallen API-wijzigingen en DB schema-wijzigingen onder "eerst voorstel nodig". Deze aangescherpte `PLAN.md` is het voorstel dat eerst goedgekeurd moet worden. Na expliciete goedkeuring kan implementatie starten.
|