Skip to content

Commit 9d3ed95

Browse files
committed
feat: add RPC middleware and reconnect strategies
1 parent 9e78e3c commit 9d3ed95

10 files changed

Lines changed: 966 additions & 30 deletions

File tree

packages/core/src/bridge/initializer.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,6 @@ export class FrpBridgeInitializer {
201201
result.rpcClient = new RpcClient({
202202
url: urlWithToken,
203203
nodeId: rpcOptions.clientNodeId,
204-
reconnectInterval: rpcOptions.clientReconnectInterval,
205204
getRegisterPayload: rpcOptions.getRegisterPayload ?? (async () => {
206205
throw new Error('rpc getRegisterPayload is required in client mode')
207206
}),

packages/core/src/rpc/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
export * from './message-types'
2+
export * from './middleware'
3+
export * from './reconnect-strategy'
14
export { RpcClient, type RpcClientOptions } from './rpc-client'
25
export { RpcServer, type RpcServerOptions } from './rpc-server'
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* RPC 消息类型定义
3+
* 提供类型安全的消息结构和类型守卫
4+
*/
5+
6+
/**
7+
* RPC 消息类型枚举
8+
*/
9+
export enum RpcMessageType {
10+
REGISTER = 'register',
11+
COMMAND = 'command',
12+
RESPONSE = 'response',
13+
PING = 'ping',
14+
PONG = 'pong'
15+
}
16+
17+
/**
18+
* 节点注册消息
19+
*/
20+
export interface RegisterMessage {
21+
type: RpcMessageType.REGISTER
22+
nodeId: string
23+
payload: Record<string, unknown>
24+
}
25+
26+
/**
27+
* RPC 请求
28+
*/
29+
export interface RpcRequest {
30+
id: string
31+
method: string
32+
params: Record<string, unknown>
33+
timeout?: number
34+
}
35+
36+
/**
37+
* RPC 响应状态
38+
*/
39+
export type RpcResponseStatus = 'success' | 'error'
40+
41+
/**
42+
* RPC 响应
43+
*/
44+
export interface RpcResponse {
45+
id: string
46+
status: RpcResponseStatus
47+
result?: unknown
48+
error?: { code: string, message: string }
49+
}
50+
51+
/**
52+
* Ping 消息
53+
*/
54+
export interface PingMessage {
55+
type: RpcMessageType.PING
56+
timestamp: number
57+
}
58+
59+
/**
60+
* Pong 消息
61+
*/
62+
export interface PongMessage {
63+
type: RpcMessageType.PONG
64+
timestamp: number
65+
}
66+
67+
/**
68+
* 所有 RPC 消息类型
69+
*/
70+
export type RpcMessage = RegisterMessage | RpcRequest | RpcResponse | PingMessage | PongMessage
71+
72+
/**
73+
* 类型守卫:检查是否为注册消息
74+
*/
75+
export function isRegisterMessage(msg: unknown): msg is RegisterMessage {
76+
return typeof msg === 'object' && msg !== null && (msg as any).type === RpcMessageType.REGISTER
77+
}
78+
79+
/**
80+
* 类型守卫:检查是否为 RPC 请求
81+
*/
82+
export function isRpcRequest(msg: unknown): msg is RpcRequest {
83+
return typeof msg === 'object' && msg !== null
84+
&& typeof (msg as any).id === 'string'
85+
&& typeof (msg as any).method === 'string'
86+
}
87+
88+
/**
89+
* 类型守卫:检查是否为 RPC 响应
90+
*/
91+
export function isRpcResponse(msg: unknown): msg is RpcResponse {
92+
return typeof msg === 'object' && msg !== null
93+
&& typeof (msg as any).id === 'string'
94+
&& typeof (msg as any).status === 'string'
95+
}
96+
97+
/**
98+
* 类型守卫:检查是否为 Ping 消息
99+
*/
100+
export function isPingMessage(msg: unknown): msg is PingMessage {
101+
return typeof msg === 'object' && msg !== null
102+
&& (msg as any).type === RpcMessageType.PING
103+
}
104+
105+
/**
106+
* 类型守卫:检查是否为 Pong 消息
107+
*/
108+
export function isPongMessage(msg: unknown): msg is PongMessage {
109+
return typeof msg === 'object' && msg !== null
110+
&& (msg as any).type === RpcMessageType.PONG
111+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* RPC 中间件系统
3+
* 提供可扩展的请求处理管道
4+
*/
5+
6+
import type { RpcRequest, RpcResponse } from './message-types'
7+
8+
/**
9+
* 中间件上下文
10+
*/
11+
export interface MiddlewareContext {
12+
request: RpcRequest
13+
response: Partial<RpcResponse>
14+
startTime: number
15+
}
16+
17+
/**
18+
* 中间件函数类型
19+
*/
20+
export type MiddlewareFn = (
21+
context: MiddlewareContext,
22+
next: () => Promise<void>
23+
) => Promise<void>
24+
25+
/**
26+
* 中间件选项
27+
*/
28+
export interface MiddlewareOptions {
29+
preHooks?: MiddlewareFn[]
30+
postHooks?: MiddlewareFn[]
31+
}
32+
33+
/**
34+
* 日志中间件
35+
*/
36+
export function loggingMiddleware(
37+
logger?: {
38+
info?: (msg: string, data?: unknown) => void
39+
warn?: (msg: string, data?: unknown) => void
40+
error?: (msg: string, data?: unknown) => void
41+
}
42+
): MiddlewareFn {
43+
return async (context, next) => {
44+
const { request } = context
45+
logger?.info?.('RPC request', { method: request.method, params: request.params })
46+
47+
try {
48+
await next()
49+
logger?.info?.('RPC response', {
50+
method: request.method,
51+
duration: Date.now() - context.startTime,
52+
status: context.response.status
53+
})
54+
}
55+
catch (error) {
56+
logger?.error?.('RPC error', {
57+
method: request.method,
58+
error: error instanceof Error ? error.message : 'Unknown error'
59+
})
60+
throw error
61+
}
62+
}
63+
}
64+
65+
/**
66+
* 认证中间件
67+
*/
68+
export function authMiddleware(
69+
validateToken: (token: string | undefined) => boolean | Promise<boolean>
70+
): MiddlewareFn {
71+
return async (context, next) => {
72+
const { request, response } = context
73+
const token = request.params.token as string | undefined
74+
75+
const isValid = await validateToken(token)
76+
if (!isValid) {
77+
response.status = 'error'
78+
response.error = { code: 'UNAUTHORIZED', message: 'Invalid or missing token' }
79+
return
80+
}
81+
82+
await next()
83+
}
84+
}
85+
86+
/**
87+
* 超时中间件
88+
*/
89+
export function timeoutMiddleware(timeoutMs: number): MiddlewareFn {
90+
return async (context, next) => {
91+
const { request } = context
92+
93+
// 使用请求指定的超时或默认超时
94+
const timeout = request.timeout ?? timeoutMs
95+
96+
const timeoutPromise = new Promise<void>((_, reject) => {
97+
setTimeout(() => {
98+
reject(new Error(`RPC timeout: ${request.method}`))
99+
}, timeout)
100+
})
101+
102+
try {
103+
await Promise.race([next(), timeoutPromise])
104+
}
105+
catch (error) {
106+
context.response.status = 'error'
107+
context.response.error = {
108+
code: 'TIMEOUT',
109+
message: error instanceof Error ? error.message : 'Request timeout'
110+
}
111+
throw error
112+
}
113+
}
114+
}
115+
116+
/**
117+
* 错误处理中间件
118+
*/
119+
export function errorHandlerMiddleware(
120+
logger?: { error?: (msg: string, data?: unknown) => void }
121+
): MiddlewareFn {
122+
return async (context, next) => {
123+
try {
124+
await next()
125+
}
126+
catch (error) {
127+
context.response.status = 'error'
128+
context.response.error = {
129+
code: error instanceof Error ? error.name : 'UNKNOWN_ERROR',
130+
message: error instanceof Error ? error.message : 'Unknown error'
131+
}
132+
logger?.error?.('RPC middleware error', error)
133+
}
134+
}
135+
}
136+
137+
/**
138+
* 中间件管道
139+
*/
140+
export class MiddlewarePipeline {
141+
private middlewares: MiddlewareFn[] = []
142+
143+
/**
144+
* 添加中间件
145+
*/
146+
use(middleware: MiddlewareFn): this {
147+
this.middlewares.push(middleware)
148+
return this
149+
}
150+
151+
/**
152+
* 执行中间件管道
153+
*/
154+
async execute(
155+
request: RpcRequest,
156+
handler: () => Promise<unknown>
157+
): Promise<Partial<RpcResponse>> {
158+
const context: MiddlewareContext = {
159+
request,
160+
response: { id: request.id },
161+
startTime: Date.now()
162+
}
163+
164+
// 构建中间件链
165+
let next: () => Promise<void> = async () => this.executeHandler(context, handler)
166+
for (let i = this.middlewares.length - 1; i >= 0; i--) {
167+
const middleware = this.middlewares[i]
168+
const prevNext = next
169+
next = async () => middleware(context, prevNext)
170+
}
171+
172+
await next()
173+
174+
return context.response
175+
}
176+
177+
/**
178+
* 执行处理器
179+
*/
180+
private async executeHandler(
181+
context: MiddlewareContext,
182+
handler: () => Promise<unknown>
183+
): Promise<void> {
184+
const result = await handler()
185+
context.response.result = result
186+
context.response.status = 'success'
187+
}
188+
189+
/**
190+
* 清空中间件
191+
*/
192+
clear(): void {
193+
this.middlewares = []
194+
}
195+
}

0 commit comments

Comments
 (0)