import { getStatus as getYtdlpStatus } from "../ytdlp"; import { config } from "../config"; import { state } from "../state"; import { getOrCreateUser } from "./helpers"; // Auth routes import { handleSignup, handleLogin, handleLogout, handleGetMe, handleKickOthers, handleListUsers, handleGrantPermission, handleRevokePermission, } from "./auth"; // Channel routes import { handleListChannels, handleCreateChannel, handleDeleteChannel, handleGetChannel, handleJump, handleSeek, handleModifyQueue, handleSetMode, handleRenameChannel, } from "./channels"; // Track routes import { handleGetLibrary, handleUpload, handleGetTrack, } from "./tracks"; // Fetch routes (yt-dlp) import { handleFetch, handleFetchConfirm, handleGetFetchQueue, } from "./fetch"; // Static file serving import { handleStatic } from "./static"; export function createRouter() { return async function fetch(req: Request, server: any): Promise { const url = new URL(req.url); const path = url.pathname; // WebSocket upgrade for channels if (path.match(/^\/api\/channels\/([^/]+)\/ws$/)) { const id = path.split("/")[3]; if (!state.channels.has(id)) return new Response("Channel not found", { status: 404 }); const { user } = getOrCreateUser(req, server); const ok = server.upgrade(req, { data: { channelId: id, userId: user?.id ?? null, username: user?.username ?? 'Guest' } }); if (ok) return undefined; return new Response("WebSocket upgrade failed", { status: 500 }); } // API: server status (public) if (path === "/api/status") { return Response.json({ name: "MusicRoom", version: "1.0.0", allowGuests: config.allowGuests, allowSignups: true, channelCount: state.channels.size, defaultPermissions: config.defaultPermissions, ytdlp: getYtdlpStatus() }); } // Channel routes if (path === "/api/channels" && req.method === "GET") { return handleListChannels(req, server); } if (path === "/api/channels" && req.method === "POST") { return handleCreateChannel(req, server); } const channelDeleteMatch = path.match(/^\/api\/channels\/([^/]+)$/); if (channelDeleteMatch && req.method === "DELETE") { return handleDeleteChannel(req, server, channelDeleteMatch[1]); } const channelPatchMatch = path.match(/^\/api\/channels\/([^/]+)$/); if (channelPatchMatch && req.method === "PATCH") { return handleRenameChannel(req, server, channelPatchMatch[1]); } const channelGetMatch = path.match(/^\/api\/channels\/([^/]+)$/); if (channelGetMatch && req.method === "GET") { return handleGetChannel(channelGetMatch[1]); } const jumpMatch = path.match(/^\/api\/channels\/([^/]+)\/jump$/); if (jumpMatch && req.method === "POST") { return handleJump(req, server, jumpMatch[1]); } const seekMatch = path.match(/^\/api\/channels\/([^/]+)\/seek$/); if (seekMatch && req.method === "POST") { return handleSeek(req, server, seekMatch[1]); } const queueMatch = path.match(/^\/api\/channels\/([^/]+)\/queue$/); if (queueMatch && req.method === "PATCH") { return handleModifyQueue(req, server, queueMatch[1]); } const modeMatch = path.match(/^\/api\/channels\/([^/]+)\/mode$/); if (modeMatch && req.method === "POST") { return handleSetMode(req, server, modeMatch[1]); } // Library routes if (path === "/api/library") { return handleGetLibrary(req, server); } if (path === "/api/upload" && req.method === "POST") { return handleUpload(req, server); } // Fetch routes (yt-dlp) if (path === "/api/fetch" && req.method === "POST") { return handleFetch(req, server); } if (path === "/api/fetch/confirm" && req.method === "POST") { return handleFetchConfirm(req, server); } if (path === "/api/fetch" && req.method === "GET") { return handleGetFetchQueue(req, server); } // Auth routes if (path === "/api/auth/signup" && req.method === "POST") { return handleSignup(req, server); } if (path === "/api/auth/login" && req.method === "POST") { return handleLogin(req, server); } if (path === "/api/auth/logout" && req.method === "POST") { return handleLogout(req); } if (path === "/api/auth/me") { return handleGetMe(req, server); } if (path === "/api/auth/kick-others" && req.method === "POST") { return handleKickOthers(req, server); } // Admin routes if (path === "/api/admin/users" && req.method === "GET") { return handleListUsers(req, server); } const permissionsMatch = path.match(/^\/api\/admin\/users\/(\d+)\/permissions$/); if (permissionsMatch && req.method === "POST") { return handleGrantPermission(req, server, parseInt(permissionsMatch[1])); } if (permissionsMatch && req.method === "DELETE") { return handleRevokePermission(req, server, parseInt(permissionsMatch[1])); } // Track serving (must be after other /api/tracks routes) const trackMatch = path.match(/^\/api\/tracks\/(.+)$/); if (trackMatch) { return handleGetTrack(req, server, decodeURIComponent(trackMatch[1])); } // Static files const staticResponse = await handleStatic(path); if (staticResponse) return staticResponse; return new Response("Not found", { status: 404 }); }; }