@taicode/common-server 4.0.0-beta.1 → 4.0.0-beta.10
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/output/app/index.d.ts +18 -15
- package/output/app/index.d.ts.map +1 -1
- package/output/app/index.js +330 -18
- package/output/auth/index.d.ts +73 -0
- package/output/auth/index.d.ts.map +1 -0
- package/output/auth/index.js +196 -0
- package/output/core/assemble.d.ts +64 -0
- package/output/core/assemble.d.ts.map +1 -0
- package/output/core/assemble.js +88 -0
- package/output/core/boot/context.d.ts +109 -0
- package/output/core/boot/context.d.ts.map +1 -0
- package/output/core/boot/context.js +145 -0
- package/output/core/boot/lifecycle.d.ts +206 -0
- package/output/core/boot/lifecycle.d.ts.map +1 -0
- package/output/core/boot/lifecycle.js +255 -0
- package/output/core/boot/shutdown.d.ts +34 -0
- package/output/core/boot/shutdown.d.ts.map +1 -0
- package/output/core/boot/shutdown.js +50 -0
- package/output/core/boot/signal.d.ts +13 -0
- package/output/core/boot/signal.d.ts.map +1 -0
- package/output/{app → core/boot}/signal.js +3 -2
- package/output/core/boot/steps.d.ts +138 -0
- package/output/core/boot/steps.d.ts.map +1 -0
- package/output/core/boot/steps.js +259 -0
- package/output/core/entries.d.ts +151 -0
- package/output/core/entries.d.ts.map +1 -0
- package/output/core/entries.js +242 -0
- package/output/core/entries.test.d.ts +2 -0
- package/output/core/entries.test.d.ts.map +1 -0
- package/output/core/entries.test.js +458 -0
- package/output/core/env.d.ts +83 -0
- package/output/core/env.d.ts.map +1 -0
- package/output/core/env.js +124 -0
- package/output/{app → core}/env.test.d.ts.map +1 -1
- package/output/core/env.test.js +105 -0
- package/output/core/modules/auth-provider/index.d.ts +10 -0
- package/output/core/modules/auth-provider/index.d.ts.map +1 -0
- package/output/core/modules/auth-provider/index.js +9 -0
- package/output/core/modules/auth-provider/types.d.ts +71 -0
- package/output/core/modules/auth-provider/types.d.ts.map +1 -0
- package/output/core/modules/context/index.d.ts +18 -0
- package/output/core/modules/context/index.d.ts.map +1 -0
- package/output/core/modules/context/index.js +23 -0
- package/output/core/modules/context/index.test.d.ts.map +1 -0
- package/output/core/modules/cookie/index.d.ts +21 -0
- package/output/core/modules/cookie/index.d.ts.map +1 -0
- package/output/{app/core/cookie.js → core/modules/cookie/index.js} +1 -6
- package/output/core/modules/identity/index.d.ts +4 -0
- package/output/core/modules/identity/index.d.ts.map +1 -0
- package/output/{app/core/identity.js → core/modules/identity/index.js} +1 -1
- package/output/core/modules/logging/index.d.ts +5 -0
- package/output/core/modules/logging/index.d.ts.map +1 -0
- package/output/{app/core/logging.js → core/modules/logging/index.js} +2 -2
- package/output/core/modules/openapi/index.d.ts +7 -0
- package/output/core/modules/openapi/index.d.ts.map +1 -0
- package/output/{app/core/openapi.js → core/modules/openapi/index.js} +11 -12
- package/output/core/modules/permission-operations/index.d.ts +26 -0
- package/output/core/modules/permission-operations/index.d.ts.map +1 -0
- package/output/core/modules/permission-operations/index.js +36 -0
- package/output/core/modules/permission-operations/index.test.d.ts +2 -0
- package/output/core/modules/permission-operations/index.test.d.ts.map +1 -0
- package/output/core/modules/permission-operations/index.test.js +45 -0
- package/output/{app/core/request-id.d.ts → core/modules/request-id/index.d.ts} +1 -4
- package/output/core/modules/request-id/index.d.ts.map +1 -0
- package/output/{app/core/request-id.js → core/modules/request-id/index.js} +2 -2
- package/output/core/modules/response/index.d.ts +50 -0
- package/output/core/modules/response/index.d.ts.map +1 -0
- package/output/{app/core/response.js → core/modules/response/index.js} +31 -18
- package/output/{schema/index.d.ts → core/modules/schema/builders.d.ts} +8 -1
- package/output/core/modules/schema/builders.d.ts.map +1 -0
- package/output/{schema/index.js → core/modules/schema/builders.js} +7 -4
- package/output/core/modules/schema/index.d.ts +34 -0
- package/output/core/modules/schema/index.d.ts.map +1 -0
- package/output/core/modules/schema/index.js +62 -0
- package/output/core/modules/schema/index.test.d.ts +2 -0
- package/output/core/modules/schema/index.test.d.ts.map +1 -0
- package/output/core/modules/schema/index.test.js +55 -0
- package/output/core/modules/system-routes/index.d.ts +15 -0
- package/output/core/modules/system-routes/index.d.ts.map +1 -0
- package/output/core/modules/system-routes/index.js +205 -0
- package/output/core/resource.d.ts +56 -0
- package/output/core/resource.d.ts.map +1 -0
- package/output/core/resource.js +56 -0
- package/output/core/test-support/database.d.ts +89 -0
- package/output/core/test-support/database.d.ts.map +1 -0
- package/output/core/test-support/database.js +131 -0
- package/output/core/test-support/database.test.d.ts +2 -0
- package/output/core/test-support/database.test.d.ts.map +1 -0
- package/output/core/test-support/database.test.js +111 -0
- package/output/core/test-support/environment.d.ts +38 -0
- package/output/core/test-support/environment.d.ts.map +1 -0
- package/output/core/test-support/environment.js +45 -0
- package/output/core/test-support/service-context.d.ts +64 -0
- package/output/core/test-support/service-context.d.ts.map +1 -0
- package/output/core/test-support/service-context.js +63 -0
- package/output/core/test-support/service-context.test.d.ts +2 -0
- package/output/core/test-support/service-context.test.d.ts.map +1 -0
- package/output/core/test-support/service-context.test.js +55 -0
- package/output/core/test-support/test-server.d.ts +154 -0
- package/output/core/test-support/test-server.d.ts.map +1 -0
- package/output/core/test-support/test-server.js +114 -0
- package/output/core/test-support/test-server.test.d.ts +2 -0
- package/output/core/test-support/test-server.test.d.ts.map +1 -0
- package/output/core/test-support/test-server.test.js +296 -0
- package/output/core/test-support/testdata.d.ts +255 -0
- package/output/core/test-support/testdata.d.ts.map +1 -0
- package/output/core/test-support/testdata.js +271 -0
- package/output/core/test-support/testdata.test.d.ts +2 -0
- package/output/core/test-support/testdata.test.d.ts.map +1 -0
- package/output/core/test-support/testdata.test.js +334 -0
- package/output/{app → core}/type-augment.d.ts +2 -2
- package/output/core/type-augment.d.ts.map +1 -0
- package/output/core/types.d.ts +582 -0
- package/output/core/types.d.ts.map +1 -0
- package/output/cors/index.d.ts +3 -0
- package/output/cors/index.d.ts.map +1 -0
- package/output/cors/index.js +28 -0
- package/output/index.d.ts +10 -4
- package/output/index.d.ts.map +1 -1
- package/output/index.js +25 -5
- package/output/rate-limit/index.d.ts +23 -0
- package/output/rate-limit/index.d.ts.map +1 -0
- package/output/rate-limit/index.js +43 -0
- package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
- package/output/rate-limit/limiter.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/memory-store.d.ts +6 -0
- package/output/rate-limit/memory-store.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/memory-store.js +8 -0
- package/output/{rate-limiter → rate-limit}/redis-store.d.ts +18 -0
- package/output/rate-limit/redis-store.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/redis-store.js +35 -0
- package/output/rate-limit/redis-store.test.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
- package/output/rate-limit/types.d.ts +45 -0
- package/output/rate-limit/types.d.ts.map +1 -0
- package/output/rate-limit/types.js +1 -0
- package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
- package/output/redis/cache.d.ts.map +1 -0
- package/output/redis/cache.test.d.ts +2 -0
- package/output/redis/cache.test.d.ts.map +1 -0
- package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
- package/output/{app/redis.d.ts → redis/index.d.ts} +12 -7
- package/output/redis/index.d.ts.map +1 -0
- package/output/{app/redis.js → redis/index.js} +15 -5
- package/output/redis/index.test.d.ts +2 -0
- package/output/{context → redis}/index.test.d.ts.map +1 -1
- package/output/redis/index.test.js +55 -0
- package/output/redis-queue/index.js +1 -1
- package/output/redis-queue/test-helpers.d.ts +1 -1
- package/output/redis-queue/test-helpers.js +1 -1
- package/output/redis-queue/types.d.ts +3 -12
- package/output/redis-queue/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/output/app/auth.test.d.ts +0 -2
- package/output/app/auth.test.d.ts.map +0 -1
- package/output/app/auth.test.js +0 -148
- package/output/app/boot.d.ts +0 -48
- package/output/app/boot.d.ts.map +0 -1
- package/output/app/boot.js +0 -123
- package/output/app/config.test.d.ts +0 -2
- package/output/app/config.test.d.ts.map +0 -1
- package/output/app/config.test.js +0 -132
- package/output/app/context.d.ts +0 -68
- package/output/app/context.d.ts.map +0 -1
- package/output/app/context.js +0 -73
- package/output/app/core/cookie.d.ts +0 -25
- package/output/app/core/cookie.d.ts.map +0 -1
- package/output/app/core/identity.d.ts +0 -12
- package/output/app/core/identity.d.ts.map +0 -1
- package/output/app/core/index.d.ts +0 -44
- package/output/app/core/index.d.ts.map +0 -1
- package/output/app/core/index.js +0 -57
- package/output/app/core/logging.d.ts +0 -13
- package/output/app/core/logging.d.ts.map +0 -1
- package/output/app/core/openapi.d.ts +0 -17
- package/output/app/core/openapi.d.ts.map +0 -1
- package/output/app/core/request-id.d.ts.map +0 -1
- package/output/app/core/response.d.ts +0 -41
- package/output/app/core/response.d.ts.map +0 -1
- package/output/app/define-server.negative-cases.d.ts +0 -32
- package/output/app/define-server.negative-cases.d.ts.map +0 -1
- package/output/app/define-server.negative-cases.js +0 -209
- package/output/app/define-server.test.d.ts +0 -2
- package/output/app/define-server.test.d.ts.map +0 -1
- package/output/app/define-server.test.js +0 -97
- package/output/app/entries.d.ts +0 -29
- package/output/app/entries.d.ts.map +0 -1
- package/output/app/entries.js +0 -64
- package/output/app/env.d.ts +0 -59
- package/output/app/env.d.ts.map +0 -1
- package/output/app/env.js +0 -66
- package/output/app/env.test.js +0 -124
- package/output/app/error-contract.test.d.ts +0 -2
- package/output/app/error-contract.test.d.ts.map +0 -1
- package/output/app/error-contract.test.js +0 -125
- package/output/app/errors.d.ts +0 -20
- package/output/app/errors.d.ts.map +0 -1
- package/output/app/errors.js +0 -51
- package/output/app/generate-openapi.test.d.ts +0 -2
- package/output/app/generate-openapi.test.d.ts.map +0 -1
- package/output/app/generate-openapi.test.js +0 -123
- package/output/app/lifecycle-logging.test.d.ts +0 -2
- package/output/app/lifecycle-logging.test.d.ts.map +0 -1
- package/output/app/lifecycle-logging.test.js +0 -155
- package/output/app/lifecycle.d.ts +0 -70
- package/output/app/lifecycle.d.ts.map +0 -1
- package/output/app/lifecycle.js +0 -99
- package/output/app/lifecycle.test.d.ts +0 -2
- package/output/app/lifecycle.test.d.ts.map +0 -1
- package/output/app/lifecycle.test.js +0 -152
- package/output/app/middleware/auth.d.ts +0 -71
- package/output/app/middleware/auth.d.ts.map +0 -1
- package/output/app/middleware/auth.js +0 -190
- package/output/app/middleware/context.d.ts +0 -13
- package/output/app/middleware/context.d.ts.map +0 -1
- package/output/app/middleware/context.js +0 -15
- package/output/app/middleware/cors.d.ts +0 -14
- package/output/app/middleware/cors.d.ts.map +0 -1
- package/output/app/middleware/cors.js +0 -30
- package/output/app/middleware/index.d.ts +0 -17
- package/output/app/middleware/index.d.ts.map +0 -1
- package/output/app/middleware/index.js +0 -16
- package/output/app/middleware/rate-limit.d.ts +0 -24
- package/output/app/middleware/rate-limit.d.ts.map +0 -1
- package/output/app/middleware/rate-limit.js +0 -46
- package/output/app/middleware-order.test.d.ts +0 -2
- package/output/app/middleware-order.test.d.ts.map +0 -1
- package/output/app/middleware-order.test.js +0 -148
- package/output/app/permission-operations.d.ts +0 -48
- package/output/app/permission-operations.d.ts.map +0 -1
- package/output/app/permission-operations.js +0 -77
- package/output/app/permission-operations.test.d.ts +0 -2
- package/output/app/permission-operations.test.d.ts.map +0 -1
- package/output/app/permission-operations.test.js +0 -72
- package/output/app/permission-registration.test.d.ts +0 -2
- package/output/app/permission-registration.test.d.ts.map +0 -1
- package/output/app/permission-registration.test.js +0 -138
- package/output/app/redis.d.ts.map +0 -1
- package/output/app/registered-operations.test.d.ts +0 -2
- package/output/app/registered-operations.test.d.ts.map +0 -1
- package/output/app/registered-operations.test.js +0 -57
- package/output/app/request-id.test.d.ts +0 -2
- package/output/app/request-id.test.d.ts.map +0 -1
- package/output/app/request-id.test.js +0 -130
- package/output/app/resources.test.d.ts +0 -2
- package/output/app/resources.test.d.ts.map +0 -1
- package/output/app/resources.test.js +0 -197
- package/output/app/shutdown.d.ts +0 -22
- package/output/app/shutdown.d.ts.map +0 -1
- package/output/app/shutdown.js +0 -30
- package/output/app/shutdown.test.d.ts +0 -2
- package/output/app/shutdown.test.d.ts.map +0 -1
- package/output/app/shutdown.test.js +0 -103
- package/output/app/signal.d.ts +0 -12
- package/output/app/signal.d.ts.map +0 -1
- package/output/app/smoke.test.d.ts +0 -2
- package/output/app/smoke.test.d.ts.map +0 -1
- package/output/app/smoke.test.js +0 -210
- package/output/app/test-support.d.ts +0 -21
- package/output/app/test-support.d.ts.map +0 -1
- package/output/app/test-support.js +0 -24
- package/output/app/type-augment.d.ts.map +0 -1
- package/output/app/types.d.ts +0 -211
- package/output/app/types.d.ts.map +0 -1
- package/output/context/index.d.ts +0 -6
- package/output/context/index.d.ts.map +0 -1
- package/output/context/index.js +0 -14
- package/output/rate-limiter/index.d.ts +0 -5
- package/output/rate-limiter/index.d.ts.map +0 -1
- package/output/rate-limiter/index.js +0 -4
- package/output/rate-limiter/memory-store.d.ts.map +0 -1
- package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
- package/output/rate-limiter/redis-store.d.ts.map +0 -1
- package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
- package/output/rate-limiter/types.d.ts +0 -27
- package/output/rate-limiter/types.d.ts.map +0 -1
- package/output/redis-cache/index.d.ts +0 -4
- package/output/redis-cache/index.d.ts.map +0 -1
- package/output/redis-cache/index.js +0 -1
- package/output/redis-cache/redis-cache.d.ts.map +0 -1
- package/output/redis-cache/redis-cache.test.d.ts +0 -2
- package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
- package/output/schema/index.d.ts.map +0 -1
- /package/output/{app → core}/env.test.d.ts +0 -0
- /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
- /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
- /package/output/{context → core/modules/context}/index.test.js +0 -0
- /package/output/{app → core}/type-augment.js +0 -0
- /package/output/{app → core}/types.js +0 -0
- /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
- /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
- /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
|
@@ -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"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import cors from '@fastify/cors';
|
|
2
|
-
import { readConfigKey } from '../context.js';
|
|
3
|
-
export function createCorsMiddleware(ctx, options = {}) {
|
|
4
|
-
return {
|
|
5
|
-
name: 'cors',
|
|
6
|
-
register: async (app) => {
|
|
7
|
-
let warned = false;
|
|
8
|
-
// 请求期现读:`cors` 配置变化立即生效;构造期不读
|
|
9
|
-
const resolveOrigins = () => {
|
|
10
|
-
const configured = readConfigKey(ctx.config, 'cors');
|
|
11
|
-
return options.origins ?? (configured?.enabled ? configured.origins : undefined);
|
|
12
|
-
};
|
|
13
|
-
await app.register(cors, {
|
|
14
|
-
origin: (origin, callback) => {
|
|
15
|
-
const origins = resolveOrigins();
|
|
16
|
-
if (origins === undefined || origins === '*') {
|
|
17
|
-
if (!warned) {
|
|
18
|
-
warned = true;
|
|
19
|
-
ctx.log.warn({}, '[middleware] cors origins not configured; allowing any origin');
|
|
20
|
-
}
|
|
21
|
-
callback(null, true);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
callback(null, origins.includes(origin ?? ''));
|
|
25
|
-
},
|
|
26
|
-
credentials: options.credentials ?? true,
|
|
27
|
-
});
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { MiddlewareContext, MiddlewareUnit } from '../types.js';
|
|
2
|
-
export { createCorsMiddleware } from './cors.js';
|
|
3
|
-
export { createContextMiddleware } from './context.js';
|
|
4
|
-
export { createAuthMiddleware, defaultAuthSkip } from './auth.js';
|
|
5
|
-
export { createRateLimitMiddleware } from './rate-limit.js';
|
|
6
|
-
export type { CorsMiddlewareOptions } from './cors.js';
|
|
7
|
-
export type { ContextMiddlewareOptions } from './context.js';
|
|
8
|
-
export type { AccountIntegrationConfig, AuthMiddlewareOptions, AuthSkip, AuthVerifyInput, AuthVerifyMetadata, AuthVerifyResult, } from './auth.js';
|
|
9
|
-
export type { RateLimitMiddlewareOptions } from './rate-limit.js';
|
|
10
|
-
/**
|
|
11
|
-
* 默认中间件预设:`[cors, context, auth]`。
|
|
12
|
-
*
|
|
13
|
-
* 顺序 `cors → context → auth` 只是**预设内容**,不是框架常量:可 `...defaultMiddlewares(ctx)`
|
|
14
|
-
* 后替换 / 插入 / 删除任意项。`rateLimit` **不在**预设内(唯一一个默认不装的项)。
|
|
15
|
-
*/
|
|
16
|
-
export declare function defaultMiddlewares<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>): MiddlewareUnit[];
|
|
17
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../source/app/middleware/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,YAAY,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAA;AACtD,YAAY,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAC5D,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,QAAQ,EACR,eAAe,EACf,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAA;AAEjE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EACzD,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC3B,cAAc,EAAE,CAElB"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { createAuthMiddleware } from './auth.js';
|
|
2
|
-
import { createContextMiddleware } from './context.js';
|
|
3
|
-
import { createCorsMiddleware } from './cors.js';
|
|
4
|
-
export { createCorsMiddleware } from './cors.js';
|
|
5
|
-
export { createContextMiddleware } from './context.js';
|
|
6
|
-
export { createAuthMiddleware, defaultAuthSkip } from './auth.js';
|
|
7
|
-
export { createRateLimitMiddleware } from './rate-limit.js';
|
|
8
|
-
/**
|
|
9
|
-
* 默认中间件预设:`[cors, context, auth]`。
|
|
10
|
-
*
|
|
11
|
-
* 顺序 `cors → context → auth` 只是**预设内容**,不是框架常量:可 `...defaultMiddlewares(ctx)`
|
|
12
|
-
* 后替换 / 插入 / 删除任意项。`rateLimit` **不在**预设内(唯一一个默认不装的项)。
|
|
13
|
-
*/
|
|
14
|
-
export function defaultMiddlewares(ctx) {
|
|
15
|
-
return [createCorsMiddleware(ctx), createContextMiddleware(), createAuthMiddleware(ctx)];
|
|
16
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { MiddlewareContext, MiddlewareUnit } from '../types.js';
|
|
2
|
-
import type { RateLimitStore } from '../../rate-limiter/types.js';
|
|
3
|
-
/**
|
|
4
|
-
* 标准中间件单元 `rateLimit`(可替换;**不在默认预设内**)。
|
|
5
|
-
*
|
|
6
|
-
* 路由级限流;存储后端由调用方注入(缺省进程内 memory)。`TOO_MANY_REQUESTS` 经 `UserError`
|
|
7
|
-
* 仍映射 **400**。路由未声明 `config.rateLimit` 且未给 `defaultLimit` 时该项空转。
|
|
8
|
-
*
|
|
9
|
-
* 注:中间件的 ctx 看不到 `resources`(中间件不得直连数据层),因此 Redis store 需由调用方
|
|
10
|
-
* 在 `services` 侧构造后注入(`store`),框架不再自行探测 Redis。
|
|
11
|
-
*/
|
|
12
|
-
export interface RateLimitMiddlewareOptions {
|
|
13
|
-
/** 存储后端(缺省进程内 memory) */
|
|
14
|
-
store?: RateLimitStore;
|
|
15
|
-
/** 限流键前缀(缺省 `<service>:`) */
|
|
16
|
-
keyPrefix?: string;
|
|
17
|
-
/** 缺省规则:路由未声明 `config.rateLimit` 时使用 */
|
|
18
|
-
defaultLimit?: {
|
|
19
|
-
max: number;
|
|
20
|
-
windowMs: number;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export declare function createRateLimitMiddleware<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>, options?: RateLimitMiddlewareOptions): MiddlewareUnit;
|
|
24
|
-
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../source/app/middleware/rate-limit.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAGjE;;;;;;;;GAQG;AAEH,MAAM,WAAW,0BAA0B;IACzC,yBAAyB;IACzB,KAAK,CAAC,EAAE,cAAc,CAAA;IACtB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wCAAwC;IACxC,YAAY,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CACjD;AAsBD,wBAAgB,yBAAyB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,0BAA+B,GAAG,cAAc,CA0B1J"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { createRateLimitKey, RateLimiter } from '../../rate-limiter/rate-limiter.js';
|
|
2
|
-
import { MemoryRateLimitStore } from '../../rate-limiter/memory-store.js';
|
|
3
|
-
import { readServerIdentity } from '../core/identity.js';
|
|
4
|
-
import { UserError } from '../errors.js';
|
|
5
|
-
function resolveRule(request, defaultRule) {
|
|
6
|
-
const configured = request.routeOptions?.config?.rateLimit;
|
|
7
|
-
return configured ?? defaultRule;
|
|
8
|
-
}
|
|
9
|
-
function buildKeys(request, keys) {
|
|
10
|
-
return keys.map((key) => {
|
|
11
|
-
if (key === 'ip') {
|
|
12
|
-
return `ip:${request.ip}`;
|
|
13
|
-
}
|
|
14
|
-
if (key === 'user') {
|
|
15
|
-
return `user:${String(request.user?.id ?? 'anonymous')}`;
|
|
16
|
-
}
|
|
17
|
-
if (key === 'path') {
|
|
18
|
-
return `path:${request.routeOptions?.url ?? request.url}`;
|
|
19
|
-
}
|
|
20
|
-
return `${key}:${String(request[key] ?? '')}`;
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
export function createRateLimitMiddleware(ctx, options = {}) {
|
|
24
|
-
void ctx;
|
|
25
|
-
return {
|
|
26
|
-
name: 'rateLimit',
|
|
27
|
-
register: (app) => {
|
|
28
|
-
const limiter = new RateLimiter({ store: options.store ?? new MemoryRateLimitStore() });
|
|
29
|
-
const prefix = options.keyPrefix ?? `${readServerIdentity(app)?.service ?? ''}:`;
|
|
30
|
-
const defaultRule = options.defaultLimit
|
|
31
|
-
? { limit: options.defaultLimit.max, window: options.defaultLimit.windowMs / 1000 }
|
|
32
|
-
: undefined;
|
|
33
|
-
app.addHook('preValidation', async (request) => {
|
|
34
|
-
const rule = resolveRule(request, defaultRule);
|
|
35
|
-
if (!rule) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const keys = buildKeys(request, rule.keys ?? ['ip']).map((key) => createRateLimitKey(prefix, key));
|
|
39
|
-
const result = await limiter.checkMultiLimit(keys, { limit: rule.limit, window: rule.window });
|
|
40
|
-
if (!result.allowed) {
|
|
41
|
-
throw new UserError('TOO_MANY_REQUESTS', undefined, '请求过于频繁');
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"middleware-order.test.d.ts","sourceRoot":"","sources":["../../source/app/middleware-order.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { describe, expect, expectTypeOf, it } from 'vitest';
|
|
2
|
-
import { defineServer, startServer } from './entries.js';
|
|
3
|
-
import { defaultMiddlewares } from './middleware/index.js';
|
|
4
|
-
/**
|
|
5
|
-
* 中间件数组序(N1 §1 / §4)。
|
|
6
|
-
*
|
|
7
|
-
* `middlewares(ctx)` 返回**有序数组**,框架**严格按数组序**注册(无键、无枚举、无内置相对顺序魔法);
|
|
8
|
-
* `rateLimit` **不在**默认预设内。中间件单元是 `{ name?, register(app) }`:**不参与生命周期**。
|
|
9
|
-
*/
|
|
10
|
-
const fakeMiddlewareContext = {
|
|
11
|
-
app: {},
|
|
12
|
-
log: { debug: () => { }, info: () => { }, warn: () => { }, error: () => { } },
|
|
13
|
-
services: {},
|
|
14
|
-
config: {},
|
|
15
|
-
};
|
|
16
|
-
describe('ctx 可见性(N1 §2)', () => {
|
|
17
|
-
it('MiddlewareContext 有 app / log / services / config;没有 resources / env / disposer', () => {
|
|
18
|
-
expectTypeOf().toHaveProperty('app');
|
|
19
|
-
expectTypeOf().toHaveProperty('services');
|
|
20
|
-
expectTypeOf().toHaveProperty('config');
|
|
21
|
-
// @ts-expect-error 中间件看不到 resources(禁直连数据层)
|
|
22
|
-
void 0..resources;
|
|
23
|
-
// @ts-expect-error 中间件看不到 env
|
|
24
|
-
void 0..env;
|
|
25
|
-
// @ts-expect-error 中间件看不到 disposer(disposer 只属 services)
|
|
26
|
-
void 0..disposer;
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
describe('默认预设', () => {
|
|
30
|
-
it('defaultMiddlewares 为 [cors, context, auth],且不含 rateLimit', () => {
|
|
31
|
-
const units = defaultMiddlewares(fakeMiddlewareContext);
|
|
32
|
-
expect(units.map((unit) => unit.name)).toEqual(['cors', 'context', 'auth']);
|
|
33
|
-
expect(units.map((unit) => unit.name)).not.toContain('rateLimit');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
describe('数组序即注册序', () => {
|
|
37
|
-
it('严格按数组序执行每一项注册的钩子', async () => {
|
|
38
|
-
const calls = [];
|
|
39
|
-
const makeUnit = (name) => ({
|
|
40
|
-
name,
|
|
41
|
-
register: (app) => {
|
|
42
|
-
app.addHook('preValidation', (_request, _reply, done) => {
|
|
43
|
-
calls.push(name);
|
|
44
|
-
done();
|
|
45
|
-
});
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
const server = defineServer({
|
|
49
|
-
app: { service: 'middleware-order', title: 'Middleware Order', version: '1.0.0' },
|
|
50
|
-
resources: () => ({}),
|
|
51
|
-
config: () => ({}),
|
|
52
|
-
services: () => ({}),
|
|
53
|
-
middlewares: () => [makeUnit('third'), makeUnit('first'), makeUnit('second')],
|
|
54
|
-
routers: (ctx) => {
|
|
55
|
-
ctx.app.get('/ping', () => ({ pong: true }));
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
const app = await startServer(server, { listen: false });
|
|
59
|
-
const response = await app.inject({ method: 'GET', url: '/ping' });
|
|
60
|
-
expect(response.statusCode).toBe(200);
|
|
61
|
-
expect(calls).toEqual(['third', 'first', 'second']);
|
|
62
|
-
await app.close();
|
|
63
|
-
});
|
|
64
|
-
it('ctx.services 与 services 槽位产物是同一个实例(M5 强类型)', async () => {
|
|
65
|
-
const servicesValue = { user: { find: (id) => `user:${id}` } };
|
|
66
|
-
let seen;
|
|
67
|
-
let seenFind;
|
|
68
|
-
const server = defineServer({
|
|
69
|
-
app: { service: 'middleware-services', title: 'Middleware Services', version: '1.0.0' },
|
|
70
|
-
resources: () => ({}),
|
|
71
|
-
config: () => ({}),
|
|
72
|
-
services: () => servicesValue,
|
|
73
|
-
middlewares: (ctx) => {
|
|
74
|
-
seen = ctx.services;
|
|
75
|
-
seenFind = ctx.services.user.find;
|
|
76
|
-
return [];
|
|
77
|
-
},
|
|
78
|
-
routers: (ctx) => {
|
|
79
|
-
ctx.app.get('/ping', () => ({ pong: true }));
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
const app = await startServer(server, { listen: false });
|
|
83
|
-
expect(seen).toBe(servicesValue);
|
|
84
|
-
expect(seenFind).toBe(servicesValue.user.find);
|
|
85
|
-
await app.close();
|
|
86
|
-
});
|
|
87
|
-
it('注册的钩子返回 reply → 终止链(后续单元不执行)', async () => {
|
|
88
|
-
const calls = [];
|
|
89
|
-
const server = defineServer({
|
|
90
|
-
app: { service: 'middleware-terminate', title: 'Middleware Terminate', version: '1.0.0' },
|
|
91
|
-
resources: () => ({}),
|
|
92
|
-
config: () => ({}),
|
|
93
|
-
services: () => ({}),
|
|
94
|
-
middlewares: () => [
|
|
95
|
-
{
|
|
96
|
-
name: 'short',
|
|
97
|
-
register: (app) => {
|
|
98
|
-
app.addHook('preValidation', (_request, reply, done) => {
|
|
99
|
-
calls.push('short');
|
|
100
|
-
void reply.status(418).send({ stopped: true });
|
|
101
|
-
done();
|
|
102
|
-
});
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
name: 'after',
|
|
107
|
-
register: (app) => {
|
|
108
|
-
app.addHook('preValidation', (_request, _reply, done) => {
|
|
109
|
-
calls.push('after');
|
|
110
|
-
done();
|
|
111
|
-
});
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
],
|
|
115
|
-
routers: (ctx) => {
|
|
116
|
-
ctx.app.get('/ping', () => ({ pong: true }));
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
const app = await startServer(server, { listen: false });
|
|
120
|
-
const response = await app.inject({ method: 'GET', url: '/ping' });
|
|
121
|
-
expect(response.statusCode).toBe(418);
|
|
122
|
-
expect(calls).toEqual(['short']);
|
|
123
|
-
await app.close();
|
|
124
|
-
});
|
|
125
|
-
it('出 spec 路径也注册中间件(保证 schema 一致)', async () => {
|
|
126
|
-
const { generateOpenApi } = await import('./entries.js');
|
|
127
|
-
const calls = [];
|
|
128
|
-
const server = defineServer({
|
|
129
|
-
app: { service: 'middleware-openapi', title: 'Middleware OpenApi', version: '1.0.0' },
|
|
130
|
-
resources: () => ({}),
|
|
131
|
-
config: () => ({}),
|
|
132
|
-
services: () => ({}),
|
|
133
|
-
middlewares: () => [
|
|
134
|
-
{
|
|
135
|
-
name: 'probe',
|
|
136
|
-
register: () => {
|
|
137
|
-
calls.push('register:probe');
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
routers: (ctx) => {
|
|
142
|
-
ctx.app.get('/ping', () => ({ pong: true }));
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
await generateOpenApi(server);
|
|
146
|
-
expect(calls).toEqual(['register:probe']);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { FastifyInstance } from 'fastify';
|
|
2
|
-
/**
|
|
3
|
-
* 权限操作提取(设计 §5.2)——**框架内置**。
|
|
4
|
-
*
|
|
5
|
-
* 两条来源:
|
|
6
|
-
*
|
|
7
|
-
* 1. **路由注册表**(`installRegisteredOperations` / `readRegisteredOperations`):core 装配期
|
|
8
|
-
* 在 root 挂 `onRoute` 钩子,采集每条路由的 `{ method, url, operationId, summary, description, tags }`。
|
|
9
|
-
* `auth` 的权限自注册只读这张表,**不依赖 `app.swagger()`**(swagger 只负责出文档)。
|
|
10
|
-
* 2. **OpenAPI 文档提取**(`extractPermissionOperations`):从 `paths` 提取。
|
|
11
|
-
* 各仓的 `onReady` 自注册(如 account 作为签发方走进程内调用)用它与 `app.swagger()` 配合,
|
|
12
|
-
* **替代各仓自写实现**(已从公共面导出)。
|
|
13
|
-
*
|
|
14
|
-
* 目录元素形状(`{ operation, summary?, description?, tags? }`)与各仓原自写实现一致:
|
|
15
|
-
*
|
|
16
|
-
* - 缺 `operationId` 的条目(如 `parameters` / vendor 扩展 / 文档路由)**跳过**;
|
|
17
|
-
* - 缺 `paths` / 空文档 → 返回 `[]`;
|
|
18
|
-
* - `summary` / `description` / `tags` 缺省分别归一为 `''` / `''` / `[]`。
|
|
19
|
-
*/
|
|
20
|
-
/** 单条权限操作(对齐 account `POST /permission/operation/register` 的 `operations` 元素) */
|
|
21
|
-
export interface PermissionOperation {
|
|
22
|
-
operation: string;
|
|
23
|
-
summary: string;
|
|
24
|
-
description: string;
|
|
25
|
-
tags: string[];
|
|
26
|
-
}
|
|
27
|
-
/** 单条已注册路由的采集项(`onRoute` 期收集;`operationId` 缺省表示该路由未声明) */
|
|
28
|
-
export interface RegisteredOperation {
|
|
29
|
-
method: string;
|
|
30
|
-
url: string;
|
|
31
|
-
operationId?: string;
|
|
32
|
-
summary?: string;
|
|
33
|
-
description?: string;
|
|
34
|
-
tags?: string[];
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* 在 root 挂 `onRoute` 钩子,收集框架自身可见的**路由注册表**(设计 §5.2)。
|
|
38
|
-
*
|
|
39
|
-
* 必须在**路由注册之前**(core 装配期)调用:钩子挂在 root,随后 `routers` 阶段注册的路由逐条被采集。
|
|
40
|
-
* 该表与 swagger 解耦:swagger 只负责出文档,`auth` 的权限自注册只读这张表。
|
|
41
|
-
*/
|
|
42
|
-
export declare function installRegisteredOperations(app: FastifyInstance): void;
|
|
43
|
-
/** 读取 root `onRoute` 采集的路由注册表;未安装(未走框架装配)时返回空数组。 */
|
|
44
|
-
export declare function readRegisteredOperations(app: FastifyInstance): RegisteredOperation[];
|
|
45
|
-
/** 从路由注册表筛出声明了 `operationId` 的项,归一为权限操作目录元素 */
|
|
46
|
-
export declare function toPermissionOperations(operations: readonly RegisteredOperation[]): PermissionOperation[];
|
|
47
|
-
export declare function extractPermissionOperations(openApiSpec: unknown): PermissionOperation[];
|
|
48
|
-
//# sourceMappingURL=permission-operations.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"permission-operations.d.ts","sourceRoot":"","sources":["../../source/app/permission-operations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,SAAS,CAAA;AAE3D;;;;;;;;;;;;;;;;;GAiBG;AAEH,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAeD,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAaD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CA+BtE;AAED,oDAAoD;AACpD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,eAAe,GAAG,mBAAmB,EAAE,CAIpF;AAED,+CAA+C;AAC/C,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,SAAS,mBAAmB,EAAE,GAAG,mBAAmB,EAAE,CAcxG;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,OAAO,GAAG,mBAAmB,EAAE,CAoBvF"}
|