diff --git a/src/module/html/stat/index.ts b/src/module/html/stat/index.ts index dec2409..4348719 100644 --- a/src/module/html/stat/index.ts +++ b/src/module/html/stat/index.ts @@ -4,8 +4,5 @@ * @returns */ export const addStat = (html: string) => { - return html.replace( - '', - ``, - ); + return html.replace('', ``); }; diff --git a/src/module/index.ts b/src/module/index.ts index ae4c3e9..39219bb 100644 --- a/src/module/index.ts +++ b/src/module/index.ts @@ -7,7 +7,7 @@ import fs from 'fs'; import { getContentType } from './get-content-type.ts'; import { createRefreshHtml } from './html/create-refresh-html.ts'; import { fileProxy } from './proxy/file-proxy.ts'; -import { httpProxy } from './proxy/http-proxy.ts'; +import { getTextFromStreamAndAddStat, httpProxy } from './proxy/http-proxy.ts'; const api = config?.api || { host: 'kevisual.xiongxiao.me', path: '/api/router' }; const domain = config?.proxy?.domain || 'kevisual.xiongxiao.me'; @@ -132,6 +132,9 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR } // const url = req.url; const pathname = new URL(req.url, `http://${dns.hostName}`).pathname; + /** + * url是pathname的路径 + */ const url = pathname; if (!domainApp && noProxyUrl.includes(url)) { if (url === '/') { @@ -252,9 +255,13 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR } // 不存在的文件,返回indexFile的文件 res.writeHead(200, { 'Content-Type': contentType, 'Cache-Control': isHTML ? 'no-cache' : 'public, max-age=3600' }); - - const readStream = fs.createReadStream(filePath); - readStream.pipe(res); + if (isHTML) { + const newHtml = await getTextFromStreamAndAddStat(fs.createReadStream(filePath)); + res.end(newHtml.html); + } else { + const readStream = fs.createReadStream(filePath); + readStream.pipe(res); + } return; } else { @@ -275,20 +282,30 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR res.setHeader('Content-Disposition', `attachment; filename=${fileName}`); } - res.writeHead(200, { - 'Content-Type': contentType, - 'Cache-Control': isHTML ? 'no-cache' : 'public, max-age=3600', // 设置缓存时间为 1 小时 - ETag: eTag, - }); if (!userApp.fileCheck(filePath)) { console.error('File expired', filePath); + res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' }); res.end('File expired\n'); await userApp.clearCacheData(); return; } - const readStream = fs.createReadStream(filePath); - readStream.pipe(res); - + let resContent = ''; + const headers = new Map(); + headers.set('Content-Type', contentType); + headers.set('Cache-Control', isHTML ? 'no-cache' : 'public, max-age=3600'); // 设置缓存时间为 1 小时 + headers.set('ETag', eTag); + res.setHeaders(headers); + if (isHTML) { + const newHtml = await getTextFromStreamAndAddStat(fs.createReadStream(filePath)); + resContent = newHtml.html; + headers.set('Content-Length', newHtml.contentLength.toString()); + res.writeHead(200); + res.end(resContent); + } else { + res.writeHead(200); + const readStream = fs.createReadStream(filePath); + readStream.pipe(res); + } return; } } catch (error) { diff --git a/src/module/proxy/http-proxy.ts b/src/module/proxy/http-proxy.ts index 4230c8a..5772cad 100644 --- a/src/module/proxy/http-proxy.ts +++ b/src/module/proxy/http-proxy.ts @@ -1,4 +1,4 @@ -import { pipeline } from 'stream'; +import { pipeline, Readable } from 'stream'; import { promisify } from 'util'; import { bucketName, minioClient, minioResources } from '../minio.ts'; import fs from 'fs'; @@ -6,6 +6,7 @@ import { IncomingMessage, ServerResponse } from 'http'; import http from 'http'; import https from 'https'; import { UserApp } from '../get-user-app.ts'; +import { addStat } from '../html/stat/index.ts'; const pipelineAsync = promisify(pipeline); @@ -48,15 +49,28 @@ export async function minioProxy( const etag = stat.etag; const lastModified = stat.lastModified.toISOString(); const fileName = objectName.split('/').pop(); - res.writeHead(200, { + + const objectStream = await minioClient.getObject(bucketName, objectName); + const headers = { 'Content-Length': contentLength, etag, 'last-modified': lastModified, 'file-name': fileName, ...filterMetaData, - }); - const objectStream = await minioClient.getObject(bucketName, objectName); - objectStream.pipe(res, { end: true }); + }; + if (objectName.endsWith('.html')) { + const { html, contentLength } = await getTextFromStreamAndAddStat(objectStream); + res.writeHead(200, { + ...headers, + 'Content-Length': contentLength, + }); + res.end(html); + } else { + res.writeHead(200, { + ...headers, + }); + objectStream.pipe(res, { end: true }); + } } catch (error) { console.error(`Proxy request error: ${error.message}`); userApp.clearCacheData(); @@ -64,6 +78,27 @@ export async function minioProxy( } } +// 添加一个辅助函数来从流中获取文本 +async function getTextFromStream(stream: Readable | IncomingMessage): Promise { + return new Promise((resolve, reject) => { + let data = ''; + stream.on('data', (chunk) => { + data += chunk; + }); + stream.on('end', () => { + resolve(data); + }); + stream.on('error', (err) => { + reject(err); + }); + }); +} +export async function getTextFromStreamAndAddStat(stream: Readable | IncomingMessage): Promise<{ html: string; contentLength: number }> { + const html = await getTextFromStream(stream); + const newHtml = addStat(html); + const newContentLength = newHtml.length; + return { html: newHtml, contentLength: newContentLength }; +} export const httpProxy = async ( req: IncomingMessage, res: ServerResponse, @@ -79,10 +114,9 @@ export const httpProxy = async ( } let protocol = proxyUrl.startsWith('https') ? https : http; // 代理 - const proxyReq = protocol.request(proxyUrl, (proxyRes) => { - res.writeHead(proxyRes.statusCode, { - ...proxyRes.headers, - }); + const proxyReq = protocol.request(proxyUrl, async (proxyRes) => { + const headers = proxyRes.headers; + if (proxyRes.statusCode === 404) { userApp.clearCacheData(); return createNotFoundPage('Invalid proxy url'); @@ -91,7 +125,24 @@ export const httpProxy = async ( res.writeHead(302, { Location: proxyRes.headers.location }); return res.end(); } - proxyRes.pipe(res, { end: true }); + if (proxyUrl.endsWith('.html')) { + try { + const { html, contentLength } = await getTextFromStreamAndAddStat(proxyRes); + res.writeHead(200, { + ...headers, + 'Content-Length': contentLength, + }); + res.end(html); + } catch (error) { + console.error(`Proxy request error: ${error.message}`); + return createNotFoundPage('Invalid proxy url:' + error.message); + } + } else { + res.writeHead(proxyRes.statusCode, { + ...headers, + }); + proxyRes.pipe(res, { end: true }); + } }); proxyReq.on('error', (err) => { console.error(`Proxy request error: ${err.message}`);