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
+37 -1
View File
@@ -1,11 +1,47 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { getFileName } from './index.js';
import { fetchModFileFromUrl, fetchModFromUrl, getFileName } from './index.js';
import { ItemNotFoundError, NoFileError, SteamApiError, TtsError } from './errors.js';
afterEach(() => {
vi.unstubAllGlobals();
});
// A minimal valid BSON document: { GameMode: 'Tabletop' }.
const bsonBytes = new Uint8Array([
28, 0, 0, 0, 2, 71, 97, 109, 101, 77, 111, 100, 101, 0, 9, 0, 0, 0, 84, 97,
98, 108, 101, 116, 111, 112, 0, 0,
]);
function bsonResponse(): Response {
return new Response(bsonBytes);
}
describe('fetchModFromUrl', () => {
it('downloads and deserializes a save from a URL', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(bsonResponse()));
const mod = await fetchModFromUrl('https://example.com/save.json');
expect(mod.GameMode).toBe('Tabletop');
});
it('throws on a failed download', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(null, { status: 404 })));
await expect(fetchModFromUrl('https://example.com/save.json')).rejects.toThrow(
'Failed to download save file (404)',
);
});
});
describe('fetchModFileFromUrl', () => {
it('returns bytes and a filename derived from the URL', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(bsonResponse()));
const { data, filename } = await fetchModFileFromUrl(
'https://example.com/files/mod.json',
);
expect(new Uint8Array(data)).toEqual(bsonBytes);
expect(filename).toBe('mod.json');
});
});
describe('getFileName', () => {
it('parses a quoted content-disposition filename', () => {
expect(