feat: integrate Convex API and add N5 proxy functionality

- Added Convex client setup in a new module for handling Convex API interactions.
- Implemented N5Proxy to handle requests for the /n5/:slug route, querying Convex for application links.
- Updated app context to include Convex client and API.
- Adjusted routing to support new Convex API endpoints.
- Enhanced error handling for missing applications in the N5 proxy.
This commit is contained in:
2026-03-03 14:35:46 +08:00
parent 75ab160509
commit 120303961c
7 changed files with 403 additions and 2 deletions

View File

@@ -7,7 +7,7 @@ import { BailianProvider } from '@kevisual/ai';
import * as schema from './db/schema.ts';
import { config } from './modules/config.ts'
import { db } from './modules/db.ts'
import { convexClient, convexApi } from './modules/convex.ts'
export const router = useContextKey('router', () => new SimpleRouter());
export const runtime = useContextKey('runtime', () => {
return {
@@ -44,3 +44,6 @@ export const ai = useContextKey('ai', () => {
export { schema };
export const convex = useContextKey('convex', () => convexClient);
export { convexApi };

16
src/modules/convex.ts Normal file
View File

@@ -0,0 +1,16 @@
import { api } from '@kevisual/convex';
import { ConvexClient, AuthTokenFetcher } from "convex/browser";
const url = process.env["CONVEX_URL"]
const convexClient = new ConvexClient(url!);
const token = process.env["KEVISUAL_CONVEX_TOKEN"]
const authTokenFetcher: AuthTokenFetcher = async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => {
console.log("AuthTokenFetcher called, forceRefreshToken:", forceRefreshToken);
return token;
}
convexClient.setAuth(authTokenFetcher, (isAuthenticated) => {
console.log("Auth isAuthenticated:", isAuthenticated);
});
export { convexClient }
export const convexApi = api;

31
src/modules/n5/index.ts Normal file
View File

@@ -0,0 +1,31 @@
import { convex, convexApi } from '@/app.ts';
import { IncomingMessage, ServerResponse } from 'http';
type ProxyOptions = {
createNotFoundPage: (msg?: string) => any;
};
// /n5/:slug
export const N5Proxy = async (req: IncomingMessage, res: ServerResponse, opts?: ProxyOptions) => {
const { url } = req;
const _url = new URL(url || '', `http://localhost`);
const { pathname, searchParams } = _url;
let [user, app, userAppKey] = pathname.split('/').slice(1);
if (!app) {
opts?.createNotFoundPage?.('应用未找到');
return false;
}
const convexResult = await convex.query(convexApi.nCode.getBySlug, { slug: app })
if (!convexResult) {
opts?.createNotFoundPage?.('应用未找到');
return false;
}
const link = convexResult.data.link;
if (!link) {
opts?.createNotFoundPage?.('应用未找到');
return false;
}
res.writeHead(302, {
Location: link,
});
res.end();
};

View File

@@ -6,7 +6,7 @@ import './routes/jwks.ts'
const simpleAppsPrefixs = [
"/api/wxmsg",
"/api/convex/"
"/api/convex/",
];

View File

@@ -17,6 +17,7 @@ import { UserV3Proxy } from '@/modules/v3/index.ts';
import { hasBadUser, userIsBanned, appIsBanned, userPathIsBanned } from '@/modules/off/index.ts';
import { robotsTxt } from '@/modules/html/index.ts';
import { isBun } from '@/utils/get-engine.ts';
import { N5Proxy } from '@/modules/n5/index.ts';
const domain = config?.proxy?.domain;
const allowedOrigins = config?.proxy?.allowedOrigin || [];
@@ -255,6 +256,11 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
createNotFoundPage,
});
}
if (user === 'n5') {
return N5Proxy(req, res, {
createNotFoundPage,
});
}
if (user !== 'api' && app === 'v3') {
return UserV3Proxy(req, res, {
createNotFoundPage,