Files
code-center/src/modules/v1-ws-proxy/proxy.ts

159 lines
5.7 KiB
TypeScript

import { IncomingMessage, ServerResponse } from 'http';
import { wsProxyManager } from './index.ts';
import { App } from '@kevisual/router';
import { logger } from '../logger.ts';
import { getLoginUser } from '@/modules/auth.ts';
import { createStudioAppListHtml } from '../html/studio-app-list/index.ts';
import { omit } from 'es-toolkit';
import { baseProxyUrl, proxyDomain } from '../domain.ts';
import { renderServerHtml } from '../html/render-server-html.ts';
type ProxyOptions = {
createNotFoundPage: (msg?: string) => any;
};
export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opts?: ProxyOptions) => {
const { url, method } = req;
const _url = new URL(url || '', `http://localhost`);
const { pathname, searchParams } = _url;
const isGet = method === 'GET';
let [user, app, userAppKey] = pathname.split('/').slice(1);
if (!user || !app) {
opts?.createNotFoundPage?.('应用未找到');
return false;
}
const data = await App.handleRequest(req, res);
const loginUser = await getLoginUser(req);
if (!loginUser) {
opts?.createNotFoundPage?.('没有登录');
return false;
}
const isAdmin = loginUser.tokenUser?.username === user
if (!userAppKey) {
if (isAdmin) {
return handleRequest(req, res, { user, app, userAppKey, isAdmin });
} else {
opts?.createNotFoundPage?.('应用访问失败');
return false;
}
}
if (!userAppKey.includes('--')) {
userAppKey = user + '--' + userAppKey;
}
// TODO: 如果不是管理员,是否需要添加其他人可以访问的逻辑?
if (!isAdmin) {
opts?.createNotFoundPage?.('没有访问应用权限');
return false;
}
if (!userAppKey.startsWith(user + '--')) {
userAppKey = user + '--' + userAppKey;
}
logger.debug('data', data);
const client = wsProxyManager.get(userAppKey);
const { ids, infoList } = wsProxyManager.getIdsInfo(user + '--');
if (!client) {
if (isGet) {
if (isAdmin) {
// const html = createStudioAppListHtml({ user, appIds: ids, userAppKey, infoList });
// res.writeHead(200, { 'Content-Type': 'text/html' });
// res.end(html);
// https://kevisual.cn/root/v1-manager/
const html = await fetch(`${baseProxyUrl}/root/v1-manager/index.html`).then(res => res.text());
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(renderServerHtml({ user, appIds: ids, userAppKey, infoList }, html));
return true;
} else {
opts?.createNotFoundPage?.('应用访问失败');
}
} else {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: '应用访问失败' }));
}
return false;
}
const path = searchParams.get('path');
if (!path) {
// 显示前端页面
const html = await fetch(`${baseProxyUrl}/root/router-studio/index.html`).then(res => res.text());
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(html);
return true;
}
let message: any = data;
if (!isAdmin) {
message = omit(data, ['token', 'cookies']);
}
if (client.status === 'waiting') {
res.writeHead(603, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: '应用没有鉴权' }));
return true;
}
const value = await client.sendData(message, {
state: { tokenUser: omit(loginUser.tokenUser, ['oauthExpand']) },
});
if (value) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(value));
return true;
}
opts?.createNotFoundPage?.('应用未启动');
return true;
};
const handleRequest = async (req: IncomingMessage, res: ServerResponse, opts?: { user?: string, app?: string, userAppKey?: string, isAdmin?: boolean }) => {
const { user, userAppKey } = opts || {};
const isGet = req.method === 'GET';
// 获取所有的管理员的应用列表
const { ids, infoList } = wsProxyManager.getIdsInfo(user + '--');
if (isGet) {
const html = await fetch(`${baseProxyUrl}/root/v1-manager/index.html`).then(res => res.text());
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(html);
return;
} else {
const url = new URL(req.url || '', 'http://localhost');
const path = url.searchParams.get('path');
if (path) {
const appId = url.searchParams.get('appId') || '';
const client = wsProxyManager.get(appId!)!;
if (!client) {
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ code: 404, message: '应用未找到' }));
return;
}
if (path === 'connected') {
client.sendConnected();
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ code: 200, message: '应用已连接' }));
return;
}
if (path === 'rename') {
const newId = url.searchParams.get('newId') || '';
if (!newId) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ code: 400, message: 'newId 参数缺失' }));
return;
}
const realNewId = user + '--' + newId;
const wsMessage = wsProxyManager.get(realNewId!)!;
if (wsMessage) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ code: 400, message: 'newId 已存在' }));
return;
}
wsProxyManager.changeId(appId, realNewId);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ code: 200, message: '应用重命名成功' }));
return;
}
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ code: 200, data: { ids, infoList } }));
return;
}
}