241 lines
7.5 KiB
TypeScript
241 lines
7.5 KiB
TypeScript
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,
|
|
handleCancelFetchItem,
|
|
} from "./fetch";
|
|
|
|
// Playlist routes
|
|
import {
|
|
handleListPlaylists,
|
|
handleCreatePlaylist,
|
|
handleGetPlaylist,
|
|
handleUpdatePlaylist,
|
|
handleDeletePlaylist,
|
|
handleModifyPlaylistTracks,
|
|
handleSharePlaylist,
|
|
handleUnsharePlaylist,
|
|
handleGetSharedPlaylist,
|
|
handleCopySharedPlaylist,
|
|
} from "./playlists";
|
|
|
|
// Static file serving
|
|
import { handleStatic } from "./static";
|
|
|
|
export function createRouter() {
|
|
return async function fetch(req: Request, server: any): Promise<Response | undefined> {
|
|
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);
|
|
}
|
|
const fetchCancelMatch = path.match(/^\/api\/fetch\/([^/]+)$/);
|
|
if (fetchCancelMatch && req.method === "DELETE") {
|
|
return handleCancelFetchItem(req, server, fetchCancelMatch[1]);
|
|
}
|
|
|
|
// Playlist routes
|
|
if (path === "/api/playlists" && req.method === "GET") {
|
|
return handleListPlaylists(req, server);
|
|
}
|
|
if (path === "/api/playlists" && req.method === "POST") {
|
|
return handleCreatePlaylist(req, server);
|
|
}
|
|
|
|
const sharedPlaylistMatch = path.match(/^\/api\/playlists\/shared\/([^/]+)$/);
|
|
if (sharedPlaylistMatch && req.method === "GET") {
|
|
return handleGetSharedPlaylist(req, server, sharedPlaylistMatch[1]);
|
|
}
|
|
if (sharedPlaylistMatch && req.method === "POST") {
|
|
return handleCopySharedPlaylist(req, server, sharedPlaylistMatch[1]);
|
|
}
|
|
|
|
const playlistMatch = path.match(/^\/api\/playlists\/([^/]+)$/);
|
|
if (playlistMatch && req.method === "GET") {
|
|
return handleGetPlaylist(req, server, playlistMatch[1]);
|
|
}
|
|
if (playlistMatch && req.method === "PATCH") {
|
|
return handleUpdatePlaylist(req, server, playlistMatch[1]);
|
|
}
|
|
if (playlistMatch && req.method === "DELETE") {
|
|
return handleDeletePlaylist(req, server, playlistMatch[1]);
|
|
}
|
|
|
|
const playlistTracksMatch = path.match(/^\/api\/playlists\/([^/]+)\/tracks$/);
|
|
if (playlistTracksMatch && req.method === "PATCH") {
|
|
return handleModifyPlaylistTracks(req, server, playlistTracksMatch[1]);
|
|
}
|
|
|
|
const playlistShareMatch = path.match(/^\/api\/playlists\/([^/]+)\/share$/);
|
|
if (playlistShareMatch && req.method === "POST") {
|
|
return handleSharePlaylist(req, server, playlistShareMatch[1]);
|
|
}
|
|
if (playlistShareMatch && req.method === "DELETE") {
|
|
return handleUnsharePlaylist(req, server, playlistShareMatch[1]);
|
|
}
|
|
|
|
// 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 });
|
|
};
|
|
}
|