/** * strfry.ts — strfry relay HTTP client * * Provides `injectEvent(rawEventJson)` which POSTs a raw NIP-01 event to * strfry's HTTP import endpoint, making it visible to relay subscribers. * * Used by ModerationService.decide() (approved events) and the relay policy * handler (elite events that bypass the queue). * * STRFRY_URL env var: base URL of the strfry relay HTTP API. * Defaults to "http://strfry:7777" (Docker internal network). * In Replit dev the relay is not running — errors are logged and swallowed. */ import { makeLogger } from "./logger.js"; const logger = makeLogger("strfry"); const STRFRY_URL = (process.env["STRFRY_URL"] ?? "http://strfry:7777").replace(/\/$/, ""); const INJECT_TIMEOUT_MS = 5000; export interface InjectResult { ok: boolean; error?: string; } /** * Inject a raw NIP-01 event JSON string into strfry via the HTTP import API. * strfry's POST /import accepts newline-delimited JSON events. * * Returns { ok: true } on success. * Returns { ok: false, error } on failure (does NOT throw — callers are * responsible for deciding whether to retry or surface the failure). */ export async function injectEvent(rawEventJson: string): Promise { const url = `${STRFRY_URL}/import`; let response: Response; try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), INJECT_TIMEOUT_MS); response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/x-ndjson" }, body: rawEventJson + "\n", signal: controller.signal, }); clearTimeout(timeout); } catch (err) { const msg = err instanceof Error ? err.message : String(err); logger.warn("strfry inject: network error", { url, error: msg }); return { ok: false, error: msg }; } if (!response.ok) { const body = await response.text().catch(() => ""); logger.warn("strfry inject: non-200 response", { url, status: response.status, body: body.slice(0, 200), }); return { ok: false, error: `HTTP ${response.status}: ${body.slice(0, 100)}` }; } logger.info("strfry inject: event published", { eventId: (() => { try { return (JSON.parse(rawEventJson) as { id?: string }).id?.slice(0, 8) ?? "?"; } catch { return "?"; } })(), }); return { ok: true }; }