"feat: 新增远程应用测试脚本,升级 pm2 依赖"
This commit is contained in:
@@ -77,6 +77,14 @@ export type AssistantConfigData = {
|
||||
path?: string;
|
||||
port?: number;
|
||||
};
|
||||
share?: {
|
||||
url: string;
|
||||
enabled?: boolean; // 是否启用远程应用
|
||||
name: string;
|
||||
};
|
||||
watch?: {
|
||||
enabled?: boolean;
|
||||
};
|
||||
/**
|
||||
* 首页
|
||||
*/
|
||||
|
||||
138
assistant/src/module/assistant/remote-app/remote-app.ts
Normal file
138
assistant/src/module/assistant/remote-app/remote-app.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import type { AssistantConfig } from '@/module/assistant/index.ts';
|
||||
import { WebSocket } from 'ws';
|
||||
import type { App } from '@kevisual/router';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { logger } from '@/module/logger.ts';
|
||||
type RemoteAppOptions = {
|
||||
app?: App;
|
||||
assistantConfig?: AssistantConfig;
|
||||
emitter?: EventEmitter;
|
||||
};
|
||||
export class RemoteApp {
|
||||
mainApp: App;
|
||||
assistantConfig: AssistantConfig;
|
||||
url: string;
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
emitter: EventEmitter;
|
||||
isConnected: boolean;
|
||||
ws: WebSocket;
|
||||
constructor(opts?: RemoteAppOptions) {
|
||||
this.mainApp = opts?.app;
|
||||
this.assistantConfig = opts?.assistantConfig;
|
||||
const share = this.assistantConfig?.getConfig()?.share;
|
||||
this.emitter = opts?.emitter || new EventEmitter();
|
||||
if (share) {
|
||||
const { url, name, enabled } = share;
|
||||
this.url = url;
|
||||
this.name = name;
|
||||
this.enabled = enabled ?? false;
|
||||
if (this.enabled) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
}
|
||||
async isConnect(): Promise<boolean> {
|
||||
const that = this;
|
||||
if (this.isConnected) {
|
||||
return true;
|
||||
}
|
||||
if (!this.enabled) {
|
||||
return false;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
const timeout = setTimeout(() => {
|
||||
resolve(false);
|
||||
that.emitter.off('open', listenOnce);
|
||||
}, 5000);
|
||||
const listenOnce = () => {
|
||||
clearTimeout(timeout);
|
||||
that.isConnected = true;
|
||||
resolve(true);
|
||||
};
|
||||
that.emitter.once('open', listenOnce);
|
||||
});
|
||||
}
|
||||
getWsURL(url: string) {
|
||||
const { protocol } = new URL(url);
|
||||
const wsProtocol = protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsURL = url.toString().replace(protocol, wsProtocol);
|
||||
return wsURL;
|
||||
}
|
||||
async init() {
|
||||
if (!this.url) {
|
||||
throw new Error('No url provided for remote app');
|
||||
}
|
||||
if (!this.name) {
|
||||
throw new Error('No name provided for remote app');
|
||||
}
|
||||
console.log('Connecting to remote app:', this.name, this.url, this.getWsURL(this.url));
|
||||
const ws = new WebSocket(this.getWsURL(this.url), {
|
||||
rejectUnauthorized: true,
|
||||
});
|
||||
const that = this;
|
||||
ws.on('open', that.onOpen.bind(that));
|
||||
ws.on('close', that.onClose.bind(that));
|
||||
ws.on('message', that.onMessage.bind(that));
|
||||
ws.on('error', that.onError.bind(that));
|
||||
this.ws = ws;
|
||||
}
|
||||
onOpen() {
|
||||
this.emitter.emit('open', this.name);
|
||||
}
|
||||
onClose() {
|
||||
this.emitter.emit('close', this.name);
|
||||
}
|
||||
onMessage(data: any) {
|
||||
this.emitter.emit('message', data);
|
||||
}
|
||||
onError(error: any) {
|
||||
console.error('Error in remote app:', this.name, error);
|
||||
this.emitter.emit('error', error);
|
||||
}
|
||||
on(event: 'open' | 'close' | 'message' | 'error', listener: (data: any) => void) {
|
||||
this.emitter.on(event, listener);
|
||||
return () => {
|
||||
this.emitter.off(event, listener);
|
||||
};
|
||||
}
|
||||
|
||||
sendData(data: any) {}
|
||||
json(data: any) {
|
||||
this.ws.send(JSON.stringify(data));
|
||||
}
|
||||
listenProxy() {
|
||||
const remoteApp = this;
|
||||
const app = this.mainApp;
|
||||
const listenFn = async (event: any) => {
|
||||
const data = event.toString();
|
||||
logger.debug('Received message:', data);
|
||||
const body = JSON.parse(data);
|
||||
const message = body.data || {};
|
||||
if (body?.type !== 'proxy') return;
|
||||
if (!body.id) {
|
||||
remoteApp.json({
|
||||
id: body.id,
|
||||
data: {
|
||||
code: 400,
|
||||
message: 'id is required',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
const res = await app.call(message);
|
||||
remoteApp.json({
|
||||
id: body.id,
|
||||
data: {
|
||||
code: res.code,
|
||||
data: res.body,
|
||||
message: res.message,
|
||||
},
|
||||
});
|
||||
};
|
||||
remoteApp.emitter.on('message', listenFn);
|
||||
return () => {
|
||||
remoteApp.emitter.off('message', listenFn);
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user