fix: harden degraded source handling
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 7s
Build / test-and-image (pull_request) Successful in 53s

This commit is contained in:
2026-07-07 21:16:24 +02:00
parent cd19c857f3
commit 62d9c680b7
10 changed files with 199 additions and 33 deletions

View File

@@ -50,29 +50,29 @@ const THRESHOLDS = {
// Get recent RadNet analytical results (JSON)
export async function getAnalyticalResults(opts = {}) {
const { rows = 50, startRow = 0 } = opts;
const { rows = 50, startRow = 0, timeout = 10000 } = opts;
return safeFetch(
`${RADNET_ANALYTICAL}/ROWS/${startRow}:${startRow + rows}/JSON`,
{ timeout: 25000 }
{ timeout, retries: 0, source: 'EPA RadNet' }
);
}
// Get results filtered by state
export async function getResultsByState(state, opts = {}) {
const { rows = 25, startRow = 0 } = opts;
const { rows = 25, startRow = 0, timeout = 8000 } = opts;
return safeFetch(
`${RADNET_ANALYTICAL}/ANA_STATE/${state}/ROWS/${startRow}:${startRow + rows}/JSON`,
{ timeout: 25000 }
{ timeout, retries: 0, source: 'EPA RadNet' }
);
}
// Get results filtered by analyte type
export async function getResultsByAnalyte(analyte, opts = {}) {
const { rows = 25, startRow = 0 } = opts;
const { rows = 25, startRow = 0, timeout = 8000 } = opts;
const encoded = encodeURIComponent(analyte);
return safeFetch(
`${RADNET_ANALYTICAL}/ANA_TYPE/${encoded}/ROWS/${startRow}:${startRow + rows}/JSON`,
{ timeout: 25000 }
{ timeout, retries: 0, source: 'EPA RadNet' }
);
}
@@ -129,7 +129,20 @@ export async function briefing() {
const signals = [];
// Fetch recent analytical results (broad pull)
const recentData = await getAnalyticalResults({ rows: 100 });
const recentData = await getAnalyticalResults({ rows: 80, timeout: 10000 });
if (recentData?.error) {
return {
source: 'EPA RadNet',
timestamp: new Date().toISOString(),
status: 'degraded',
error: recentData.error,
message: 'EPA RadNet broad analytical endpoint did not return current data within the source budget.',
readings: [],
signals: ['EPA RadNet unavailable — cannot assess official US radiation readings'],
monitoredAnalytes: KEY_ANALYTES,
thresholds: THRESHOLDS,
};
}
const recentRecords = Array.isArray(recentData) ? recentData : [];
// Compact all readings
@@ -137,15 +150,21 @@ export async function briefing() {
readings.push(...allReadings);
// Also try to pull key analytes specifically
const analyteResults = await Promise.all(
const analyteResults = await Promise.allSettled(
['GROSS BETA', 'IODINE-131', 'CESIUM-137'].map(async analyte => {
const data = await getResultsByAnalyte(analyte, { rows: 20 });
const data = await getResultsByAnalyte(analyte, { rows: 10, timeout: 6000 });
const records = Array.isArray(data) ? data : [];
return { analyte, records: records.map(compactReading) };
})
);
for (const { analyte, records } of analyteResults) {
const analyteErrors = [];
for (const item of analyteResults) {
if (item.status !== 'fulfilled') {
analyteErrors.push(item.reason?.message || 'unknown analyte fetch error');
continue;
}
const { records } = item.value;
// Add any records not already in our list
for (const r of records) {
if (!readings.some(existing =>
@@ -196,6 +215,8 @@ export async function briefing() {
return {
source: 'EPA RadNet',
timestamp: new Date().toISOString(),
status: analyteErrors.length ? 'degraded' : 'ok',
...(analyteErrors.length ? { error: 'epa_analyte_fetch_degraded', analyteErrors: analyteErrors.slice(0, 3) } : {}),
totalReadings: readings.length,
readings: readings.slice(0, 50), // cap for briefing size
stateSummary,