@h-ai/serv 0.1.0-alpha.36 → 0.1.0-alpha.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  - 默认 feature procedures:`createIamProcedures()`、`createStorageProcedures()`、`createAiProcedures()`
11
11
  - 内置安全响应头、健康检查、可选 OpenAPI JSON、可选 Scalar 文档页、可选内部 RPC endpoint
12
12
  - 可选传输加密:`serv.createApp({ transport: { crypto } })` 自动挂载密钥协商与请求/响应加解密
13
- - 可选语音入口:`serv.createApp({ audio: { ai, verifyTicket, authorize } })` 挂载统一语音 WebSocket;一次性 ticket 负责身份验证,授权回调负责服务端确认操作、模型、音色、配额与并发占用
13
+ - 可选语音入口:`serv.createApp({ audio: { ai, verifyTicket, authorize } })` 挂载统一语音 WebSocket;连接建立即用一次性 ticket 鉴权(返回 `{ session, grant? }`,独立预鉴权超时),每帧运行时 Zod 校验 + 严格状态机,输入/积压/发送缓冲背压与级联取消;授权回调负责服务端确认操作、模型、音色、配额与并发占用
14
14
 
15
15
  > 生命周期说明:`@h-ai/serv` 本身是无状态 HTTP App 装配器,不需要 `init()` / `close()`;请在创建 app 前初始化 `iam` / `storage` / `ai` / `crypto` 等依赖,并在 `serv.listen(..., { onClose })` 中反向释放它们。`serv.createApp()` 遇到启动期配置错误(例如 transport manager 创建失败)会 fail-fast 抛出,便于进程启动阶段暴露问题。
16
16
 
@@ -70,7 +70,7 @@ export default { fetch: handler }
70
70
 
71
71
  ### Audio WebSocket:ticket 与服务端授权
72
72
 
73
- 语音入口必须使用短期、一次性 ticket。应用通过已登录 HTTP endpoint 签发 ticket,并在 `verifyTicket` 中原子消费;框架保存返回的 `ServSession`,再调用 `authorize` 确认付费参数:
73
+ 语音入口必须使用短期、一次性 ticket。应用通过已登录 HTTP endpoint 签发 ticket(推荐 `iam.ticket.issue`),并在 `verifyTicket` 中原子消费(推荐 `iam.ticket.consume`);连接建立即完成鉴权(独立预鉴权超时),未鉴权不处理任何业务帧。框架保存返回的 `ServSession` 与可选 `grant`,再调用 `authorize` 确认付费参数:
74
74
 
75
75
  ```ts
76
76
  const app = serv.createApp({
@@ -79,18 +79,20 @@ const app = serv.createApp({
79
79
  iam,
80
80
  audio: {
81
81
  ai,
82
- verifyTicket: consumeAudioTicket,
83
- authorize: async (session, request) => authorizeAudioRequest(session, request),
82
+ verifyTicket: consumeAudioTicket, // → HaiResult<{ session, grant? }>
83
+ authorize: async (session, request, grant) => authorizeAudioRequest(session, request, grant),
84
84
  onSessionEnd: async (session, request) => releaseAudioConcurrency(session.userId, request.operation),
85
85
  },
86
86
  })
87
87
  ```
88
88
 
89
- - `verifyTicket(ticket)` 必须校验用途、过期时间并保证同一 ticket 只能成功一次,返回 `HaiResult<ServSession>`。
90
- - `authorize(session, request)` 适合检查 IAM 权限、模型/音色白名单、Persona、调用配额并占用并发名额;返回的 `AuthorizedAudioRequest` 才会传给 `ai.audio`。
91
- - 未提供 `authorize` 时,框架会忽略客户端的 `model`、`voice` 与 `instruction`,只使用服务端默认模型。
89
+ - `verifyTicket(ticket)` 必须校验用途、过期时间并保证同一 ticket 只能成功一次,返回 `HaiResult<AudioTicketVerification>`(`{ session, grant? }`);`grant` 承载票据绑定的操作/模型/会话。
90
+ - `authorize(session, request, grant)` 适合检查 IAM 权限、模型/音色白名单、Persona、调用配额并占用并发名额,并用 `grant` 交叉校验 `start` 未越权;返回的 `AuthorizedAudioRequest` 才会传给 `ai.audio`。
91
+ - 未提供 `authorize` 时,框架会忽略客户端的 `model`、`voice` 与 `instruction`,只使用服务端默认模型(若票据 `grant.model` 存在则采用之),并拒绝与 `grant.operation` 不符的 `start`。
92
+ - 协议边界对每一帧做运行时 Zod 校验并按严格状态机接受(识别只收音频、合成只收文本、`start`/`done` 各一次、`segmentId` 会话内唯一);输入队列、消息积压与发送缓冲均有上限,超限或取消时以领域错误关闭并级联中止上游。
93
+ - 可调参数:`preAuthTimeoutMs`(默认 5000)、`maxPendingMessages`(默认 256)、`maxSendBufferBytes`(默认 8 MiB)、`maxMessageBytes` / `maxBufferedBytes` / `maxTextBytes` / `maxSessionMs`。
92
94
  - `onSessionEnd` 在成功、失败或断连后至多调用一次,供应用释放并发占用。
93
- - 浏览器 `createAIClient({ audio: { url, getTicket } })` 只把一次性 `ticket` 放入 WebSocket URL;禁止传普通 IAM access token。
95
+ - 浏览器 `createAIClient({ audio: { url, getTicket } })` 只把一次性 `ticket` 放入 WebSocket URL;`getTicket(request)` 接收本次操作摘要(`operation` / `model`),可签发与本次操作严格绑定的票据;禁止传普通 IAM access token。
94
96
 
95
97
  ### 传输加密(与 `@h-ai/crypto` 统一)
96
98
 
@@ -239,11 +239,11 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
239
239
  query: zod.ZodString;
240
240
  topK: zod.ZodOptional<zod.ZodNumber>;
241
241
  types: zod.ZodOptional<zod.ZodArray<zod.ZodEnum<{
242
+ instruction: "instruction";
242
243
  fact: "fact";
243
244
  preference: "preference";
244
245
  event: "event";
245
246
  entity: "entity";
246
- instruction: "instruction";
247
247
  }>>>;
248
248
  minImportance: zod.ZodOptional<zod.ZodNumber>;
249
249
  objectId: zod.ZodOptional<zod.ZodString>;
@@ -254,11 +254,11 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
254
254
  id: zod.ZodString;
255
255
  content: zod.ZodString;
256
256
  type: zod.ZodEnum<{
257
+ instruction: "instruction";
257
258
  fact: "fact";
258
259
  preference: "preference";
259
260
  event: "event";
260
261
  entity: "entity";
261
- instruction: "instruction";
262
262
  }>;
263
263
  importance: zod.ZodNumber;
264
264
  objectId: zod.ZodOptional<zod.ZodString>;
@@ -283,11 +283,11 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
283
283
  }, zod_v4_core.$strip>], "success">, _orpc_contract.MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
284
284
  list: _orpc_server.Procedure<_orpc_server.MergedInitialContext<ServContext & Record<never, never>, ServContext & Record<never, never>, ServContext>, ServContext, zod.ZodObject<{
285
285
  types: zod.ZodOptional<zod.ZodArray<zod.ZodEnum<{
286
+ instruction: "instruction";
286
287
  fact: "fact";
287
288
  preference: "preference";
288
289
  event: "event";
289
290
  entity: "entity";
290
- instruction: "instruction";
291
291
  }>>>;
292
292
  objectId: zod.ZodOptional<zod.ZodString>;
293
293
  offset: zod.ZodOptional<zod.ZodNumber>;
@@ -299,11 +299,11 @@ declare function createAiProcedures(deps: AiProcedureDeps): {
299
299
  id: zod.ZodString;
300
300
  content: zod.ZodString;
301
301
  type: zod.ZodEnum<{
302
+ instruction: "instruction";
302
303
  fact: "fact";
303
304
  preference: "preference";
304
305
  event: "event";
305
306
  entity: "entity";
306
- instruction: "instruction";
307
307
  }>;
308
308
  importance: zod.ZodNumber;
309
309
  objectId: zod.ZodOptional<zod.ZodString>;
@@ -144,6 +144,8 @@ declare function createIamProcedures(deps: IamProcedureDeps): {
144
144
  createdAt: zod.ZodCoercedDate<unknown>;
145
145
  updatedAt: zod.ZodCoercedDate<unknown>;
146
146
  metadata: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodUnknown>>;
147
+ roles: zod.ZodArray<zod.ZodString>;
148
+ permissions: zod.ZodArray<zod.ZodString>;
147
149
  }, zod_v4_core.$strip>;
148
150
  }, zod_v4_core.$strip>, zod.ZodObject<{
149
151
  success: zod.ZodLiteral<false>;
@@ -20,11 +20,15 @@ function createIamProcedures(deps) {
20
20
  return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM("serv_errorUnauthorized", { locale: context.locale })));
21
21
  return iam.auth.logout(token);
22
22
  })),
23
- currentUser: p.auth.currentUser.handler(requireAuth(({ context }) => {
23
+ currentUser: p.auth.currentUser.handler(requireAuth(async ({ context }) => {
24
24
  const token = context.accessToken;
25
25
  if (!token)
26
26
  return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM("serv_errorUnauthorized", { locale: context.locale })));
27
- return iam.user.getCurrentUser(token);
27
+ return mapHaiResult(await iam.user.getCurrentUser(token), (user) => ({
28
+ ...user,
29
+ roles: context.session?.roles ?? [],
30
+ permissions: context.session?.permissions ?? []
31
+ }));
28
32
  })),
29
33
  refresh: p.auth.refresh.handler(async ({ input }) => {
30
34
  return mapHaiResult(await iam.session.refresh(input.refreshToken), (tokens) => ({ tokens }));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/features/serv-feature-iam.ts"],"names":[],"mappings":"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","file":"iam.js","sourcesContent":["/**\n * @h-ai/serv — IAM 默认 procedures\n *\n * 基于 `@h-ai/iam` 提供开箱即用的 IAM procedures 实现:认证、用户管理、角色权限 CRUD。\n * 通过 `createIamProcedures(deps)` 组装后直接挂载到 oRPC router。\n * @module features/serv-feature-iam\n */\n\nimport type {\n IamAdminCreateUserInput,\n IamAdminResetPasswordInput,\n IamAdminUpdateUserInput,\n IamChangePasswordInput,\n IamCreatePermissionInput,\n IamCreateRoleInput,\n IamListPermissionsInput,\n IamListUsersInput,\n IamLoginInput,\n IamLogoutInput,\n IamOtpLoginInput,\n IamRegisterInput,\n IamSendOtpInput,\n IamUpdateCurrentUserInput,\n IamUpdateRoleInput,\n IamUserIdInput,\n} from '@h-ai/api-contract'\nimport type { HaiResult, PaginatedResult } from '@h-ai/core'\nimport type { IamFunctions, Permission, Role, TokenPair, User } from '@h-ai/iam'\nimport type { ServContext } from '../serv-context.js'\nimport { apiContract } from '@h-ai/api-contract'\nimport { core, err, HaiCommonError, ok } from '@h-ai/core'\nimport { implement } from '@orpc/server'\nimport { requireAuth } from '../pipelines/serv-pipeline-require-auth.js'\nimport { requirePermission } from '../pipelines/serv-pipeline-require-permission.js'\nimport { servM } from '../serv-i18n.js'\nimport { mapHaiResult } from './serv-feature-helpers.js'\n\nconst iamContract = apiContract.iam\n\nconst iamFeatureLogger = core.logger.child({ module: 'serv', scope: 'feature-iam' })\n\n/** IAM 默认 procedures 依赖。 */\nexport interface IamProcedureDeps {\n readonly iam: IamFunctions\n}\n\n/** 创建 IAM 默认 procedures。 */\nexport function createIamProcedures(deps: IamProcedureDeps) {\n const p = implement(iamContract).$context<ServContext>()\n const { iam } = deps\n\n return p.router({\n auth: {\n login: p.auth.login.handler(({ input }: { input: IamLoginInput }) => iam.auth.login(input)),\n loginWithOtp: p.auth.loginWithOtp.handler(({ input }: { input: IamOtpLoginInput }) => iam.auth.loginWithOtp(input)),\n logout: p.auth.logout.handler(requireAuth<IamLogoutInput, void>(({ input, context }) => {\n // 不再使用空字符串兜底:requireAuth 已确保 session 存在,但 accessToken 字段可能仍缺失\n // (例如自定义 createContext 未填充);此时显式返回 UNAUTHORIZED 而非把空 token 透传给 IAM。\n const token = input.accessToken ?? context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return iam.auth.logout(token)\n })),\n currentUser: p.auth.currentUser.handler(requireAuth<unknown, User>(({ context }) => {\n const token = context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return iam.user.getCurrentUser(token)\n })),\n refresh: p.auth.refresh.handler(async ({ input }: { input: { refreshToken: string } }) => {\n return mapHaiResult(await iam.session.refresh(input.refreshToken), tokens => ({ tokens }))\n }),\n sendOtp: p.auth.sendOtp.handler(({ input }: { input: IamSendOtpInput }) => iam.auth.sendOtp(input.identifier)),\n register: p.auth.register.handler(({ input }: { input: IamRegisterInput }) => iam.auth.registerAndLogin(input)),\n changePassword: p.auth.changePassword.handler(requireAuth<IamChangePasswordInput, void>(({ input, context }) => {\n const token = context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return iam.user.changeCurrentUserPassword(token, input.oldPassword, input.newPassword)\n })),\n updateCurrentUser: p.auth.updateCurrentUser.handler(requireAuth<IamUpdateCurrentUserInput, User>(({ input, context }) => {\n const token = context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return iam.user.updateCurrentUser(token, input)\n })),\n },\n users: {\n list: p.users.list.handler(requirePermission<IamListUsersInput, PaginatedResult<User>>('iam.users.read', ({ input }) => {\n return iam.user.listUsers(input)\n })),\n get: p.users.get.handler(requirePermission<IamUserIdInput, User | null>('iam.users.read', ({ input }) => {\n return iam.user.getUser(input.id, { include: ['roles'] })\n })),\n create: p.users.create.handler(requirePermission<IamAdminCreateUserInput, User>('iam.users.write', ({ input }) => {\n return createUser(iam, input)\n })),\n update: p.users.update.handler(requirePermission<IamAdminUpdateUserInput, User>('iam.users.write', ({ input }) => {\n return updateUser(iam, input)\n })),\n delete: p.users.delete.handler(requirePermission<IamUserIdInput, void>('iam.users.write', ({ input, context }) => {\n // 防止自删:避免管理员误删自己的账号导致授权锁死。\n if (context.session?.userId === input.id)\n return Promise.resolve(err(HaiCommonError.FORBIDDEN, servM('serv_iamCannotDeleteCurrentUser', { locale: context.locale })))\n return iam.user.deleteUser(input.id)\n })),\n resetPassword: p.users.resetPassword.handler(requirePermission<IamAdminResetPasswordInput, void>('iam.users.write', ({ input }) => {\n return iam.user.adminResetPassword(input.id, input.newPassword)\n })),\n },\n roles: {\n list: p.roles.list.handler(requirePermission<unknown, PaginatedResult<Role>>('iam.roles.read', () => {\n return iam.authz.getAllRoles()\n })),\n get: p.roles.get.handler(requirePermission<IamUserIdInput, Role | null>('iam.roles.read', ({ input }) => {\n return iam.authz.getRole(input.id)\n })),\n create: p.roles.create.handler(requirePermission<IamCreateRoleInput, Role>('iam.roles.write', ({ input }) => {\n return iam.authz.createRole(input)\n })),\n update: p.roles.update.handler(requirePermission<IamUpdateRoleInput, Role>('iam.roles.write', ({ input }) => {\n const { id, ...data } = input\n return iam.authz.updateRole(id, data)\n })),\n delete: p.roles.delete.handler(requirePermission<IamUserIdInput, void>('iam.roles.write', ({ input }) => {\n return iam.authz.deleteRole(input.id)\n })),\n },\n permissions: {\n list: p.permissions.list.handler(requirePermission<IamListPermissionsInput, PaginatedResult<Permission>>('iam.permissions.read', ({ input }) => {\n return iam.authz.getAllPermissions(input)\n })),\n get: p.permissions.get.handler(requirePermission<IamUserIdInput, Permission | null>('iam.permissions.read', ({ input }) => {\n return iam.authz.getPermission(input.id)\n })),\n create: p.permissions.create.handler(requirePermission<IamCreatePermissionInput, Permission>('iam.permissions.write', ({ input }) => {\n return iam.authz.createPermission(input)\n })),\n delete: p.permissions.delete.handler(requirePermission<IamUserIdInput, void>('iam.permissions.write', ({ input }) => {\n return iam.authz.deletePermission(input.id)\n })),\n },\n })\n}\n\nasync function createUser(iam: IamFunctions, input: IamAdminCreateUserInput): Promise<HaiResult<User>> {\n const registerResult = await iam.user.register(input)\n if (!registerResult.success)\n return registerResult\n\n const userId = registerResult.data.user.id\n\n // 任何后续步骤失败都需要回滚刚创建的用户,以避免重复用户名/邮箱错误阅存。\n async function rollback(reason: string): Promise<void> {\n const deleteResult = await iam.user.deleteUser(userId)\n if (!deleteResult.success)\n iamFeatureLogger.error('createUser rollback failed, orphan user may exist', { userId, reason, error: deleteResult.error })\n }\n\n if (input.roleIds?.length) {\n const rolesResult = await iam.authz.syncRoles(userId, input.roleIds)\n if (!rolesResult.success) {\n await rollback('syncRoles failed')\n return rolesResult\n }\n }\n\n if (input.enabled === false) {\n const updateResult = await iam.user.updateUser(userId, { enabled: false })\n if (!updateResult.success) {\n await rollback('initial disable failed')\n return updateResult\n }\n return updateResult\n }\n\n return ok(registerResult.data.user)\n}\n\nasync function updateUser(iam: IamFunctions, input: IamAdminUpdateUserInput): Promise<HaiResult<User>> {\n const { id, roleIds, ...data } = input\n const updateResult = await iam.user.updateUser(id, data)\n if (!updateResult.success)\n return updateResult\n\n if (roleIds) {\n const rolesResult = await iam.authz.syncRoles(id, roleIds)\n if (!rolesResult.success)\n return rolesResult\n }\n\n return updateResult\n}\n\n// re-export 类型以便 IDE 跳转\nexport type { TokenPair }\n"]}
1
+ {"version":3,"sources":["../../src/features/serv-feature-iam.ts"],"names":[],"mappings":"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","file":"iam.js","sourcesContent":["/**\n * @h-ai/serv — IAM 默认 procedures\n *\n * 基于 `@h-ai/iam` 提供开箱即用的 IAM procedures 实现:认证、用户管理、角色权限 CRUD。\n * 通过 `createIamProcedures(deps)` 组装后直接挂载到 oRPC router。\n * @module features/serv-feature-iam\n */\n\nimport type {\n IamAdminCreateUserInput,\n IamAdminResetPasswordInput,\n IamAdminUpdateUserInput,\n IamChangePasswordInput,\n IamCreatePermissionInput,\n IamCreateRoleInput,\n IamCurrentUser,\n IamListPermissionsInput,\n IamListUsersInput,\n IamLoginInput,\n IamLogoutInput,\n IamOtpLoginInput,\n IamRegisterInput,\n IamSendOtpInput,\n IamUpdateCurrentUserInput,\n IamUpdateRoleInput,\n IamUserIdInput,\n} from '@h-ai/api-contract'\nimport type { HaiResult, PaginatedResult } from '@h-ai/core'\nimport type { IamFunctions, Permission, Role, TokenPair, User } from '@h-ai/iam'\nimport type { ServContext } from '../serv-context.js'\nimport { apiContract } from '@h-ai/api-contract'\nimport { core, err, HaiCommonError, ok } from '@h-ai/core'\nimport { implement } from '@orpc/server'\nimport { requireAuth } from '../pipelines/serv-pipeline-require-auth.js'\nimport { requirePermission } from '../pipelines/serv-pipeline-require-permission.js'\nimport { servM } from '../serv-i18n.js'\nimport { mapHaiResult } from './serv-feature-helpers.js'\n\nconst iamContract = apiContract.iam\n\nconst iamFeatureLogger = core.logger.child({ module: 'serv', scope: 'feature-iam' })\n\n/** IAM 默认 procedures 依赖。 */\nexport interface IamProcedureDeps {\n readonly iam: IamFunctions\n}\n\n/** 创建 IAM 默认 procedures。 */\nexport function createIamProcedures(deps: IamProcedureDeps) {\n const p = implement(iamContract).$context<ServContext>()\n const { iam } = deps\n\n return p.router({\n auth: {\n login: p.auth.login.handler(({ input }: { input: IamLoginInput }) => iam.auth.login(input)),\n loginWithOtp: p.auth.loginWithOtp.handler(({ input }: { input: IamOtpLoginInput }) => iam.auth.loginWithOtp(input)),\n logout: p.auth.logout.handler(requireAuth<IamLogoutInput, void>(({ input, context }) => {\n // 不再使用空字符串兜底:requireAuth 已确保 session 存在,但 accessToken 字段可能仍缺失\n // (例如自定义 createContext 未填充);此时显式返回 UNAUTHORIZED 而非把空 token 透传给 IAM。\n const token = input.accessToken ?? context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return iam.auth.logout(token)\n })),\n currentUser: p.auth.currentUser.handler(requireAuth<unknown, IamCurrentUser>(async ({ context }) => {\n const token = context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return mapHaiResult<User, IamCurrentUser>(await iam.user.getCurrentUser(token), user => ({\n ...user,\n roles: context.session?.roles ?? [],\n permissions: context.session?.permissions ?? [],\n }))\n })),\n refresh: p.auth.refresh.handler(async ({ input }: { input: { refreshToken: string } }) => {\n return mapHaiResult(await iam.session.refresh(input.refreshToken), tokens => ({ tokens }))\n }),\n sendOtp: p.auth.sendOtp.handler(({ input }: { input: IamSendOtpInput }) => iam.auth.sendOtp(input.identifier)),\n register: p.auth.register.handler(({ input }: { input: IamRegisterInput }) => iam.auth.registerAndLogin(input)),\n changePassword: p.auth.changePassword.handler(requireAuth<IamChangePasswordInput, void>(({ input, context }) => {\n const token = context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return iam.user.changeCurrentUserPassword(token, input.oldPassword, input.newPassword)\n })),\n updateCurrentUser: p.auth.updateCurrentUser.handler(requireAuth<IamUpdateCurrentUserInput, User>(({ input, context }) => {\n const token = context.accessToken\n if (!token)\n return Promise.resolve(err(HaiCommonError.UNAUTHORIZED, servM('serv_errorUnauthorized', { locale: context.locale })))\n return iam.user.updateCurrentUser(token, input)\n })),\n },\n users: {\n list: p.users.list.handler(requirePermission<IamListUsersInput, PaginatedResult<User>>('iam.users.read', ({ input }) => {\n return iam.user.listUsers(input)\n })),\n get: p.users.get.handler(requirePermission<IamUserIdInput, User | null>('iam.users.read', ({ input }) => {\n return iam.user.getUser(input.id, { include: ['roles'] })\n })),\n create: p.users.create.handler(requirePermission<IamAdminCreateUserInput, User>('iam.users.write', ({ input }) => {\n return createUser(iam, input)\n })),\n update: p.users.update.handler(requirePermission<IamAdminUpdateUserInput, User>('iam.users.write', ({ input }) => {\n return updateUser(iam, input)\n })),\n delete: p.users.delete.handler(requirePermission<IamUserIdInput, void>('iam.users.write', ({ input, context }) => {\n // 防止自删:避免管理员误删自己的账号导致授权锁死。\n if (context.session?.userId === input.id)\n return Promise.resolve(err(HaiCommonError.FORBIDDEN, servM('serv_iamCannotDeleteCurrentUser', { locale: context.locale })))\n return iam.user.deleteUser(input.id)\n })),\n resetPassword: p.users.resetPassword.handler(requirePermission<IamAdminResetPasswordInput, void>('iam.users.write', ({ input }) => {\n return iam.user.adminResetPassword(input.id, input.newPassword)\n })),\n },\n roles: {\n list: p.roles.list.handler(requirePermission<unknown, PaginatedResult<Role>>('iam.roles.read', () => {\n return iam.authz.getAllRoles()\n })),\n get: p.roles.get.handler(requirePermission<IamUserIdInput, Role | null>('iam.roles.read', ({ input }) => {\n return iam.authz.getRole(input.id)\n })),\n create: p.roles.create.handler(requirePermission<IamCreateRoleInput, Role>('iam.roles.write', ({ input }) => {\n return iam.authz.createRole(input)\n })),\n update: p.roles.update.handler(requirePermission<IamUpdateRoleInput, Role>('iam.roles.write', ({ input }) => {\n const { id, ...data } = input\n return iam.authz.updateRole(id, data)\n })),\n delete: p.roles.delete.handler(requirePermission<IamUserIdInput, void>('iam.roles.write', ({ input }) => {\n return iam.authz.deleteRole(input.id)\n })),\n },\n permissions: {\n list: p.permissions.list.handler(requirePermission<IamListPermissionsInput, PaginatedResult<Permission>>('iam.permissions.read', ({ input }) => {\n return iam.authz.getAllPermissions(input)\n })),\n get: p.permissions.get.handler(requirePermission<IamUserIdInput, Permission | null>('iam.permissions.read', ({ input }) => {\n return iam.authz.getPermission(input.id)\n })),\n create: p.permissions.create.handler(requirePermission<IamCreatePermissionInput, Permission>('iam.permissions.write', ({ input }) => {\n return iam.authz.createPermission(input)\n })),\n delete: p.permissions.delete.handler(requirePermission<IamUserIdInput, void>('iam.permissions.write', ({ input }) => {\n return iam.authz.deletePermission(input.id)\n })),\n },\n })\n}\n\nasync function createUser(iam: IamFunctions, input: IamAdminCreateUserInput): Promise<HaiResult<User>> {\n const registerResult = await iam.user.register(input)\n if (!registerResult.success)\n return registerResult\n\n const userId = registerResult.data.user.id\n\n // 任何后续步骤失败都需要回滚刚创建的用户,以避免重复用户名/邮箱错误阅存。\n async function rollback(reason: string): Promise<void> {\n const deleteResult = await iam.user.deleteUser(userId)\n if (!deleteResult.success)\n iamFeatureLogger.error('createUser rollback failed, orphan user may exist', { userId, reason, error: deleteResult.error })\n }\n\n if (input.roleIds?.length) {\n const rolesResult = await iam.authz.syncRoles(userId, input.roleIds)\n if (!rolesResult.success) {\n await rollback('syncRoles failed')\n return rolesResult\n }\n }\n\n if (input.enabled === false) {\n const updateResult = await iam.user.updateUser(userId, { enabled: false })\n if (!updateResult.success) {\n await rollback('initial disable failed')\n return updateResult\n }\n return updateResult\n }\n\n return ok(registerResult.data.user)\n}\n\nasync function updateUser(iam: IamFunctions, input: IamAdminUpdateUserInput): Promise<HaiResult<User>> {\n const { id, roleIds, ...data } = input\n const updateResult = await iam.user.updateUser(id, data)\n if (!updateResult.success)\n return updateResult\n\n if (roleIds) {\n const rolesResult = await iam.authz.syncRoles(id, roleIds)\n if (!rolesResult.success)\n return rolesResult\n }\n\n return updateResult\n}\n\n// re-export 类型以便 IDE 跳转\nexport type { TokenPair }\n"]}
package/dist/index.d.ts CHANGED
@@ -17,6 +17,11 @@ import { AddressInfo } from 'node:net';
17
17
  * 基于 `@h-ai/ai` 的 `ai.audio` 提供开箱即用的语音 WebSocket 端点,同时支持非实时 / 实时
18
18
  * 的语音识别与语音合成。传输细节(WebSocket 帧、二进制/JSON 编解码)在此内部完成,
19
19
  * 客户端只使用统一的领域协议,不接触任何厂商原生事件。
20
+ *
21
+ * 安全与健壮性:
22
+ * - 连接建立后立即用一次性 ticket 完成鉴权(独立预鉴权超时),未鉴权不处理任何业务帧;
23
+ * - 每一帧经运行时 Zod 校验,按严格状态机接受(识别只收音频、合成只收文本、start/done 各一次);
24
+ * - 输入队列、消息积压、发送缓冲均有上限,超限即以领域错误关闭,取消时级联中止。
20
25
  * @module features/serv-feature-audio
21
26
  */
22
27
 
@@ -24,10 +29,10 @@ import { AddressInfo } from 'node:net';
24
29
  interface AudioWsDeps {
25
30
  /** AI 服务对象的语音能力(`ai.audio`)。 */
26
31
  readonly ai: Pick<AIFunctions, 'audio'>;
27
- /** 校验并原子消费短期、一次性的 Audio ticket。 */
28
- readonly verifyTicket: (ticket: string) => Promise<HaiResult<ServSession>>;
29
- /** 基于会话授权操作、模型、音色、格式和配额。 */
30
- readonly authorize?: (session: ServSession, request: AudioWsStartMessage) => Promise<HaiResult<AuthorizedAudioRequest>>;
32
+ /** 校验并原子消费短期、一次性的 Audio ticket,返回会话与可选授权。 */
33
+ readonly verifyTicket: (ticket: string) => Promise<HaiResult<AudioTicketVerification>>;
34
+ /** 基于会话授权操作、模型、音色、格式和配额;第三参为票据绑定授权(如有)。 */
35
+ readonly authorize?: (session: ServSession, request: AudioWsStartMessage, grant?: AudioTicketGrant) => Promise<HaiResult<AuthorizedAudioRequest>>;
31
36
  /** 会话结束时释放应用侧并发占用。 */
32
37
  readonly onSessionEnd?: (session: ServSession, request: AuthorizedAudioRequest) => void | Promise<void>;
33
38
  /** 单条消息字节上限(默认 1 MiB,防止内存放大)。 */
@@ -38,6 +43,12 @@ interface AudioWsDeps {
38
43
  readonly maxTextBytes?: number;
39
44
  /** 单连接最长持续时间(毫秒,默认 5 分钟,防止恶意长连接)。 */
40
45
  readonly maxSessionMs?: number;
46
+ /** 预鉴权超时(毫秒,默认 5000):连接建立后未完成鉴权并收到 start 即关闭。 */
47
+ readonly preAuthTimeoutMs?: number;
48
+ /** 未处理消息积压上限(默认 256):串行处理链积压超过此数立即关闭。 */
49
+ readonly maxPendingMessages?: number;
50
+ /** 发送缓冲高水位(字节,默认 8 MiB):bufferedAmount 超过即关闭慢客户端。 */
51
+ readonly maxSendBufferBytes?: number;
41
52
  }
42
53
 
43
54
  /**
@@ -361,18 +372,21 @@ interface ServAudioConfig {
361
372
  /** AI 服务对象(提供 `ai.audio` 领域能力)。 */
362
373
  readonly ai: AudioWsDeps['ai'];
363
374
  /**
364
- * 校验并原子消费短期、一次性的 Audio ticket。
375
+ * 校验并原子消费短期、一次性的 Audio ticket,返回会话与可选的能力授权(grant)。
365
376
  *
366
- * ticket 的签发和存储由应用决定;成功结果必须返回建立授权所需的 IAM 会话摘要。
377
+ * ticket 的签发和存储由应用决定(推荐 `iam.ticket.issue` / `iam.ticket.consume`);
378
+ * 成功结果必须返回建立授权所需的 IAM 会话摘要,可选返回签发时绑定的 `grant`
379
+ * (操作 / 模型 / 会话),供服务端交叉校验 `start` 帧与票据一致。
367
380
  */
368
- readonly verifyTicket: (ticket: string) => Promise<HaiResult<ServSession>>;
381
+ readonly verifyTicket: (ticket: string) => Promise<HaiResult<AudioTicketVerification>>;
369
382
  /**
370
383
  * 基于已验证会话授权本次操作,并返回服务端确认后的付费参数。
371
384
  *
372
385
  * 未提供时只保留操作及音频格式,客户端提交的 model / voice / instruction 不会生效。
373
386
  * 这里适合完成 IAM 权限、Persona、套餐配额和并发会话的检查/占用。
387
+ * 第三参 `grant` 为票据绑定的能力(如有),可用于确认 `start` 未越权。
374
388
  */
375
- readonly authorize?: (session: ServSession, request: AudioWsStartMessage) => Promise<HaiResult<AuthorizedAudioRequest>>;
389
+ readonly authorize?: (session: ServSession, request: AudioWsStartMessage, grant?: AudioTicketGrant) => Promise<HaiResult<AuthorizedAudioRequest>>;
376
390
  /** 会话结束钩子,供应用释放并发占用;无论成功、失败或断连都至多调用一次。 */
377
391
  readonly onSessionEnd?: (session: ServSession, request: AuthorizedAudioRequest) => void | Promise<void>;
378
392
  /** 语音入口路径(相对 apiPrefix,默认 `/ai/audio`)。 */
@@ -385,6 +399,30 @@ interface ServAudioConfig {
385
399
  readonly maxTextBytes?: number;
386
400
  /** 单连接最长持续时间(毫秒,默认 5 分钟)。 */
387
401
  readonly maxSessionMs?: number;
402
+ /** 预鉴权超时(毫秒,默认 5000):连接建立后未完成鉴权并收到 `start` 即关闭,独立于会话超时。 */
403
+ readonly preAuthTimeoutMs?: number;
404
+ /** 未处理消息积压上限(默认 256):串行处理链积压超过此数立即关闭,防止内存放大。 */
405
+ readonly maxPendingMessages?: number;
406
+ /** 发送缓冲高水位(字节,默认 8 MiB):`ws.bufferedAmount` 超过即以领域错误关闭慢客户端。 */
407
+ readonly maxSendBufferBytes?: number;
408
+ }
409
+ /** Audio ticket 绑定的能力授权(消费时原样返回,供服务端交叉校验 start 帧)。 */
410
+ interface AudioTicketGrant {
411
+ /** 允许的操作 */
412
+ readonly operation?: 'transcribe' | 'synthesize';
413
+ /** 允许的模型 ID */
414
+ readonly model?: string;
415
+ /** 绑定的会话 ID */
416
+ readonly sessionId?: string;
417
+ /** 应用自定义绑定字段 */
418
+ readonly [key: string]: unknown;
419
+ }
420
+ /** Audio ticket 验证结果:IAM 会话 + 可选能力授权。 */
421
+ interface AudioTicketVerification {
422
+ /** IAM 会话摘要 */
423
+ readonly session: ServSession;
424
+ /** 票据绑定的能力授权(如有) */
425
+ readonly grant?: AudioTicketGrant;
388
426
  }
389
427
  /** Audio WebSocket 经应用授权后允许生效的服务端参数。 */
390
428
  interface AuthorizedAudioRequest {
@@ -768,4 +806,4 @@ declare const serv: {
768
806
  m: (key: "$schema" | "serv_validationFailed" | "serv_validationRequired" | "serv_validationInvalid" | "serv_validationInvalidType" | "serv_validationStringMin" | "serv_validationStringMax" | "serv_validationNumberMin" | "serv_validationNumberMax" | "serv_validationArrayMin" | "serv_validationArrayMax" | "serv_validationTooSmall" | "serv_validationTooBig" | "serv_validationEmail" | "serv_validationUrl" | "serv_validationUuid" | "serv_validationEnum" | "serv_errorUnauthorized" | "serv_errorForbidden" | "serv_errorInternal" | "serv_storageKeyRequired" | "serv_storageKeyTooLong" | "serv_storageKeyIllegalChars" | "serv_storageKeyDotSegments" | "serv_iamCannotDeleteCurrentUser" | "serv_transportInvalidPayload" | "serv_transportUnknownClient" | "serv_transportClientIdRequired" | "serv_transportClientKeyNotFound" | "serv_transportDecryptFailed" | "serv_transportEncryptFailed" | "serv_transportKeyExchangeFailed", options?: _h_ai_core.MessageOptions) => string;
769
807
  };
770
808
 
771
- export { type AuthorizedAudioRequest, type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServAudioConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServDocsHttpConfig, type ServFetchHandler, type ServGuardedProcedureWrapper, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, type ServProcedureWrapper, type ServRpcHttpConfig, ServSession, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };
809
+ export { type AudioTicketGrant, type AudioTicketVerification, type AuthorizedAudioRequest, type CreateServAppOptions, CreateServContext, type GenerateOpenAPISpecOptions, RefreshCookieConfig, type ServAudioConfig, type ServConfig, type ServConfigInput, ServConfigSchema, ServContext, type ServCorsConfig, type ServDocsHttpConfig, type ServFetchHandler, type ServGuardedProcedureWrapper, type ServHealthHttpConfig, type ServHttpApp, type ServHttpConfig, type ServHttpConfigInput, ServIam, type ServListenOptions, type ServMiddleware, type ServMiddlewareFactory, type ServMiddlewareMount, type ServNodeServer, type ServOpenAPIHttpConfig, type ServProcedureHandler, type ServProcedureOptions, type ServProcedureWrapper, type ServRpcHttpConfig, ServSession, type ServTransportConfig, type ServTransportRuntimeConfig, type ServTransportRuntimeConfigInput, type ServValidationFailureBody, serv };