feat: enhance WebSocket proxy with user connection management and status reporting
- Updated StudioOpts type to include infoList for user connection status. - Added rendering of connection info in createStudioAppListHtml. - Modified self-restart logic to use a specific app path. - Improved WebSocket connection handling in wsProxyManager, including user registration and ID management. - Implemented connection status checks and responses in UserV1Proxy. - Introduced renderServerHtml function to inject server data into HTML responses. - Refactored page-proxy request handling for better URL management.
This commit is contained in:
@@ -7,14 +7,17 @@ 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 } = req;
|
||||
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?.('应用未找到');
|
||||
@@ -32,14 +35,9 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt
|
||||
|
||||
if (!userAppKey) {
|
||||
if (isAdmin) {
|
||||
// 获取所有的管理员的应用列表
|
||||
const ids = wsProxyManager.getIds(user + '--');
|
||||
const html = createStudioAppListHtml({ user, appIds: ids, userAppKey });
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(html);
|
||||
return;
|
||||
return handleRequest(req, res, { user, app, userAppKey, isAdmin });
|
||||
} else {
|
||||
opts?.createNotFoundPage?.('没有访问应用权限');
|
||||
opts?.createNotFoundPage?.('应用访问失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -57,14 +55,19 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt
|
||||
}
|
||||
logger.debug('data', data);
|
||||
const client = wsProxyManager.get(userAppKey);
|
||||
const ids = wsProxyManager.getIds(user + '--');
|
||||
const { ids, infoList } = wsProxyManager.getIdsInfo(user + '--');
|
||||
if (!client) {
|
||||
if (isAdmin) {
|
||||
const html = createStudioAppListHtml({ user, appIds: ids, userAppKey });
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(html);
|
||||
if (isGet) {
|
||||
if (isAdmin) {
|
||||
const html = createStudioAppListHtml({ user, appIds: ids, userAppKey, infoList });
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
res.end(html);
|
||||
} else {
|
||||
opts?.createNotFoundPage?.('应用访问失败');
|
||||
}
|
||||
} else {
|
||||
opts?.createNotFoundPage?.('应用访问失败');
|
||||
res.writeHead(404, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ error: '应用访问失败' }));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -80,6 +83,11 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt
|
||||
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']) },
|
||||
});
|
||||
@@ -91,3 +99,38 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt
|
||||
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 = createStudioAppListHtml({ user, appIds: ids, userAppKey, infoList });
|
||||
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({ message: '应用未找到' }));
|
||||
return;
|
||||
}
|
||||
if (path === 'connected') {
|
||||
|
||||
client.sendConnected();
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user