generated from tailored/router-template
	base module
This commit is contained in:
		
							
								
								
									
										1
									
								
								assistant-module/src/assistant-config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assistant-module/src/assistant-config.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './config/index.ts'; | ||||
							
								
								
									
										1
									
								
								assistant-module/src/assistant-process.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assistant-module/src/assistant-process.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './process/index.ts'; | ||||
							
								
								
									
										1
									
								
								assistant-module/src/assistant-proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								assistant-module/src/assistant-proxy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| export * from './proxy/index.ts'; | ||||
							
								
								
									
										111
									
								
								assistant-module/src/config/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								assistant-module/src/config/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| import path from 'path'; | ||||
| import { homedir } from 'os'; | ||||
| import fs from 'fs'; | ||||
| import { checkFileExists, createDir } from '../file/index.ts'; | ||||
| import { ProxyInfo } from '../proxy/proxy.ts'; | ||||
|  | ||||
| export const kevisualUrl = 'https://kevisual.xiongxiao.me'; | ||||
| const configDir = createDir(path.join(homedir(), '.config/envision')); | ||||
| export const configPath = path.join(configDir, 'assistant-config.json'); | ||||
| export const appConfigPath = path.join(configDir, 'assistant-app-config.json'); | ||||
| export const appDir = createDir(path.join(configDir, 'assistant-app/frontend')); | ||||
| export const appPidPath = path.join(configDir, 'assistant-app.pid'); | ||||
| export const LocalElectronAppUrl = 'https://assistant.app/user/tiptap/'; | ||||
|  | ||||
| type AssistantConfig = { | ||||
|   pageApi?: string; // https://kevisual.silkyai.cn | ||||
|   loadURL?: string; // https://assistant.app/user/tiptap/ | ||||
|   proxy?: { user: string; key: string; path: string }[]; | ||||
|   apiProxyList?: ProxyInfo[]; | ||||
| }; | ||||
| let assistantConfig: AssistantConfig; | ||||
| export const getConfig = () => { | ||||
|   try { | ||||
|     if (!checkFileExists(configPath)) { | ||||
|       fs.writeFileSync(configPath, JSON.stringify({ proxy: [] }, null, 2)); | ||||
|       return { | ||||
|         loadURL: LocalElectronAppUrl, | ||||
|         pageApi: '', | ||||
|         proxy: [], | ||||
|       }; | ||||
|     } | ||||
|     assistantConfig = JSON.parse(fs.readFileSync(configPath, 'utf8')); | ||||
|     return assistantConfig; | ||||
|   } catch (error) { | ||||
|     console.error(error); | ||||
|     return { | ||||
|       loadURL: LocalElectronAppUrl, | ||||
|       pageApi: '', | ||||
|       proxy: [], | ||||
|     }; | ||||
|   } | ||||
| }; | ||||
| export const getCacheAssistantConfig = () => { | ||||
|   if (assistantConfig) { | ||||
|     return assistantConfig; | ||||
|   } | ||||
|   return getConfig(); | ||||
| }; | ||||
|  | ||||
| export const setConfig = (config?: AssistantConfig) => { | ||||
|   if (!config) { | ||||
|     return assistantConfig; | ||||
|   } | ||||
|   assistantConfig = config; | ||||
|   fs.writeFileSync(configPath, JSON.stringify(config, null, 2)); | ||||
|   return assistantConfig; | ||||
| }; | ||||
| type AppConfig = { | ||||
|   list: any[]; | ||||
| }; | ||||
| /** | ||||
|  * 应用配置 | ||||
|  * @returns | ||||
|  */ | ||||
| export const getAppConfig = (): AppConfig => { | ||||
|   if (!checkFileExists(appConfigPath)) { | ||||
|     return { | ||||
|       list: [], | ||||
|     }; | ||||
|   } | ||||
|   return JSON.parse(fs.readFileSync(appConfigPath, 'utf8')); | ||||
| }; | ||||
|  | ||||
| export const setAppConfig = (config: AppConfig) => { | ||||
|   fs.writeFileSync(appConfigPath, JSON.stringify(config, null, 2)); | ||||
|   return config; | ||||
| }; | ||||
|  | ||||
| export const addAppConfig = (app: any) => { | ||||
|   const config = getAppConfig(); | ||||
|   const assistantConfig = getCacheAssistantConfig(); | ||||
|   const _apps = config.list; | ||||
|   const _proxy = assistantConfig.proxy || []; | ||||
|   const { user, key } = app; | ||||
|   const newProxyInfo = { | ||||
|     user, | ||||
|     key, | ||||
|     path: `/${user}/${key}`, | ||||
|   }; | ||||
|   const _proxyIndex = _proxy.findIndex((_proxy: any) => _proxy.path === newProxyInfo.path); | ||||
|   if (_proxyIndex !== -1) { | ||||
|     _proxy[_proxyIndex] = newProxyInfo; | ||||
|   } else { | ||||
|     _proxy.push(newProxyInfo); | ||||
|   } | ||||
|  | ||||
|   const _app = _apps.findIndex((_app: any) => _app.id === app.id); | ||||
|   if (_app !== -1) { | ||||
|     _apps[_app] = app; | ||||
|   } else { | ||||
|     _apps.push(app); | ||||
|   } | ||||
|   setAppConfig({ ...config, list: _apps }); | ||||
|   setConfig({ ...assistantConfig, proxy: _proxy }); | ||||
|   return config; | ||||
| }; | ||||
|  | ||||
| export const getAppList = () => { | ||||
|   const config = getAppConfig(); | ||||
|   return config.list || []; | ||||
| }; | ||||
							
								
								
									
										20
									
								
								assistant-module/src/file/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								assistant-module/src/file/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| import fs from 'fs'; | ||||
|  | ||||
| export const checkFileExists = (filePath: string, checkIsFile = false) => { | ||||
|   try { | ||||
|     fs.accessSync(filePath); | ||||
|     if (checkIsFile) { | ||||
|       return fs.statSync(filePath).isFile(); | ||||
|     } | ||||
|     return true; | ||||
|   } catch (error) { | ||||
|     return false; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| export const createDir = (dirPath: string) => { | ||||
|   if (!checkFileExists(dirPath)) { | ||||
|     fs.mkdirSync(dirPath, { recursive: true }); | ||||
|   } | ||||
|   return dirPath; | ||||
| }; | ||||
							
								
								
									
										2
									
								
								assistant-module/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								assistant-module/src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| export * from './install/index.ts'; | ||||
| export * from './config/index.ts'; | ||||
							
								
								
									
										127
									
								
								assistant-module/src/install/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								assistant-module/src/install/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| import path from 'path'; | ||||
| import fs from 'fs'; | ||||
|  | ||||
| type DownloadTask = { | ||||
|   downloadPath: string; | ||||
|   downloadUrl: string; | ||||
|   user: string; | ||||
|   key: string; | ||||
|   version: string; | ||||
| }; | ||||
| export type Package = { | ||||
|   id: string; | ||||
|   name?: string; | ||||
|   version?: string; | ||||
|   description?: string; | ||||
|   title?: string; | ||||
|   user?: string; | ||||
|   key?: string; | ||||
|   [key: string]: any; | ||||
| }; | ||||
| type InstallAppOpts = { | ||||
|   appDir?: string; | ||||
|   kevisualUrl?: string; | ||||
|   /** | ||||
|    * 是否是客户端, 下载到 assistant-config的下面 | ||||
|    */ | ||||
| }; | ||||
| export const installApp = async (app: Package, opts: InstallAppOpts = {}) => { | ||||
|   // const _app = demoData; | ||||
|   const { appDir = '', kevisualUrl = 'https://kevisual.cn' } = opts; | ||||
|   const _app = app; | ||||
|   try { | ||||
|     let files = _app.data.files || []; | ||||
|     const version = _app.version; | ||||
|     const user = _app.user; | ||||
|     const key = _app.key; | ||||
|  | ||||
|     const downFiles = files.map((file: any) => { | ||||
|       const noVersionPath = file.path.replace(`/${version}`, ''); | ||||
|       return { | ||||
|         ...file, | ||||
|         downloadPath: path.join(appDir, noVersionPath), | ||||
|         downloadUrl: `${kevisualUrl}/${noVersionPath}`, | ||||
|       }; | ||||
|     }); | ||||
|     const downloadTasks: DownloadTask[] = downFiles as any; | ||||
|     for (const file of downloadTasks) { | ||||
|       const downloadPath = file.downloadPath; | ||||
|       const downloadUrl = file.downloadUrl; | ||||
|       const dir = path.dirname(downloadPath); | ||||
|       if (!fs.existsSync(dir)) { | ||||
|         fs.mkdirSync(dir, { recursive: true }); | ||||
|       } | ||||
|       const res = await fetch(downloadUrl); | ||||
|       const blob = await res.blob(); | ||||
|       fs.writeFileSync(downloadPath, Buffer.from(await blob.arrayBuffer())); | ||||
|     } | ||||
|     let indexHtml = files.find((file: any) => file.name === 'index.html'); | ||||
|     if (!indexHtml) { | ||||
|       files.push({ | ||||
|         name: 'index.html', | ||||
|         path: `${user}/${key}/index.html`, | ||||
|       }); | ||||
|       fs.writeFileSync(path.join(appDir, `${user}/${key}/index.html`), JSON.stringify(app, null, 2)); | ||||
|     } | ||||
|     _app.data.files = files; | ||||
|     return { | ||||
|       code: 200, | ||||
|       data: _app, | ||||
|       message: 'Install app success', | ||||
|     }; | ||||
|   } catch (error) { | ||||
|     console.error(error); | ||||
|     return { | ||||
|       code: 500, | ||||
|       message: 'Install app failed', | ||||
|     }; | ||||
|   } | ||||
| }; | ||||
| export const checkAppDir = (appDir: string) => { | ||||
|   const files = fs.readdirSync(appDir); | ||||
|   if (files.length === 0) { | ||||
|     fs.rmSync(appDir, { recursive: true }); | ||||
|   } | ||||
| }; | ||||
| export const checkFileExists = (path: string) => { | ||||
|   try { | ||||
|     fs.accessSync(path); | ||||
|     return true; | ||||
|   } catch (error) { | ||||
|     return false; | ||||
|   } | ||||
| }; | ||||
| type UninstallAppOpts = { | ||||
|   appDir?: string; | ||||
| }; | ||||
| export const uninstallApp = async (app: Partial<Package>, opts: UninstallAppOpts = {}) => { | ||||
|   const { appDir = '' } = opts; | ||||
|   try { | ||||
|     const { user, key } = app; | ||||
|     const keyDir = path.join(appDir, user, key); | ||||
|     const parentDir = path.join(appDir, user); | ||||
|     if (!checkFileExists(appDir) || !checkFileExists(keyDir)) { | ||||
|       return { | ||||
|         code: 200, | ||||
|         message: 'uninstall app success', | ||||
|       }; | ||||
|     } | ||||
|     try { | ||||
|       // 删除appDir和文件 | ||||
|       fs.rmSync(keyDir, { recursive: true }); | ||||
|     } catch (error) { | ||||
|       console.error(error); | ||||
|     } | ||||
|     checkAppDir(parentDir); | ||||
|     return { | ||||
|       code: 200, | ||||
|       message: 'Uninstall app success', | ||||
|     }; | ||||
|   } catch (error) { | ||||
|     console.error(error); | ||||
|     return { | ||||
|       code: 500, | ||||
|       message: 'Uninstall app failed', | ||||
|     }; | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										70
									
								
								assistant-module/src/process/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								assistant-module/src/process/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| import { ChildProcess, fork } from 'child_process'; | ||||
|  | ||||
| export const runProcess = (appPath: string) => { | ||||
|   const process = fork(appPath); | ||||
|   process.on('exit', (code) => { | ||||
|     console.log(`Process exited with code ${code}`); | ||||
|   }); | ||||
|  | ||||
|   process.on('message', (message) => { | ||||
|     console.log('Message from child:', message); | ||||
|   }); | ||||
|  | ||||
|   // Example of sending a message to the child process | ||||
|   // process.send({ hello: 'world' }); | ||||
| }; | ||||
| class BaseProcess { | ||||
|   private process: ChildProcess; | ||||
|   status: 'running' | 'stopped' | 'error' = 'stopped'; | ||||
|   appPath: string; | ||||
|   constructor(appPath: string) { | ||||
|     this.appPath = appPath; | ||||
|     // this.createProcess(appPath); | ||||
|   } | ||||
|   createProcess(appPath: string = this.appPath) { | ||||
|     if (this.process) { | ||||
|       this.process.kill(); | ||||
|     } | ||||
|     this.appPath = appPath; | ||||
|     this.process = fork(appPath); | ||||
|     return this; | ||||
|   } | ||||
|   kill(signal?: NodeJS.Signals | number) { | ||||
|     if (this.process) { | ||||
|       this.process.kill(signal); | ||||
|     } | ||||
|     return this; | ||||
|   } | ||||
|   public send(message: any) { | ||||
|     this.process.send(message); | ||||
|   } | ||||
|  | ||||
|   public on(event: string, callback: (message: any) => void) { | ||||
|     this.process.on(event, callback); | ||||
|   } | ||||
|  | ||||
|   public onExit(callback: (code: number) => void) { | ||||
|     this.process.on('exit', callback); | ||||
|   } | ||||
|  | ||||
|   public onError(callback: (error: Error) => void) { | ||||
|     this.process.on('error', callback); | ||||
|   } | ||||
|  | ||||
|   public onMessage(callback: (message: any) => void) { | ||||
|     this.process.on('message', callback); | ||||
|   } | ||||
|  | ||||
|   public onClose(callback: () => void) { | ||||
|     this.process.on('close', callback); | ||||
|   } | ||||
|  | ||||
|   public onDisconnect(callback: () => void) { | ||||
|     this.process.on('disconnect', callback); | ||||
|   } | ||||
| } | ||||
| export class AssistantProcess extends BaseProcess { | ||||
|   constructor(appPath: string) { | ||||
|     super(appPath); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										82
									
								
								assistant-module/src/proxy/api-proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								assistant-module/src/proxy/api-proxy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| import http from 'http'; | ||||
| import https from 'https'; | ||||
|  | ||||
| import { ProxyInfo } from './proxy.ts'; | ||||
| export const defaultApiProxy = [ | ||||
|   { | ||||
|     path: '/api/router', | ||||
|     target: 'https://kevisual.xiongxiao.me', | ||||
|   }, | ||||
|   { | ||||
|     path: '/v1', | ||||
|     target: 'https://kevisual.xiongxiao.me', | ||||
|   }, | ||||
| ]; | ||||
| /** | ||||
|  * 创建api代理 | ||||
|  * @param api  | ||||
|  * @param paths ['/api/router', '/v1' ] | ||||
|  * @returns  | ||||
|  */ | ||||
| export const createApiProxy = (api: string, paths: string[] = ['/api/router', '/v1']) => { | ||||
|   const pathList = paths.map((item) => { | ||||
|     return { | ||||
|       path: item, | ||||
|       target: new URL(api).origin, | ||||
|     }; | ||||
|   }); | ||||
|   return pathList; | ||||
| }; | ||||
|  | ||||
| export const apiProxy = (req: http.IncomingMessage, res: http.ServerResponse, proxyApi: ProxyInfo) => { | ||||
|   const _u = new URL(req.url, `${proxyApi.target}`); | ||||
|   console.log('proxyApi', req.url, _u.href); | ||||
|   // 设置代理请求的目标 URL 和请求头 | ||||
|   let header: any = {}; | ||||
|   if (req.headers?.['Authorization'] && !req.headers?.['authorization']) { | ||||
|     header.authorization = req.headers['Authorization']; | ||||
|   } | ||||
|   // 提取req的headers中的非HOST的header | ||||
|   const headers = Object.keys(req.headers).filter((item) => item && item.toLowerCase() !== 'host'); | ||||
|   headers.forEach((item) => { | ||||
|     if (item.toLowerCase() === 'origin') { | ||||
|       header.origin = new URL(proxyApi.target).origin; | ||||
|       return; | ||||
|     } | ||||
|     if (item.toLowerCase() === 'referer') { | ||||
|       header.referer = new URL(req.url, proxyApi.target).href; | ||||
|       return; | ||||
|     } | ||||
|     header[item] = req.headers[item]; | ||||
|   }); | ||||
|   const options = { | ||||
|     host: _u.hostname, | ||||
|     path: req.url, | ||||
|     method: req.method, | ||||
|     headers: { | ||||
|       ...header, | ||||
|     }, | ||||
|   }; | ||||
|   console.log('options', JSON.stringify(options, null, 2)); | ||||
|   if (_u.port) { | ||||
|     // @ts-ignore | ||||
|     options.port = _u.port; | ||||
|   } | ||||
|   const httpProxy = _u.protocol === 'https:' ? https : http; | ||||
|   // 创建代理请求 | ||||
|   const proxyReq = httpProxy.request(options, (proxyRes) => { | ||||
|     // 将代理服务器的响应头和状态码返回给客户端 | ||||
|     res.writeHead(proxyRes.statusCode, proxyRes.headers); | ||||
|     // 将代理响应流写入客户端响应 | ||||
|     proxyRes.pipe(res, { end: true }); | ||||
|   }); | ||||
|   // 处理代理请求的错误事件 | ||||
|   proxyReq.on('error', (err) => { | ||||
|     console.error(`Proxy request error: ${err.message}`); | ||||
|     res.writeHead(500, { 'Content-Type': 'text/plain' }); | ||||
|     res.write(`Proxy request error: ${err.message}`); | ||||
|   }); | ||||
|   // 处理 POST 请求的请求体(传递数据到目标服务器),end:true 表示当请求体结束时,关闭请求 | ||||
|   req.pipe(proxyReq, { end: true }); | ||||
|   return; | ||||
| }; | ||||
							
								
								
									
										47
									
								
								assistant-module/src/proxy/file-proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								assistant-module/src/proxy/file-proxy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| import http from 'http'; | ||||
| import send from 'send'; | ||||
| import fs from 'fs'; | ||||
| import { fileIsExist } from '@kevisual/use-config'; | ||||
| import path from 'path'; | ||||
| import { ProxyInfo } from './proxy.ts'; | ||||
|  | ||||
| export const fileProxy = (req: http.IncomingMessage, res: http.ServerResponse, proxyApi: ProxyInfo) => { | ||||
|   // url开头的文件 | ||||
|   const url = new URL(req.url, 'http://localhost'); | ||||
|   let pathname = url.pathname.slice(1); | ||||
|   const { indexPath = '', target = '', rootPath = process.cwd() } = proxyApi; | ||||
|   try { | ||||
|     if (pathname.endsWith('/')) { | ||||
|       pathname = pathname + 'index.html'; | ||||
|     } | ||||
|     // 检测文件是否存在,如果文件不存在,则返回404 | ||||
|     let filePath = path.join(rootPath, target, pathname); | ||||
|     let exist = fileIsExist(filePath); | ||||
|     if (!exist) { | ||||
|       filePath = path.join(rootPath, target, '/' + indexPath); | ||||
|       exist = fileIsExist(filePath); | ||||
|     } | ||||
|     console.log('filePath', filePath, exist); | ||||
|      | ||||
|     if (!exist) { | ||||
|       res.statusCode = 404; | ||||
|       res.end('Not Found File'); | ||||
|       return; | ||||
|     } | ||||
|     const ext = path.extname(filePath); | ||||
|     let maxAge = 24 * 60 * 60 * 1000; // 24小时 | ||||
|     if (ext === '.html') { | ||||
|       maxAge = 0; | ||||
|     } | ||||
|     let sendFilePath = filePath.replace(rootPath + '/', ''); | ||||
|     const file = send(req, sendFilePath, { | ||||
|       root: rootPath, | ||||
|       maxAge, | ||||
|     }); | ||||
|     file.pipe(res); | ||||
|   } catch (error) { | ||||
|     res.statusCode = 404; | ||||
|     res.end('Error:Not Found File'); | ||||
|     return; | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										5
									
								
								assistant-module/src/proxy/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								assistant-module/src/proxy/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export * from './proxy.ts'; | ||||
| export * from './file-proxy.ts'; | ||||
| export { default as send } from 'send'; | ||||
| export * from './api-proxy.ts'; | ||||
| export * from './wx-proxy.ts'; | ||||
							
								
								
									
										35
									
								
								assistant-module/src/proxy/proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								assistant-module/src/proxy/proxy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| export type ProxyInfo = { | ||||
|   path?: string; | ||||
|   target?: string; | ||||
|   type?: 'static' | 'dynamic' | 'minio'; | ||||
|   /** | ||||
|    * 首要文件,比如index.html, 设置了首要文件,如果文件不存在,则访问首要文件 | ||||
|    */ | ||||
|   indexPath?: string; | ||||
|   /** | ||||
|    * 根路径, 默认是process.cwd() | ||||
|    */ | ||||
|   rootPath?: string; | ||||
| }; | ||||
| export type ApiList = { | ||||
|   path: string; | ||||
|   /** | ||||
|    * url或者相对路径 | ||||
|    */ | ||||
|   target: string; | ||||
|   /** | ||||
|    * 类型 | ||||
|    */ | ||||
|   type?: 'static' | 'dynamic' | 'minio'; | ||||
| }[]; | ||||
|  | ||||
| /** | ||||
|  | ||||
| [ | ||||
|   { | ||||
|     path: '/api/v1/user', | ||||
|     target: 'http://localhost:3000/api/v1/user', | ||||
|     type: 'dynamic', | ||||
|   }, | ||||
| ] | ||||
|  */ | ||||
							
								
								
									
										48
									
								
								assistant-module/src/proxy/wx-proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								assistant-module/src/proxy/wx-proxy.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| import { Server } from 'http'; | ||||
| import WebSocket from 'ws'; | ||||
| /** | ||||
|  * websocket代理 | ||||
|  * apiList: [{ path: '/api/router', target: 'https://kevisual.xiongxiao.me' }] | ||||
|  * @param server | ||||
|  * @param config | ||||
|  */ | ||||
| export const wsProxy = (server: Server, config: { apiList: any[] }) => { | ||||
|   console.log('Upgrade initialization started'); | ||||
|  | ||||
|   server.on('upgrade', (req, socket, head) => { | ||||
|     const proxyApiList = config?.apiList || []; | ||||
|     const proxyApi = proxyApiList.find((item) => req.url.startsWith(item.path)); | ||||
|  | ||||
|     if (proxyApi) { | ||||
|       const _u = new URL(req.url, `${proxyApi.target}`); | ||||
|       const isHttps = _u.protocol === 'https:'; | ||||
|       const wsProtocol = isHttps ? 'wss' : 'ws'; | ||||
|       const wsUrl = `${wsProtocol}://${_u.hostname}${_u.pathname}`; | ||||
|  | ||||
|       const proxySocket = new WebSocket(wsUrl, { | ||||
|         headers: req.headers, | ||||
|       }); | ||||
|  | ||||
|       proxySocket.on('open', () => { | ||||
|         socket.on('data', (data) => { | ||||
|           proxySocket.send(data); | ||||
|         }); | ||||
|  | ||||
|         proxySocket.on('message', (message) => { | ||||
|           socket.write(message); | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       proxySocket.on('error', (err) => { | ||||
|         console.error(`WebSocket proxy error: ${err.message}`); | ||||
|         socket.end(); | ||||
|       }); | ||||
|  | ||||
|       socket.on('error', () => { | ||||
|         proxySocket.close(); | ||||
|       }); | ||||
|     } else { | ||||
|       socket.end(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user