reamping the mardkwon
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user