Bläddra i källkod

♻️ refactor(server): 重构服务器启动逻辑与日志系统

- 优化服务器初始化流程,增加详细启动日志输出
- 改进服务器创建方式,使用createAdaptorServer替代原serve方法
- 添加环境信息打印,包括环境类型、端口和基础路径
- 增加服务器启动和初始化的状态日志
- 为开发/生产环境加载过程添加详细日志
- 优化SSR渲染过程的日志输出,便于问题排查
- 添加服务器关闭事件处理,支持优雅退出

✨ feat(server): 增强生产环境构建处理

- 实现生产环境下模板自动注入CSS和JS资源
- 添加manifest.json解析逻辑,自动处理资源链接
- 优化生产环境模板加载流程,提高启动效率

🐛 fix(server): 修复路径匹配和错误处理

- 将404提示信息改为中文"未找到"
- 统一500错误提示为"服务器内部错误"
- 修复基础路径解析逻辑,确保路由正确匹配
yourname 7 månader sedan
förälder
incheckning
a4c06e948f
1 ändrade filer med 63 tillägg och 118 borttagningar
  1. 63 118
      server.js

+ 63 - 118
server.js

@@ -3,62 +3,59 @@ import { URL } from 'node:url';
 import { Transform } from 'node:stream';
 import { Readable } from 'node:stream';
 import { pipeline } from 'node:stream/promises';
-import { serve } from '@hono/node-server';
 import { Hono } from '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路由
 
-// Constants
+// 常量定义
 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}`);
-
-// 使用 Hono 的 serve 启动服务器
-// const parentServer = serve({
-//   fetch: app.fetch,
-//   createServer: createNodeServer,
-//   port: port,
-// }, () => {
-//   console.log(`Server started at http://localhost:${port}`);
-// });
+console.log(`基础URL解析完成: ${baseUrl.href}`);
 
 // 先创建服务器实例  
+console.log('正在创建服务器实例...');
 const parentServer = createAdaptorServer({  
   fetch: app.fetch,  
   createServer: createNodeServer,  
   port: port,  
 })  
-
-// Cached production assets
-// let templateHtml = '';
-// if (isProduction) {
-//   templateHtml = await fs.readFile('./dist/client/index.html', 'utf-8');
-// }
+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: {
@@ -72,7 +69,7 @@ if (!isProduction) {
       proxy: {
         '/vite-hmr': {
           target: 'ws://localhost:8081',
-          // Proxying WebSocket
+          // 代理 WebSocket
           ws: true,
         },
       },
@@ -80,21 +77,26 @@ if (!isProduction) {
     appType: 'custom',
     base,
   });
+  console.log('Vite 开发服务器初始化完成');
 }
 
+// 加载 API 路由
 if (!isProduction) {
-  // 使用 vite.ssrLoadModule 替代原生 import
+  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 路由加载完成');
 }
 
-// 请求处理逻辑适配为 Hono 中间件
+// 请求处理中间件 - 通用逻辑
 app.use(async (c, next) => {
   try {
-    // 使用 c.env 获取原生请求响应对象(关键修复)
+    // 使用 c.env 获取原生请求响应对象
     const req = c.env.incoming;
     const res = c.env.outgoing;
     const url = new URL(req.url, `http://${req.headers.host}`);
@@ -102,12 +104,9 @@ app.use(async (c, next) => {
 
     // 检查是否匹配基础路径
     if (!path.startsWith(baseUrl.pathname)) {
-      return c.text('Not found', 404);
+      return c.text('未找到', 404);
     }
 
-    // 处理基础路径
-    // const normalizedUrl = path.replace(baseUrl.pathname, '/') || '/';
-
     // 开发环境:使用 Vite 中间件
     if (!isProduction && vite) {
       // 使用 Vite 中间件处理请求
@@ -141,95 +140,21 @@ app.use(async (c, next) => {
       }
     }
 
-    // // 处理所有其他请求的 SSR 逻辑
-    // /** @type {string} */
-    // let template;
-    // /** @type {import('./src/server/index.tsx').render} */
-    // let render;
-    
-    // if (!isProduction && vite) {
-    //   // 开发环境:读取最新模板并转换
-    //   template = await fs.readFile('./index.html', 'utf-8');
-    //   template = await vite.transformIndexHtml(normalizedUrl, template);
-    //   render = (await vite.ssrLoadModule('/src/server/index.tsx')).render;
-    // } else {
-    //   // 生产环境:使用缓存的模板
-    //   template = templateHtml;
-    //   render = (await import('./dist/server/index.js')).render;
-    // }
-
-    // let didError = false;
-    // let abortController;
-
-    // // 创建一个可读流用于 SSR 渲染内容
-    // const [htmlStart, htmlEnd] = template.split(`<!--app-html-->`);
-    // const ssrStream = new Readable({ read: () => {} });
-    
-    // // 写入 HTML 头部
-    // ssrStream.push(htmlStart);
-
-    // // 设置响应头和状态码
-    // c.header('Content-Type', 'text/html');
-    
-    // // 处理渲染
-    // const { pipe, abort } = render(normalizedUrl, {
-    //   onShellError() {
-    //     didError = true;
-    //     c.status(500);
-    //     ssrStream.push('<h1>Something went wrong</h1>');
-    //     ssrStream.push(null); // 结束流
-    //   },
-    //   onShellReady() {
-    //     // 将渲染结果通过管道传入 ssrStream
-    //     const transformStream = new Transform({
-    //       transform(chunk, encoding, callback) {
-    //         ssrStream.push(chunk, encoding);
-    //         callback();
-    //       }
-    //     });
-
-    //     pipe(transformStream);
-        
-    //     // 当 transformStream 完成时,添加 HTML 尾部
-    //     transformStream.on('finish', () => {
-    //       ssrStream.push(htmlEnd);
-    //       ssrStream.push(null); // 结束流
-    //     });
-    //   },
-    //   onError(error) {
-    //     didError = true;
-    //     console.error(error);
-    //   },
-    // });
-
-    // // 设置超时中止
-    // abortController = new AbortController();
-    // const abortTimeout = setTimeout(() => {
-    //   abort();
-    //   abortController.abort();
-    // }, ABORT_DELAY);
-
-    // // 将流通过 Hono 响应返回
-    // return c.body(ssrStream, {
-    //   onEnd: () => {
-    //     clearTimeout(abortTimeout);
-    //   }
-    // });
     await next()
   } catch (e) {
     if (!isProduction && vite) {
       vite.ssrFixStacktrace(e);
     }
-    console.error(e.stack);
-    return c.text(e.stack, 500);
+    console.error('请求处理中间件错误:', e.stack);
+    return c.text('服务器内部错误', 500);
   }
 });
 
-// 请求处理逻辑适配为 Hono 中间件
+// 请求处理中间件 - SSR 渲染逻辑
 app.use(async (c) => {
   
   try {
-    // 使用 c.env 获取原生请求响应对象(关键修复)
+    // 使用 c.env 获取原生请求响应对象
     const req = c.env.incoming;
     const res = c.env.outgoing;
     const url = new URL(req.url, `http://${req.headers.host}`);
@@ -237,11 +162,12 @@ app.use(async (c) => {
 
     // 检查是否匹配基础路径
     if (!path.startsWith(baseUrl.pathname)) {
-      return c.text('Not found', 404);
+      return c.text('未找到', 404);
     }
 
     // 处理基础路径
     const normalizedUrl = path.replace(baseUrl.pathname, '/') || '/';
+    console.log(`处理请求: ${normalizedUrl}`);
 
     // 处理所有其他请求的 SSR 逻辑
     /** @type {string} */
@@ -250,25 +176,25 @@ app.use(async (c) => {
     let render;
     
     if (!isProduction && vite) {
+      console.log('开发环境: 加载模板和渲染函数...');
       // 开发环境:读取最新模板并转换
-      // template = await fs.readFile('./index.html', 'utf-8');
       const module = (await vite.ssrLoadModule('/src/server/index.tsx'));
       template = module.template;
       template = await vite.transformIndexHtml(normalizedUrl, template);
       render = module.render;
+      console.log('开发环境模板处理完成');
     } else {
       // 生产环境:使用缓存的模板
-      // const module = (await import('/src/server/index.tsx'))
-      // template = module.template;
-
-      // 读取原始模板
+      console.log('生产环境: 加载编译后的模板和渲染函数...');
       const module = await import('./dist/server/index.js');
-       // 读取 manifest.json 并处理模板
+      
+      // 读取 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'];
@@ -294,6 +220,8 @@ app.use(async (c) => {
           .replace(/<link href="\/src\/style.css" rel="stylesheet"\/>/, cssLinks)
           .replace(/<script type="module" src="\/src\/client\/index.tsx"><\/script>/, entryScript);
 
+        console.log('生产环境模板处理完成');
+
       } catch (err) {
         console.error('生产环境模板处理失败:', err);
         throw err; // 终止启动,避免使用错误模板
@@ -320,10 +248,11 @@ app.use(async (c) => {
       onShellError() {
         didError = true;
         c.status(500);
-        ssrStream.push('<h1>Something went wrong</h1>');
+        ssrStream.push('<h1>服务器渲染出错</h1>');
         ssrStream.push(null); // 结束流
       },
       onShellReady() {
+        console.log(`开始渲染页面: ${normalizedUrl}`);
         // 将渲染结果通过管道传入 ssrStream
         const transformStream = new Transform({
           transform(chunk, encoding, callback) {
@@ -338,17 +267,19 @@ app.use(async (c) => {
         transformStream.on('finish', () => {
           ssrStream.push(htmlEnd);
           ssrStream.push(null); // 结束流
+          console.log(`页面渲染完成: ${normalizedUrl}`);
         });
       },
       onError(error) {
         didError = true;
-        console.error(error);
+        console.error('渲染过程出错:', error);
       },
     });
 
     // 设置超时中止
     abortController = new AbortController();
     const abortTimeout = setTimeout(() => {
+      console.log(`渲染超时,终止请求: ${normalizedUrl}`);
       abort();
       abortController.abort();
     }, ABORT_DELAY);
@@ -363,13 +294,27 @@ app.use(async (c) => {
     if (!isProduction && vite) {
       vite.ssrFixStacktrace(e);
     }
-    console.error(e.stack);
-    return c.text(e.stack, 500);
+    console.error('SSR 处理错误:', e.stack);
+    return c.text('服务器内部错误', 500);
   }
 });
 
 
-// 然后手动启动  
+// 启动服务器
+console.log('准备启动服务器...');
 parentServer.listen(port, () => {  
-  console.log(`Server started at http://localhost:${port}`)  
-})
+  console.log('========================================');
+  console.log(`服务器已成功启动!`);
+  console.log(`访问地址: http://localhost:${port}`);
+  console.log(`环境: ${isProduction ? '生产环境' : '开发环境'}`);
+  console.log('========================================');
+})
+
+// 处理服务器关闭事件
+process.on('SIGINT', () => {
+  console.log('正在关闭服务器...');
+  parentServer.close(() => {
+    console.log('服务器已关闭');
+    process.exit(0);
+  });
+});