diff --git a/CLAUDE.md b/CLAUDE.md index 8f50d20..1b05380 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,7 @@ Standard Go HTTP server (not Cloudflare Workers) serving as a temporary file upl **Request flow (`handler.go`):** 1. Parse `Authorization` header + `project`/`projectId` from query params or `param-*` headers (query params take precedence). `project` accepts **either** a project **sid** (stable slug, e.g. `my-project`) **or** a numeric project ID; `projectId` is always the numeric ID. Because a sid always starts with a letter (api `ReValidSID`: `^[a-z][a-z0-9\-]*[^\-]$`), an all-digit `project` is unambiguously an ID, so `uploadHandler` routes it to `projectID` (an explicit `projectId` still wins). Both are relayed to `me.authorized`, which resolves `project` by sid and `projectId` by numeric ID. 2. Authorize via `checkAuth()` in `auth.go` -3. Parse TTL (1–7 days, default 1) and optional filename the same way +3. Parse TTL (1–365 days, default 1) and optional filename the same way 4. Generate a 24-char alphanumeric `fn` (`generateFilename`, rejection-sampled to stay unbiased) 5. Stream body to GCS with cache-control and optional content-disposition, keyed by `fn` 6. Insert metadata into PostgreSQL via `pgctx.Exec` diff --git a/README.md b/README.md index 192f553..458fcf3 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Endpoint: https://dropbox.deploys.app/ | Name | Type | Data Type | Description | |-----------------|----------|-----------|---------------------------------| | Authorization | required | string | Authorization token | -| Param-Ttl | optional | number | 1-7, default 1 | +| Param-Ttl | optional | number | 1-365, default 1 | | Param-Project | required | string | Project sid (stable slug) or numeric project ID | | Param-Filename | optional | string | Filename in Content-Disposition | @@ -58,7 +58,7 @@ Endpoint: https://dropbox.deploys.app/ | Name | Type | Data Type | Description | |------------|----------|-----------|---------------------------------| -| ttl | optional | number | 1-7, default 1 | +| ttl | optional | number | 1-365, default 1 | | project | required | string | Project sid (stable slug) **or** numeric project ID — an all-digit value is treated as the ID | | projectId | optional | string | Numeric project ID (same as passing a numeric `project`) | | filename | optional | string | Filename in Content-Disposition | @@ -148,7 +148,7 @@ Two steps: **create** the URL, then **PUT** the file to it. |---------------|--------|-----------------------------------------------------------------------------| | `project` | string | Project sid **or** numeric project ID (all-digit ⇒ ID). Required. | | `projectId` | string | Numeric project ID (same as a numeric `project`). | -| `ttl` | number | Download lifetime of the resulting file, 1–7 days (default 1). | +| `ttl` | number | Download lifetime of the resulting file, 1–365 days (default 1). | | `filename` | string | Optional `Content-Disposition` filename on download. | | `contentType` | string | Optional. When set, the PUT **must** send this exact `Content-Type`. | | `minSize` | number | Optional lower bound in bytes (clamped to ≥ 1, so empty uploads are refused).| diff --git a/handler.go b/handler.go index d15410e..9203b93 100644 --- a/handler.go +++ b/handler.go @@ -76,7 +76,7 @@ func (a *App) uploadHandler(w http.ResponseWriter, r *http.Request) { ttlStr := firstNonEmpty(r.URL.Query().Get("ttl"), r.Header.Get("param-ttl")) ttlDays, _ := strconv.Atoi(ttlStr) - if ttlDays < 1 || ttlDays > 7 { + if ttlDays < 1 || ttlDays > 365 { ttlDays = 1 } diff --git a/handler_test.go b/handler_test.go index 966b69c..236cdd7 100644 --- a/handler_test.go +++ b/handler_test.go @@ -323,7 +323,9 @@ func TestUpload_TTL(t *testing.T) { {"1", 1}, {"3", 3}, {"7", 7}, - {"8", 1}, + {"8", 8}, + {"365", 365}, + {"366", 1}, {"-1", 1}, {"abc", 1}, {"", 1}, @@ -658,17 +660,17 @@ func TestUpload_ExplicitProjectIDWinsOverNumericProject(t *testing.T) { func TestIsAllDigits(t *testing.T) { t.Parallel() cases := map[string]bool{ - "": false, - "12345": true, - "0": true, - "acme": false, - "acme-prod": false, - "12a": false, - "a123": false, - " 12": false, - "12 ": false, - "-1": false, - "1.0": false, + "": false, + "12345": true, + "0": true, + "acme": false, + "acme-prod": false, + "12a": false, + "a123": false, + " 12": false, + "12 ": false, + "-1": false, + "1.0": false, } for in, want := range cases { if got := isAllDigits(in); got != want { diff --git a/upload_url.go b/upload_url.go index e2b1df5..febfbb0 100644 --- a/upload_url.go +++ b/upload_url.go @@ -128,7 +128,7 @@ func parseUploadToken(key []byte, token string) (uploadGrant, bool) { type uploadURLRequest struct { Project string `json:"project"` // project sid or numeric id ProjectID string `json:"projectId"` // numeric id (same as a numeric project) - TTL int `json:"ttl"` // file download lifetime, 1-7 days (default 1) + TTL int `json:"ttl"` // file download lifetime, 1-365 days (default 1) Filename string `json:"filename"` // optional Content-Disposition on download ContentType string `json:"contentType"` // optional; the PUT must send this exactly MinSize int64 `json:"minSize"` // optional lower bound in bytes (>= 1) @@ -161,7 +161,7 @@ func (a *App) uploadURLHandler(w http.ResponseWriter, r *http.Request) { } ttlDays := req.TTL - if ttlDays < 1 || ttlDays > 7 { + if ttlDays < 1 || ttlDays > 365 { ttlDays = 1 }