import fs from 'node:fs/promises'; import { URL } from 'node:url'; import { Transform } from 'node:stream'; import { Readable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import { Hono } from 'hono'; import { logger } from 'hono/logger'; // 引入 Hono 日志中间件 import { createServer as createNodeServer } from 'node:http'; import process from 'node:process'; import { createAdaptorServer } from '@hono/node-server' // 创建 Hono 应用 const app = new Hono();// API路由 // 全局使用 Hono 日志中间件(记录所有请求) app.use('*', logger()); // 新增:添加请求日志中间件 // 常量定义 const isProduction = process.env.NODE_ENV === 'production'; const port = process.env.PORT || 8080; const base = process.env.BASE || '/'; const ABORT_DELAY = 10000; console.log('========================================'); console.log('开始初始化服务器...'); console.log(`环境: ${isProduction ? '生产环境' : '开发环境'}`); console.log(`端口: ${port}`); console.log(`基础路径: ${base}`); console.log('========================================'); // 解析基础路径为 URL 对象,方便后续处理 const baseUrl = new URL(base, `http://localhost:${port}`); console.log(`基础URL解析完成: ${baseUrl.href}`); // 先创建服务器实例 console.log('正在创建服务器实例...'); const parentServer = createAdaptorServer({ fetch: app.fetch, createServer: createNodeServer, port: port, }) console.log('服务器实例创建成功'); // 生产环境中间件 let compressionMiddleware; let sirvMiddleware; if (isProduction) { console.log('生产环境: 加载压缩和静态文件中间件...'); compressionMiddleware = (await import('compression')).default(); sirvMiddleware = (await import('sirv')).default('./dist/client', { extensions: [], baseUrl: base }); console.log('生产环境中间件加载完成'); } // Vite 开发服务器 /** @type {import('vite').ViteDevServer | undefined} */ let vite; if (!isProduction) { console.log('开发环境: 初始化 Vite 开发服务器...'); const { createServer } = await import('vite'); vite = await createServer({ server: { middlewareMode: { server: parentServer }, hmr: { port: 8081, clientPort: 443, // 或其他可用端口 path: 'vite-hmr' }, proxy: { '/vite-hmr': { target: 'ws://localhost:8081', // 代理 WebSocket ws: true, }, }, }, appType: 'custom', base, }); console.log('Vite 开发服务器初始化完成'); } // 加载 API 路由 if (!isProduction) { console.log('开发环境: 从 Vite 加载 API 路由...'); const apiModule = await vite.ssrLoadModule('./src/server/api.ts'); app.route('/', apiModule.default); console.log('API 路由加载完成'); }else{ console.log('生产环境: 加载编译后的 API 路由...'); const api = (await import('./dist/api/api.js')).default app.route('/', api); console.log('API 路由加载完成'); } // 请求处理中间件 - 通用逻辑 app.use(async (c, next) => { try { // 使用 c.env 获取原生请求响应对象 const req = c.env.incoming; const res = c.env.outgoing; const url = new URL(req.url, `http://${req.headers.host}`); const path = url.pathname; // 检查是否匹配基础路径 if (!path.startsWith(baseUrl.pathname)) { return c.text('未找到', 404); } // 开发环境:使用 Vite 中间件 if (!isProduction && vite) { // 使用 Vite 中间件处理请求 const handled = await new Promise((resolve) => { vite.middlewares(req, res, () => resolve(false)); }); // 如果 Vite 中间件已经处理了请求,直接返回 if (handled) { return c.body; } } // 生产环境:使用 compression 和 sirv 中间件 else if (isProduction) { // 先尝试 compression 中间件 const compressed = await new Promise((resolve) => { compressionMiddleware(req, res, () => resolve(false)); }); if (compressed) { return c.body; } // 再尝试 sirv 中间件处理静态文件 const served = await new Promise((resolve) => { sirvMiddleware(req, res, () => resolve(false)); }); if (served) { return c.body; } } await next() } catch (e) { if (!isProduction && vite) { vite.ssrFixStacktrace(e); } console.error('请求处理中间件错误:', e.stack); return c.text('服务器内部错误', 500); } }); // 请求处理中间件 - SSR 渲染逻辑 app.use(async (c) => { try { // 使用 c.env 获取原生请求响应对象 const req = c.env.incoming; const res = c.env.outgoing; const url = new URL(req.url, `http://${req.headers.host}`); const path = url.pathname; // 检查是否匹配基础路径 if (!path.startsWith(baseUrl.pathname)) { return c.text('未找到', 404); } // 处理基础路径 const normalizedUrl = path.replace(baseUrl.pathname, '/') || '/'; console.log(`处理请求: ${normalizedUrl}`); // 处理所有其他请求的 SSR 逻辑 /** @type {string} */ let template; /** @type {import('./src/server/index.tsx').render} */ let render; if (!isProduction && vite) { console.log('开发环境: 加载模板和渲染函数...'); // 开发环境:读取最新模板并转换 const module = (await vite.ssrLoadModule('/src/server/index.tsx')); template = module.template; template = await vite.transformIndexHtml(normalizedUrl, template); render = module.render; console.log('开发环境模板处理完成'); } else { // 生产环境:使用缓存的模板 console.log('生产环境: 加载编译后的模板和渲染函数...'); const module = await import('./dist/server/index.js'); // 读取 manifest.json 并处理模板 try { // 读取 manifest const manifestPath = new URL('./dist/client/.vite/manifest.json', import.meta.url); const manifestContent = await fs.readFile(manifestPath, 'utf-8'); const manifest = JSON.parse(manifestContent); console.log('生产环境: 成功读取 manifest.json'); // 获取 index.html 对应的资源信息 const indexManifest = manifest['index.html']; if (!indexManifest) { throw new Error('manifest 中未找到 index.html 入口配置'); } template = module.template; // 替换 CSS 链接 const cssLinks = indexManifest.css?.map(cssFile => { // 结合基础路径生成完整 URL(处理 base 前缀) const cssUrl = new URL(cssFile, baseUrl).pathname; return ``; }).join('\n') || ''; // 无 CSS 则清空 // 替换入口脚本 const jsEntryPath = new URL(indexManifest.file, baseUrl).pathname; const entryScript = ``; // 执行替换 template = template .replace(//, cssLinks) .replace(/