From b2725b825a1be7fb7dd5f6be60f4f1378a6f1a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ne=C3=9Flauer?= Date: Sat, 4 Jul 2026 16:18:47 +0200 Subject: [PATCH] Add KSA to the status page game filters - Add a ksa GameConfig (status /status/netkan-ksa.json, netkan/history/metadata URLs at KSAModding/KSA-NetKAN and KSAModding/KSA-CKAN-meta on main) and append it to the games array that drives all filtering, fetching, and links. - Cover the new config in the game-config tests (URL builders plus the three-game array). - fetch-status.js: also fetch netkan-ksa.json; skip a status file that returns 404 (not published yet) with a warning so the other games still get fetched, keep any other failure fatal, and fail only when no file is published at all. - README: document the per-game status files; the KSA wget line is commented out until the NetKAN bot publishes the file. --- README.md | 7 +++- scripts/fetch-status.js | 20 ++++++++++-- src/lib/__tests__/game-config.test.ts | 47 +++++++++++++++++++++++++-- src/lib/game-config.ts | 18 +++++++++- 4 files changed, 84 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 39345ff..a167e64 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,17 @@ pnpm fetch:status mkdir -p public/status wget https://status.ksp-ckan.space/status/netkan.json -O public/status/netkan.json wget https://status.ksp-ckan.space/status/netkan-ksp2.json -O public/status/netkan-ksp2.json +# once the NetKAN bot publishes KSA status (returns 404 until then), also: +# wget https://status.ksp-ckan.space/status/netkan-ksa.json -O public/status/netkan-ksa.json ``` -The app fetches two separate JSON files: +The app fetches a separate JSON file per game: - `/status/netkan.json` - KSP mods status - `/status/netkan-ksp2.json` - KSP2 mods status +- `/status/netkan-ksa.json` - KSA mods status + +The fetcher skips a status file that is not published yet (HTTP 404) with a warning, and the app shows an empty list for that game. Then run the development server: diff --git a/scripts/fetch-status.js b/scripts/fetch-status.js index da8770e..1cfe649 100644 --- a/scripts/fetch-status.js +++ b/scripts/fetch-status.js @@ -4,7 +4,8 @@ import * as path from 'path'; const urls = { 'netkan.json': 'https://status.ksp-ckan.space/status/netkan.json', - 'netkan-ksp2.json': 'https://status.ksp-ckan.space/status/netkan-ksp2.json' + 'netkan-ksp2.json': 'https://status.ksp-ckan.space/status/netkan-ksp2.json', + 'netkan-ksa.json': 'https://status.ksp-ckan.space/status/netkan-ksa.json' }; const outputDir = 'public/status'; @@ -25,6 +26,15 @@ async function fetchAndSaveStatus() { const response = await fetch(url); + // A game whose status file is not published yet comes back as 404; + // skip just that file so the other games are still fetched. Any + // other failure (5xx, network error, disk error) still fails the + // whole run loudly. + if (response.status === 404) { + console.warn(` āš ļø Skipped ${filename}: not published (HTTP 404)`); + return null; + } + if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status} for URL: ${url}`); } @@ -39,9 +49,13 @@ async function fetchAndSaveStatus() { return outputPath; }); - await Promise.all(savePromises); + const saved = (await Promise.all(savePromises)).filter((p) => p !== null); + + if (saved.length === 0) { + throw new Error('none of the status files are published'); + } - console.log('\nšŸŽ‰ All status files have been successfully fetched and saved.'); + console.log(`\nšŸŽ‰ Saved ${saved.length} of ${savePromises.length} status files.`); } catch (error) { console.error('\nāŒ An error occurred during the fetch and save process:', error.message); diff --git a/src/lib/__tests__/game-config.test.ts b/src/lib/__tests__/game-config.test.ts index dba8e04..a0c000e 100644 --- a/src/lib/__tests__/game-config.test.ts +++ b/src/lib/__tests__/game-config.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { ksp, ksp2, games } from '../game-config'; +import { ksp, ksp2, ksa, games } from '../game-config'; describe('ksp config', () => { it('should have correct id and name', () => { @@ -78,11 +78,48 @@ describe('ksp2 config', () => { }); }); +describe('ksa config', () => { + it('should have correct id and name', () => { + expect(ksa.id).toBe('ksa'); + expect(ksa.name).toBe('KSA'); + }); + + it('should have correct status URL', () => { + expect(ksa.status).toBe('/status/netkan-ksa.json'); + }); + + it('should generate netkan URL for active mod', () => { + const url = ksa.netkan('TestMod', false); + expect(url).toBe('https://github.com/KSAModding/KSA-NetKAN/tree/main/NetKAN/TestMod.netkan'); + }); + + it('should generate netkan URL for frozen mod', () => { + const url = ksa.netkan('TestMod', true); + expect(url).toBe('https://github.com/KSAModding/KSA-NetKAN/tree/main/NetKAN/TestMod.frozen'); + }); + + it('should generate history URL for active mod', () => { + const url = ksa.history('TestMod', false); + expect(url).toBe('https://github.com/KSAModding/KSA-NetKAN/commits/main/NetKAN/TestMod.netkan'); + }); + + it('should generate history URL for frozen mod', () => { + const url = ksa.history('TestMod', true); + expect(url).toBe('https://github.com/KSAModding/KSA-NetKAN/commits/main/NetKAN/TestMod.frozen'); + }); + + it('should generate metadata URL', () => { + const url = ksa.metadata('TestMod'); + expect(url).toBe('https://github.com/KSAModding/KSA-CKAN-meta/tree/main/TestMod'); + }); +}); + describe('games array', () => { - it('should contain both ksp and ksp2', () => { - expect(games).toHaveLength(2); + it('should contain ksp, ksp2, and ksa', () => { + expect(games).toHaveLength(3); expect(games).toContain(ksp); expect(games).toContain(ksp2); + expect(games).toContain(ksa); }); it('should have ksp as first element', () => { @@ -92,4 +129,8 @@ describe('games array', () => { it('should have ksp2 as second element', () => { expect(games[1]).toBe(ksp2); }); + + it('should have ksa as third element', () => { + expect(games[2]).toBe(ksa); + }); }); diff --git a/src/lib/game-config.ts b/src/lib/game-config.ts index 74f4888..6e175b8 100644 --- a/src/lib/game-config.ts +++ b/src/lib/game-config.ts @@ -32,4 +32,20 @@ export const ksp2: GameConfig = { `https://github.com/KSP-CKAN/KSP2-CKAN-meta/tree/main/${ident}`, }; -export const games: GameConfig[] = [ksp, ksp2]; +export const ksa: GameConfig = { + id: 'ksa', + name: 'KSA', + status: '/status/netkan-ksa.json', + netkan: (ident: string, frozen?: boolean) => + frozen + ? `https://github.com/KSAModding/KSA-NetKAN/tree/main/NetKAN/${ident}.frozen` + : `https://github.com/KSAModding/KSA-NetKAN/tree/main/NetKAN/${ident}.netkan`, + history: (ident: string, frozen?: boolean) => + frozen + ? `https://github.com/KSAModding/KSA-NetKAN/commits/main/NetKAN/${ident}.frozen` + : `https://github.com/KSAModding/KSA-NetKAN/commits/main/NetKAN/${ident}.netkan`, + metadata: (ident: string) => + `https://github.com/KSAModding/KSA-CKAN-meta/tree/main/${ident}`, +}; + +export const games: GameConfig[] = [ksp, ksp2, ksa];