download wf update

This commit is contained in:
Anaïs Saraiva
2026-08-25 11:44:16 +02:00
parent c6a96b08c3
commit ccc29212cd
8 changed files with 239 additions and 33 deletions
+23 -10
View File
@@ -10,6 +10,8 @@ database, no object storage, and no queue. Expired files are swept automatically
## Features
- Drag & drop / file-picker uploads with progress, straight from the browser.
- **Media files render inline** (image/video/audio/PDF/text) as direct, embeddable
links, with one-click **Direct / HTML / BBCode / Markdown** link formats.
- **Optional per-file password**: set a password to gate downloads behind a prompt.
- Storage on the local filesystem; files survive restarts.
- A cryptographically secure, unguessable download key for every file.
@@ -131,10 +133,11 @@ curl -F "password=hunter2" -F "[email protected]" http://localhost:3000/api/uploa
{
"id": "u8wcfcm7",
"key": "u8wcfcm7",
"url": "/u8wcfcm7",
"url": "/u8wcfcm7.zip",
"originalName": "example.zip",
"downloadName": "u8wcfcm7.zip",
"size": 123456,
"mimeType": "application/zip",
"protected": true
}
```
@@ -161,9 +164,10 @@ curl -sS -F "[email protected]" "$BASE/api/upload"
{
"id": "u8wcfcm7",
"key": "u8wcfcm7",
"url": "/u8wcfcm7",
"url": "/u8wcfcm7.zip",
"originalName": "myfile.zip",
"size": 123456,
"mimeType": "application/zip",
"protected": false
}
```
@@ -210,13 +214,20 @@ Keep `id`/`key` around to delete the file afterwards via `DELETE /api/files/:id`
### `GET /:key` (also `HEAD`)
The download URL is just the key at the site root (`https://host/u8wcfcm7`).
The legacy `/d/:key` form still works as an alias. A file without a password
streams immediately; if a password is set, the server shows a password prompt
before streaming. A wrong or non-existent key returns a generic `404`.
The download URL is the key at the site root, optionally with the file extension
appended for a Catbox-style link (`https://host/u8wcfcm7` or
`https://host/u8wcfcm7.zip`). The legacy `/d/:key` form still works as an alias. A
file without a password streams immediately; if a password is set, the server shows
a password prompt before streaming. A wrong or non-existent key returns a generic
`404`.
Media files (images, video, audio, PDF, plain text) are served `inline` so they
render directly in the browser and can be embedded (`<img>`, Discord, etc.).
Everything else — and any URL with `?dl=1` — is sent as an `attachment` download.
```bash
curl -O http://localhost:3000/u8wcfcm7 # streams, or prompts if password set
curl -O http://localhost:3000/u8wcfcm7 # streams, or prompts if password set
curl -O "http://localhost:3000/u8wcfcm7.zip?dl=1" # always force a download
```
### Password protection
@@ -279,9 +290,11 @@ paths or stack traces.
`../`, encoded traversal, null bytes, and symlink injection via user input are
not possible.
Downloads additionally refuse symlinks (`lstat`) as defense in depth.
- **MIME / XSS.** Downloads are sent as `Content-Disposition: attachment` (browsers
download rather than render) with `X-Content-Type-Options: nosniff`, so a spoofed
`text/html` or `image/svg+xml` cannot execute scripts.
- **MIME / XSS.** Non-media downloads are sent as `Content-Disposition: attachment`
(browsers download rather than render) with `X-Content-Type-Options: nosniff`, so a
spoofed `text/html` or `image/svg+xml` cannot execute scripts. Only a whitelist of
inert media types (image, video, audio, PDF, plain text — never HTML or SVG) is
served `inline` for direct/embed links.
- **Secrecy of URLs.** All responses carry `Referrer-Policy: no-referrer`, and
downloads use `Cache-Control: private, no-store`, so the keyed URL is not leaked
to referrers or shared caches.