|
|
@@ -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);
|
|
|
+ });
|
|
|
+});
|