Skip to content

Commit a72e83d

Browse files
committed
migate function to framework
1 parent 3452d44 commit a72e83d

File tree

8 files changed

+153
-164
lines changed

8 files changed

+153
-164
lines changed

src/app.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import cors from '@fastify/cors';
44
import * as jwt from 'jsonwebtoken';
55
import { authRouter } from './routers/auth';
66
import { userRouter } from './routers/user';
7-
import { announcementRouter } from './routers/announcement';
8-
import { corsDebugRouter } from './routers/cors-debug';
9-
import { echoRouter } from './routers/echo';
7+
import { announcementRouter } from './framework/routers/announcement';
8+
import { corsDebugRouter } from './framework/routers/cors-debug';
9+
import { echoRouter } from './framework/routers/echo';
1010
import { router } from './trpc';
1111
import { join } from 'path';
1212
import { corsPluginOptions, corsMiddleware } from './middleware';
@@ -89,7 +89,7 @@ export async function buildServer(): Promise<FastifyInstance> {
8989
});
9090

9191
// Register OpenAI-compatible proxy routes (both /v1/... and /api/v1/... aliases)
92-
server.register(require('./routers/llm-proxy').llmProxyRoutes);
92+
server.register(require('./framework/routers/llm-proxy').llmProxyRoutes);
9393
server.register((instance: any, _opts: any, done: any) => {
9494
instance.post('/api/v1/chat/completions', (req: any, reply: any) => {
9595
// delegate to the main handler by internally calling the same logic

src/framework/routers/announcement.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { publicProcedure, router } from '../../trpc';
2+
import * as fs from 'fs';
3+
import * as path from 'path';
4+
5+
export const announcementRouter = router({
6+
getAnnouncement: publicProcedure.query(async () => {
7+
const announcementPath = path.join(process.cwd(), 'announcement.txt');
8+
try {
9+
const content = fs.readFileSync(announcementPath, 'utf-8');
10+
return { announcement: content };
11+
} catch (_error) {
12+
return { announcement: '暂无公告' };
13+
}
14+
}),
15+
});
16+
17+

src/framework/routers/cors-debug.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { z } from 'zod';
2+
import { publicProcedure, router } from '../../trpc';
3+
import { getCorsConfig, isOriginAllowed } from '../../config/cors';
4+
5+
export const corsDebugRouter = router({
6+
getConfig: publicProcedure.query(() => {
7+
const corsConfig = getCorsConfig();
8+
return {
9+
enabled: corsConfig.enabled,
10+
origins: corsConfig.origins,
11+
methods: corsConfig.methods,
12+
allowedHeaders: corsConfig.allowedHeaders,
13+
credentials: corsConfig.credentials,
14+
maxAge: corsConfig.maxAge,
15+
environment: process.env.NODE_ENV || 'development'
16+
};
17+
}),
18+
19+
testOrigin: publicProcedure
20+
.input(z.object({ origin: z.string() }))
21+
.query(({ input }) => {
22+
const corsConfig = getCorsConfig();
23+
const allowed = isOriginAllowed(input.origin, corsConfig);
24+
return {
25+
origin: input.origin,
26+
allowed,
27+
corsConfig: {
28+
enabled: corsConfig.enabled,
29+
origins: corsConfig.origins,
30+
environment: process.env.NODE_ENV || 'development'
31+
}
32+
};
33+
}),
34+
35+
testPreflight: publicProcedure
36+
.input(z.object({
37+
origin: z.string(),
38+
method: z.string().optional(),
39+
headers: z.array(z.string()).optional()
40+
}))
41+
.query(({ input }) => {
42+
const corsConfig = getCorsConfig();
43+
const originAllowed = isOriginAllowed(input.origin, corsConfig);
44+
return {
45+
origin: input.origin,
46+
originAllowed,
47+
method: input.method || 'GET',
48+
headers: input.headers || [],
49+
corsConfig: {
50+
enabled: corsConfig.enabled,
51+
methods: corsConfig.methods,
52+
allowedHeaders: corsConfig.allowedHeaders,
53+
credentials: corsConfig.credentials
54+
}
55+
};
56+
}),
57+
58+
health: publicProcedure.query(() => {
59+
return {
60+
status: 'ok',
61+
timestamp: new Date().toISOString(),
62+
cors: {
63+
enabled: getCorsConfig().enabled,
64+
environment: process.env.NODE_ENV || 'development'
65+
}
66+
};
67+
}),
68+
});
69+
70+

src/routers/echo.ts renamed to src/framework/routers/echo.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { z } from 'zod';
2-
import { publicProcedure, router } from '../trpc';
2+
import { publicProcedure, router } from '../../trpc';
33

44
const inputSchema = z.object({
55
message: z.string().min(1).max(1000),

src/framework/routers/llm-proxy.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { FastifyInstance, FastifyPluginCallback } from 'fastify';
2+
import { LlmClient, ChatCompletionParams } from '../../utils/llm-client';
3+
4+
export const llmProxyRoutes: FastifyPluginCallback = (server: FastifyInstance, _opts, done) => {
5+
server.post('/v1/chat/completions', async (request, reply) => {
6+
const body = request.body as ChatCompletionParams | undefined;
7+
if (!body || !Array.isArray(body.messages) || !body.model) {
8+
return reply.code(400).send({ error: 'Invalid request: model and messages are required.' });
9+
}
10+
11+
const upstream = new LlmClient();
12+
13+
if (body.stream) {
14+
reply.raw.setHeader('Content-Type', 'text/event-stream');
15+
reply.raw.setHeader('Cache-Control', 'no-cache');
16+
reply.raw.setHeader('Connection', 'keep-alive');
17+
18+
const abortController = new AbortController();
19+
const onClose = () => { abortController.abort(); };
20+
reply.raw.on('close', onClose);
21+
22+
try {
23+
const upstreamRes = await upstream.fetchChatCompletionStream(body, abortController.signal);
24+
if (!upstreamRes.ok || !upstreamRes.body) {
25+
const text = await upstreamRes.text().catch(() => '');
26+
reply.code(upstreamRes.status);
27+
reply.raw.write(`: upstream error ${text}\n\n`);
28+
} else {
29+
const reader = upstreamRes.body.getReader();
30+
const decoder = new TextDecoder();
31+
while (true) {
32+
const { value, done } = await reader.read();
33+
if (done) break;
34+
if (value) reply.raw.write(decoder.decode(value, { stream: true }));
35+
}
36+
}
37+
} catch (err: any) {
38+
try {
39+
const message = typeof err?.message === 'string' ? err.message : 'Upstream error';
40+
reply.raw.write(`: error ${message}\n\n`);
41+
} catch {}
42+
} finally {
43+
reply.raw.off('close', onClose);
44+
reply.raw.end();
45+
}
46+
return reply;
47+
}
48+
49+
try {
50+
const result = await upstream.createChatCompletion({ ...body, stream: false });
51+
return reply.code(200).send(result);
52+
} catch (err: any) {
53+
const text = typeof err?.message === 'string' ? err.message : 'Upstream error';
54+
return reply.code(500).send({ error: text });
55+
}
56+
});
57+
58+
done();
59+
};
60+
61+

src/routers/announcement.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/routers/cors-debug.ts

Lines changed: 0 additions & 78 deletions
This file was deleted.

src/routers/llm-proxy.ts

Lines changed: 0 additions & 65 deletions
This file was deleted.

0 commit comments

Comments
 (0)