From bafd02452b0a848c418b051fef916f4424b3d10c Mon Sep 17 00:00:00 2001 From: abearxiong Date: Thu, 22 Jan 2026 23:29:20 +0800 Subject: [PATCH] update --- src/modules/fm-manager/proxy/ai-proxy.ts | 1 + src/modules/ws-proxy/manager.ts | 4 +++- src/modules/ws-proxy/proxy.ts | 14 +++++++++++++- src/routes-simple/code/upload.ts | 6 ++++-- src/routes-simple/handle-request.ts | 6 ++++-- src/routes-simple/resources/chunk.ts | 6 ++++-- src/routes-simple/resources/upload.ts | 6 ++++-- 7 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/modules/fm-manager/proxy/ai-proxy.ts b/src/modules/fm-manager/proxy/ai-proxy.ts index f144def..2cc29ba 100644 --- a/src/modules/fm-manager/proxy/ai-proxy.ts +++ b/src/modules/fm-manager/proxy/ai-proxy.ts @@ -197,6 +197,7 @@ export const postProxy = async (req: IncomingMessage, res: ServerResponse, opts: fileSize: 100 * 1024 * 1024, // 100MB files: 1, }, + defCharset: 'utf-8', }); let fileProcessed = false; bb.on('file', async (name, file, info) => { diff --git a/src/modules/ws-proxy/manager.ts b/src/modules/ws-proxy/manager.ts index 566c93e..04514d8 100644 --- a/src/modules/ws-proxy/manager.ts +++ b/src/modules/ws-proxy/manager.ts @@ -58,7 +58,9 @@ export class WsProxyManager { value.ws.close(); } } - console.log('WsProxyManager register', id); + const [username, appId] = id.split('-'); + const url = new URL(`/${username}/v1/${appId}`, 'https://kevisual.cn/'); + console.log('WsProxyManager register', id, '访问地址', url.toString()); const value = new WsMessage({ ws: opts?.ws, user: opts?.user }); this.wssMap.set(id, value); } diff --git a/src/modules/ws-proxy/proxy.ts b/src/modules/ws-proxy/proxy.ts index f1f2bd6..1deda4d 100644 --- a/src/modules/ws-proxy/proxy.ts +++ b/src/modules/ws-proxy/proxy.ts @@ -10,12 +10,16 @@ type ProxyOptions = { }; export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opts?: ProxyOptions) => { const { url } = req; - const { pathname } = new URL(url || '', `http://localhost`); + const _url = new URL(url || '', `http://localhost`); + const { pathname, searchParams } = _url; let [user, app, userAppKey] = pathname.split('/').slice(1); if (!user || !app || !userAppKey) { opts?.createNotFoundPage?.('应用未找到'); return false; } + if (!userAppKey.includes('-')) { + userAppKey = user + '-' + userAppKey; + } const data = await App.handleRequest(req, res); const loginUser = await getLoginUser(req); @@ -43,6 +47,14 @@ export const UserV1Proxy = async (req: IncomingMessage, res: ServerResponse, opt } return false; } + const path = searchParams.get('path'); + if (!path) { + // 显示前端页面 + const html = fetch('https://kevisual.cn/root/router-studio/index.html').then(res => res.text()); + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(await html); + return true; + } const value = await client.sendData(data); if (value) { res.writeHead(200, { 'Content-Type': 'application/json' }); diff --git a/src/routes-simple/code/upload.ts b/src/routes-simple/code/upload.ts index 3138c03..cbeb3c5 100644 --- a/src/routes-simple/code/upload.ts +++ b/src/routes-simple/code/upload.ts @@ -20,7 +20,7 @@ router.post('/api/micro-app/upload', async (req, res) => { if (!tokenUser) return; // 使用 busboy 解析 multipart/form-data - const busboy = Busboy({ headers: req.headers, preservePath: true }); + const busboy = Busboy({ headers: req.headers, preservePath: true, defCharset: 'utf-8' }); const fields: any = {}; let file: any = null; let filePromise: Promise | null = null; @@ -33,6 +33,8 @@ router.post('/api/micro-app/upload', async (req, res) => { busboy.on('file', (fieldname, fileStream, info) => { const { filename, encoding, mimeType } = info; + // 处理 UTF-8 文件名编码 + const decodedFilename = typeof filename === 'string' ? Buffer.from(filename, 'latin1').toString('utf8') : filename; const tempPath = path.join(cacheFilePath, `${Date.now()}-${Math.random().toString(36).substring(7)}`); const writeStream = createWriteStream(tempPath); const hash = crypto.createHash('md5'); @@ -59,7 +61,7 @@ router.post('/api/micro-app/upload', async (req, res) => { writeStream.on('finish', () => { file = { filepath: tempPath, - originalFilename: filename, + originalFilename: decodedFilename, mimetype: mimeType, hash: hash.digest('hex'), size: size, diff --git a/src/routes-simple/handle-request.ts b/src/routes-simple/handle-request.ts index d561d8b..ed6bed2 100644 --- a/src/routes-simple/handle-request.ts +++ b/src/routes-simple/handle-request.ts @@ -28,7 +28,7 @@ router.post('/api/app/upload', async (req, res) => { if (!tokenUser) return; // 使用 busboy 解析 multipart/form-data - const busboy = Busboy({ headers: req.headers, preservePath: true }); + const busboy = Busboy({ headers: req.headers, preservePath: true, defCharset: 'utf-8' }); const fields: any = {}; const files: any = []; const filePromises: Promise[] = []; @@ -41,6 +41,8 @@ router.post('/api/app/upload', async (req, res) => { busboy.on('file', (fieldname, fileStream, info) => { const { filename, encoding, mimeType } = info; + // 处理 UTF-8 文件名编码 + const decodedFilename = typeof filename === 'string' ? Buffer.from(filename, 'latin1').toString('utf8') : filename; const tempPath = path.join(cacheFilePath, `${Date.now()}-${Math.random().toString(36).substring(7)}`); const writeStream = createWriteStream(tempPath); @@ -63,7 +65,7 @@ router.post('/api/app/upload', async (req, res) => { writeStream.on('finish', () => { files.push({ filepath: tempPath, - originalFilename: filename, + originalFilename: decodedFilename, mimetype: mimeType, }); resolve(); diff --git a/src/routes-simple/resources/chunk.ts b/src/routes-simple/resources/chunk.ts index 888de26..4ba2e7c 100644 --- a/src/routes-simple/resources/chunk.ts +++ b/src/routes-simple/resources/chunk.ts @@ -35,7 +35,7 @@ router.post('/api/s1/resources/upload/chunk', async (req, res) => { } // 使用 busboy 解析 multipart/form-data - const busboy = Busboy({ headers: req.headers, preservePath: true }); + const busboy = Busboy({ headers: req.headers, preservePath: true, defCharset: 'utf-8' }); const fields: any = {}; let file: any = null; let tempPath = ''; @@ -47,6 +47,8 @@ router.post('/api/s1/resources/upload/chunk', async (req, res) => { busboy.on('file', (fieldname, fileStream, info) => { const { filename, encoding, mimeType } = info; + // 处理 UTF-8 文件名编码 + const decodedFilename = typeof filename === 'string' ? Buffer.from(filename, 'latin1').toString('utf8') : filename; tempPath = path.join(cacheFilePath, `${Date.now()}-${Math.random().toString(36).substring(7)}`); const writeStream = createWriteStream(tempPath); @@ -56,7 +58,7 @@ router.post('/api/s1/resources/upload/chunk', async (req, res) => { writeStream.on('finish', () => { file = { filepath: tempPath, - originalFilename: filename, + originalFilename: decodedFilename, mimetype: mimeType, }; resolve(); diff --git a/src/routes-simple/resources/upload.ts b/src/routes-simple/resources/upload.ts index c1a69cf..1db98a1 100644 --- a/src/routes-simple/resources/upload.ts +++ b/src/routes-simple/resources/upload.ts @@ -107,7 +107,7 @@ router.post('/api/s1/resources/upload', async (req, res) => { const meta = parseIfJson(url.searchParams.get('meta')); const noCheckAppFiles = !!url.searchParams.get('noCheckAppFiles'); // 使用 busboy 解析 multipart/form-data - const busboy = Busboy({ headers: req.headers, preservePath: true }); + const busboy = Busboy({ headers: req.headers, preservePath: true, defCharset: 'utf-8' }); const fields: any = {}; const files: any[] = []; const filePromises: Promise[] = []; @@ -119,6 +119,8 @@ router.post('/api/s1/resources/upload', async (req, res) => { busboy.on('file', (fieldname, fileStream, info) => { const { filename, encoding, mimeType } = info; + // 处理 UTF-8 文件名编码(busboy 可能返回 Latin-1 编码的缓冲区) + const decodedFilename = typeof filename === 'string' ? Buffer.from(filename, 'latin1').toString('utf8') : filename; const tempPath = path.join(cacheFilePath, `${Date.now()}-${Math.random().toString(36).substring(7)}`); const writeStream = createWriteStream(tempPath); const filePromise = new Promise((resolve, reject) => { @@ -140,7 +142,7 @@ router.post('/api/s1/resources/upload', async (req, res) => { writeStream.on('finish', () => { files.push({ filepath: tempPath, - originalFilename: filename, + originalFilename: decodedFilename, mimetype: mimeType, }); resolve();