Stand up the HTTP surface Prospector calls (this app had no listening port). Bun
service: bearer-auth (MRNUMBER_SERVICE_TOKEN, constant-time) POST /api/screening/requests
{phone, ref} → 202 {accepted, id}, enqueued in a durable SQLite queue; a single serial
worker (one Android box) drains by invoking mr_lookup.py and records the people-service
signal. GET /api/screening/requests/:id returns the row; GET /health is open. Crash-safe
(requeues stale in-flight rows on restart). 7 bun tests + typecheck; smoke-tested end to
end (auth 401, enqueue 202, drain→verdict, invalid-phone 400).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
/**
|
|
* Trigger-service config. All env-resolved; the service token falls back to the
|
|
* 0600 plum secret file. No secrets are baked in.
|
|
*/
|
|
import { readFileSync } from 'node:fs';
|
|
import { homedir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
|
|
function fileOrEnv(envVar: string, file: string): string {
|
|
if (process.env[envVar]) return process.env[envVar] as string;
|
|
try {
|
|
return readFileSync(file, 'utf8').trim();
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
export interface ServiceConfig {
|
|
readonly port: number;
|
|
/** Bearer token Prospector must present to POST /api/screening/requests. */
|
|
readonly serviceToken: string;
|
|
/** Absolute path to client/mr_lookup.py. */
|
|
readonly lookupScript: string;
|
|
readonly python: string;
|
|
/** SQLite file backing the durable queue. */
|
|
readonly dbPath: string;
|
|
/** Worker poll interval (ms) when the queue is empty. */
|
|
readonly pollIntervalMs: number;
|
|
/** Per-lookup timeout (ms) — a lookup sleeps ~9s for paid content + vision. */
|
|
readonly lookupTimeoutMs: number;
|
|
}
|
|
|
|
export function loadConfig(): ServiceConfig {
|
|
const root = join(import.meta.dir, '..');
|
|
return {
|
|
port: Number(process.env['MRNUMBER_PORT'] ?? 8787),
|
|
serviceToken: fileOrEnv(
|
|
'MRNUMBER_SERVICE_TOKEN',
|
|
join(homedir(), '.config/cocotte-secrets/mr-number.service-token'),
|
|
),
|
|
lookupScript: process.env['MR_NUMBER_LOOKUP_SCRIPT'] ?? join(root, 'client', 'mr_lookup.py'),
|
|
python: process.env['MR_NUMBER_PYTHON'] ?? '/opt/homebrew/bin/python3',
|
|
dbPath: process.env['MRNUMBER_QUEUE_DB'] ?? join(root, 'service', 'queue.sqlite'),
|
|
pollIntervalMs: Number(process.env['MRNUMBER_POLL_MS'] ?? 2000),
|
|
lookupTimeoutMs: Number(process.env['MRNUMBER_LOOKUP_TIMEOUT_MS'] ?? 180_000),
|
|
};
|
|
}
|