Skip to content

Commit 4340815

Browse files
committed
refactor: clean server architecture with routers
1 parent eb97880 commit 4340815

File tree

6 files changed

+168
-92
lines changed

6 files changed

+168
-92
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Router } from "express";
2+
import type { Logger } from "pino";
3+
import type { AppConfig } from "../../config/types";
4+
import type { LLMClientBundle } from "../../llm/types";
5+
import type { SupabaseVectorStore } from "../../supabase/client";
6+
import { handleHealthRequest } from "../routes/health";
7+
import { handleIngestRequest } from "../routes/ingest";
8+
import { handleChatCompletions } from "../routes/chatCompletions";
9+
10+
interface ApiRouterContext {
11+
config: AppConfig;
12+
llm: LLMClientBundle;
13+
store: SupabaseVectorStore;
14+
ingestionBusy: boolean;
15+
setIngestionBusy: (busy: boolean) => void;
16+
}
17+
18+
export function createApiRouter(context: ApiRouterContext, logger: Logger): Router {
19+
const router = Router();
20+
21+
router.get("/health", (req, res) => {
22+
handleHealthRequest(req, res, { ingestionBusy: context.ingestionBusy });
23+
});
24+
25+
router.post("/ingest", async (req, res) => {
26+
await handleIngestRequest(req, res, {
27+
config: context.config,
28+
llm: context.llm,
29+
store: context.store,
30+
ingestionBusy: context.ingestionBusy,
31+
setIngestionBusy: context.setIngestionBusy,
32+
}, logger);
33+
});
34+
35+
router.post("/v1/chat/completions", async (req, res) => {
36+
await handleChatCompletions(req, res, {
37+
config: context.config,
38+
llm: context.llm,
39+
store: context.store,
40+
}, logger);
41+
});
42+
43+
return router;
44+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Router } from "express";
2+
import type { Logger } from "pino";
3+
import type { AppConfig } from "../../config/types";
4+
import type { LLMClientBundle } from "../../llm/types";
5+
import type { SupabaseVectorStore } from "../../supabase/client";
6+
import { handleMcpAskRequest } from "../routes/mcpAsk";
7+
import { handleMcpMatchRequest } from "../routes/mcpMatch";
8+
9+
interface McpRouterContext {
10+
config: AppConfig;
11+
llm: LLMClientBundle;
12+
store: SupabaseVectorStore;
13+
}
14+
15+
export function createMcpRouter(context: McpRouterContext, logger: Logger): Router {
16+
const router = Router();
17+
18+
router.post("/ask", async (req, res) => {
19+
await handleMcpAskRequest(req, res, {
20+
config: context.config,
21+
store: context.store,
22+
}, logger);
23+
});
24+
25+
router.post("/match", async (req, res) => {
26+
await handleMcpMatchRequest(req, res, {
27+
config: context.config,
28+
llm: context.llm,
29+
store: context.store,
30+
}, logger);
31+
});
32+
33+
return router;
34+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Router } from "express";
2+
import type { Logger } from "pino";
3+
import type { AppConfig } from "../../config/types";
4+
import type { LLMClientBundle } from "../../llm/types";
5+
import type { SupabaseVectorStore } from "../../supabase/client";
6+
import { handleGithubWebhookRequest, type RequestWithRawBody } from "../routes/githubWebhook";
7+
8+
interface WebhookRouterContext {
9+
config: AppConfig;
10+
llm: LLMClientBundle;
11+
store: SupabaseVectorStore;
12+
ingestionBusy: boolean;
13+
setIngestionBusy: (busy: boolean) => void;
14+
}
15+
16+
export function createWebhookRouter(context: WebhookRouterContext, logger: Logger): Router {
17+
const router = Router();
18+
19+
router.post("/github", async (req: RequestWithRawBody, res) => {
20+
await handleGithubWebhookRequest(req, res, {
21+
config: context.config,
22+
llm: context.llm,
23+
store: context.store,
24+
ingestionBusy: context.ingestionBusy,
25+
setIngestionBusy: context.setIngestionBusy,
26+
githubWebhookSecret: context.config.server.githubWebhookSecret,
27+
}, logger);
28+
});
29+
30+
return router;
31+
}

mimir-rag/src/server/server.ts

Lines changed: 17 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,22 @@
11
import type { Server } from "node:http";
22
import express from "express";
3-
import type { AppConfig } from "../config/types";
43
import { loadAppConfig, resolveConfigPath } from "../config/loadConfig";
54
import { configureLogger, getLogger } from "../utils/logger";
65
import { createLLMClient } from "../llm/factory";
7-
import type { LLMClientBundle } from "../llm/types";
8-
import { createSupabaseStore, type SupabaseVectorStore } from "../supabase/client";
6+
import { createSupabaseStore } from "../supabase/client";
97
import { createApiKeyMiddleware } from "./middleware/apiKey";
10-
import { handleMcpAskRequest } from "./routes/mcpAsk";
11-
import { handleMcpMatchRequest } from "./routes/mcpMatch";
12-
import { handleHealthRequest } from "./routes/health";
13-
import { handleIngestRequest } from "./routes/ingest";
14-
import { handleGithubWebhookRequest, type RequestWithRawBody } from "./routes/githubWebhook";
15-
import { handleChatCompletions } from "./routes/chatCompletions";
8+
import { type RequestWithRawBody } from "./routes/githubWebhook";
9+
import { createApiRouter } from "./routers/api";
10+
import { createMcpRouter } from "./routers/mcp";
11+
import { createWebhookRouter } from "./routers/webhook";
12+
import { applyCors } from "./utils/cors";
13+
import { type ServerContext, createRouterContext } from "./utils/context";
1614

1715
export interface ServerOptions {
1816
configPath?: string;
1917
port?: number;
2018
}
2119

22-
interface ServerContext {
23-
config: AppConfig;
24-
llm: LLMClientBundle;
25-
store: SupabaseVectorStore;
26-
ingestionBusy: boolean;
27-
}
28-
2920
type ExpressApp = ReturnType<typeof express>;
3021

3122
export interface RunningServer {
@@ -54,17 +45,6 @@ async function createContext(configPath?: string): Promise<ServerContext> {
5445
};
5546
}
5647

57-
function applyCors(req: any, res: any, next: any): void {
58-
res.setHeader("Access-Control-Allow-Origin", "*");
59-
res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
60-
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
61-
if (req.method === "OPTIONS") {
62-
res.sendStatus(204);
63-
return;
64-
}
65-
next();
66-
}
67-
6848
export async function createServer(options: ServerOptions = {}): Promise<{ app: ExpressApp; context: ServerContext }> {
6949
const context = await createContext(options.configPath);
7050
const logger = getLogger();
@@ -79,82 +59,27 @@ export async function createServer(options: ServerOptions = {}): Promise<{ app:
7959
);
8060
app.use(applyCors);
8161

82-
// Apply API key middleware to all routes except /mcp/* endpoints
62+
// Apply API key middleware to all routes except /mcp/* and /webhook/* endpoints
8363
const apiKeyMiddleware = createApiKeyMiddleware(context.config.server.apiKey);
8464
app.use((req: any, res: any, next: any) => {
85-
if (req.path.startsWith('/mcp/')) {
65+
if (req.path.startsWith('/mcp/') || req.path.startsWith('/webhook/')) {
8666
next();
8767
} else {
8868
apiKeyMiddleware(req, res, next);
8969
}
9070
});
9171

92-
app.get("/health", (req: any, res: any) => {
93-
handleHealthRequest(req, res, { ingestionBusy: context.ingestionBusy });
94-
});
72+
// Prepare router context
73+
const routerContext = createRouterContext(context);
9574

96-
app.post("/ingest", async (req: any, res: any) => {
97-
await handleIngestRequest(req, res, {
98-
config: context.config,
99-
llm: context.llm,
100-
store: context.store,
101-
ingestionBusy: context.ingestionBusy,
102-
setIngestionBusy: (busy: boolean) => {
103-
context.ingestionBusy = busy;
104-
},
105-
}, logger);
106-
});
75+
// API routes (require API key)
76+
app.use(createApiRouter(routerContext, logger));
10777

108-
app.post("/v1/chat/completions", async (req: any, res: any) => {
109-
await handleChatCompletions(
110-
req,
111-
res,
112-
{
113-
config: context.config,
114-
llm: context.llm,
115-
store: context.store,
116-
},
117-
logger
118-
);
119-
});
78+
// MCP routes (no API key required)
79+
app.use('/mcp', createMcpRouter(routerContext, logger));
12080

121-
app.post("/mcp/ask", async (req: any, res: any) => {
122-
await handleMcpAskRequest(
123-
req,
124-
res,
125-
{
126-
config: context.config,
127-
store: context.store,
128-
},
129-
logger
130-
);
131-
});
132-
133-
app.post("/mcp/match", async (req: any, res: any) => {
134-
await handleMcpMatchRequest(
135-
req,
136-
res,
137-
{
138-
config: context.config,
139-
llm: context.llm,
140-
store: context.store,
141-
},
142-
logger
143-
);
144-
});
145-
146-
app.post("/webhook/github", async (req: RequestWithRawBody, res: any) => {
147-
await handleGithubWebhookRequest(req, res, {
148-
config: context.config,
149-
llm: context.llm,
150-
store: context.store,
151-
ingestionBusy: context.ingestionBusy,
152-
setIngestionBusy: (busy: boolean) => {
153-
context.ingestionBusy = busy;
154-
},
155-
githubWebhookSecret: context.config.server.githubWebhookSecret,
156-
}, logger);
157-
});
81+
// Webhook routes (no API key required)
82+
app.use('/webhook', createWebhookRouter(routerContext, logger));
15883

15984
return { app, context };
16085
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { AppConfig } from "../../config/types";
2+
import type { LLMClientBundle } from "../../llm/types";
3+
import type { SupabaseVectorStore } from "../../supabase/client";
4+
5+
export interface ServerContext {
6+
config: AppConfig;
7+
llm: LLMClientBundle;
8+
store: SupabaseVectorStore;
9+
ingestionBusy: boolean;
10+
}
11+
12+
export interface RouterContext {
13+
config: AppConfig;
14+
llm: LLMClientBundle;
15+
store: SupabaseVectorStore;
16+
ingestionBusy: boolean;
17+
setIngestionBusy: (busy: boolean) => void;
18+
}
19+
20+
export function createRouterContext(context: ServerContext): RouterContext {
21+
return {
22+
config: context.config,
23+
llm: context.llm,
24+
store: context.store,
25+
ingestionBusy: context.ingestionBusy,
26+
setIngestionBusy: (busy: boolean) => {
27+
context.ingestionBusy = busy;
28+
},
29+
};
30+
}

mimir-rag/src/server/utils/cors.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { Request, Response, NextFunction } from "express";
2+
3+
export function applyCors(req: Request, res: Response, next: NextFunction): void {
4+
res.setHeader("Access-Control-Allow-Origin", "*");
5+
res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
6+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
7+
if (req.method === "OPTIONS") {
8+
res.sendStatus(204);
9+
return;
10+
}
11+
next();
12+
}

0 commit comments

Comments
 (0)