feat: retain free-only startup and provider verification

This commit is contained in:
Redacted-Coder 2026-09-08 18:35:59 -04:00 committed by GitHub
parent c9ec6da894
commit ef7b760668
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 105 additions and 0 deletions

View File

@ -0,0 +1,42 @@
// Read-only source smoke check. Never prints or submits API credentials.
const checks = [
['Free configuration', '/api/free-providers'],
['Place search', '/api/free-geocode?q=Reykjavik'],
['Flights', '/api/opensky'],
['Military flights', '/api/adsblol/mil'],
['Satellites', '/api/celestrak/stations'],
['Space missions', '/api/launches'],
['Camera directory', '/api/cctv/sources'],
['Traffic configuration', '/api/tomtom/status'],
['Fire configuration', '/api/firms/status'],
['Ships', '/api/ais-live'],
['Weather', '/api/weather-effects?latitude=30.2672&longitude=-97.7431'],
];
const results = [];
for (let i = 0; i < checks.length; i += 3) {
await Promise.all(checks.slice(i, i + 3).map(async ([name, path]) => {
const start = Date.now();
try {
const response = await fetch(`http://localhost:4173${path}`, { signal: AbortSignal.timeout(30000) });
const text = await response.text();
const body = name === 'Satellites' && !text.trim().startsWith('{')
? { results: text.trim().split('\n').filter(line => line.startsWith('1 ')) }
: JSON.parse(text);
const rows = body.states || body.ac || body.results || body.sources || body.vessels;
const result = { name, http: response.status, seconds: ((Date.now() - start) / 1000).toFixed(1),
...(Array.isArray(rows) ? { count: rows.length } : {}),
...(body.status ? { status: body.status } : {}),
...(typeof body.hasKey === 'boolean' ? { hasKey: body.hasKey } : {}),
...(body.place ? { place: body.place.label } : {}),
...(path === '/api/free-providers' ? { configuration: body } : {}),
};
results.push(result);
console.log(JSON.stringify(result));
} catch (error) {
const result = { name, error: error.name === 'TimeoutError' ? 'timeout' : 'request failed' };
results.push(result);
console.log(JSON.stringify(result));
}
}));
}
if (results.some(r => r.error || (r.http >= 400 && r.name !== 'Ships'))) process.exitCode = 1;

21
scripts/start-free.mjs Normal file
View File

@ -0,0 +1,21 @@
import { spawn } from 'node:child_process';
import { existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
const root=fileURLToPath(new URL('../',import.meta.url));
const vite=fileURLToPath(new URL('../node_modules/vite/bin/vite.js',import.meta.url));
const [major,minor]=process.versions.node.split('.').map(Number);
if(!((major===24&&minor>=14)||major===26)){
console.error('Install Node 24.14+ (24.x) or Node 26 before starting Gods Eye.');
process.exit(1);
}
if(!existsSync(vite)){
console.error('Run npm ci first to install dependencies.');
process.exit(1);
}
const child=spawn(process.execPath,[vite,'--host','localhost','--port','4173','--strictPort','--open'],{
cwd:root,stdio:'inherit',
env:{...process.env,GEV_FREE_ONLY:'1',GOOGLE_MAPS_API_KEY:'',OPENAI_API_KEY:'',HOST:'localhost',PORT:'4173'}
});
child.on('error',()=>{console.error('Unable to start the local server.');process.exitCode=1;});
child.on('exit',code=>{process.exitCode=code??1;});
for(const signal of ['SIGINT','SIGTERM'])process.on(signal,()=>child.kill(signal));

View File

@ -0,0 +1,42 @@
import { lonLatToTile } from '../src/data/tomtomTiles.js';
import { decodeFlowTile } from '../src/data/flowTiles.js';
const root = 'http://localhost:4173';
const { x, y } = lonLatToTile(-97.7431, 30.2672, 12);
const checks = [
['Traffic tile', `/api/tomtom/flow/12/${x}/${y}.pbf`, 'tile'],
['Fire detections', '/api/firms', 'fires'],
['Ships', '/api/ais-live?maxRows=5000', 'ships'],
['Camera health', '/api/cctv/health', 'cameras'],
['Mapped installations', '/api/military-installations?south=30.15&west=-97.85&north=30.35&east=-97.65', 'mapped'],
];
await Promise.all(checks.map(async ([name, path, kind]) => {
const start = Date.now();
try {
const r = await fetch(root + path, { signal: AbortSignal.timeout(90000) });
const bytes = new Uint8Array(await r.arrayBuffer());
const result = { name, http: r.status, seconds: Math.round((Date.now() - start) / 1000) };
if (kind === 'tile' && r.ok) {
result.bytes = bytes.length;
result.contentType = r.headers.get('content-type');
result.cache = r.headers.get('x-tomtom-cache');
result.decodedRoadSegments = decodeFlowTile(bytes, 12, x, y).length;
} else {
const body = JSON.parse(new TextDecoder().decode(bytes));
if (kind === 'fires') Object.assign(result, { count: body.fires?.length, stale: body.stale, sourceCount: body.sources?.length });
if (kind === 'ships') Object.assign(result, { count: body.rows?.length, status: body.status, lastMessageAt: body.lastMessageAt });
if (kind === 'mapped') Object.assign(result, { count: body.elements?.length, status: body.status });
if (kind === 'cameras') {
const cameras = Array.isArray(body.cameras) ? body.cameras : Object.values(body.cameras || {});
result.observed = cameras.length;
result.statusCounts = cameras.reduce((counts, camera) => {
const status = camera.status || 'unknown'; counts[status] = (counts[status] || 0) + 1; return counts;
}, {});
}
}
console.log(JSON.stringify(result));
if (!r.ok) process.exitCode = 1;
} catch (e) {
console.log(JSON.stringify({ name, error: e.name === 'TimeoutError' ? 'timeout' : 'request failed' }));
process.exitCode = 1;
}
}));