style: format satellite and launch provider modules

This commit is contained in:
Sameh Khamis committed 2026-09-11 21:22:41 -07:00
1 parent 9a4b98700e
commit 290afd808d
6 files changed
+163 -69

No files matched your search

+5 -1
View File
@@ -1,3 +1,7 @@
/** Satellite and launch-feed middleware for Node development servers. */
export { celestrakProxy } from './space/celestrak.js';
export { rocketLaunchesProxy, LL2_CACHE_TTL_MS, launchLibraryRequestHeaders } from './space/launch-library.js';
export {
rocketLaunchesProxy,
LL2_CACHE_TTL_MS,
launchLibraryRequestHeaders,
} from './space/launch-library.js';
+32 -16
View File
@@ -31,8 +31,11 @@ export function celestrakProxy() {
async function readDisk(group) {
try {
const parsed = JSON.parse(await fsp.readFile(diskPath(group), 'utf8'));
if (typeof parsed?.body === 'string' && Number.isFinite(parsed?.at)) return parsed;
} catch { /* no disk cache yet */ }
if (typeof parsed?.body === 'string' && Number.isFinite(parsed?.at))
return parsed;
} catch {
/* no disk cache yet */
}
return null;
}
@@ -51,7 +54,10 @@ export function celestrakProxy() {
signal: AbortSignal.timeout(20000),
// CelesTrak 403s bulk groups (e.g. `active`) unless the request carries a
// descriptive User-Agent with a contact point.
headers: { 'User-Agent': 'gods-eye-view-celestrak-proxy/1.0 (+https://github.com/bilawalsidhu/gods-eye-view)' },
headers: {
'User-Agent':
'gods-eye-view-celestrak-proxy/1.0 (+https://github.com/bilawalsidhu/gods-eye-view)',
},
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.text();
@@ -64,7 +70,9 @@ export function celestrakProxy() {
name: 'celestrak-proxy',
configureServer(server) {
server.middlewares.use('/api/celestrak', async (req, res) => {
const group = String(req.url || '').replace(/^\//, '').split('?')[0];
const group = String(req.url || '')
.replace(/^\//, '')
.split('?')[0];
if (!/^[a-z0-9-]+$/i.test(group)) {
res.writeHead(400, { 'Content-Type': 'text/plain' });
res.end('invalid group');
@@ -75,7 +83,10 @@ export function celestrakProxy() {
// went out routing into the catch's send): writeHead after headersSent
// throws "Cannot set headers after they are sent".
if (res.headersSent) return;
res.writeHead(status, { 'Content-Type': 'text/plain', 'x-tle-cache': cacheStatus });
res.writeHead(status, {
'Content-Type': 'text/plain',
'x-tle-cache': cacheStatus,
});
res.end(body);
};
try {
@@ -91,17 +102,22 @@ export function celestrakProxy() {
}
// Stale or missing → refresh, single-flight per group.
if (!inflight.has(group)) {
inflight.set(group, fetchUpstream(group)
.then(async (fresh) => {
mem.set(group, fresh);
await writeDisk(group, fresh);
return fresh;
})
.catch((err) => {
console.warn('[celestrak-proxy] refresh failed — serving cache if any');
return null;
})
.finally(() => inflight.delete(group)));
inflight.set(
group,
fetchUpstream(group)
.then(async (fresh) => {
mem.set(group, fresh);
await writeDisk(group, fresh);
return fresh;
})
.catch((err) => {
console.warn(
'[celestrak-proxy] refresh failed — serving cache if any',
);
return null;
})
.finally(() => inflight.delete(group)),
);
}
const fresh = await inflight.get(group);
if (fresh) {
+28 -8
View File
@@ -1,6 +1,9 @@
import path from 'node:path';
import { promises as fsp } from 'node:fs';
import { readResponseTextCapped, coalesceProxyRequest } from '../common/http.js';
import {
readResponseTextCapped,
coalesceProxyRequest,
} from '../common/http.js';
import { launchLibraryRecentUrl } from '../../../src/data/spaceProviderRequests.js';
export const LL2_CACHE_TTL_MS = 15 * 60_000;
@@ -19,7 +22,11 @@ export function rocketLaunchesProxy() {
const ttlMs = LL2_CACHE_TTL_MS;
const maxResponseBytes = 12 * 1024 * 1024;
const maxDiskCacheBytes = 24 * 1024 * 1024;
const cachePath = path.join(process.cwd(), '.gev-cache', 'launch-library-2-v2.3.json');
const cachePath = path.join(
process.cwd(),
'.gev-cache',
'launch-library-2-v2.3.json',
);
let cache = null;
let diskLoaded = false;
const inFlight = new Map();
@@ -29,13 +36,16 @@ export function rocketLaunchesProxy() {
diskLoaded = true;
try {
const stat = await fsp.stat(cachePath);
if (stat.size > maxDiskCacheBytes) throw new Error('cache file too large');
if (stat.size > maxDiskCacheBytes)
throw new Error('cache file too large');
const parsed = JSON.parse(await fsp.readFile(cachePath, 'utf8'));
if (Number.isFinite(parsed?.at) && typeof parsed?.body === 'string') {
const body = JSON.parse(parsed.body);
if (Array.isArray(body?.results)) cache = parsed;
}
} catch { /* first run or invalid cache */ }
} catch {
/* first run or invalid cache */
}
}
async function saveDiskCache(entry) {
@@ -70,7 +80,8 @@ export function rocketLaunchesProxy() {
throw error;
}
const parsed = JSON.parse(body);
if (!Array.isArray(parsed?.results)) throw new Error('malformed upstream response');
if (!Array.isArray(parsed?.results))
throw new Error('malformed upstream response');
const fresh = { at: Date.now(), body };
cache = fresh;
void saveDiskCache(fresh);
@@ -90,14 +101,23 @@ export function rocketLaunchesProxy() {
return;
}
const stale = cache;
const request = coalesceProxyRequest(inFlight, 'recent-launches', refreshUpstream);
const request = coalesceProxyRequest(
inFlight,
'recent-launches',
refreshUpstream,
);
try {
const fresh = await request.promise;
send(res, 200, fresh.body, request.shared ? 'INFLIGHT' : 'MISS');
} catch (error) {
// Log only a bounded status, never upstream bodies, URLs, or credentials.
const status = Number.isInteger(error?.upstreamStatus) ? error.upstreamStatus : 502;
if (!request.shared) console.warn(`[launch-library-proxy] refresh failed (HTTP ${status})${stale ? ' — serving stale cache' : ''}`);
const status = Number.isInteger(error?.upstreamStatus)
? error.upstreamStatus
: 502;
if (!request.shared)
console.warn(
`[launch-library-proxy] refresh failed (HTTP ${status})${stale ? ' — serving stale cache' : ''}`,
);
if (stale) {
send(res, 200, stale.body, 'STALE-ERROR');
return;