(req: http.IncomingMessage, res: http.ServerResponse)
| 286 | // ─── Request routing ─── |
| 287 | |
| 288 | private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void { |
| 289 | const url = new URL(req.url ?? "/", `http://${req.headers.host}`); |
| 290 | const p = url.pathname; |
| 291 | |
| 292 | res.setHeader("Access-Control-Allow-Origin", "*"); |
| 293 | res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); |
| 294 | res.setHeader("Access-Control-Allow-Headers", "Content-Type"); |
| 295 | |
| 296 | if (req.method === "OPTIONS") { res.writeHead(204); res.end(); return; } |
| 297 | |
| 298 | try { |
| 299 | if (p === "/api/auth/status") { |
| 300 | return this.jsonResponse(res, { needsSetup: this.needsSetup, loggedIn: this.isValidSession(req) }); |
| 301 | } |
| 302 | if (p === "/api/auth/setup" && req.method === "POST") { |
| 303 | return this.handleSetup(req, res); |
| 304 | } |
| 305 | if (p === "/api/auth/login" && req.method === "POST") { |
| 306 | return this.handleLogin(req, res); |
| 307 | } |
| 308 | if (p === "/api/auth/reset" && req.method === "POST") { |
| 309 | return this.handlePasswordReset(req, res); |
| 310 | } |
| 311 | if (p === "/branding-logo.svg" && this.branding?.logoSvgPath) { |
| 312 | return this.serveBrandingLogo(res); |
| 313 | } |
| 314 | if (p === "/" || p === "/viewer") { |
| 315 | return this.serveViewer(res); |
| 316 | } |
| 317 | |
| 318 | if (!this.isValidSession(req)) { |
| 319 | res.writeHead(401, { "Content-Type": "application/json" }); |
| 320 | res.end(JSON.stringify({ error: "unauthorized" })); |
| 321 | return; |
| 322 | } |
| 323 | |
| 324 | if (p === "/api/memories" && req.method === "GET") this.serveMemories(res, url); |
| 325 | else if (p === "/api/memories/share-local" && req.method === "POST") this.handleMemoryLocalShare(req, res); |
| 326 | else if (p === "/api/memories/unshare-local" && req.method === "POST") this.handleMemoryLocalUnshare(req, res); |
| 327 | else if (p.match(/^\/api\/memory\/[^/]+\/scope$/) && req.method === "PUT") this.handleMemoryScope(req, res, p); |
| 328 | else if (p.match(/^\/api\/task\/[^/]+\/scope$/) && req.method === "PUT") this.handleTaskScope(req, res, p); |
| 329 | else if (p.match(/^\/api\/skill\/[^/]+\/scope$/) && req.method === "PUT") this.handleSkillScope(req, res, p); |
| 330 | else if (p === "/api/stats") this.serveStats(res, url); |
| 331 | else if (p === "/api/metrics") this.serveMetrics(res, url); |
| 332 | else if (p === "/api/tool-metrics") this.serveToolMetrics(res, url); |
| 333 | else if (p === "/api/search") this.serveSearch(req, res, url); |
| 334 | else if (p === "/api/tasks" && req.method === "GET") this.serveTasks(res, url); |
| 335 | else if (p === "/api/task-search" && req.method === "GET") this.serveTaskSearch(res, url); |
| 336 | else if (p.match(/^\/api\/task\/[^/]+\/retry-skill$/) && req.method === "POST") this.handleTaskRetrySkill(req, res, p); |
| 337 | else if (p.startsWith("/api/task/") && req.method === "DELETE") this.handleTaskDelete(res, p); |
| 338 | else if (p.startsWith("/api/task/") && req.method === "PUT") this.handleTaskUpdate(req, res, p); |
| 339 | else if (p.startsWith("/api/task/") && req.method === "GET") this.serveTaskDetail(res, p); |
| 340 | else if (p === "/api/skills" && req.method === "GET") this.serveSkills(res, url); |
| 341 | else if (p.match(/^\/api\/skill\/[^/]+\/download$/) && req.method === "GET") this.serveSkillDownload(res, p); |
| 342 | else if (p.match(/^\/api\/skill\/[^/]+\/files$/) && req.method === "GET") this.serveSkillFiles(res, p); |
| 343 | else if (p.match(/^\/api\/skill\/[^/]+\/visibility$/) && req.method === "PUT") this.handleSkillVisibility(req, res, p); |
| 344 | else if (p.match(/^\/api\/skill\/[^/]+\/disable$/) && req.method === "PUT") this.handleSkillDisable(res, p); |
| 345 | else if (p.match(/^\/api\/skill\/[^/]+\/enable$/) && req.method === "PUT") this.handleSkillEnable(res, p); |
no test coverage detected