/** * Lightweight ACLED OAuth helper for seed scripts. * * Mirrors the credential exchange from server/_shared/acled-auth.ts * without the Redis/TypeScript dependencies so plain .mjs scripts * can import it directly. */ const ACLED_TOKEN_URL = 'https://acleddata.com/oauth/token'; const ACLED_CLIENT_ID = 'acled'; /** * Obtain a valid ACLED access token. * * Priority: * 1. ACLED_EMAIL + ACLED_PASSWORD: OAuth exchange * 2. ACLED_ACCESS_TOKEN: static token (legacy, expires 24h) * 3. Neither: null * * @param {object} options * @param {string} [options.userAgent] - User-Agent header value. * @returns {Promise} */ export async function getAcledToken({ userAgent } = {}) { const email = (process.env.ACLED_EMAIL || '').trim(); const password = (process.env.ACLED_PASSWORD || '').trim(); if (email && password) { console.log(' ACLED: exchanging credentials for OAuth token...'); const body = new URLSearchParams({ username: email, password, grant_type: 'password', client_id: ACLED_CLIENT_ID, }); const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }; if (userAgent) headers['User-Agent'] = userAgent; const resp = await fetch(ACLED_TOKEN_URL, { method: 'POST', headers, body, signal: AbortSignal.timeout(15_000), }); if (!resp.ok) { const text = await resp.text().catch(() => ''); console.warn(` ACLED OAuth exchange failed (${resp.status}): ${text.slice(0, 200)}`); // Fall through to static token check } else { const data = await resp.json(); if (data.access_token) { console.log(' ACLED: OAuth token obtained successfully'); return data.access_token; } console.warn(' ACLED: OAuth response missing access_token'); } } const staticToken = (process.env.ACLED_ACCESS_TOKEN || '').trim(); if (staticToken) { console.log(' ACLED: using static ACLED_ACCESS_TOKEN (expires after 24h)'); return staticToken; } return null; }