feat: add ai proxy
This commit is contained in:
parent
28a5d82e52
commit
4d9df0fee3
@ -1,6 +1,7 @@
|
|||||||
import { User } from '@/module/models.ts';
|
import { User } from '@/module/models.ts';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import cookie from 'cookie';
|
import cookie from 'cookie';
|
||||||
|
import { logger } from '@/module/logger.ts';
|
||||||
export const error = (msg: string, code = 500) => {
|
export const error = (msg: string, code = 500) => {
|
||||||
return JSON.stringify({ code, message: msg });
|
return JSON.stringify({ code, message: msg });
|
||||||
};
|
};
|
||||||
@ -55,6 +56,7 @@ export const getLoginUser = async (req: http.IncomingMessage) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let tokenUser;
|
let tokenUser;
|
||||||
|
logger.debug('getLoginUser', token);
|
||||||
try {
|
try {
|
||||||
tokenUser = await User.verifyToken(token);
|
tokenUser = await User.verifyToken(token);
|
||||||
return { tokenUser, token };
|
return { tokenUser, token };
|
||||||
|
@ -360,7 +360,7 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
|
|||||||
headers.set('Content-Type', contentType);
|
headers.set('Content-Type', contentType);
|
||||||
headers.set('Cache-Control', isHTML ? 'no-cache' : 'public, max-age=3600'); // 设置缓存时间为 1 小时
|
headers.set('Cache-Control', isHTML ? 'no-cache' : 'public, max-age=3600'); // 设置缓存时间为 1 小时
|
||||||
headers.set('ETag', eTag);
|
headers.set('ETag', eTag);
|
||||||
res.setHeaders(headers);
|
res?.setHeaders?.(headers);
|
||||||
if (isHTML) {
|
if (isHTML) {
|
||||||
const newHtml = await getTextFromStreamAndAddStat(fs.createReadStream(filePath));
|
const newHtml = await getTextFromStreamAndAddStat(fs.createReadStream(filePath));
|
||||||
resContent = newHtml.html;
|
resContent = newHtml.html;
|
||||||
|
@ -25,7 +25,7 @@ const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: Proxy
|
|||||||
try {
|
try {
|
||||||
if (dir) {
|
if (dir) {
|
||||||
if (!isOwner) {
|
if (!isOwner) {
|
||||||
return createNotFoundPage('no permission');
|
return createNotFoundPage('no dir permission');
|
||||||
}
|
}
|
||||||
const list = await oss.listObjects<true>(objectName, { recursive: recursive });
|
const list = await oss.listObjects<true>(objectName, { recursive: recursive });
|
||||||
res.writeHead(200, { 'content-type': 'application/json' });
|
res.writeHead(200, { 'content-type': 'application/json' });
|
||||||
@ -63,6 +63,7 @@ const getAiProxy = async (req: IncomingMessage, res: ServerResponse, opts: Proxy
|
|||||||
password: password,
|
password: password,
|
||||||
});
|
});
|
||||||
if (!checkPermission.success) {
|
if (!checkPermission.success) {
|
||||||
|
logger.info('no permission', checkPermission, loginUser, owner);
|
||||||
return createNotFoundPage('no permission');
|
return createNotFoundPage('no permission');
|
||||||
}
|
}
|
||||||
if (hash && stat.etag === hash) {
|
if (hash && stat.etag === hash) {
|
||||||
@ -202,15 +203,15 @@ export const getObjectName = async (req: IncomingMessage, opts?: { checkOwner?:
|
|||||||
if (app === 'ai') {
|
if (app === 'ai') {
|
||||||
const version = params.get('version') || '1.0.0'; // root/ai
|
const version = params.get('version') || '1.0.0'; // root/ai
|
||||||
objectName = pathname.replace(`/${user}/${app}/`, `${user}/${app}/${version}/`);
|
objectName = pathname.replace(`/${user}/${app}/`, `${user}/${app}/${version}/`);
|
||||||
owner = user;
|
|
||||||
} else {
|
} else {
|
||||||
objectName = pathname.replace(`/${user}/${app}/`, `${user}/`); // resources/root/
|
objectName = pathname.replace(`/${user}/${app}/`, `${user}/`); // resources/root/
|
||||||
owner = user;
|
|
||||||
}
|
}
|
||||||
|
owner = user;
|
||||||
let isOwner = undefined;
|
let isOwner = undefined;
|
||||||
let loginUser: Awaited<ReturnType<typeof getLoginUser>> = null;
|
let loginUser: Awaited<ReturnType<typeof getLoginUser>> = null;
|
||||||
if (checkOwner) {
|
if (checkOwner) {
|
||||||
const loginUser = await getLoginUser(req);
|
loginUser = await getLoginUser(req);
|
||||||
|
logger.debug('getObjectName', loginUser, user, app);
|
||||||
isOwner = loginUser?.tokenUser?.username === owner;
|
isOwner = loginUser?.tokenUser?.username === owner;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
@ -2,6 +2,7 @@ import { WebSocketServer } from 'ws';
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { WsProxyManager } from './manager.ts';
|
import { WsProxyManager } from './manager.ts';
|
||||||
import { getLoginUser } from '@/middleware/auth.ts';
|
import { getLoginUser } from '@/middleware/auth.ts';
|
||||||
|
import { logger } from '../logger.ts';
|
||||||
export const wsProxyManager = new WsProxyManager();
|
export const wsProxyManager = new WsProxyManager();
|
||||||
|
|
||||||
export const upgrade = async (request: any, socket: any, head: any) => {
|
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) => {
|
wss.on('connection', async (ws, req) => {
|
||||||
console.log('connected', req.url);
|
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 url = new URL(req.url, 'http://localhost');
|
||||||
const id = url?.searchParams?.get('id') || nanoid();
|
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 });
|
wsProxyManager.register(id, { user, ws });
|
||||||
ws.send(
|
ws.send(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
type: 'connected',
|
type: 'connected',
|
||||||
|
user: user,
|
||||||
id,
|
id,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -53,10 +50,10 @@ wss.on('connection', async (ws, req) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = JSON.parse(eventData);
|
const data = JSON.parse(eventData);
|
||||||
console.log('message', data);
|
logger.debug('message', data);
|
||||||
});
|
});
|
||||||
ws.on('close', () => {
|
ws.on('close', () => {
|
||||||
console.log('ws closed');
|
logger.debug('ws closed');
|
||||||
wsProxyManager.unregister(id, user);
|
wsProxyManager.unregister(id, user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,8 +2,8 @@ import { IncomingMessage, ServerResponse } from 'http';
|
|||||||
import { wsProxyManager } from './index.ts';
|
import { wsProxyManager } from './index.ts';
|
||||||
|
|
||||||
import { App } from '@kevisual/router';
|
import { App } from '@kevisual/router';
|
||||||
import { log } from 'console';
|
|
||||||
import { logger } from '../logger.ts';
|
import { logger } from '../logger.ts';
|
||||||
|
import { getLoginUser } from '@/middleware/auth.ts';
|
||||||
|
|
||||||
type ProxyOptions = {
|
type ProxyOptions = {
|
||||||
createNotFoundPage: (msg?: string) => any;
|
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 { pathname } = new URL(url || '', `http://localhost`);
|
||||||
const [user, app, userAppKey] = pathname.split('/').slice(1);
|
const [user, app, userAppKey] = pathname.split('/').slice(1);
|
||||||
if (!user || !app || !userAppKey) {
|
if (!user || !app || !userAppKey) {
|
||||||
opts?.createNotFoundPage?.('应用未启动');
|
opts?.createNotFoundPage?.('应用未找到');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const data = await App.handleRequest(req, res);
|
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);
|
logger.debug('data', data);
|
||||||
const client = wsProxyManager.get(userAppKey, user);
|
const client = wsProxyManager.get(userAppKey, user);
|
||||||
const ids = wsProxyManager.getIds();
|
const ids = wsProxyManager.getIds();
|
||||||
if (!client) {
|
if (!client) {
|
||||||
opts?.createNotFoundPage?.(`应用未启动, 未找到应用, ${userAppKey}, ${ids.join(',')}`);
|
opts?.createNotFoundPage?.(`未找到应用, ${userAppKey}, ${ids.join(',')}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const value = await client.sendData(data);
|
const value = await client.sendData(data);
|
||||||
|
@ -4,6 +4,7 @@ import { redis } from '@/module/redis/redis.ts';
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { fileStore } from '../../module/config.ts';
|
import { fileStore } from '../../module/config.ts';
|
||||||
import { getAppLoadStatus } from '@/module/redis/get-app-status.ts';
|
import { getAppLoadStatus } from '@/module/redis/get-app-status.ts';
|
||||||
|
import { getLoginUser } from '@/middleware/auth.ts';
|
||||||
|
|
||||||
export class CenterUserApp {
|
export class CenterUserApp {
|
||||||
user: string;
|
user: string;
|
||||||
@ -63,9 +64,15 @@ app
|
|||||||
})
|
})
|
||||||
.define(async (ctx) => {
|
.define(async (ctx) => {
|
||||||
const { user } = ctx.query;
|
const { user } = ctx.query;
|
||||||
if (user !== 'admin') {
|
const loginUser = await getLoginUser(ctx.req);
|
||||||
ctx.throw('Not Found');
|
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);
|
.addTo(app);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user