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 { cors } from 'hono/cors'
import { logger } from 'hono/logger';
import { createServer as createNodeServer } from 'node:http';
import process from 'node:process';
import { createAdaptorServer } from '@hono/node-server'
// 新增:导入 Socket.IO
import { Server } from 'socket.io';
// 创建 Hono 应用
const app = new Hono();// API路由
// 全局使用 Hono 日志中间件(记录所有请求)
app.use('*', logger());
app.use('*', cors(
// {
// origin: ['http://localhost:3000'],
// allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
// credentials: true
// }
))
// 常量定义
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('服务器实例创建成功');
// 新增:初始化 Socket.IO 服务器
console.log('正在初始化 Socket.IO 服务器...');
const io = new Server(parentServer, {
// // 配置 CORS,根据实际需求调整
// cors: {
// origin: isProduction ? process.env.FRONTEND_URL || false : "http://localhost:8080",
// methods: ["GET", "POST"],
// credentials: true
// },
// // 路径配置,避免与其他路由冲突
// path: `${base}socket.io`
});
console.log('Socket.IO 服务器初始化完成');
// 生产环境中间件
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',
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 路由加载完成');
}
// 新增:加载 Socket.IO 中间件和路由
if (!isProduction) {
console.log('开发环境: 从 Vite 加载 Socket.IO 路由和中间件...');
const socketModule = await vite.ssrLoadModule('./src/server/socket.ts');
// 应用认证中间件
if (socketModule.authMiddleware) {
console.log('应用 Socket.IO 认证中间件');
io.use(socketModule.authMiddleware);
}
// 应用路由
if (socketModule.default) {
console.log('注册 Socket.IO 路由处理');
socketModule.default(io);
}
console.log('Socket.IO 路由和中间件加载完成');
} else {
console.log('生产环境: 加载编译后的 Socket.IO 路由和中间件...');
try {
const socket = (await import('./dist/socket/socket.js')).default;
// 应用认证中间件
if (socket.authMiddleware) {
console.log('应用 Socket.IO 认证中间件');
io.use(socket.authMiddleware);
}
// 应用路由
if (socket.default) {
console.log('注册 Socket.IO 路由处理');
socket.default(io);
}
console.log('Socket.IO 路由和中间件加载完成');
} catch (err) {
console.error('加载 Socket.IO 模块失败:', err);
}
}
// 请求处理中间件 - 通用逻辑
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(/