- Sprint 1 (Quick Wins): Remove dead analytics modules, fix 4 unresolved imports, replace 3 console.warn with logger, remove mock-crc import - Sprint 2 (Architecture): Create MascotChatRepository, AnalysisRepository, 3 Zod schemas (mascot-chat, analysis, voice), deduplicate error classes, move 3 SQL queries from routes to repository - Sprint 3 (Complexity): Replace 7 any types with proper interfaces, extract 6 helpers from prepareMediaMessage (CC 85 -> ~15) - Sprint 4 (Config): Remove 22 dead env vars from .env, add 30 missing vars to .env.example, standardize naming Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
90 lines
2.8 KiB
TypeScript
90 lines
2.8 KiB
TypeScript
import { createChildLogger } from "@bete/shared/logger";
|
|
import express, {
|
|
type Express,
|
|
type NextFunction,
|
|
type Request,
|
|
type Response,
|
|
} from "express";
|
|
import helmet from "helmet";
|
|
import { createAnalysisRouter } from "../modules/analysis/analysis.routes.js";
|
|
import { createAuthRouter } from "../modules/auth/auth.routes.js";
|
|
import { createConfigRouter } from "../modules/config/config.routes.js";
|
|
import { createHealthRouter } from "../modules/health/health.routes.js";
|
|
import { createMascotChatRouter } from "../modules/mascot-chat/mascot-chat.routes.js";
|
|
import { createMediaRouter } from "../modules/media/media.routes.js";
|
|
import { createMessagesRouter } from "../modules/messages/messages.routes.js";
|
|
import { createRecordingsRouter } from "../modules/recordings/recordings.routes.js";
|
|
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";
|
|
|
|
const logger = createChildLogger("http.app");
|
|
|
|
export function createHttpApp(): Express {
|
|
const app = express();
|
|
|
|
// Security middleware
|
|
app.use(
|
|
helmet({
|
|
contentSecurityPolicy: false,
|
|
}),
|
|
);
|
|
|
|
// Body parsing
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
// Request logging
|
|
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
if (req.path.startsWith("/api/")) {
|
|
res.set("Cache-Control", "no-store");
|
|
}
|
|
res.on("finish", () => {
|
|
if (req.originalUrl.startsWith("/.well-known/")) return;
|
|
if (req.originalUrl === "/favicon.ico") return;
|
|
if (res.statusCode >= 400) {
|
|
logger.warn(
|
|
{
|
|
method: req.method,
|
|
url: req.originalUrl,
|
|
statusCode: res.statusCode,
|
|
},
|
|
"HTTP request failed",
|
|
);
|
|
}
|
|
});
|
|
next();
|
|
});
|
|
|
|
// Health check (no auth required)
|
|
app.use("/api", createHealthRouter());
|
|
|
|
// API routes
|
|
app.use("/api", createAuthRouter());
|
|
app.use("/api", createConfigRouter());
|
|
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());
|
|
|
|
// Guilds routes (must be before catch-all, after other /api routes)
|
|
app.use("/api/guilds", createGuildsRouter());
|
|
|
|
// 404 handler
|
|
app.use((_req: Request, res: Response) => {
|
|
res.status(404).json({
|
|
error: "NOT_FOUND",
|
|
message: "Endpoint not found",
|
|
});
|
|
});
|
|
|
|
// Error handler (must be last)
|
|
app.use(errorHandler);
|
|
|
|
return app;
|
|
}
|