Sfoglia il codice sorgente

✨ feat(socket): 集成 Socket.IO 实时通信功能

- 添加 socket.io 依赖包,版本 ^4.8.1
- 在 server.js 中初始化 Socket.IO 服务器
- 实现开发环境和生产环境下的 Socket.IO 路由加载逻辑
- 添加 Socket.IO 服务器启动和关闭的生命周期管理
- 创建 src/server/socket.ts 文件,包含认证中间件和事件处理示例
- 实现基础的聊天消息广播和管理员命名空间功能
- 添加 Socket.IO 连接状态日志输出

✨ feat(server): 增强服务器启动和关闭流程

- 服务器启动时显示 Socket.IO 路径信息
- 服务器关闭时先关闭 Socket.IO 连接再关闭主服务器
- 优化服务器启动日志输出格式
yourname 7 mesi fa
parent
commit
7d27eedf75
4 ha cambiato i file con 294 aggiunte e 8 eliminazioni
  1. 1 0
      package.json
  2. 145 0
      pnpm-lock.yaml
  3. 68 8
      server.js
  4. 80 0
      src/server/socket.ts

+ 1 - 0
package.json

@@ -36,6 +36,7 @@
     "react-router-dom": "^7.7.0",
     "react-router-dom": "^7.7.0",
     "reflect-metadata": "^0.2.2",
     "reflect-metadata": "^0.2.2",
     "sirv": "^3.0.1",
     "sirv": "^3.0.1",
+    "socket.io": "^4.8.1",
     "typeorm": "^0.3.25"
     "typeorm": "^0.3.25"
   },
   },
   "devDependencies": {
   "devDependencies": {

+ 145 - 0
pnpm-lock.yaml

@@ -80,6 +80,9 @@ importers:
       sirv:
       sirv:
         specifier: ^3.0.1
         specifier: ^3.0.1
         version: 3.0.1
         version: 3.0.1
+      socket.io:
+        specifier: ^4.8.1
+        version: 4.8.1
       typeorm:
       typeorm:
         specifier: ^0.3.25
         specifier: ^0.3.25
         version: 0.3.25(mysql2@3.14.2)(reflect-metadata@0.2.2)
         version: 0.3.25(mysql2@3.14.2)(reflect-metadata@0.2.2)
@@ -583,6 +586,9 @@ packages:
     cpu: [x64]
     cpu: [x64]
     os: [win32]
     os: [win32]
 
 
+  '@socket.io/component-emitter@3.1.2':
+    resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
+
   '@sqltools/formatter@1.2.5':
   '@sqltools/formatter@1.2.5':
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
 
 
@@ -773,6 +779,9 @@ packages:
   '@types/connect@3.4.38':
   '@types/connect@3.4.38':
     resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
     resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
 
 
+  '@types/cors@2.8.19':
+    resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
+
   '@types/estree@1.0.8':
   '@types/estree@1.0.8':
     resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
     resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
 
 
@@ -816,6 +825,10 @@ packages:
     peerDependencies:
     peerDependencies:
       vite: ^4 || ^5 || ^6 || ^7
       vite: ^4 || ^5 || ^6 || ^7
 
 
+  accepts@1.3.8:
+    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+    engines: {node: '>= 0.6'}
+
   accepts@2.0.0:
   accepts@2.0.0:
     resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
     resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
     engines: {node: '>= 0.6'}
     engines: {node: '>= 0.6'}
@@ -870,6 +883,10 @@ packages:
   base64-js@1.5.1:
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
 
+  base64id@2.0.0:
+    resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
+    engines: {node: ^4.5.0 || >= 5.9}
+
   bcrypt@6.0.0:
   bcrypt@6.0.0:
     resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
     resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
     engines: {node: '>= 18'}
     engines: {node: '>= 18'}
@@ -959,6 +976,10 @@ packages:
   copy-to-clipboard@3.3.3:
   copy-to-clipboard@3.3.3:
     resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
     resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
 
 
+  cors@2.8.5:
+    resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+    engines: {node: '>= 0.10'}
+
   cross-env@7.0.3:
   cross-env@7.0.3:
     resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
     resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
     engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
     engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
@@ -982,6 +1003,15 @@ packages:
       supports-color:
       supports-color:
         optional: true
         optional: true
 
 
+  debug@4.3.7:
+    resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
   debug@4.4.1:
   debug@4.4.1:
     resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
     resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
     engines: {node: '>=6.0'}
     engines: {node: '>=6.0'}
@@ -1046,6 +1076,14 @@ packages:
     resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
     resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
     engines: {node: '>= 0.8'}
     engines: {node: '>= 0.8'}
 
 
+  engine.io-parser@5.2.3:
+    resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==}
+    engines: {node: '>=10.0.0'}
+
+  engine.io@6.6.4:
+    resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==}
+    engines: {node: '>=10.2.0'}
+
   enhanced-resolve@5.18.2:
   enhanced-resolve@5.18.2:
     resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
     resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==}
     engines: {node: '>=10.13.0'}
     engines: {node: '>=10.13.0'}
@@ -1422,6 +1460,10 @@ packages:
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
     hasBin: true
 
 
+  negotiator@0.6.3:
+    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+    engines: {node: '>= 0.6'}
+
   negotiator@0.6.4:
   negotiator@0.6.4:
     resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
     resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==}
     engines: {node: '>= 0.6'}
     engines: {node: '>= 0.6'}
@@ -1438,6 +1480,10 @@ packages:
     resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
     resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
     hasBin: true
     hasBin: true
 
 
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
   object-inspect@1.13.4:
   object-inspect@1.13.4:
     resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
     resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
     engines: {node: '>= 0.4'}
     engines: {node: '>= 0.4'}
@@ -1869,6 +1915,17 @@ packages:
     resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
     resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==}
     engines: {node: '>=18'}
     engines: {node: '>=18'}
 
 
+  socket.io-adapter@2.5.5:
+    resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==}
+
+  socket.io-parser@4.2.4:
+    resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
+    engines: {node: '>=10.0.0'}
+
+  socket.io@4.8.1:
+    resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==}
+    engines: {node: '>=10.2.0'}
+
   source-map-js@1.2.1:
   source-map-js@1.2.1:
     resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
     resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
     engines: {node: '>=0.10.0'}
     engines: {node: '>=0.10.0'}
@@ -2100,6 +2157,18 @@ packages:
   wrappy@1.0.2:
   wrappy@1.0.2:
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
 
 
+  ws@8.17.1:
+    resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: '>=5.0.2'
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+
   y18n@5.0.8:
   y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     engines: {node: '>=10'}
     engines: {node: '>=10'}
@@ -2485,6 +2554,8 @@ snapshots:
   '@rollup/rollup-win32-x64-msvc@4.45.1':
   '@rollup/rollup-win32-x64-msvc@4.45.1':
     optional: true
     optional: true
 
 
+  '@socket.io/component-emitter@3.1.2': {}
+
   '@sqltools/formatter@1.2.5': {}
   '@sqltools/formatter@1.2.5': {}
 
 
   '@swc/core-darwin-arm64@1.13.2':
   '@swc/core-darwin-arm64@1.13.2':
@@ -2626,6 +2697,10 @@ snapshots:
     dependencies:
     dependencies:
       '@types/node': 24.1.0
       '@types/node': 24.1.0
 
 
+  '@types/cors@2.8.19':
+    dependencies:
+      '@types/node': 24.1.0
+
   '@types/estree@1.0.8': {}
   '@types/estree@1.0.8': {}
 
 
   '@types/express-serve-static-core@5.0.7':
   '@types/express-serve-static-core@5.0.7':
@@ -2680,6 +2755,11 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - '@swc/helpers'
       - '@swc/helpers'
 
 
+  accepts@1.3.8:
+    dependencies:
+      mime-types: 2.1.35
+      negotiator: 0.6.3
+
   accepts@2.0.0:
   accepts@2.0.0:
     dependencies:
     dependencies:
       mime-types: 3.0.1
       mime-types: 3.0.1
@@ -2777,6 +2857,8 @@ snapshots:
 
 
   base64-js@1.5.1: {}
   base64-js@1.5.1: {}
 
 
+  base64id@2.0.0: {}
+
   bcrypt@6.0.0:
   bcrypt@6.0.0:
     dependencies:
     dependencies:
       node-addon-api: 8.5.0
       node-addon-api: 8.5.0
@@ -2880,6 +2962,11 @@ snapshots:
     dependencies:
     dependencies:
       toggle-selection: 1.0.6
       toggle-selection: 1.0.6
 
 
+  cors@2.8.5:
+    dependencies:
+      object-assign: 4.1.1
+      vary: 1.1.2
+
   cross-env@7.0.3:
   cross-env@7.0.3:
     dependencies:
     dependencies:
       cross-spawn: 7.0.6
       cross-spawn: 7.0.6
@@ -2898,6 +2985,10 @@ snapshots:
     dependencies:
     dependencies:
       ms: 2.0.0
       ms: 2.0.0
 
 
+  debug@4.3.7:
+    dependencies:
+      ms: 2.1.3
+
   debug@4.4.1:
   debug@4.4.1:
     dependencies:
     dependencies:
       ms: 2.1.3
       ms: 2.1.3
@@ -2940,6 +3031,24 @@ snapshots:
 
 
   encodeurl@2.0.0: {}
   encodeurl@2.0.0: {}
 
 
+  engine.io-parser@5.2.3: {}
+
+  engine.io@6.6.4:
+    dependencies:
+      '@types/cors': 2.8.19
+      '@types/node': 24.1.0
+      accepts: 1.3.8
+      base64id: 2.0.0
+      cookie: 0.7.2
+      cors: 2.8.5
+      debug: 4.3.7
+      engine.io-parser: 5.2.3
+      ws: 8.17.1
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
   enhanced-resolve@5.18.2:
   enhanced-resolve@5.18.2:
     dependencies:
     dependencies:
       graceful-fs: 4.2.11
       graceful-fs: 4.2.11
@@ -3326,6 +3435,8 @@ snapshots:
 
 
   nanoid@3.3.11: {}
   nanoid@3.3.11: {}
 
 
+  negotiator@0.6.3: {}
+
   negotiator@0.6.4: {}
   negotiator@0.6.4: {}
 
 
   negotiator@1.0.0: {}
   negotiator@1.0.0: {}
@@ -3334,6 +3445,8 @@ snapshots:
 
 
   node-gyp-build@4.8.4: {}
   node-gyp-build@4.8.4: {}
 
 
+  object-assign@4.1.1: {}
+
   object-inspect@1.13.4: {}
   object-inspect@1.13.4: {}
 
 
   on-finished@2.4.1:
   on-finished@2.4.1:
@@ -3885,6 +3998,36 @@ snapshots:
       mrmime: 2.0.1
       mrmime: 2.0.1
       totalist: 3.0.1
       totalist: 3.0.1
 
 
+  socket.io-adapter@2.5.5:
+    dependencies:
+      debug: 4.3.7
+      ws: 8.17.1
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
+  socket.io-parser@4.2.4:
+    dependencies:
+      '@socket.io/component-emitter': 3.1.2
+      debug: 4.3.7
+    transitivePeerDependencies:
+      - supports-color
+
+  socket.io@4.8.1:
+    dependencies:
+      accepts: 1.3.8
+      base64id: 2.0.0
+      cors: 2.8.5
+      debug: 4.3.7
+      engine.io: 6.6.4
+      socket.io-adapter: 2.5.5
+      socket.io-parser: 4.2.4
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
   source-map-js@1.2.1: {}
   source-map-js@1.2.1: {}
 
 
   sql-highlight@6.1.0: {}
   sql-highlight@6.1.0: {}
@@ -4049,6 +4192,8 @@ snapshots:
 
 
   wrappy@1.0.2: {}
   wrappy@1.0.2: {}
 
 
+  ws@8.17.1: {}
+
   y18n@5.0.8: {}
   y18n@5.0.8: {}
 
 
   yallist@5.0.0: {}
   yallist@5.0.0: {}

+ 68 - 8
server.js

@@ -4,17 +4,19 @@ import { Transform } from 'node:stream';
 import { Readable } from 'node:stream';
 import { Readable } from 'node:stream';
 import { pipeline } from 'node:stream/promises';
 import { pipeline } from 'node:stream/promises';
 import { Hono } from 'hono';
 import { Hono } from 'hono';
-import { logger } from 'hono/logger'; // 引入 Hono 日志中间件
+import { logger } from 'hono/logger';
 import { createServer as createNodeServer } from 'node:http';
 import { createServer as createNodeServer } from 'node:http';
 import process from 'node:process';
 import process from 'node:process';
-import { createAdaptorServer } from '@hono/node-server'  
+import { createAdaptorServer } from '@hono/node-server'
+// 新增:导入 Socket.IO
+import { Server } from 'socket.io';
 
 
 
 
 // 创建 Hono 应用
 // 创建 Hono 应用
 const app = new Hono();// API路由
 const app = new Hono();// API路由
 
 
 // 全局使用 Hono 日志中间件(记录所有请求)
 // 全局使用 Hono 日志中间件(记录所有请求)
-app.use('*', logger()); // 新增:添加请求日志中间件
+app.use('*', logger());
 
 
 // 常量定义
 // 常量定义
 const isProduction = process.env.NODE_ENV === 'production';
 const isProduction = process.env.NODE_ENV === 'production';
@@ -42,6 +44,20 @@ const parentServer = createAdaptorServer({
 })  
 })  
 console.log('服务器实例创建成功');
 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 compressionMiddleware;
 let sirvMiddleware;
 let sirvMiddleware;
@@ -67,13 +83,12 @@ if (!isProduction) {
       },
       },
       hmr: {  
       hmr: {  
         port: 8081,
         port: 8081,
-        clientPort: 443, // 或其他可用端口  
+        clientPort: 443,
         path: 'vite-hmr'
         path: 'vite-hmr'
       },
       },
       proxy: {
       proxy: {
         '/vite-hmr': {
         '/vite-hmr': {
           target: 'ws://localhost:8081',
           target: 'ws://localhost:8081',
-          // 代理 WebSocket
           ws: true,
           ws: true,
         },
         },
       },
       },
@@ -97,6 +112,45 @@ if (!isProduction) {
   console.log('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) => {
 app.use(async (c, next) => {
   try {
   try {
@@ -310,6 +364,7 @@ parentServer.listen(port, () => {
   console.log('========================================');
   console.log('========================================');
   console.log(`服务器已成功启动!`);
   console.log(`服务器已成功启动!`);
   console.log(`访问地址: http://localhost:${port}`);
   console.log(`访问地址: http://localhost:${port}`);
+  console.log(`Socket.IO 路径: ${base}socket.io`);
   console.log(`环境: ${isProduction ? '生产环境' : '开发环境'}`);
   console.log(`环境: ${isProduction ? '生产环境' : '开发环境'}`);
   console.log('========================================');
   console.log('========================================');
 })
 })
@@ -318,18 +373,23 @@ parentServer.listen(port, () => {
 const shutdownServer = async () => {
 const shutdownServer = async () => {
   console.log('正在关闭服务器...');
   console.log('正在关闭服务器...');
   
   
-  // 1. 先关闭 Vite 开发服务器(包括 HMR 服务)
+  // 新增:关闭 Socket.IO 服务器
+  console.log('正在关闭 Socket.IO 服务器...');
+  await io.close();
+  console.log('Socket.IO 服务器已关闭');
+  
+  // 关闭 Vite 开发服务器(包括 HMR 服务)
   if (!isProduction && vite) {
   if (!isProduction && vite) {
     console.log('正在关闭 Vite 开发服务器(包括 HMR 服务)...');
     console.log('正在关闭 Vite 开发服务器(包括 HMR 服务)...');
     try {
     try {
-      await vite.close(); // 关闭 Vite 实例,会自动终止 HMR 服务(8081 端口)
+      await vite.close();
       console.log('Vite 开发服务器已关闭');
       console.log('Vite 开发服务器已关闭');
     } catch (err) {
     } catch (err) {
       console.error('关闭 Vite 服务器时出错:', err);
       console.error('关闭 Vite 服务器时出错:', err);
     }
     }
   }
   }
   
   
-  // 2. 关闭主服务器
+  // 关闭主服务器
   parentServer.close((err) => {
   parentServer.close((err) => {
     if (err) {
     if (err) {
       console.error('关闭主服务器时出错:', err);
       console.error('关闭主服务器时出错:', err);

+ 80 - 0
src/server/socket.ts

@@ -0,0 +1,80 @@
+// src/server/socket.ts
+import { Server, Socket } from 'socket.io';
+
+/**
+ * Socket.IO 认证中间件
+ * 可以在这里实现基于 token 或其他方式的认证
+ */
+export const authMiddleware = async (socket: Socket, next: (err?: Error) => void) => {
+  try {
+    // 从握手信息中获取认证数据
+    const token = socket.handshake.auth.token;
+    
+    if (!token) {
+      return next(new Error('未提供认证令牌'));
+    }
+    
+    // 实现你的认证逻辑
+    // 例如: 验证 JWT 令牌
+    // const user = await verifyToken(token);
+    
+    // 如果认证成功,可以将用户信息附加到 socket 对象
+    // socket.data.user = user;
+    
+    console.log(`客户端认证成功: ${socket.id}`);
+    next();
+  } catch (err) {
+    console.error(`客户端认证失败: ${socket.id}`, err);
+    next(new Error('认证失败'));
+  }
+};
+
+/**
+ * Socket.IO 路由处理
+ * 在这里定义所有的 Socket.IO 事件处理逻辑
+ */
+export default (io: Server) => {
+  console.log('注册 Socket.IO 事件处理');
+  
+  // 全局命名空间事件
+  io.on('connection', (socket) => {
+    console.log(`客户端连接: ${socket.id}`);
+    
+    // 示例: 响应客户端的 ping 事件
+    socket.on('ping', (callback) => {
+      callback({ timestamp: Date.now() });
+    });
+    
+    // 示例: 处理自定义事件
+    socket.on('chat_message', (data) => {
+      console.log(`收到消息: ${JSON.stringify(data)}`);
+      // 广播消息给其他用户
+      socket.broadcast.emit('new_message', {
+        ...data,
+        from: socket.id,
+        timestamp: Date.now()
+      });
+    });
+    
+    // 断开连接事件
+    socket.on('disconnect', (reason) => {
+      console.log(`客户端断开连接: ${socket.id}, 原因: ${reason}`);
+    });
+  });
+  
+  // 示例: 创建命名空间
+  const adminNamespace = io.of('/admin');
+  
+  adminNamespace.on('connection', (socket) => {
+    console.log(`管理员连接: ${socket.id}`);
+    
+    // 管理员专属事件
+    socket.on('dashboard_stats', (callback) => {
+      // 模拟返回统计数据
+      callback({
+        onlineUsers: io.engine.clientsCount,
+        rooms: io.sockets.adapter.rooms.size
+      });
+    });
+  });
+};