diff --git a/packages/napcat-onebot/config/config.ts b/packages/napcat-onebot/config/config.ts index b43c2c49..189d61ad 100644 --- a/packages/napcat-onebot/config/config.ts +++ b/packages/napcat-onebot/config/config.ts @@ -6,7 +6,7 @@ const HttpServerConfigSchema = Type.Object({ port: Type.Number({ default: 3000 }), host: Type.String({ default: '127.0.0.1' }), enableCors: Type.Boolean({ default: true }), - enableWebsocket: Type.Boolean({ default: true }), + enableWebsocket: Type.Boolean({ default: false }), messagePostFormat: Type.String({ default: 'array' }), token: Type.String({ default: '' }), debug: Type.Boolean({ default: false }), @@ -18,7 +18,7 @@ const HttpSseServerConfigSchema = Type.Object({ port: Type.Number({ default: 3000 }), host: Type.String({ default: '127.0.0.1' }), enableCors: Type.Boolean({ default: true }), - enableWebsocket: Type.Boolean({ default: true }), + enableWebsocket: Type.Boolean({ default: false }), messagePostFormat: Type.String({ default: 'array' }), token: Type.String({ default: '' }), debug: Type.Boolean({ default: false }), diff --git a/packages/napcat-onebot/index.ts b/packages/napcat-onebot/index.ts index 4fd656d0..5a9e288e 100644 --- a/packages/napcat-onebot/index.ts +++ b/packages/napcat-onebot/index.ts @@ -246,7 +246,7 @@ export class NapCatOneBot11Adapter { await this.handleConfigChange(prev.network.websocketClients, now.network.websocketClients, OB11WebSocketClientAdapter); } - private async handleConfigChange( + private async handleConfigChange ( prevConfig: NetworkAdapterConfig[], nowConfig: NetworkAdapterConfig[], adapterClass: new ( @@ -305,6 +305,9 @@ export class NapCatOneBot11Adapter { }; msgListener.onRecvMsg = async (msg) => { + if (!this.networkManager.hasActiveAdapters()) { + return; + } for (const m of msg) { if (this.bootTime > parseInt(m.msgTime)) { this.context.logger.logDebug(`消息时间${m.msgTime}早于启动时间${this.bootTime},忽略上报`); @@ -517,15 +520,14 @@ export class NapCatOneBot11Adapter { } private async emitMsg (message: RawMessage) { - const network = await this.networkManager.getAllConfig(); this.context.logger.logDebug('收到新消息 RawMessage', message); await Promise.allSettled([ - this.handleMsg(message, network), + this.handleMsg(message), message.chatType === ChatType.KCHATTYPEGROUP ? this.handleGroupEvent(message) : this.handlePrivateMsgEvent(message), ]); } - private async handleMsg (message: RawMessage, network: Array) { + private async handleMsg (message: RawMessage) { // 过滤无效消息 if (message.msgType === NTMsgType.KMSGTYPENULL) { return; @@ -535,10 +537,36 @@ export class NapCatOneBot11Adapter { if (ob11Msg) { const isSelfMsg = this.isSelfMessage(ob11Msg); this.context.logger.logDebug('转化为 OB11Message', ob11Msg); - const msgMap = this.createMsgMap(network, ob11Msg, isSelfMsg, message); - this.handleDebugNetwork(network, msgMap, message); - this.handleNotReportSelfNetwork(network, msgMap, isSelfMsg); - this.networkManager.emitEventByNames(msgMap); + if (isSelfMsg || message.chatType !== ChatType.KCHATTYPEGROUP) { + const targetId = parseInt(message.peerUin); + ob11Msg.stringMsg.target_id = targetId; + ob11Msg.arrayMsg.target_id = targetId; + } + + const msgMap = new Map(); + + for (const adapter of this.networkManager.adapters.values()) { + if (!adapter.isActive) continue; + const config = adapter.config; + if (isSelfMsg) { + if (!('reportSelfMessage' in config) || !config.reportSelfMessage) { + continue; + } + } + const msgData = config.messagePostFormat === 'string' ? ob11Msg.stringMsg : ob11Msg.arrayMsg; + if (config.debug) { + const clone = structuredClone(msgData); + clone.raw = message; + msgMap.set(adapter.name, clone); + } else { + msgMap.set(adapter.name, msgData); + } + } + if (msgMap.size > 0) { + this.networkManager.emitEventByNames(msgMap); + } else if (this.networkManager.hasActiveAdapters()) { + this.context.logger.logDebug('没有可用的网络适配器发送消息,消息内容:', message); + } } } catch (e) { this.context.logger.logError('constructMessage error: ', e); @@ -553,48 +581,6 @@ export class NapCatOneBot11Adapter { ob11Msg.arrayMsg.user_id.toString() === this.core.selfInfo.uin; } - private createMsgMap (network: Array, ob11Msg: { - stringMsg: OB11Message; - arrayMsg: OB11Message; - }, isSelfMsg: boolean, message: RawMessage): Map { - const msgMap: Map = new Map(); - network.filter(e => e.enable).forEach(e => { - if (isSelfMsg || message.chatType !== ChatType.KCHATTYPEGROUP) { - ob11Msg.stringMsg.target_id = parseInt(message.peerUin); - ob11Msg.arrayMsg.target_id = parseInt(message.peerUin); - } - if ('messagePostFormat' in e && e.messagePostFormat === 'string') { - msgMap.set(e.name, structuredClone(ob11Msg.stringMsg)); - } else { - msgMap.set(e.name, structuredClone(ob11Msg.arrayMsg)); - } - }); - return msgMap; - } - - private handleDebugNetwork (network: Array, msgMap: Map, message: RawMessage) { - const debugNetwork = network.filter(e => e.enable && e.debug); - if (debugNetwork.length > 0) { - debugNetwork.forEach(adapter => { - const msg = msgMap.get(adapter.name); - if (msg) { - msg.raw = message; - } - }); - } else if (msgMap.size === 0) { - this.context.logger.logDebug('没有可用的网络适配器发送消息,消息内容:', message); - } - } - - private handleNotReportSelfNetwork (network: Array, msgMap: Map, isSelfMsg: boolean) { - if (isSelfMsg) { - const notReportSelfNetwork = network.filter(e => e.enable && (('reportSelfMessage' in e && !e.reportSelfMessage) || !('reportSelfMessage' in e))); - notReportSelfNetwork.forEach(adapter => { - msgMap.delete(adapter.name); - }); - } - } - private async handleGroupEvent (message: RawMessage) { try { // 群名片修改事件解析 任何都该判断 diff --git a/packages/napcat-onebot/network/adapter.ts b/packages/napcat-onebot/network/adapter.ts index 63b90e9f..a4f1c1f8 100644 --- a/packages/napcat-onebot/network/adapter.ts +++ b/packages/napcat-onebot/network/adapter.ts @@ -23,11 +23,15 @@ export abstract class IOB11NetworkAdapter { this.logger = core.context.logger; } - abstract onEvent(event: T): Promise; + abstract onEvent (event: T): Promise; abstract open (): void | Promise; abstract close (): void | Promise; abstract reload (config: unknown): OB11NetworkReloadType | Promise; + + get isActive (): boolean { + return this.isEnable; + } } diff --git a/packages/napcat-onebot/network/http-server-sse.ts b/packages/napcat-onebot/network/http-server-sse.ts index 9ae3ec8c..db5d5aa1 100644 --- a/packages/napcat-onebot/network/http-server-sse.ts +++ b/packages/napcat-onebot/network/http-server-sse.ts @@ -5,6 +5,10 @@ import { OB11HttpServerAdapter } from './http-server'; export class OB11HttpSSEServerAdapter extends OB11HttpServerAdapter { private sseClients: Response[] = []; + override get isActive (): boolean { + return this.isEnable && (this.sseClients.length > 0 || super.isActive); + } + override async handleRequest (req: Request, res: Response) { if (req.path === '/_events') { this.createSseSupport(req, res); @@ -25,7 +29,8 @@ export class OB11HttpSSEServerAdapter extends OB11HttpServerAdapter { }); } - override async onEvent(event: T) { + override async onEvent (event: T) { + super.onEvent(event); const promises: Promise[] = []; this.sseClients.forEach((res) => { promises.push(new Promise((resolve, reject) => { diff --git a/packages/napcat-onebot/network/http-server.ts b/packages/napcat-onebot/network/http-server.ts index 9a06e924..a2b19260 100644 --- a/packages/napcat-onebot/network/http-server.ts +++ b/packages/napcat-onebot/network/http-server.ts @@ -1,6 +1,6 @@ import { OB11EmitEventContent, OB11NetworkReloadType } from './index'; import express, { Express, NextFunction, Request, Response } from 'express'; -import http from 'http'; +import http, { IncomingMessage } from 'http'; import { OB11Response } from '@/napcat-onebot/action/OneBotAction'; import cors from 'cors'; import { HttpServerConfig } from '@/napcat-onebot/config/config'; @@ -8,13 +8,41 @@ import { IOB11NetworkAdapter } from '@/napcat-onebot/network/adapter'; import json5 from 'json5'; import { isFinished } from 'on-finished'; import typeis from 'type-is'; +import { WebSocket, WebSocketServer, RawData } from 'ws'; +import { URL } from 'url'; +import { ActionName } from '@/napcat-onebot/action/router'; +import { OB11HeartbeatEvent } from '@/napcat-onebot/event/meta/OB11HeartbeatEvent'; +import { OB11LifeCycleEvent, LifeCycleSubType } from '@/napcat-onebot/event/meta/OB11LifeCycleEvent'; +import { Mutex } from 'async-mutex'; export class OB11HttpServerAdapter extends IOB11NetworkAdapter { private app: Express | undefined; private server: http.Server | undefined; + private wsServer?: WebSocketServer; + private wsClients: WebSocket[] = []; + private wsClientsMutex = new Mutex(); + private heartbeatIntervalId: NodeJS.Timeout | null = null; + private wsClientWithEvent: WebSocket[] = []; - override async onEvent (_event: T) { + override get isActive (): boolean { + return this.isEnable && this.wsClientWithEvent.length > 0; + } + + override async onEvent (event: T) { // http server is passive, no need to emit event + this.wsClientsMutex.runExclusive(async () => { + const promises = this.wsClientWithEvent.map((wsClient) => { + return new Promise((resolve, reject) => { + if (wsClient.readyState === WebSocket.OPEN) { + wsClient.send(JSON.stringify(event)); + resolve(); + } else { + reject(new Error('WebSocket is not open')); + } + }); + }); + await Promise.allSettled(promises); + }); } open () { @@ -36,11 +64,24 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter this.isEnable = false; this.server?.close(); this.app = undefined; + this.stopHeartbeat(); + await this.wsClientsMutex.runExclusive(async () => { + this.wsClients.forEach((wsClient) => { + wsClient.close(); + }); + this.wsClients = []; + this.wsClientWithEvent = []; + }); + this.wsServer?.close(); } private initializeServer () { this.app = express(); this.server = http.createServer(this.app); + if (this.config.enableWebsocket) { + this.wsServer = new WebSocketServer({ server: this.server }); + this.createWSServer(this.wsServer); + } this.app.use(cors()); this.app.use(express.urlencoded({ extended: true, limit: '5000mb' })); @@ -93,6 +134,137 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter } } + createWSServer (newServer: WebSocketServer) { + newServer.on('connection', async (wsClient, wsReq) => { + if (!this.isEnable) { + wsClient.close(); + return; + } + if (!this.authorizeWS(this.config.token, wsClient, wsReq)) { + return; + } + const paramUrl = wsReq.url?.indexOf('?') !== -1 ? wsReq.url?.substring(0, wsReq.url?.indexOf('?')) : wsReq.url; + const isApiConnect = paramUrl === '/api' || paramUrl === '/api/'; + if (!isApiConnect) { + this.connectEvent(this.core, wsClient); + } + + wsClient.on('error', (err) => this.logger.log('[OneBot] [HTTP WebSocket] Client Error:', err.message)); + wsClient.on('message', (message) => { + this.handleWSMessage(wsClient, message).then().catch(e => this.logger.logError(e)); + }); + wsClient.on('ping', () => { + wsClient.pong(); + }); + wsClient.on('pong', () => { + // this.logger.logDebug('[OneBot] [HTTP WebSocket] Pong received'); + }); + wsClient.once('close', () => { + this.wsClientsMutex.runExclusive(async () => { + const NormolIndex = this.wsClients.indexOf(wsClient); + if (NormolIndex !== -1) { + this.wsClients.splice(NormolIndex, 1); + } + const EventIndex = this.wsClientWithEvent.indexOf(wsClient); + if (EventIndex !== -1) { + this.wsClientWithEvent.splice(EventIndex, 1); + } + if (this.wsClientWithEvent.length === 0) { + this.stopHeartbeat(); + } + }); + }); + await this.wsClientsMutex.runExclusive(async () => { + if (!isApiConnect) { + this.wsClientWithEvent.push(wsClient); + } + this.wsClients.push(wsClient); + if (this.wsClientWithEvent.length > 0) { + this.startHeartbeat(); + } + }); + }).on('error', (err) => this.logger.log('[OneBot] [HTTP WebSocket] Server Error:', err.message)); + } + + connectEvent (core: any, wsClient: WebSocket) { + try { + this.checkStateAndReply(new OB11LifeCycleEvent(core, LifeCycleSubType.CONNECT), wsClient).catch(e => this.logger.logError('[OneBot] [HTTP WebSocket] 发送生命周期失败', e)); + } catch (e) { + this.logger.logError('[OneBot] [HTTP WebSocket] 发送生命周期失败', e); + } + } + + private startHeartbeat () { + if (this.heartbeatIntervalId) return; + this.heartbeatIntervalId = setInterval(() => { + this.wsClientsMutex.runExclusive(async () => { + this.wsClientWithEvent.forEach((wsClient) => { + if (wsClient.readyState === WebSocket.OPEN) { + wsClient.send(JSON.stringify(new OB11HeartbeatEvent(this.core, 30000, this.core.selfInfo.online ?? true, true))); + } + }); + }); + }, 30000); + } + + private stopHeartbeat () { + if (this.heartbeatIntervalId) { + clearInterval(this.heartbeatIntervalId); + this.heartbeatIntervalId = null; + } + } + + private authorizeWS (token: string | undefined, wsClient: WebSocket, wsReq: IncomingMessage) { + if (!token || token.length === 0) return true; + const url = new URL(wsReq?.url || '', `http://${wsReq.headers.host}`); + const QueryClientToken = url.searchParams.get('access_token'); + const HeaderClientToken = wsReq.headers.authorization?.split('Bearer ').pop() || ''; + const ClientToken = typeof (QueryClientToken) === 'string' && QueryClientToken !== '' ? QueryClientToken : HeaderClientToken; + if (ClientToken === token) { + return true; + } + wsClient.send(JSON.stringify(OB11Response.res(null, 'failed', 1403, 'token验证失败'))); + wsClient.close(); + return false; + } + + private async checkStateAndReply (data: T, wsClient: WebSocket) { + return await new Promise((resolve, reject) => { + if (wsClient.readyState === WebSocket.OPEN) { + wsClient.send(JSON.stringify(data)); + resolve(); + } else { + reject(new Error('WebSocket is not open')); + } + }); + } + + private async handleWSMessage (wsClient: WebSocket, message: RawData) { + let receiveData: { action: typeof ActionName[keyof typeof ActionName], params?: any, echo?: any; } = { action: ActionName.Unknown, params: {} }; + let echo; + try { + receiveData = json5.parse(message.toString()); + echo = receiveData.echo; + } catch { + await this.checkStateAndReply(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient); + return; + } + receiveData.params = (receiveData?.params) ? receiveData.params : {}; + + const action = this.actions.get(receiveData.action as any); + if (!action) { + this.logger.logError('[OneBot] [HTTP WebSocket] 发生错误', '不支持的API ' + receiveData.action); + await this.checkStateAndReply(OB11Response.error('不支持的API ' + receiveData.action, 1404, echo), wsClient); + return; + } + const retdata = await action.websocketHandle(receiveData.params, echo ?? '', this.name, this.config, { + send: async (data: object) => { + await this.checkStateAndReply({ ...OB11Response.ok(data, echo ?? '', true) }, wsClient); + }, + }); + await this.checkStateAndReply({ ...retdata }, wsClient); + } + async httpApiRequest (req: Request, res: Response, request_sse: boolean = false) { let payload = req.body; if (req.method === 'get') { @@ -152,6 +324,7 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter async reload (newConfig: HttpServerConfig) { const wasEnabled = this.isEnable; const oldPort = this.config.port; + const oldEnableWebsocket = this.config.enableWebsocket; this.config = newConfig; if (newConfig.enable && !wasEnabled) { @@ -162,7 +335,7 @@ export class OB11HttpServerAdapter extends IOB11NetworkAdapter return OB11NetworkReloadType.NetWorkClose; } - if (oldPort !== newConfig.port) { + if (oldPort !== newConfig.port || oldEnableWebsocket !== newConfig.enableWebsocket) { this.close(); if (newConfig.enable) { this.open(); diff --git a/packages/napcat-onebot/network/index.ts b/packages/napcat-onebot/network/index.ts index 1eda2dbb..94d51d74 100644 --- a/packages/napcat-onebot/network/index.ts +++ b/packages/napcat-onebot/network/index.ts @@ -49,23 +49,23 @@ export class OB11NetworkManager { })); } - registerAdapter(adapter: IOB11NetworkAdapter) { + registerAdapter (adapter: IOB11NetworkAdapter) { this.adapters.set(adapter.name, adapter); } - async registerAdapterAndOpen(adapter: IOB11NetworkAdapter) { + async registerAdapterAndOpen (adapter: IOB11NetworkAdapter) { this.registerAdapter(adapter); await adapter.open(); } - async closeSomeAdapters(adaptersToClose: IOB11NetworkAdapter[]) { + async closeSomeAdapters (adaptersToClose: IOB11NetworkAdapter[]) { for (const adapter of adaptersToClose) { this.adapters.delete(adapter.name); await adapter.close(); } } - async closeSomeAdaterWhenOpen(adaptersToClose: IOB11NetworkAdapter[]) { + async closeSomeAdaterWhenOpen (adaptersToClose: IOB11NetworkAdapter[]) { for (const adapter of adaptersToClose) { this.adapters.delete(adapter.name); if (adapter.isEnable) { @@ -88,17 +88,21 @@ export class OB11NetworkManager { this.adapters.clear(); } - async readloadAdapter(name: string, config: T) { + async readloadAdapter (name: string, config: T) { const adapter = this.adapters.get(name); if (adapter) { await adapter.reload(config); } } - async readloadSomeAdapters(configMap: Map) { + async readloadSomeAdapters (configMap: Map) { await Promise.all(Array.from(configMap.entries()).map(([name, config]) => this.readloadAdapter(name, config))); } + hasActiveAdapters (): boolean { + return Array.from(this.adapters.values()).some(adapter => adapter.isActive); + } + async getAllConfig () { return Array.from(this.adapters.values()).map(adapter => adapter.config); } diff --git a/packages/napcat-onebot/network/websocket-client.ts b/packages/napcat-onebot/network/websocket-client.ts index 67a014aa..b40e668f 100644 --- a/packages/napcat-onebot/network/websocket-client.ts +++ b/packages/napcat-onebot/network/websocket-client.ts @@ -13,6 +13,10 @@ export class OB11WebSocketClientAdapter extends IOB11NetworkAdapter (event: T) { if (this.connection && this.connection.readyState === WebSocket.OPEN) { this.connection.send(JSON.stringify(event)); diff --git a/packages/napcat-onebot/network/websocket-server.ts b/packages/napcat-onebot/network/websocket-server.ts index 18f9cc49..d5a9affe 100644 --- a/packages/napcat-onebot/network/websocket-server.ts +++ b/packages/napcat-onebot/network/websocket-server.ts @@ -21,6 +21,10 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter 0; + } + constructor ( name: string, config: WebsocketServerConfig, core: NapCatCore, obContext: NapCatOneBot11Adapter, actions: ActionMap ) { @@ -70,6 +74,9 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter { @@ -77,6 +84,9 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter 0) { + this.startHeartbeat(); + } }); }).on('error', (err) => this.logger.log('[OneBot] [WebSocket Server] Server Error:', err.message)); } @@ -114,9 +124,6 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter 0) { - this.registerHeartBeat(); - } } async close () { @@ -128,10 +135,7 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter { this.wsClients.forEach((wsClient) => { wsClient.close(); @@ -141,7 +145,8 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter { this.wsClientsMutex.runExclusive(async () => { this.wsClientWithEvent.forEach((wsClient) => { @@ -153,6 +158,13 @@ export class OB11WebSocketServerAdapter extends IOB11NetworkAdapter 0 && this.isEnable) { - this.registerHeartBeat(); + this.stopHeartbeat(); + if (newConfig.heartInterval > 0 && this.isEnable && this.wsClientWithEvent.length > 0) { + this.startHeartbeat(); } return OB11NetworkReloadType.NetWorkReload; } diff --git a/packages/napcat-webui-frontend/src/components/network_edit/http_server.tsx b/packages/napcat-webui-frontend/src/components/network_edit/http_server.tsx index 7fb74883..3df93844 100644 --- a/packages/napcat-webui-frontend/src/components/network_edit/http_server.tsx +++ b/packages/napcat-webui-frontend/src/components/network_edit/http_server.tsx @@ -2,9 +2,9 @@ import GenericForm, { random_token } from './generic_form'; import type { Field } from './generic_form'; export interface HTTPServerFormProps { - data?: OneBotConfig['network']['httpServers'][0] - onClose: () => void - onSubmit: (data: OneBotConfig['network']['httpServers'][0]) => Promise + data?: OneBotConfig['network']['httpServers'][0]; + onClose: () => void; + onSubmit: (data: OneBotConfig['network']['httpServers'][0]) => Promise; } type HTTPServerFormType = OneBotConfig['network']['httpServers']; @@ -20,7 +20,7 @@ const HTTPServerForm: React.FC = ({ host: '127.0.0.1', port: 3000, enableCors: true, - enableWebsocket: true, + enableWebsocket: false, messagePostFormat: 'array', token: random_token(16), debug: false, diff --git a/packages/napcat-webui-frontend/src/components/network_edit/http_sse.tsx b/packages/napcat-webui-frontend/src/components/network_edit/http_sse.tsx index 06dbbf09..18f23cc3 100644 --- a/packages/napcat-webui-frontend/src/components/network_edit/http_sse.tsx +++ b/packages/napcat-webui-frontend/src/components/network_edit/http_sse.tsx @@ -2,11 +2,11 @@ import GenericForm, { random_token } from './generic_form'; import type { Field } from './generic_form'; export interface HTTPServerSSEFormProps { - data?: OneBotConfig['network']['httpSseServers'][0] - onClose: () => void + data?: OneBotConfig['network']['httpSseServers'][0]; + onClose: () => void; onSubmit: ( data: OneBotConfig['network']['httpSseServers'][0] - ) => Promise + ) => Promise; } type HTTPServerSSEFormType = OneBotConfig['network']['httpSseServers']; @@ -22,7 +22,7 @@ const HTTPServerSSEForm: React.FC = ({ host: '127.0.0.1', port: 3000, enableCors: true, - enableWebsocket: true, + enableWebsocket: false, messagePostFormat: 'array', token: random_token(16), debug: false, diff --git a/packages/napcat-webui-frontend/src/components/network_edit/modal.tsx b/packages/napcat-webui-frontend/src/components/network_edit/modal.tsx index dab5b78e..409c89f5 100644 --- a/packages/napcat-webui-frontend/src/components/network_edit/modal.tsx +++ b/packages/napcat-webui-frontend/src/components/network_edit/modal.tsx @@ -2,6 +2,7 @@ import { Modal, ModalContent, ModalHeader } from '@heroui/modal'; import toast from 'react-hot-toast'; import useConfig from '@/hooks/use-config'; +import useDialog from '@/hooks/use-dialog'; import HTTPClientForm from './http_client'; import HTTPServerForm from './http_server'; @@ -31,23 +32,57 @@ const NetworkFormModal = ( ) => { const { isOpen, onOpenChange, field, data } = props; const { createNetworkConfig, updateNetworkConfig } = useConfig(); + const dialog = useDialog(); const isCreate = !data; const onSubmit = async (data: OneBotConfig['network'][typeof field][0]) => { - try { - if (isCreate) { - await createNetworkConfig(field, data); - } else { - await updateNetworkConfig(field, data); + const saveData = async (dataToSave: OneBotConfig['network'][typeof field][0]) => { + try { + if (isCreate) { + await createNetworkConfig(field, dataToSave); + } else { + await updateNetworkConfig(field, dataToSave); + } + toast.success('保存配置成功'); + } catch (error) { + const msg = (error as Error).message; + + toast.error(`保存配置失败: ${msg}`); + + throw error; } - toast.success('保存配置成功'); - } catch (error) { - const msg = (error as Error).message; + }; - toast.error(`保存配置失败: ${msg}`); - - throw error; + if (['httpServers', 'httpSseServers', 'websocketServers'].includes(field)) { + const serverData = data as any; + if (!serverData.token) { + await new Promise((resolve, reject) => { + dialog.confirm({ + title: '安全警告', + content: ( +
+

检测到未配置Token,这可能导致安全风险。确认要继续吗?

+

(未配置Token时,Host将被强制限制为 127.0.0.1)

+
+ ), + onConfirm: async () => { + serverData.host = '127.0.0.1'; + try { + await saveData(serverData); + resolve(); + } catch (e) { + reject(e); + } + }, + onCancel: () => { + reject(new Error('Cancelled')); + }, + }); + }); + return; + } } + await saveData(data); }; const renderFormComponent = (onClose: () => void) => {