@taicode/common-server 4.0.0-beta.3 → 4.0.0-beta.4

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.
Files changed (271) hide show
  1. package/output/app/index.d.ts +13 -15
  2. package/output/app/index.d.ts.map +1 -1
  3. package/output/app/index.js +256 -18
  4. package/output/auth/index.d.ts +72 -0
  5. package/output/auth/index.d.ts.map +1 -0
  6. package/output/auth/index.js +176 -0
  7. package/output/core/assemble.d.ts +64 -0
  8. package/output/core/assemble.d.ts.map +1 -0
  9. package/output/core/assemble.js +88 -0
  10. package/output/core/boot/context.d.ts +89 -0
  11. package/output/core/boot/context.d.ts.map +1 -0
  12. package/output/{app → core/boot}/context.js +44 -17
  13. package/output/{app → core/boot}/lifecycle.d.ts +13 -15
  14. package/output/core/boot/lifecycle.d.ts.map +1 -0
  15. package/output/{app → core/boot}/lifecycle.js +15 -12
  16. package/output/{app → core/boot}/shutdown.d.ts +9 -6
  17. package/output/core/boot/shutdown.d.ts.map +1 -0
  18. package/output/{app → core/boot}/shutdown.js +7 -4
  19. package/output/core/boot/signal.d.ts +13 -0
  20. package/output/core/boot/signal.d.ts.map +1 -0
  21. package/output/{app → core/boot}/signal.js +3 -2
  22. package/output/core/boot/steps.d.ts +107 -0
  23. package/output/core/boot/steps.d.ts.map +1 -0
  24. package/output/core/boot/steps.js +200 -0
  25. package/output/core/entries.d.ts +123 -0
  26. package/output/core/entries.d.ts.map +1 -0
  27. package/output/core/entries.js +193 -0
  28. package/output/core/entries.test.d.ts +2 -0
  29. package/output/core/entries.test.d.ts.map +1 -0
  30. package/output/core/entries.test.js +217 -0
  31. package/output/core/env.d.ts +62 -0
  32. package/output/core/env.d.ts.map +1 -0
  33. package/output/core/env.js +96 -0
  34. package/output/{app → core}/env.test.d.ts.map +1 -1
  35. package/output/core/env.test.js +105 -0
  36. package/output/core/modules/auth-provider/index.d.ts +10 -0
  37. package/output/core/modules/auth-provider/index.d.ts.map +1 -0
  38. package/output/core/modules/auth-provider/index.js +9 -0
  39. package/output/core/modules/auth-provider/types.d.ts +71 -0
  40. package/output/core/modules/auth-provider/types.d.ts.map +1 -0
  41. package/output/core/modules/context/index.d.ts +18 -0
  42. package/output/core/modules/context/index.d.ts.map +1 -0
  43. package/output/core/modules/context/index.js +23 -0
  44. package/output/core/modules/context/index.test.d.ts.map +1 -0
  45. package/output/core/modules/cookie/index.d.ts +21 -0
  46. package/output/core/modules/cookie/index.d.ts.map +1 -0
  47. package/output/{app/core/cookie.js → core/modules/cookie/index.js} +1 -6
  48. package/output/core/modules/identity/index.d.ts +4 -0
  49. package/output/core/modules/identity/index.d.ts.map +1 -0
  50. package/output/{app/core/identity.js → core/modules/identity/index.js} +1 -1
  51. package/output/core/modules/logging/index.d.ts +5 -0
  52. package/output/core/modules/logging/index.d.ts.map +1 -0
  53. package/output/{app/core/logging.js → core/modules/logging/index.js} +2 -2
  54. package/output/core/modules/openapi/index.d.ts +7 -0
  55. package/output/core/modules/openapi/index.d.ts.map +1 -0
  56. package/output/{app/core/openapi.js → core/modules/openapi/index.js} +11 -12
  57. package/output/core/modules/permission-operations/index.d.ts +26 -0
  58. package/output/core/modules/permission-operations/index.d.ts.map +1 -0
  59. package/output/core/modules/permission-operations/index.js +36 -0
  60. package/output/core/modules/permission-operations/index.test.d.ts +2 -0
  61. package/output/core/modules/permission-operations/index.test.d.ts.map +1 -0
  62. package/output/core/modules/permission-operations/index.test.js +45 -0
  63. package/output/{app/core/request-id.d.ts → core/modules/request-id/index.d.ts} +1 -4
  64. package/output/core/modules/request-id/index.d.ts.map +1 -0
  65. package/output/{app/core/request-id.js → core/modules/request-id/index.js} +2 -2
  66. package/output/core/modules/response/index.d.ts +22 -0
  67. package/output/core/modules/response/index.d.ts.map +1 -0
  68. package/output/{app/core/response.js → core/modules/response/index.js} +22 -11
  69. package/output/{schema/index.d.ts → core/modules/schema/builders.d.ts} +8 -1
  70. package/output/core/modules/schema/builders.d.ts.map +1 -0
  71. package/output/{schema/index.js → core/modules/schema/builders.js} +7 -4
  72. package/output/core/modules/schema/index.d.ts +34 -0
  73. package/output/core/modules/schema/index.d.ts.map +1 -0
  74. package/output/core/modules/schema/index.js +62 -0
  75. package/output/core/modules/schema/index.test.d.ts +2 -0
  76. package/output/core/modules/schema/index.test.d.ts.map +1 -0
  77. package/output/core/modules/schema/index.test.js +55 -0
  78. package/output/core/modules/system-routes/index.d.ts +15 -0
  79. package/output/core/modules/system-routes/index.d.ts.map +1 -0
  80. package/output/core/modules/system-routes/index.js +171 -0
  81. package/output/core/test-support/database.d.ts +96 -0
  82. package/output/core/test-support/database.d.ts.map +1 -0
  83. package/output/core/test-support/database.js +144 -0
  84. package/output/core/test-support/database.test.d.ts +2 -0
  85. package/output/core/test-support/database.test.d.ts.map +1 -0
  86. package/output/core/test-support/database.test.js +110 -0
  87. package/output/core/test-support/environment.d.ts +33 -0
  88. package/output/core/test-support/environment.d.ts.map +1 -0
  89. package/output/core/test-support/environment.js +42 -0
  90. package/output/core/test-support/test-server.d.ts +94 -0
  91. package/output/core/test-support/test-server.d.ts.map +1 -0
  92. package/output/core/test-support/test-server.js +60 -0
  93. package/output/core/test-support/test-server.test.d.ts +2 -0
  94. package/output/core/test-support/test-server.test.d.ts.map +1 -0
  95. package/output/core/test-support/test-server.test.js +162 -0
  96. package/output/{app → core}/type-augment.d.ts +2 -2
  97. package/output/core/type-augment.d.ts.map +1 -0
  98. package/output/{app → core}/types.d.ts +153 -66
  99. package/output/core/types.d.ts.map +1 -0
  100. package/output/cors/index.d.ts +3 -0
  101. package/output/cors/index.d.ts.map +1 -0
  102. package/output/cors/index.js +27 -0
  103. package/output/index.d.ts +8 -4
  104. package/output/index.d.ts.map +1 -1
  105. package/output/index.js +24 -5
  106. package/output/rate-limit/index.d.ts +23 -0
  107. package/output/rate-limit/index.d.ts.map +1 -0
  108. package/output/rate-limit/index.js +42 -0
  109. package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
  110. package/output/rate-limit/limiter.d.ts.map +1 -0
  111. package/output/rate-limit/memory-store.d.ts.map +1 -0
  112. package/output/rate-limit/redis-store.d.ts.map +1 -0
  113. package/output/rate-limit/redis-store.test.d.ts.map +1 -0
  114. package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
  115. package/output/{rate-limiter → rate-limit}/types.d.ts +0 -1
  116. package/output/rate-limit/types.d.ts.map +1 -0
  117. package/output/rate-limit/types.js +1 -0
  118. package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
  119. package/output/redis/cache.d.ts.map +1 -0
  120. package/output/redis/cache.test.d.ts +2 -0
  121. package/output/redis/cache.test.d.ts.map +1 -0
  122. package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
  123. package/output/{app/redis.d.ts → redis/index.d.ts} +7 -6
  124. package/output/redis/index.d.ts.map +1 -0
  125. package/output/{app/redis.js → redis/index.js} +10 -4
  126. package/output/redis/index.test.d.ts +2 -0
  127. package/output/{context → redis}/index.test.d.ts.map +1 -1
  128. package/output/redis/index.test.js +55 -0
  129. package/output/redis-queue/index.js +1 -1
  130. package/output/redis-queue/test-helpers.d.ts +1 -1
  131. package/output/redis-queue/test-helpers.js +1 -1
  132. package/output/redis-queue/types.d.ts +3 -12
  133. package/output/redis-queue/types.d.ts.map +1 -1
  134. package/package.json +16 -15
  135. package/LICENSE +0 -21
  136. package/output/app/auth.test.d.ts +0 -2
  137. package/output/app/auth.test.d.ts.map +0 -1
  138. package/output/app/auth.test.js +0 -148
  139. package/output/app/boot.d.ts +0 -44
  140. package/output/app/boot.d.ts.map +0 -1
  141. package/output/app/boot.js +0 -158
  142. package/output/app/config.test.d.ts +0 -2
  143. package/output/app/config.test.d.ts.map +0 -1
  144. package/output/app/config.test.js +0 -132
  145. package/output/app/context.d.ts +0 -74
  146. package/output/app/context.d.ts.map +0 -1
  147. package/output/app/core/cookie.d.ts +0 -25
  148. package/output/app/core/cookie.d.ts.map +0 -1
  149. package/output/app/core/identity.d.ts +0 -12
  150. package/output/app/core/identity.d.ts.map +0 -1
  151. package/output/app/core/index.d.ts +0 -44
  152. package/output/app/core/index.d.ts.map +0 -1
  153. package/output/app/core/index.js +0 -57
  154. package/output/app/core/logging.d.ts +0 -13
  155. package/output/app/core/logging.d.ts.map +0 -1
  156. package/output/app/core/openapi.d.ts +0 -17
  157. package/output/app/core/openapi.d.ts.map +0 -1
  158. package/output/app/core/request-id.d.ts.map +0 -1
  159. package/output/app/core/response.d.ts +0 -41
  160. package/output/app/core/response.d.ts.map +0 -1
  161. package/output/app/define-server.negative-cases.d.ts +0 -40
  162. package/output/app/define-server.negative-cases.d.ts.map +0 -1
  163. package/output/app/define-server.negative-cases.js +0 -268
  164. package/output/app/define-server.test.d.ts +0 -2
  165. package/output/app/define-server.test.d.ts.map +0 -1
  166. package/output/app/define-server.test.js +0 -97
  167. package/output/app/entries.d.ts +0 -31
  168. package/output/app/entries.d.ts.map +0 -1
  169. package/output/app/entries.js +0 -66
  170. package/output/app/env.d.ts +0 -59
  171. package/output/app/env.d.ts.map +0 -1
  172. package/output/app/env.js +0 -66
  173. package/output/app/env.test.js +0 -124
  174. package/output/app/error-contract.test.d.ts +0 -2
  175. package/output/app/error-contract.test.d.ts.map +0 -1
  176. package/output/app/error-contract.test.js +0 -227
  177. package/output/app/errors.d.ts +0 -20
  178. package/output/app/errors.d.ts.map +0 -1
  179. package/output/app/errors.js +0 -51
  180. package/output/app/generate-openapi.test.d.ts +0 -2
  181. package/output/app/generate-openapi.test.d.ts.map +0 -1
  182. package/output/app/generate-openapi.test.js +0 -123
  183. package/output/app/lifecycle-logging.test.d.ts +0 -2
  184. package/output/app/lifecycle-logging.test.d.ts.map +0 -1
  185. package/output/app/lifecycle-logging.test.js +0 -175
  186. package/output/app/lifecycle.d.ts.map +0 -1
  187. package/output/app/lifecycle.test.d.ts +0 -2
  188. package/output/app/lifecycle.test.d.ts.map +0 -1
  189. package/output/app/lifecycle.test.js +0 -153
  190. package/output/app/middleware/auth.d.ts +0 -71
  191. package/output/app/middleware/auth.d.ts.map +0 -1
  192. package/output/app/middleware/auth.js +0 -190
  193. package/output/app/middleware/context.d.ts +0 -13
  194. package/output/app/middleware/context.d.ts.map +0 -1
  195. package/output/app/middleware/context.js +0 -15
  196. package/output/app/middleware/cors.d.ts +0 -14
  197. package/output/app/middleware/cors.d.ts.map +0 -1
  198. package/output/app/middleware/cors.js +0 -30
  199. package/output/app/middleware/index.d.ts +0 -17
  200. package/output/app/middleware/index.d.ts.map +0 -1
  201. package/output/app/middleware/index.js +0 -16
  202. package/output/app/middleware/rate-limit.d.ts +0 -24
  203. package/output/app/middleware/rate-limit.d.ts.map +0 -1
  204. package/output/app/middleware/rate-limit.js +0 -46
  205. package/output/app/middleware-order.test.d.ts +0 -2
  206. package/output/app/middleware-order.test.d.ts.map +0 -1
  207. package/output/app/middleware-order.test.js +0 -149
  208. package/output/app/permission-operations.d.ts +0 -48
  209. package/output/app/permission-operations.d.ts.map +0 -1
  210. package/output/app/permission-operations.js +0 -77
  211. package/output/app/permission-operations.test.d.ts +0 -2
  212. package/output/app/permission-operations.test.d.ts.map +0 -1
  213. package/output/app/permission-operations.test.js +0 -72
  214. package/output/app/permission-registration.test.d.ts +0 -2
  215. package/output/app/permission-registration.test.d.ts.map +0 -1
  216. package/output/app/permission-registration.test.js +0 -138
  217. package/output/app/redis.d.ts.map +0 -1
  218. package/output/app/registered-operations.test.d.ts +0 -2
  219. package/output/app/registered-operations.test.d.ts.map +0 -1
  220. package/output/app/registered-operations.test.js +0 -57
  221. package/output/app/request-id.test.d.ts +0 -2
  222. package/output/app/request-id.test.d.ts.map +0 -1
  223. package/output/app/request-id.test.js +0 -130
  224. package/output/app/resources.test.d.ts +0 -2
  225. package/output/app/resources.test.d.ts.map +0 -1
  226. package/output/app/resources.test.js +0 -222
  227. package/output/app/shutdown.d.ts.map +0 -1
  228. package/output/app/shutdown.test.d.ts +0 -2
  229. package/output/app/shutdown.test.d.ts.map +0 -1
  230. package/output/app/shutdown.test.js +0 -210
  231. package/output/app/signal.d.ts +0 -12
  232. package/output/app/signal.d.ts.map +0 -1
  233. package/output/app/smoke.test.d.ts +0 -2
  234. package/output/app/smoke.test.d.ts.map +0 -1
  235. package/output/app/smoke.test.js +0 -210
  236. package/output/app/test-support.d.ts +0 -21
  237. package/output/app/test-support.d.ts.map +0 -1
  238. package/output/app/test-support.js +0 -24
  239. package/output/app/type-augment.d.ts.map +0 -1
  240. package/output/app/types.d.ts.map +0 -1
  241. package/output/context/index.d.ts +0 -6
  242. package/output/context/index.d.ts.map +0 -1
  243. package/output/context/index.js +0 -14
  244. package/output/rate-limiter/index.d.ts +0 -5
  245. package/output/rate-limiter/index.d.ts.map +0 -1
  246. package/output/rate-limiter/index.js +0 -4
  247. package/output/rate-limiter/memory-store.d.ts.map +0 -1
  248. package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
  249. package/output/rate-limiter/redis-store.d.ts.map +0 -1
  250. package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
  251. package/output/rate-limiter/types.d.ts.map +0 -1
  252. package/output/redis-cache/index.d.ts +0 -4
  253. package/output/redis-cache/index.d.ts.map +0 -1
  254. package/output/redis-cache/index.js +0 -1
  255. package/output/redis-cache/redis-cache.d.ts.map +0 -1
  256. package/output/redis-cache/redis-cache.test.d.ts +0 -2
  257. package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
  258. package/output/schema/index.d.ts.map +0 -1
  259. /package/output/{app → core}/env.test.d.ts +0 -0
  260. /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
  261. /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
  262. /package/output/{context → core/modules/context}/index.test.js +0 -0
  263. /package/output/{app → core}/type-augment.js +0 -0
  264. /package/output/{app → core}/types.js +0 -0
  265. /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
  266. /package/output/{rate-limiter → rate-limit}/memory-store.d.ts +0 -0
  267. /package/output/{rate-limiter → rate-limit}/memory-store.js +0 -0
  268. /package/output/{rate-limiter → rate-limit}/redis-store.d.ts +0 -0
  269. /package/output/{rate-limiter → rate-limit}/redis-store.js +0 -0
  270. /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
  271. /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
@@ -1,175 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { Disposer } from '@taicode/common-base';
3
- import { defineServer, startServer } from './entries.js';
4
- import { buildDisposeOrder, disposeStage, initServiceUnits, LIFECYCLE_PHASE_DISPOSE, LIFECYCLE_PHASE_INIT, MIDDLEWARES_LOG_STAGE, middlewareUnitName, RESOURCES_STAGE, SERVICES_STAGE, serviceUnitName, } from './lifecycle.js';
5
- import { createRecordingLogger } from './test-support.js';
6
- /**
7
- * 逐单元日志。
8
- *
9
- * - **注册期**:每个中间件注册后打一条 `debug`,带 `{ service, unit, stage: 'middlewares' }`;
10
- * - **init / dispose**:逐单元 `debug`(开始 / 完成),失败 `error`;带 `{ service, unit, stage, phase }`;
11
- * - **阶段 flush**:`disposeStage` 每阶段一条 `debug`(`unit` = 阶段名);
12
- * - 中间件名字:`unit.name ?? '<service>:middleware[<index>]'`;services 用对象键。
13
- *
14
- * 分两层验证:
15
- * 1. **单元层**:直接调 `initServiceUnits` / `buildDisposeOrder`,注入记录型 logger;
16
- * 2. **真路径**:`startServer(server, { listen: false })` 走完整装配;把框架 logger 的写入流接到
17
- * 捕获数组(mock `./core/logging.js` 的 `createLoggerOptions`),再按字段断言。
18
- */
19
- // ── 单元层:记录型 logger ────────────────────────────────────────
20
- /** 按相位 / 阶段 / 消息片段挑出 `unit` 列表(保序) */
21
- function unitsOf(log, phase, stage, messagePart) {
22
- return log.entries
23
- .filter((entry) => entry.level === 'debug' && entry.msg?.includes(messagePart) === true)
24
- .map((entry) => entry.obj)
25
- .filter((fields) => fields.phase === phase && fields.stage === stage)
26
- .map((fields) => fields.unit);
27
- }
28
- function expectServiceField(log, service) {
29
- const fields = log.entries
30
- .filter((entry) => entry.level === 'debug')
31
- .map((entry) => entry.obj);
32
- expect(fields.length).toBeGreaterThan(0);
33
- for (const field of fields) {
34
- expect(field.service).toBe(service);
35
- }
36
- }
37
- describe('逐单元日志:init / dispose', () => {
38
- it('init 逐单元 debug:单元名 = 对象键', async () => {
39
- const log = createRecordingLogger();
40
- const services = {
41
- alpha: { init: () => undefined },
42
- beta: { init: () => undefined },
43
- };
44
- await initServiceUnits({ service: 'svc', log, services });
45
- expect(unitsOf(log, LIFECYCLE_PHASE_INIT, SERVICES_STAGE, 'start')).toEqual(['alpha', 'beta']);
46
- expect(unitsOf(log, LIFECYCLE_PHASE_INIT, SERVICES_STAGE, 'done')).toEqual(['alpha', 'beta']);
47
- expectServiceField(log, 'svc');
48
- expect(serviceUnitName('alpha')).toBe('alpha');
49
- });
50
- it('dispose 逐单元 debug:声明序逆序', async () => {
51
- const log = createRecordingLogger();
52
- const services = {
53
- alpha: { dispose: () => undefined },
54
- beta: { dispose: () => undefined },
55
- };
56
- for (const step of buildDisposeOrder({ service: 'svc', log, services })) {
57
- await step();
58
- }
59
- expect(unitsOf(log, LIFECYCLE_PHASE_DISPOSE, SERVICES_STAGE, 'start')).toEqual(['beta', 'alpha']);
60
- expectServiceField(log, 'svc');
61
- });
62
- it('单元 init 抛错 → 逐单元 error(带 unit / stage / phase)后重新抛出', async () => {
63
- const log = createRecordingLogger();
64
- const services = {
65
- boom: {
66
- init: () => {
67
- throw new Error('init boom');
68
- },
69
- },
70
- };
71
- await expect(initServiceUnits({ service: 'svc', log, services })).rejects.toThrow('init boom');
72
- const failure = log.entries.find((entry) => entry.level === 'error');
73
- expect(failure).toBeDefined();
74
- const fields = failure?.obj;
75
- expect(fields.unit).toBe('boom');
76
- expect(fields.stage).toBe(SERVICES_STAGE);
77
- expect(fields.phase).toBe(LIFECYCLE_PHASE_INIT);
78
- expect(fields.error.message).toBe('init boom');
79
- });
80
- it('不传 logger → 生命周期函数照常工作(纯函数式调用不发日志)', async () => {
81
- const calls = [];
82
- const services = {
83
- alpha: {
84
- init: () => {
85
- calls.push('init');
86
- },
87
- },
88
- };
89
- await initServiceUnits({ services });
90
- expect(calls).toEqual(['init']);
91
- });
92
- it('中间件日志名字退路:`<service>:middleware[index]`', () => {
93
- expect(middlewareUnitName('svc', {}, 3)).toBe('svc:middleware[3]');
94
- expect(middlewareUnitName('svc', { name: 'named' }, 1)).toBe('named');
95
- });
96
- it('阶段 flush:`unit` / `stage` = 阶段名,`phase` = dispose', async () => {
97
- const log = createRecordingLogger();
98
- const disposer = new Disposer();
99
- await disposeStage({ service: 'svc', log, stage: RESOURCES_STAGE, disposer });
100
- const fields = log.entries.map((entry) => entry.obj);
101
- expect(fields.length).toBeGreaterThan(0);
102
- for (const field of fields) {
103
- expect(field.service).toBe('svc');
104
- expect(field.unit).toBe(RESOURCES_STAGE);
105
- expect(field.stage).toBe(RESOURCES_STAGE);
106
- expect(field.phase).toBe(LIFECYCLE_PHASE_DISPOSE);
107
- }
108
- });
109
- });
110
- // ── 真路径:`startServer` 走完整装配,捕获框架 logger 写入 ────────
111
- const captured = vi.hoisted(() => ({ lines: [] }));
112
- vi.mock('./core/logging.js', () => ({
113
- LOG_REDACT_PATHS: [],
114
- // 把 pino 的目标流换成捕获数组(level=debug,否则逐单元 debug 不会落盘)
115
- createLoggerOptions: () => ({
116
- level: 'debug',
117
- stream: {
118
- write: (chunk) => {
119
- captured.lines.push(chunk);
120
- },
121
- },
122
- }),
123
- }));
124
- function capturedRecords() {
125
- return captured.lines
126
- .join('')
127
- .split('\n')
128
- .filter((line) => line.trim().length > 0)
129
- .map((line) => JSON.parse(line));
130
- }
131
- /** 按字段匹配捕获到的记录(pino 还会加 `time` / `pid` 等,故只比关键字段) */
132
- function matchRecords(records, match) {
133
- return records.filter((record) => Object.entries(match).every(([key, value]) => record[key] === value));
134
- }
135
- describe('startServer 真路径:注册 / init / dispose 逐单元日志', () => {
136
- it('中间件注册、services 的 init / dispose 日志都能拿到名字;未命名用退路', async () => {
137
- captured.lines.length = 0;
138
- const server = defineServer({
139
- app: { service: 'logging-svc', title: 'Logging Service', version: '1.0.0' },
140
- resources: () => ({}),
141
- config: () => ({}),
142
- services: () => ({
143
- alpha: {
144
- init: () => undefined,
145
- dispose: () => undefined,
146
- },
147
- }),
148
- middlewares: () => [
149
- { name: 'named', register: () => undefined },
150
- // 未命名 → 退路 `logging-svc:middleware[1]`
151
- { register: () => undefined },
152
- ],
153
- routers: () => undefined,
154
- });
155
- const app = await startServer(server, { listen: false });
156
- await app.close();
157
- const records = capturedRecords();
158
- // 注册期:字段 `{ service, unit, stage }`,未命名走退路
159
- const registered = matchRecords(records, { msg: '[middleware] registered' });
160
- expect(matchRecords(registered, { unit: 'named', stage: MIDDLEWARES_LOG_STAGE }).length).toBe(1);
161
- expect(matchRecords(registered, { unit: 'logging-svc:middleware[1]', stage: MIDDLEWARES_LOG_STAGE }).length).toBe(1);
162
- expect(matchRecords(registered, { service: 'logging-svc' }).length).toBe(2);
163
- // init / dispose:services 逐单元 debug(带 `phase`)
164
- expect(matchRecords(records, { unit: 'alpha', stage: SERVICES_STAGE, phase: LIFECYCLE_PHASE_INIT }).length).toBeGreaterThan(0);
165
- expect(matchRecords(records, { unit: 'alpha', stage: SERVICES_STAGE, phase: LIFECYCLE_PHASE_DISPOSE }).length).toBeGreaterThan(0);
166
- // 中间件**没有逐单元生命周期**:没有 middlewares 阶段的 init 日志;dispose 只有阶段 flush
167
- expect(matchRecords(records, { stage: MIDDLEWARES_LOG_STAGE, phase: LIFECYCLE_PHASE_INIT })).toHaveLength(0);
168
- const middlewareDispose = matchRecords(records, {
169
- stage: MIDDLEWARES_LOG_STAGE,
170
- phase: LIFECYCLE_PHASE_DISPOSE,
171
- });
172
- expect(middlewareDispose.length).toBeGreaterThan(0);
173
- expect(middlewareDispose.every((record) => record.unit === MIDDLEWARES_LOG_STAGE)).toBe(true);
174
- });
175
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../source/app/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,cAAc,aAAa,CAAA;AACxC,eAAO,MAAM,YAAY,WAAW,CAAA;AACpC,eAAO,MAAM,iBAAiB,gBAAgB,CAAA;AAC9C,eAAO,MAAM,aAAa,YAAY,CAAA;AACtC,eAAO,MAAM,eAAe,cAAc,CAAA;AAC1C,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB,gBAAoB,CAAA;AAEtD,8BAA8B;AAC9B,eAAO,MAAM,oBAAoB,SAAS,CAAA;AAC1C,eAAO,MAAM,uBAAuB,YAAY,CAAA;AAChD,MAAM,MAAM,cAAc,GAAG,OAAO,oBAAoB,GAAG,OAAO,uBAAuB,CAAA;AAEzF;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB;AAED,8DAA8D;AAC9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,cAAc,CAAA;CACtB;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKxF;AAED,oDAAoD;AACpD,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAmCD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpF;AAED,6EAA6E;AAC7E,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACpE,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,yBAAyB,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAMtG;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtF;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb,6CAA6C;IAC7C,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5E"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=lifecycle.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle.test.d.ts","sourceRoot":"","sources":["../../source/app/lifecycle.test.ts"],"names":[],"mappings":""}
@@ -1,153 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { defineServer, startServer } from './entries.js';
3
- import { buildDisposeOrder, initServiceUnits, serviceUnitName } from './lifecycle.js';
4
- /**
5
- * 生命周期(N1 §4)。
6
- *
7
- * - **每个阶段一个 `Disposer`**(`ctx.disposer`):创建处注册释放,关闭时按阶段逆序 flush;
8
- * - **`services` 额外有逐单元生命周期**:按**对象声明序** `init` / **逆序** `dispose`;
9
- * - services 阶段内部:先 flush 自己的 `disposer`,再逐单元 `dispose`;
10
- * - **没有 `dependsOn`**(`services` 的 ctx 没有 `services` 字段,编译期禁止互依);
11
- * - 单元名 = **对象键**。
12
- */
13
- describe('init / dispose 顺序:声明序 / 逆序(N1 §4 / §5)', () => {
14
- it('init:services 按对象声明序', async () => {
15
- const calls = [];
16
- const services = {
17
- ledger: {
18
- init: () => {
19
- calls.push('init:ledger');
20
- },
21
- },
22
- repository: {
23
- init: () => {
24
- calls.push('init:repository');
25
- },
26
- },
27
- mailer: {
28
- init: () => {
29
- calls.push('init:mailer');
30
- },
31
- },
32
- };
33
- await initServiceUnits({ services });
34
- expect(calls).toEqual(['init:ledger', 'init:repository', 'init:mailer']);
35
- });
36
- it('dispose:services 按声明序逆序', async () => {
37
- const calls = [];
38
- const services = {
39
- ledger: {
40
- dispose: () => {
41
- calls.push('dispose:ledger');
42
- },
43
- },
44
- repository: {
45
- dispose: () => {
46
- calls.push('dispose:repository');
47
- },
48
- },
49
- };
50
- for (const step of buildDisposeOrder({ services })) {
51
- await step();
52
- }
53
- expect(calls).toEqual(['dispose:repository', 'dispose:ledger']);
54
- });
55
- it('不是 ServiceUnit 的值照样能放(框架只检测 init / dispose 是否为函数)', async () => {
56
- const calls = [];
57
- const services = {
58
- plain: { hello: 'world' },
59
- lifecycle: {
60
- init: () => {
61
- calls.push('init:lifecycle');
62
- },
63
- },
64
- };
65
- await initServiceUnits({ services });
66
- expect(calls).toEqual(['init:lifecycle']);
67
- });
68
- it('单元名 = 对象键', () => {
69
- expect(serviceUnitName('ledger')).toBe('ledger');
70
- expect(serviceUnitName('repository')).toBe('repository');
71
- });
72
- });
73
- describe('startServer 集成:声明序 init / 逆序 dispose', () => {
74
- it('services 声明序 init;中间件按数组序注册但不参与生命周期;close 时 services 逆序 dispose', async () => {
75
- const calls = [];
76
- const server = defineServer({
77
- app: { service: 'lifecycle-integration', title: 'Lifecycle Integration', version: '1.0.0' },
78
- resources: () => ({}),
79
- config: () => ({}),
80
- services: () => ({
81
- ledger: {
82
- init: () => {
83
- calls.push('init:ledger');
84
- },
85
- dispose: () => {
86
- calls.push('dispose:ledger');
87
- },
88
- },
89
- repository: {
90
- init: () => {
91
- calls.push('init:repository');
92
- },
93
- dispose: () => {
94
- calls.push('dispose:repository');
95
- },
96
- },
97
- mailer: {
98
- init: () => {
99
- calls.push('init:mailer');
100
- },
101
- dispose: () => {
102
- calls.push('dispose:mailer');
103
- },
104
- },
105
- }),
106
- middlewares: () => [
107
- {
108
- name: 'm1',
109
- register: () => {
110
- calls.push('register:m1');
111
- },
112
- },
113
- {
114
- name: 'm2',
115
- register: () => {
116
- calls.push('register:m2');
117
- },
118
- },
119
- ],
120
- routers: () => undefined,
121
- });
122
- const app = await startServer(server, { listen: false });
123
- expect(calls).toEqual(['init:ledger', 'init:repository', 'init:mailer', 'register:m1', 'register:m2']);
124
- await app.close();
125
- expect(calls).toEqual([
126
- 'init:ledger',
127
- 'init:repository',
128
- 'init:mailer',
129
- 'register:m1',
130
- 'register:m2',
131
- 'dispose:mailer',
132
- 'dispose:repository',
133
- 'dispose:ledger',
134
- ]);
135
- });
136
- it('单元 init 抛错 → startServer 冒泡失败', async () => {
137
- const server = defineServer({
138
- app: { service: 'lifecycle-init-error', title: 'Lifecycle Init Error', version: '1.0.0' },
139
- resources: () => ({}),
140
- config: () => ({}),
141
- services: () => ({
142
- broken: {
143
- init: () => {
144
- throw new Error('init boom');
145
- },
146
- },
147
- }),
148
- middlewares: () => [],
149
- routers: () => undefined,
150
- });
151
- await expect(startServer(server, { listen: false })).rejects.toThrow('init boom');
152
- });
153
- });
@@ -1,71 +0,0 @@
1
- import type { MiddlewareContext, MiddlewareUnit } from '../types.js';
2
- import type { FastifyRequest } from 'fastify';
3
- /**
4
- * 标准中间件单元 `auth`(可替换)。
5
- *
6
- * 校验下游 token(4/5 服务用)。默认 `skip`:`OPTIONS` → 未匹配路由 → `NoLoginRequired` → `docPath`。
7
- * 默认 `verify` 为**框架内置 account HTTP 客户端**(`POST /gateway/verify` + 信封解析),可经
8
- * `verify` 整体替换;account `serviceUrl` 门槛**仅作用于内置 verify**(自定义 `verify` 不受约束)。
9
- *
10
- * 另在 `onReady` 阶段默认(`registerPermissions !== false`)向 account **自注册权限操作目录**
11
- * (`POST /permission/operation/register`)。
12
- */
13
- export interface AuthVerifyMetadata {
14
- requestId?: string;
15
- method?: string;
16
- url?: string;
17
- ip?: string;
18
- ips?: string[];
19
- userAgent?: string;
20
- referrer?: string;
21
- }
22
- export interface AuthVerifyInput {
23
- token?: string;
24
- apiKey?: string;
25
- operation?: string;
26
- /** 路由参数(`request.params`),如 `:id` 这类路径参数 */
27
- resource?: Record<string, unknown>;
28
- accessControls?: string[];
29
- /** 请求元信息(仅供下游审计 / 日志;不参与鉴权判定) */
30
- metadata?: AuthVerifyMetadata;
31
- }
32
- export interface AuthVerifyResult {
33
- user?: unknown;
34
- token?: unknown;
35
- }
36
- /** 跳过条件(返回 true 即放行);缺省为框架默认 skip 集合 */
37
- export type AuthSkip<C = unknown, S = unknown> = (request: FastifyRequest, ctx: MiddlewareContext<C, S>) => boolean | undefined;
38
- export interface AccountIntegrationConfig {
39
- enabled?: boolean;
40
- serviceUrl?: string;
41
- accessKey?: string;
42
- secretKey?: string;
43
- }
44
- export interface AuthMiddlewareOptions<C = unknown, S = unknown> {
45
- /**
46
- * 整体替换校验实现(缺省走框架内置 account HTTP 客户端)。
47
- *
48
- * 提供本项时**不做** account `serviceUrl` 门槛:`serviceUrl` 只服务于内置 verify。
49
- */
50
- verify?: (input: AuthVerifyInput & {
51
- service: string;
52
- }, ctx: MiddlewareContext<C, S>) => Promise<AuthVerifyResult>;
53
- /** 读取 token 的 cookie 名(缺省现读配置的 `server.cookieNames`) */
54
- cookieNames?: string[];
55
- /**
56
- * 是否在 `startServer` 的 `onReady` 阶段向 account 自注册权限操作(默认 `true`)。
57
- *
58
- * 跳过条件:本项为 `false`;或配置的 `account` 无 `serviceUrl`(自然排除 account 自身);
59
- * 或出 spec 路径(`generateOpenApi` 不跑 `config` 槽位,配置不可用)。注册失败**不致命**:
60
- * `log.error` 后继续启动,不中断 `startServer`。
61
- */
62
- registerPermissions?: boolean;
63
- /** 跳过条件(返回 true 即放行);缺省为框架默认 skip 集合 */
64
- skip?: AuthSkip<C, S>;
65
- /** 错误映射(缺省:非 `UserError` / `SystemError` 一律 → `SystemError('EXTERNAL_SERVICE_ERROR')`) */
66
- mapError?: (error: unknown) => Error;
67
- }
68
- /** 框架默认 skip:OPTIONS → 未匹配路由 → NoLoginRequired → docPath */
69
- export declare function defaultAuthSkip(request: FastifyRequest, ctx: MiddlewareContext<unknown, unknown>): boolean;
70
- export declare function createAuthMiddleware<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>, options?: AuthMiddlewareOptions<C, S>): MiddlewareUnit;
71
- //# sourceMappingURL=auth.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../source/app/middleware/auth.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C;;;;;;;;;GASG;AAEH,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,wCAAwC;AACxC,MAAM,MAAM,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,IAAI,CAC/C,OAAO,EAAE,cAAc,EACvB,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,KACzB,OAAO,GAAG,SAAS,CAAA;AAExB,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,qBAAqB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IAC7D;;;;OAIG;IACH,MAAM,CAAC,EAAE,CACP,KAAK,EAAE,eAAe,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,EAC5C,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,KACzB,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC9B,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,wCAAwC;IACxC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACrB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAA;CACrC;AAmCD,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,EAAE,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAgB1G;AAgFD,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,qBAAqB,CAAC,CAAC,EAAE,CAAC,CAAM,GAAG,cAAc,CAoGtJ"}
@@ -1,190 +0,0 @@
1
- import { readConfigKey } from '../context.js';
2
- import { readServerIdentity } from '../core/identity.js';
3
- import { DOC_ROUTE_PREFIX } from '../core/openapi.js';
4
- import { SystemError, UserError } from '../errors.js';
5
- import { readRegisteredOperations, toPermissionOperations } from '../permission-operations.js';
6
- function readAccount(config) {
7
- return readConfigKey(config, 'account');
8
- }
9
- function readServer(config) {
10
- return readConfigKey(config, 'server');
11
- }
12
- function readCookieNames(config, fallback) {
13
- if (fallback && fallback.length > 0) {
14
- return fallback;
15
- }
16
- const configured = readServer(config)?.cookieNames;
17
- return Array.isArray(configured) && configured.length > 0 ? configured : ['token'];
18
- }
19
- function extractAuth(request, cookieNames) {
20
- const authorization = request.headers.authorization;
21
- if (typeof authorization === 'string' && authorization.startsWith('Bearer ')) {
22
- return { token: authorization.slice('Bearer '.length).trim() };
23
- }
24
- if (typeof authorization === 'string' && authorization.startsWith('apikey-')) {
25
- return { apiKey: authorization.slice('apikey-'.length).trim() };
26
- }
27
- for (const name of cookieNames) {
28
- const value = request.cookies?.[name];
29
- if (typeof value === 'string' && value) {
30
- return { token: value };
31
- }
32
- }
33
- return {};
34
- }
35
- /** 框架默认 skip:OPTIONS → 未匹配路由 → NoLoginRequired → docPath */
36
- export function defaultAuthSkip(request, ctx) {
37
- void ctx;
38
- if (request.method === 'OPTIONS') {
39
- return true;
40
- }
41
- const url = request.routeOptions?.url;
42
- if (!url) {
43
- return true;
44
- }
45
- if (url.startsWith(DOC_ROUTE_PREFIX)) {
46
- return true;
47
- }
48
- if (request.routeOptions?.config?.accessControls?.includes('NoLoginRequired')) {
49
- return true;
50
- }
51
- return false;
52
- }
53
- async function defaultVerify(input, account) {
54
- const serviceUrl = account?.serviceUrl;
55
- if (!serviceUrl) {
56
- // 内置 verify 依赖 account `serviceUrl`;缺失时抛明确错误
57
- // (不再 `new URL(path, undefined)` 抛 TypeError)
58
- throw new SystemError('CONFIG_UNAVAILABLE', undefined, 'account 未配置 serviceUrl,无法使用内置 verify(可用 verify 选项整体替换校验实现)');
59
- }
60
- const target = new URL('/gateway/verify', serviceUrl);
61
- const response = await fetch(target, {
62
- method: 'POST',
63
- headers: { 'content-type': 'application/json' },
64
- body: JSON.stringify({
65
- token: input.token,
66
- apiKey: input.apiKey,
67
- service: input.service,
68
- operation: input.operation,
69
- resource: input.resource,
70
- accessControls: input.accessControls,
71
- metadata: input.metadata,
72
- }),
73
- });
74
- const payload = (await response.json());
75
- if (!response.ok || payload.status !== 'SUCCESS') {
76
- throw new UserError(payload.status ?? 'AUTH_FAILED', undefined, payload.message ?? '');
77
- }
78
- return { user: payload.data?.user, token: payload.data?.token ?? input.token };
79
- }
80
- /** account 权限操作注册端点(固定路径) */
81
- const PERMISSION_OPERATION_REGISTER_PATH = '/permission/operation/register';
82
- /**
83
- * 框架内置 account HTTP 客户端:`POST /permission/operation/register`。
84
- *
85
- * 成功判定与 `/gateway/verify` 同口径:HTTP ok **且** 信封 `status === 'SUCCESS'`。
86
- */
87
- async function requestPermissionRegistration(account, body) {
88
- const target = new URL(PERMISSION_OPERATION_REGISTER_PATH, account.serviceUrl);
89
- const response = await fetch(target, {
90
- method: 'POST',
91
- headers: { 'content-type': 'application/json' },
92
- body: JSON.stringify(body),
93
- });
94
- const payload = (await response.json().catch(() => undefined));
95
- if (!response.ok || payload?.status !== 'SUCCESS') {
96
- throw new SystemError('EXTERNAL_SERVICE_ERROR', { cause: response }, payload?.message ?? `权限操作注册失败(HTTP ${response.status})`);
97
- }
98
- }
99
- export function createAuthMiddleware(ctx, options = {}) {
100
- const skip = options.skip ?? defaultAuthSkip;
101
- const mapError = options.mapError ??
102
- ((error) => UserError.is(error) || SystemError.is(error)
103
- ? error
104
- : new SystemError('EXTERNAL_SERVICE_ERROR', { cause: error }));
105
- return {
106
- name: 'auth',
107
- register: (app) => {
108
- const identity = readServerIdentity(app);
109
- const service = identity?.service ?? '';
110
- app.addHook('preValidation', async (request) => {
111
- if (skip(request, ctx)) {
112
- return;
113
- }
114
- const account = readAccount(ctx.config);
115
- // account `serviceUrl` 门槛仅作用于内置 verify:自定义 verify 不受约束,仍照常校验
116
- if (!options.verify && !account?.serviceUrl) {
117
- return;
118
- }
119
- const { token, apiKey } = extractAuth(request, readCookieNames(ctx.config, options.cookieNames));
120
- if (!token && !apiKey) {
121
- throw new UserError('INVALID_TOKEN', undefined, '缺少访问凭证');
122
- }
123
- const verifyInput = {
124
- token,
125
- apiKey,
126
- service,
127
- // 权限目录同源、与 url 解耦:优先路由 `schema.operationId`,未声明时回落 url
128
- operation: request.routeOptions?.schema?.operationId ?? request.routeOptions?.url,
129
- resource: request.params,
130
- accessControls: request.routeOptions?.config?.accessControls,
131
- metadata: {
132
- requestId: request.id,
133
- method: request.method,
134
- url: request.url,
135
- ip: request.ip,
136
- ips: request.ips,
137
- userAgent: request.headers['user-agent'],
138
- referrer: request.headers.referer,
139
- },
140
- };
141
- try {
142
- const result = options.verify
143
- ? await options.verify(verifyInput, ctx)
144
- : await defaultVerify(verifyInput, account);
145
- request.user = result.user;
146
- request.token = result.token;
147
- }
148
- catch (error) {
149
- throw mapError(error);
150
- }
151
- });
152
- if (options.registerPermissions === false) {
153
- return;
154
- }
155
- app.addHook('onReady', async () => {
156
- let account;
157
- try {
158
- account = readAccount(ctx.config);
159
- }
160
- catch (error) {
161
- if (SystemError.is(error) && error.type === 'CONFIG_UNAVAILABLE') {
162
- // 出 spec 路径(generateOpenApi 不跑 config 槽位):配置不可用 → 跳过自注册
163
- return;
164
- }
165
- ctx.log.error({ error }, '[PermissionOperation][auth] read account config failed');
166
- return;
167
- }
168
- // 未配置 account `serviceUrl`(含 account 自身)→ 不注册
169
- if (!account?.serviceUrl) {
170
- return;
171
- }
172
- try {
173
- const operations = toPermissionOperations(readRegisteredOperations(app));
174
- if (operations.length === 0) {
175
- return;
176
- }
177
- await requestPermissionRegistration(account, {
178
- service,
179
- version: identity?.version ?? '',
180
- operations,
181
- });
182
- }
183
- catch (error) {
184
- // 失败不致命:只记日志、继续启动(不得抛错中断)
185
- ctx.log.error({ error }, '[PermissionOperation][auth] self-registration failed');
186
- }
187
- });
188
- },
189
- };
190
- }
@@ -1,13 +0,0 @@
1
- import type { MiddlewareUnit } from '../types.js';
2
- /**
3
- * 标准中间件单元 `context`(可替换)。
4
- *
5
- * `decorateRequest(decorateAs, ...)`(默认 `'ctx'`),消费既有 `context/createContext`。
6
- * 装饰必须在 `ready()` 之前注册(`register` 期完成)。
7
- */
8
- export interface ContextMiddlewareOptions {
9
- /** 默认 `'ctx'` */
10
- decorateAs?: string;
11
- }
12
- export declare function createContextMiddleware(options?: ContextMiddlewareOptions): MiddlewareUnit;
13
- //# sourceMappingURL=context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../source/app/middleware/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD;;;;;GAKG;AAEH,MAAM,WAAW,wBAAwB;IACvC,iBAAiB;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,cAAc,CAa9F"}
@@ -1,15 +0,0 @@
1
- import { createContext } from '../../context/index.js';
2
- export function createContextMiddleware(options = {}) {
3
- const decorateAs = options.decorateAs ?? 'ctx';
4
- return {
5
- name: 'context',
6
- register: (app) => {
7
- app.decorateRequest(decorateAs, null);
8
- app.addHook('onRequest', (request, _reply, done) => {
9
- ;
10
- request[decorateAs] = createContext(request);
11
- done();
12
- });
13
- },
14
- };
15
- }
@@ -1,14 +0,0 @@
1
- import type { MiddlewareContext, MiddlewareUnit } from '../types.js';
2
- /**
3
- * 标准中间件单元 `cors`(可替换)。
4
- *
5
- * 不传 `origins` → **每次请求现读**配置的 `cors` 键(配置加载后即生效,不得缓存);
6
- * **未配置即放行任意源 + 首次请求 warn 一次**(对齐四家现状)。构造期**不读配置**,
7
- * 故出 spec 路径(配置不可用)不受影响。
8
- */
9
- export interface CorsMiddlewareOptions {
10
- origins?: string[] | '*';
11
- credentials?: boolean;
12
- }
13
- export declare function createCorsMiddleware<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>, options?: CorsMiddlewareOptions): MiddlewareUnit;
14
- //# sourceMappingURL=cors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../../source/app/middleware/cors.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEpE;;;;;;GAMG;AAEH,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAOD,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,qBAA0B,GAAG,cAAc,CA6BhJ"}