chore: added documentation
This commit is contained in:
94
docs/file-management.md
Normal file
94
docs/file-management.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# File Management
|
||||
|
||||
## Overview
|
||||
|
||||
Each user has an isolated directory under `/downloads/{user_id}/`. The frontend provides a browser-style file tree with support for downloading individual files, downloading folders as ZIP archives, and deleting files or folders.
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
/downloads/
|
||||
└── {user_id}/
|
||||
└── {collection or album name}/
|
||||
├── Track 1.flac
|
||||
├── Track 2.flac
|
||||
└── cover.jpg
|
||||
```
|
||||
|
||||
Single-track downloads are automatically wrapped in a folder named after the track.
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
All file access goes through a path traversal check:
|
||||
|
||||
1. The requested relative path is joined with the user's base directory.
|
||||
2. `.resolve()` canonicalizes the result (expands `..`, symlinks, etc.).
|
||||
3. The resolved path is checked to ensure it starts with the resolved user directory.
|
||||
4. Any path that escapes the user directory returns `400 Bad Request`.
|
||||
|
||||
Admins can browse any user's directory through dedicated admin endpoints that accept a `user_id` parameter.
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### User endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/api/files` | GET | List directory contents at `?path=` (relative to user root) |
|
||||
| `/api/files/download` | GET | Download a single file at `?path=` |
|
||||
| `/api/files/download-folder` | GET | Download a directory as a ZIP at `?path=` |
|
||||
| `/api/files/delete` | DELETE | Delete a file or directory at `?path=` |
|
||||
|
||||
### Admin endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/api/admin/files` | GET | List a specific user's directory; requires `?user_id=` and optional `?path=` |
|
||||
| `/api/admin/files/download` | GET | Download a file from any user's directory |
|
||||
| `/api/admin/files/download-folder` | GET | Download a folder as ZIP from any user's directory |
|
||||
| `/api/admin/files/delete` | DELETE | Delete from any user's directory |
|
||||
|
||||
---
|
||||
|
||||
## Directory Listing Response
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "Album Name", "path": "Album Name", "is_dir": true },
|
||||
{ "name": "Track.flac", "path": "Album Name/Track.flac", "is_dir": false, "size": 24601234 }
|
||||
]
|
||||
```
|
||||
|
||||
Directories always appear before files. Paths are relative to the user's root.
|
||||
|
||||
---
|
||||
|
||||
## ZIP Downloads
|
||||
|
||||
Folder downloads are streamed directly as a ZIP file using Python's `zipfile` module. Files are added with paths relative to the requested folder, so the ZIP extracts cleanly into a single directory.
|
||||
|
||||
---
|
||||
|
||||
## Post-Processing (after download)
|
||||
|
||||
After a Votify or Monochrome download completes, several cleanup steps run automatically:
|
||||
|
||||
1. **Flatten nested directories** — removes redundant intermediate folders.
|
||||
2. **Rename from metadata** — reads embedded ID3/FLAC tags and renames files to `Title - Artist.ext` (see `rename_from_metadata()` in `utils.py`).
|
||||
3. **Wrap single tracks** — if a download produces only one file with no folder, it is moved into a named subfolder.
|
||||
4. **Cleanup empty dirs** — removes any directories left empty after the above steps.
|
||||
|
||||
---
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Relevance |
|
||||
|------|-----------|
|
||||
| [app.py](../app.py) | All file route handlers |
|
||||
| [utils.py](../utils.py) | `sanitize_filename`, `rename_from_metadata`, `cleanup_empty_dirs` |
|
||||
Reference in New Issue
Block a user