/** * REST route handlers for the OpenFinClaw dashboard HTTP server. */ import { readFileSync } from "node:fs"; import type { IncomingMessage, ServerResponse } from "node:http"; import { join } from "node:path"; import type { DatabaseSync } from "node:sqlite"; import { fileURLToPath } from "node:url"; import { queryActivityLog, queryAgentEvents, queryBacktestResults, queryPriceAlerts, queryScanHistory, queryStrategies, } from "../db/repositories.js"; // Resolve path to web/index.html relative to this file's location const WEB_DIR = join(fileURLToPath(import.meta.url), "..", "..", "..", "web"); function getIndexHtml(): string { return readFileSync(join(WEB_DIR, "index.html"), "utf8"); } function parseQueryParam(url: string, name: string, fallback: number): number { try { const u = new URL(url, "http://localhost"); const v = u.searchParams.get(name); const n = v != null ? parseInt(v, 10) : NaN; return Number.isFinite(n) && n >= 0 ? n : fallback; } catch { return fallback; } } function parseStringParam(url: string, name: string): string | undefined { try { const u = new URL(url, "http://localhost"); return u.searchParams.get(name) ?? undefined; } catch { return undefined; } } function sendJson(res: ServerResponse, data: unknown): void { const body = JSON.stringify(data); res.writeHead(200, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body), "Access-Control-Allow-Origin": "*", }); res.end(body); } function sendHtml(res: ServerResponse, html: string): void { res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); res.end(html); } function send404(res: ServerResponse): void { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("Not found"); } /** * Dispatch an incoming HTTP request to the appropriate handler. */ export function handleRoute(db: DatabaseSync, req: IncomingMessage, res: ServerResponse): void { const url = req.url ?? "/"; const pathname = url.split("?")[0]; // Dashboard HTML if (pathname === "/" || pathname === "/index.html") { try { sendHtml(res, getIndexHtml()); } catch { res.writeHead(500); res.end("Failed to load dashboard HTML"); } return; } if (pathname === "/api/activity-log") { const limit = parseQueryParam(url, "limit", 50); const offset = parseQueryParam(url, "offset", 0); sendJson(res, queryActivityLog(db, limit, offset)); return; } if (pathname === "/api/agent-events") { const limit = parseQueryParam(url, "limit", 50); const offset = parseQueryParam(url, "offset", 0); sendJson(res, queryAgentEvents(db, limit, offset)); return; } if (pathname === "/api/strategies") { sendJson(res, queryStrategies(db)); return; } if (pathname === "/api/backtest-results") { const strategyId = parseStringParam(url, "strategy_id"); sendJson(res, queryBacktestResults(db, strategyId)); return; } if (pathname === "/api/scan-history") { const limit = parseQueryParam(url, "limit", 20); const offset = parseQueryParam(url, "offset", 0); const scanType = parseStringParam(url, "scan_type"); sendJson(res, queryScanHistory(db, { scanType, limit, offset })); return; } if (pathname === "/api/price-alerts") { const limit = parseQueryParam(url, "limit", 50); const offset = parseQueryParam(url, "offset", 0); const strategyId = parseStringParam(url, "strategy_id"); sendJson(res, queryPriceAlerts(db, { strategyId, limit, offset })); return; } send404(res); }