reamping the mardkwon

This commit is contained in:
Anaïs Saraiva
2026-08-15 10:49:26 +02:00
parent 4867cf57b6
commit e97eddbc1a
3 changed files with 85 additions and 15 deletions
+75 -10
View File
@@ -93,7 +93,7 @@ Example metadata record (`storage/metadata/<file-id>.json`):
```json
{
"id": "nkj2p0ulkijp",
"id": "u8wcfcm7",
"originalName": "example.zip",
"size": 123456,
"mimeType": "application/zip",
@@ -128,28 +128,93 @@ curl -F "password=hunter2" -F "[email protected]" http://localhost:3000/api/uploa
```json
{
"id": "xmK5lLMRErlN",
"key": "xmK5lLMRErlN",
"url": "/xmK5lLMRErlN",
"id": "u8wcfcm7",
"key": "u8wcfcm7",
"url": "/u8wcfcm7",
"originalName": "example.zip",
"size": 123456,
"protected": true
}
```
`id` and `key` are the same 12-character lowercase alphanumeric token (~62 bits
`id` and `key` are the same 8-character lowercase alphanumeric token (~41 bits
of entropy); the short length keeps share URLs tidy, and an optional password
can add a second factor of protection.
### Uploading with curl (tutorial)
Start an instance, then point curl at `/api/upload`:
```bash
BASE=https://download.wf
```
**1. Public upload (no password).** The only required field is `file`:
```bash
curl -sS -F "[email protected]" "$BASE/api/upload"
```
```json
{
"id": "u8wcfcm7",
"key": "u8wcfcm7",
"url": "/u8wcfcm7",
"originalName": "myfile.zip",
"size": 123456,
"protected": false
}
```
`url` is the share link — put it in a variable with `jq`:
```bash
url=$(curl -sS -F "[email protected]" "$BASE/api/upload" | jq -r .url)
echo "share: $BASE$url"
```
**2. Password-protected upload.** Add the `password` field; anyone with the link
must then enter it to download:
```bash
curl -sS -F "password=hunter2" -F "[email protected]" "$BASE/api/upload"
# => "protected": true
```
**3. Download.** Public files stream immediately:
```bash
curl -O "$BASE$url"
```
A password-protected file needs the auth cookie first:
```bash
curl -sS -c cookies.txt -X POST -d "password=hunter2" "$BASE/api/auth/$key"
curl -sS -b cookies.txt -O "$BASE/$key"
```
**4. Troubleshooting.**
- `400 expected a multipart/form-data upload` — missing or malformed
`Content-Type` (curl's `-F` already sets it correctly).
- `400 no file part in the upload` — no `file=` field was sent.
- `413 file too large` — the upload exceeds `MAX_FILE_SIZE`.
- `503 storage is full, try again later` — disk usage on the storage
filesystem is above `MAX_STORAGE_USAGE`; retry later.
Keep `id`/`key` around to delete the file afterwards via `DELETE /api/files/:id`
(see below).
### `GET /:key` (also `HEAD`)
The download URL is just the key at the site root (`https://host/xmK5lLMRErlN`).
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`.
```bash
curl -O http://localhost:3000/xmK5lLMRErlN # streams, or prompts if password set
curl -O http://localhost:3000/u8wcfcm7 # streams, or prompts if password set
```
### Password protection
@@ -177,7 +242,7 @@ Permanently deletes the file and its metadata. The correct download key must be
sent in the `X-Delete-Key` header — proving you hold the secret.
```bash
curl -X DELETE -H "X-Delete-Key: nkj2p0ulkijp" http://localhost:3000/api/files/nkj2p0ulkijp
curl -X DELETE -H "X-Delete-Key: u8wcfcm7" http://localhost:3000/api/files/u8wcfcm7
```
### `GET /api/health`
@@ -194,8 +259,8 @@ paths or stack traces.
## Security model
- **Secrets.** Each file gets a fresh 12-character lowercase alphanumeric key
(`crypto.randomBytes` mapped onto `a-z0-9`, ~62 bits of entropy), used as both
- **Secrets.** Each file gets a fresh 8-character lowercase alphanumeric key
(`crypto.randomBytes` mapped onto `a-z0-9`, ~41 bits of entropy), used as both
the share id and the download key. Guessing one is infeasible at scale, and an
optional password can be an added second factor. No timestamps, incrementing
ids, `Math.random()`, or filenames are ever used as secrets.