fix: graceful degradation for seed scripts with missing keys or downed sources (#1045)

- seed-unrest-events: relax validation (ACLED missing + GDELT 404 = crash),
  console.warn → console.log for non-fatal failures
- seed-natural-events: relax validation, console.error → console.log
- seed-climate-anomalies: relax validation, console.error → console.log
- seed-internet-outages: console.error → console.log for missing key

Railway tags console.warn/error as severity:error, making healthy runs
look like crashes in the dashboard.
This commit is contained in:
Elie Habib
2026-03-05 10:09:27 +04:00
committed by GitHub
parent ca131bb075
commit f06db59720
4 changed files with 10 additions and 10 deletions

View File

@@ -107,7 +107,7 @@ async function fetchClimateAnomalies() {
if (r.status === 'fulfilled') {
if (r.value != null) anomalies.push(r.value);
} else {
console.error(` [CLIMATE] ${r.reason?.message ?? r.reason}`);
console.log(` [CLIMATE] ${r.reason?.message ?? r.reason}`);
}
}
@@ -115,7 +115,7 @@ async function fetchClimateAnomalies() {
}
function validate(data) {
return Array.isArray(data?.anomalies) && data.anomalies.length >= 1;
return Array.isArray(data?.anomalies);
}
runSeed('climate', 'anomalies', CANONICAL_KEY, fetchClimateAnomalies, {

View File

@@ -64,7 +64,7 @@ function toEpochMs(value) {
async function fetchOutages() {
const token = process.env.CLOUDFLARE_API_TOKEN;
if (!token) {
console.error('CLOUDFLARE_API_TOKEN not set — skipping');
console.log('CLOUDFLARE_API_TOKEN not set — skipping');
process.exit(0);
}

View File

@@ -131,8 +131,8 @@ async function fetchNaturalEvents() {
const eonetEvents = eonetResult.status === 'fulfilled' ? eonetResult.value : [];
const gdacsEvents = gdacsResult.status === 'fulfilled' ? gdacsResult.value : [];
if (eonetResult.status === 'rejected') console.error('[EONET]', eonetResult.reason?.message);
if (gdacsResult.status === 'rejected') console.error('[GDACS]', gdacsResult.reason?.message);
if (eonetResult.status === 'rejected') console.log('[EONET]', eonetResult.reason?.message);
if (gdacsResult.status === 'rejected') console.log('[GDACS]', gdacsResult.reason?.message);
const seenLocations = new Set();
const merged = [];
@@ -157,7 +157,7 @@ async function fetchNaturalEvents() {
}
function validate(data) {
return Array.isArray(data?.events) && data.events.length >= 1;
return Array.isArray(data?.events);
}
runSeed('natural', 'events', CANONICAL_KEY, fetchNaturalEvents, {

View File

@@ -92,7 +92,7 @@ function sortBySeverityAndRecency(events) {
async function fetchAcledProtests() {
const token = process.env.ACLED_ACCESS_TOKEN;
if (!token) {
console.warn(' ACLED_ACCESS_TOKEN not set, skipping ACLED');
console.log(' ACLED_ACCESS_TOKEN not set, skipping ACLED');
return [];
}
@@ -226,8 +226,8 @@ async function fetchUnrestEvents() {
const acledEvents = results[0].status === 'fulfilled' ? results[0].value : [];
const gdeltEvents = results[1].status === 'fulfilled' ? results[1].value : [];
if (results[0].status === 'rejected') console.warn(` ACLED failed: ${results[0].reason?.message || results[0].reason}`);
if (results[1].status === 'rejected') console.warn(` GDELT failed: ${results[1].reason?.message || results[1].reason}`);
if (results[0].status === 'rejected') console.log(` ACLED failed: ${results[0].reason?.message || results[0].reason}`);
if (results[1].status === 'rejected') console.log(` GDELT failed: ${results[1].reason?.message || results[1].reason}`);
const merged = deduplicateEvents([...acledEvents, ...gdeltEvents]);
const sorted = sortBySeverityAndRecency(merged);
@@ -238,7 +238,7 @@ async function fetchUnrestEvents() {
}
function validate(data) {
return Array.isArray(data?.events) && data.events.length >= 1;
return Array.isArray(data?.events);
}
runSeed('unrest', 'events', CANONICAL_KEY, fetchUnrestEvents, {