From 4d9df0fee3f09b6760b2671e28fc1ef856e74302 Mon Sep 17 00:00:00 2001 From: xion Date: Fri, 23 May 2025 11:09:54 +0800 Subject: [PATCH] feat: add ai proxy --- src/middleware/auth.ts | 2 ++ src/module/index.ts | 2 +- src/module/proxy/ai-proxy.ts | 9 +++++---- src/module/ws-proxy/index.ts | 25 +++++++++++-------------- src/module/ws-proxy/proxy.ts | 15 ++++++++++++--- src/route/app/list.ts | 11 +++++++++-- 6 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index 41038a7..5a93bda 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -1,6 +1,7 @@ import { User } from '@/module/models.ts'; import http from 'http'; import cookie from 'cookie'; +import { logger } from '@/module/logger.ts'; export const error = (msg: string, code = 500) => { return JSON.stringify({ code, message: msg }); }; @@ -55,6 +56,7 @@ export const getLoginUser = async (req: http.IncomingMessage) => { return null; } let tokenUser; + logger.debug('getLoginUser', token); try { tokenUser = await User.verifyToken(token); return { tokenUser, token }; diff --git a/src/module/index.ts b/src/module/index.ts index 0a58912..c1604e8 100644 --- a/src/module/index.ts +++ b/src/module/index.ts @@ -360,7 +360,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR headers.set('Content-Type', contentType); headers.set('Cache-Control', isHTML ? 'no-cache' : 'public, max-age=3600'); // 设置缓存时间为 1 小时 headers.set('ETag', eTag); - res.setHeaders(headers); + res?.setHeaders?.(headers); if (isHTML) { const newHtml = await getTextFromStreamAndAddStat(fs.createReadStream(filePath)); resContent = newHtml.html; diff --git a/src/module/proxy/ai-proxy.ts b/src/module/proxy/ai-proxy.ts index 409f8f2..fdcb949 100644 --- a/src/module/proxy/ai-proxy.ts +++ b/src/module/proxy/ai-proxy.ts @@ -25,7 +25,7 @@ const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: Proxy try { if (dir) { if (!isOwner) { - return createNotFoundPage('no permission'); + return createNotFoundPage('no dir permission'); } const list = await oss.listObjects(objectName, { recursive: recursive }); res.writeHead(200, { 'content-type': 'application/json' }); @@ -63,6 +63,7 @@ const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: Proxy password: password, }); if (!checkPermission.success) { + logger.info('no permission', checkPermission, loginUser, owner); return createNotFoundPage('no permission'); } if (hash && stat.etag === hash) { @@ -202,15 +203,15 @@ export const getObjectName = async (req: IncomingMessage, opts?: { checkOwner?: if (app === 'ai') { const version = params.get('version') || '1.0.0'; // root/ai objectName = pathname.replace(`/${user}/${app}/`, `${user}/${app}/${version}/`); - owner = user; } else { objectName = pathname.replace(`/${user}/${app}/`, `${user}/`); // resources/root/ - owner = user; } + owner = user; let isOwner = undefined; let loginUser: Awaited> = null; if (checkOwner) { - const loginUser = await getLoginUser(req); + loginUser = await getLoginUser(req); + logger.debug('getObjectName', loginUser, user, app); isOwner = loginUser?.tokenUser?.username === owner; } return { diff --git a/src/module/ws-proxy/index.ts b/src/module/ws-proxy/index.ts index 4008846..12c5141 100644 --- a/src/module/ws-proxy/index.ts +++ b/src/module/ws-proxy/index.ts @@ -2,6 +2,7 @@ import { WebSocketServer } from 'ws'; import { nanoid } from 'nanoid'; import { WsProxyManager } from './manager.ts'; import { getLoginUser } from '@/middleware/auth.ts'; +import { logger } from '../logger.ts'; export const wsProxyManager = new WsProxyManager(); export const upgrade = async (request: any, socket: any, head: any) => { @@ -26,24 +27,20 @@ export const wss = new WebSocketServer({ wss.on('connection', async (ws, req) => { console.log('connected', req.url); - // const user = await getLoginUser(req); - // if (!user) { - // ws.send( - // JSON.stringify({ - // type: 'error', - // message: 'Invalid authorization', - // }), - // ); - // ws.close(); - // return; - // } const url = new URL(req.url, 'http://localhost'); const id = url?.searchParams?.get('id') || nanoid(); - const user = 'root'; + const loginUser = await getLoginUser(req); + if (!loginUser) { + ws.send(JSON.stringify({ code: 401, message: 'No Login' })); + ws.close(); + return; + } + const user = loginUser.tokenUser?.username; wsProxyManager.register(id, { user, ws }); ws.send( JSON.stringify({ type: 'connected', + user: user, id, }), ); @@ -53,10 +50,10 @@ wss.on('connection', async (ws, req) => { return; } const data = JSON.parse(eventData); - console.log('message', data); + logger.debug('message', data); }); ws.on('close', () => { - console.log('ws closed'); + logger.debug('ws closed'); wsProxyManager.unregister(id, user); }); }); diff --git a/src/module/ws-proxy/proxy.ts b/src/module/ws-proxy/proxy.ts index 7e39004..06ed753 100644 --- a/src/module/ws-proxy/proxy.ts +++ b/src/module/ws-proxy/proxy.ts @@ -2,8 +2,8 @@ import { IncomingMessage, ServerResponse } from 'http'; import { wsProxyManager } from './index.ts'; import { App } from '@kevisual/router'; -import { log } from 'console'; import { logger } from '../logger.ts'; +import { getLoginUser } from '@/middleware/auth.ts'; type ProxyOptions = { createNotFoundPage: (msg?: string) => any; @@ -13,15 +13,24 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt const { pathname } = new URL(url || '', `http://localhost`); const [user, app, userAppKey] = pathname.split('/').slice(1); if (!user || !app || !userAppKey) { - opts?.createNotFoundPage?.('应用未启动'); + opts?.createNotFoundPage?.('应用未找到'); return false; } const data = await App.handleRequest(req, res); + const loginUser = await getLoginUser(req); + if (!loginUser) { + opts?.createNotFoundPage?.('没有登录'); + return false; + } + if (loginUser.tokenUser?.username !== user) { + opts?.createNotFoundPage?.('没有访问应用权限'); + return false; + } logger.debug('data', data); const client = wsProxyManager.get(userAppKey, user); const ids = wsProxyManager.getIds(); if (!client) { - opts?.createNotFoundPage?.(`应用未启动, 未找到应用, ${userAppKey}, ${ids.join(',')}`); + opts?.createNotFoundPage?.(`未找到应用, ${userAppKey}, ${ids.join(',')}`); return false; } const value = await client.sendData(data); diff --git a/src/route/app/list.ts b/src/route/app/list.ts index 351a3af..09512b9 100644 --- a/src/route/app/list.ts +++ b/src/route/app/list.ts @@ -4,6 +4,7 @@ import { redis } from '@/module/redis/redis.ts'; import fs from 'fs'; import { fileStore } from '../../module/config.ts'; import { getAppLoadStatus } from '@/module/redis/get-app-status.ts'; +import { getLoginUser } from '@/middleware/auth.ts'; export class CenterUserApp { user: string; @@ -63,9 +64,15 @@ app }) .define(async (ctx) => { const { user } = ctx.query; - if (user !== 'admin') { - ctx.throw('Not Found'); + const loginUser = await getLoginUser(ctx.req); + if (loginUser) { + const root = ['admin', 'root']; + if (root.includes(loginUser.tokenUser?.username)) { + return; + } + ctx.throw(401, 'No Proxy App Permission'); } + ctx.throw(401, 'No Login And No Proxy App Permission'); }) .addTo(app);