diff --git a/services/backend/src/http/app.ts b/services/backend/src/http/app.ts index 2fef612..07d1f3c 100644 --- a/services/backend/src/http/app.ts +++ b/services/backend/src/http/app.ts @@ -18,7 +18,14 @@ import { createRecordingsRouter } from "../modules/recordings/recordings.routes. import { createUiStateRouter } from "../modules/ui-state/ui-state.routes.js"; import { createGuildsRouter } from "../modules/voice/guilds.routes.js"; import { createVoiceRouter } from "../modules/voice/voice.routes.js"; -import { errorHandler } from "../shared/middlewares/index.js"; +import { + adminAuth, + errorHandler, +} from "../shared/middlewares/index.js"; +import { config } from "../shared/config/index.js"; + +const ADMIN_PASSWORD = config.ADMIN_PASSWORD || "admin"; +const requireAuth = adminAuth(ADMIN_PASSWORD); const logger = createChildLogger("http.app"); @@ -61,20 +68,24 @@ export function createHttpApp(): Express { // Health check (no auth required) app.use("/api", createHealthRouter()); - // API routes + // Auth (no auth required) app.use("/api", createAuthRouter()); + + // Public read-only endpoints app.use("/api", createConfigRouter()); app.use("/api", createDashboardRouter()); - app.use("/api", createMessagesRouter()); - app.use("/api", createAnalysisRouter()); - app.use("/api", createMascotChatRouter()); - app.use("/api", createMediaRouter()); - app.use("/api", createVoiceRouter()); - app.use("/api", createRecordingsRouter()); - app.use("/api", createUiStateRouter()); + + // Protected routes — require auth (routers define full /api/… paths internally) + app.use("/api", requireAuth, createMessagesRouter()); + app.use("/api", requireAuth, createAnalysisRouter()); + app.use("/api", requireAuth, createMascotChatRouter()); + app.use("/api", requireAuth, createMediaRouter()); + app.use("/api", requireAuth, createVoiceRouter()); + app.use("/api", requireAuth, createRecordingsRouter()); + app.use("/api", requireAuth, createUiStateRouter()); // Guilds routes (must be before catch-all, after other /api routes) - app.use("/api/guilds", createGuildsRouter()); + app.use("/api/guilds", requireAuth, createGuildsRouter()); // 404 handler app.use((_req: Request, res: Response) => { diff --git a/services/backend/src/modules/voice/voice.controller.ts b/services/backend/src/modules/voice/voice.controller.ts index d03a9d5..af33067 100644 --- a/services/backend/src/modules/voice/voice.controller.ts +++ b/services/backend/src/modules/voice/voice.controller.ts @@ -2,12 +2,7 @@ import { createChildLogger } from "@bete/shared/logger"; import type { Request, Response } from "express"; import { asyncHandler } from "../../shared/middlewares/index.js"; import { publishCommandNoReply } from "../../shared/redis/index.js"; -import { - connectVoice, - disconnectVoice, - getVoiceChannels, - getVoiceStatus, -} from "./voice.service.js"; +import { connectVoice, disconnectVoice, getVoiceStatus } from "./voice.service.js"; const logger = createChildLogger("voice.controller"); @@ -48,15 +43,6 @@ export const handleDisconnectVoice = asyncHandler( }, ); -export const handleGetVoiceChannels = asyncHandler( - async (req: Request, res: Response) => { - const guildId = asString(req.params.guildId); - logger.debug({ guildId }, "Fetching voice channels"); - const channels = await getVoiceChannels(guildId); - res.json(channels); - }, -); - export const handleVoiceCommand = asyncHandler( async (req: Request, res: Response) => { const command = asString(req.body.command); diff --git a/services/backend/src/modules/voice/voice.routes.ts b/services/backend/src/modules/voice/voice.routes.ts index f388874..4c0abb4 100644 --- a/services/backend/src/modules/voice/voice.routes.ts +++ b/services/backend/src/modules/voice/voice.routes.ts @@ -10,14 +10,14 @@ import { export function createVoiceRouter(): Router { const router = express.Router(); - // GET /api/status - router.get("/status", handleGetVoiceStatus); + // GET /api/voice/status + router.get("/voice/status", handleGetVoiceStatus); - // POST /api/connect - router.post("/connect", handleConnectVoice); + // POST /api/voice/connect + router.post("/voice/connect", handleConnectVoice); - // POST /api/disconnect - router.post("/disconnect", handleDisconnectVoice); + // POST /api/voice/disconnect + router.post("/voice/disconnect", handleDisconnectVoice); // POST /api/voice/command — send arbitrary voice command (transmit start/stop) router.post("/voice/command", handleVoiceCommand); diff --git a/services/backend/src/ws/redis-bridge.ts b/services/backend/src/ws/redis-bridge.ts index 57c55be..37fa00f 100644 --- a/services/backend/src/ws/redis-bridge.ts +++ b/services/backend/src/ws/redis-bridge.ts @@ -16,6 +16,7 @@ import { DISCORD_THREAD_DELETED, DISCORD_THREAD_UPDATED, DISCORD_VOICE_ACTIVE_USER, + DISCORD_VOICE_ANALYZED, DISCORD_VOICE_PCM, DISCORD_VOICE_STARTED, DISCORD_VOICE_STOPPED, @@ -49,6 +50,7 @@ const SUBSCRIPTIONS: ChannelMapping[] = [ }, { channel: DISCORD_VOICE_ACTIVE_USER, eventType: "voice_active_user" }, { channel: DISCORD_VOICE_PCM, eventType: "voice_pcm_data" }, + { channel: DISCORD_VOICE_ANALYZED, eventType: "voice_analyzed" }, { channel: DISCORD_REACTION_ADDED, eventType: "reaction_added" }, { channel: DISCORD_REACTION_REMOVED, eventType: "reaction_removed" }, { channel: DISCORD_THREAD_CREATED, eventType: "thread_created" }, diff --git a/services/frontend/src/shared/api/client.ts b/services/frontend/src/shared/api/client.ts index 005b949..31842ca 100644 --- a/services/frontend/src/shared/api/client.ts +++ b/services/frontend/src/shared/api/client.ts @@ -187,21 +187,21 @@ export function getTextChannels(guildId: string): Promise { } export function getVoiceStatus(): Promise { - return request("/api/status"); + return request("/api/voice/status"); } export function connectVoice( guildId: string, channelId: string, ): Promise { - return request("/api/connect", { + return request("/api/voice/connect", { method: "POST", body: JSON.stringify({ guildId, channelId }), }); } export function disconnectVoice(): Promise { - return request("/api/disconnect", { method: "POST" }); + return request("/api/voice/disconnect", { method: "POST" }); } // ─── Media ───────────────────────────────────────────────────────────────────