feat(server): add HTTP file-based routing support

- Add file-based HTTP routing with httpDir and httpPrefix config options
- Create defineHttp<TBody>() helper for type-safe route definitions
- Support dynamic routes with [param].ts file naming convention
- Add CORS support for cross-origin requests
- Allow merging file routes with inline http config
- RPC server now supports attaching to existing HTTP server via server option
- Add comprehensive documentation for HTTP routing
This commit is contained in:
yhh
2025-12-31 09:52:45 +08:00
parent 12051d987f
commit d3e489aad3
19 changed files with 1226 additions and 37 deletions

View File

@@ -1,5 +1,49 @@
# @esengine/rpc
## 1.1.2
### Patch Changes
- feat(server): add HTTP file-based routing support
New feature that allows organizing HTTP routes in separate files, similar to API and message handlers:
```typescript
// src/http/login.ts
import { defineHttp } from '@esengine/server';
export default defineHttp<{ username: string; password: string }>({
method: 'POST',
handler(req, res) {
const { username, password } = req.body;
// ... authentication logic
res.json({ token: '...', userId: '...' });
}
});
```
Server configuration:
```typescript
const server = await createServer({
port: 8080,
httpDir: 'src/http', // HTTP routes directory
httpPrefix: '/api', // Route prefix
cors: true
});
```
File naming convention:
- `login.ts` → POST /api/login
- `users/profile.ts` → POST /api/users/profile
- `users/[id].ts` → POST /api/users/:id (dynamic routes)
- Set `method: 'GET'` in defineHttp for GET requests
Also includes:
- `defineHttp<TBody>()` helper function for type-safe route definitions
- Support for merging file routes with inline `http` config
- RPC server now supports attaching to existing HTTP server via `server` option
## 1.1.1
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@esengine/rpc",
"version": "1.1.1",
"version": "1.1.2",
"description": "Elegant type-safe RPC library for ESEngine",
"type": "module",
"main": "./dist/index.js",

View File

@@ -4,6 +4,7 @@
*/
import { WebSocketServer, WebSocket } from 'ws'
import type { Server as HttpServer } from 'node:http'
import type {
ProtocolDef,
ApiNames,
@@ -66,10 +67,19 @@ type MsgHandlers<P extends ProtocolDef, TConnData> = {
*/
export interface ServeOptions<P extends ProtocolDef, TConnData = unknown> {
/**
* @zh 监听端口
* @en Listen port
* @zh 监听端口(与 server 二选一)
* @en Listen port (mutually exclusive with server)
*/
port: number
port?: number
/**
* @zh 已有的 HTTP 服务器(与 port 二选一)
* @en Existing HTTP server (mutually exclusive with port)
*
* @zh 使用此选项可以在同一端口同时支持 HTTP 和 WebSocket
* @en Use this option to support both HTTP and WebSocket on the same port
*/
server?: HttpServer
/**
* @zh API 处理器
@@ -280,7 +290,16 @@ export function serve<P extends ProtocolDef, TConnData = unknown>(
async start() {
return new Promise((resolve) => {
wss = new WebSocketServer({ port: options.port })
// 根据配置创建 WebSocketServer
if (options.server) {
// 附加到已有的 HTTP 服务器
wss = new WebSocketServer({ server: options.server })
} else if (options.port) {
// 独立创建
wss = new WebSocketServer({ port: options.port })
} else {
throw new Error('Either port or server must be provided')
}
wss.on('connection', async (ws, req) => {
const id = String(++connIdCounter)
@@ -318,10 +337,16 @@ export function serve<P extends ProtocolDef, TConnData = unknown>(
await options.onConnect?.(conn)
})
wss.on('listening', () => {
options.onStart?.(options.port)
// 如果使用已有的 HTTP 服务器WebSocketServer 不会触发 listening 事件
if (options.server) {
options.onStart?.(0) // 端口由 HTTP 服务器管理
resolve()
})
} else {
wss.on('listening', () => {
options.onStart?.(options.port!)
resolve()
})
}
})
},