upload volledige repo
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user