feat: allow loading mods without a Steam API key

Add fetchModFromUrl/fetchModFileFromUrl and accept a fileUrl query param on /items routes so the frontend can download saves directly from a search result's file_url, with no key required.
This commit is contained in:
2026-08-08 11:32:57 +08:00
parent 4ffe858c36
commit f7a6eb6ee1
9 changed files with 170 additions and 48 deletions
+17 -5
View File
@@ -124,6 +124,10 @@ Low-level fetcher, extracted from the existing scraper.
- `fetchMod(id: string): Promise<TTSMod>` — Steam API call to
`ISteamRemoteStorage/GetPublishedFileDetails/v1` to get `file_url`, then
download + BSON-deserialize into `TTSMod`.
- `fetchModFromUrl(fileUrl)` — download + BSON-deserialize from a direct URL
(no Steam API call).
- `fetchModFile(id, apiKey)` / `fetchModFileFromUrl(fileUrl)` — raw save
bytes + derived filename, with and without the Steam API.
- `getFileName(url: string): Promise<string>` — derive filename from the
`content-disposition` header.
- `errors.ts`
@@ -171,8 +175,11 @@ Hono server exposing search + fetch.
`cheerio`, extract `{ id, title, author, previewImageUrl }` from the result
grid. Supports `pagenum` pagination. No API key required.
- `routes/items.ts`
- `GET /items/:id` — full parsed `TTSMod`.
- `GET /items/:id/file` — raw save bytes, filename from `getFileName`.
- `GET /items/:id` — full parsed `TTSMod`. Accepts an optional `fileUrl`
query param to download the save directly (no Steam API key needed);
otherwise resolves via the Steam API.
- `GET /items/:id/file` — raw save bytes, filename from `getFileName`. Also
accepts `fileUrl`.
- `routes/health.ts`
- `GET /health` — liveness.
- `env.ts` — zod validation of `STEAM_API_KEY`, `PORT`.
@@ -204,15 +211,20 @@ proxy API and `packages/extract` directly for analysis.
| ------ | ------------------- | -------------------------------------------- | ---- |
| GET | `/health` | Liveness | — |
| GET | `/search?q=&page=` | Scrape Workshop browse, return item list | — |
| GET | `/items/:id` | Full parsed `TTSMod` | key |
| GET | `/items/:id/file` | Raw save bytes, filename from header | key |
| GET | `/items/:id` | Full parsed `TTSMod` (`?fileUrl=` skips key) | key* |
| GET | `/items/:id/file` | Raw save bytes, filename from header | key* |
\* `STEAM_API_KEY` is optional; `/items/*` works without it when a `fileUrl`
query param is supplied.
## Data flow
```
/search?q=wingspan → scrape browse page → list of {id, title, ...}
/search?q=wingspan → scrape browse page → list of {id, title, fileUrl, ...}
/items/:id → Steam API (needs key) → file_url
└─ /items/:id?fileUrl=... → download directly (no key)
download BSON → parse → TTSMod