@southwind-ai/shared 0.2.8 → 0.3.1
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/index.ts +3 -1
- package/package.json +3 -2
- package/src/license-console-capabilities.ts +163 -0
- package/src/license-console.ts +94 -0
- package/src/license.ts +5 -0
- package/src/structured-log.ts +215 -0
- package/src/ruoyi.ts +0 -98
package/index.ts
CHANGED
|
@@ -17,13 +17,13 @@ export * from "./src/migration-bundle-validation.js";
|
|
|
17
17
|
export * from "./src/mcp.js";
|
|
18
18
|
export * from "./src/module-configuration.js";
|
|
19
19
|
export * from "./src/correlation.js";
|
|
20
|
+
export * from "./src/structured-log.js";
|
|
20
21
|
export * from "./src/notification.js";
|
|
21
22
|
export * from "./src/password.js";
|
|
22
23
|
export * from "./src/platform-settings.js";
|
|
23
24
|
export * from "./src/profile.js";
|
|
24
25
|
export * from "./src/primitives.js";
|
|
25
26
|
export * from "./src/rbac.js";
|
|
26
|
-
export * from "./src/ruoyi.js";
|
|
27
27
|
export * from "./src/scheduler.js";
|
|
28
28
|
export * from "./src/search-provider.js";
|
|
29
29
|
// @windy-module system.agent begin
|
|
@@ -31,3 +31,5 @@ export * from "./src/screenshot-context-digest.js";
|
|
|
31
31
|
// @windy-module system.agent end
|
|
32
32
|
export * from "./src/stable-key.js";
|
|
33
33
|
export * from "./src/workflow.js";
|
|
34
|
+
|
|
35
|
+
export * from "./src/license-console-capabilities.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@southwind-ai/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit --project tsconfig.json"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
|
-
".": "./index.ts"
|
|
18
|
+
".": "./index.ts",
|
|
19
|
+
"./structured-log": "./src/structured-log.ts"
|
|
19
20
|
},
|
|
20
21
|
"publishConfig": {
|
|
21
22
|
"access": "public"
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { LicenseOperatorPermission } from "./license.js";
|
|
2
|
+
|
|
3
|
+
/** 厂商签发中心独立认证域;不装配到客户 system.license。 */
|
|
4
|
+
export const LICENSE_CONSOLE_CAPABILITIES = {
|
|
5
|
+
records: {
|
|
6
|
+
feature: "license.record",
|
|
7
|
+
permission: "license.record.read",
|
|
8
|
+
entry: "records",
|
|
9
|
+
apis: [
|
|
10
|
+
"GET /api/licenses",
|
|
11
|
+
"GET /api/licenses/:id",
|
|
12
|
+
"GET /api/licenses/:id/download",
|
|
13
|
+
"GET /api/licenses/:id/activation-code",
|
|
14
|
+
"GET /api/licenses/:id/events",
|
|
15
|
+
"GET /api/events",
|
|
16
|
+
"GET /api/customers",
|
|
17
|
+
"GET /api/customers/:id",
|
|
18
|
+
"GET /api/projects",
|
|
19
|
+
"GET /api/projects/:id",
|
|
20
|
+
"GET /api/contracts",
|
|
21
|
+
"GET /api/contracts/:id",
|
|
22
|
+
"GET /api/license-versions",
|
|
23
|
+
"GET /api/license-versions/:id",
|
|
24
|
+
"GET /api/nodes",
|
|
25
|
+
"GET /api/nodes/:id",
|
|
26
|
+
"GET /api/keys/active",
|
|
27
|
+
"GET /api/projects/:projectId/contracts",
|
|
28
|
+
"GET /api/projects/:projectId/nodes",
|
|
29
|
+
"GET /api/projects/:projectId/license-versions",
|
|
30
|
+
"GET /api/projects/:projectId/licenses",
|
|
31
|
+
"GET /api/keys",
|
|
32
|
+
"GET /api/signing/diagnostics",
|
|
33
|
+
],
|
|
34
|
+
auditActions: ["license.downloaded"],
|
|
35
|
+
},
|
|
36
|
+
projects: {
|
|
37
|
+
feature: "license.project",
|
|
38
|
+
permission: "license.project.manage",
|
|
39
|
+
entry: "maintenance",
|
|
40
|
+
apis: [
|
|
41
|
+
"POST /api/customers",
|
|
42
|
+
"POST /api/projects",
|
|
43
|
+
"POST /api/contracts",
|
|
44
|
+
"POST /api/license-versions",
|
|
45
|
+
"POST /api/nodes",
|
|
46
|
+
"PATCH /api/customers/:id",
|
|
47
|
+
"PATCH /api/projects/:id",
|
|
48
|
+
"PATCH /api/contracts/:id",
|
|
49
|
+
"PATCH /api/license-versions/:id",
|
|
50
|
+
"PATCH /api/nodes/:id",
|
|
51
|
+
"PATCH /api/customers/:id/archive",
|
|
52
|
+
"PATCH /api/projects/:id/archive",
|
|
53
|
+
"PATCH /api/contracts/:id/archive",
|
|
54
|
+
"PATCH /api/license-versions/:id/archive",
|
|
55
|
+
"PATCH /api/nodes/:id/archive",
|
|
56
|
+
],
|
|
57
|
+
auditActions: ["crud.create", "crud.update"],
|
|
58
|
+
},
|
|
59
|
+
issuance: {
|
|
60
|
+
feature: "license.issue",
|
|
61
|
+
permission: "license.issue",
|
|
62
|
+
entry: "project-issuance",
|
|
63
|
+
apis: [
|
|
64
|
+
"POST /api/projects/:projectId/licenses",
|
|
65
|
+
"POST /api/projects/:projectId/license-review",
|
|
66
|
+
"GET /api/projects/:projectId/issuance-requests/:key",
|
|
67
|
+
"POST /api/licenses",
|
|
68
|
+
"POST /api/licenses/:id/revocation-marks",
|
|
69
|
+
"POST /api/licenses/:id/note-corrections",
|
|
70
|
+
],
|
|
71
|
+
auditActions: [
|
|
72
|
+
"license.issued",
|
|
73
|
+
"license.replaced",
|
|
74
|
+
"license.superseded",
|
|
75
|
+
"license.revocation-marked",
|
|
76
|
+
"license.note-corrected",
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
keys: {
|
|
80
|
+
feature: "license.key",
|
|
81
|
+
permission: "license.key.rotate",
|
|
82
|
+
entry: "operations",
|
|
83
|
+
apis: ["POST /api/keys/rotate", "POST /api/keys/:keyId/compatibility"],
|
|
84
|
+
auditActions: ["license.key.rotated", "license.key.compatibility-declared"],
|
|
85
|
+
},
|
|
86
|
+
operators: {
|
|
87
|
+
feature: "license.operator",
|
|
88
|
+
permission: "license.operator.manage",
|
|
89
|
+
entry: "administration",
|
|
90
|
+
apis: [
|
|
91
|
+
"GET /api/operators",
|
|
92
|
+
"POST /api/operators",
|
|
93
|
+
"PATCH /api/operators/:id",
|
|
94
|
+
],
|
|
95
|
+
auditActions: ["license.operator.created", "license.operator.updated"],
|
|
96
|
+
},
|
|
97
|
+
sessions: {
|
|
98
|
+
feature: "license.session",
|
|
99
|
+
permission: "license.session.revoke",
|
|
100
|
+
entry: "administration",
|
|
101
|
+
apis: ["POST /api/auth/sessions/:id/revoke"],
|
|
102
|
+
auditActions: ["auth.session.revoke"],
|
|
103
|
+
},
|
|
104
|
+
audit: {
|
|
105
|
+
feature: "license.audit",
|
|
106
|
+
permission: "license.audit.read",
|
|
107
|
+
entry: "administration",
|
|
108
|
+
apis: ["GET /api/audit-logs"],
|
|
109
|
+
auditActions: [],
|
|
110
|
+
},
|
|
111
|
+
} as const satisfies Record<
|
|
112
|
+
string,
|
|
113
|
+
{
|
|
114
|
+
feature: string;
|
|
115
|
+
permission: LicenseOperatorPermission;
|
|
116
|
+
entry: string;
|
|
117
|
+
apis: readonly string[];
|
|
118
|
+
auditActions: readonly string[];
|
|
119
|
+
}
|
|
120
|
+
>;
|
|
121
|
+
|
|
122
|
+
export interface LicenseConsoleCapability {
|
|
123
|
+
feature: string;
|
|
124
|
+
permission: LicenseOperatorPermission;
|
|
125
|
+
entry: string;
|
|
126
|
+
apis: readonly string[];
|
|
127
|
+
auditActions: readonly string[];
|
|
128
|
+
}
|
|
129
|
+
export function validateLicenseConsoleCapabilities(
|
|
130
|
+
items: readonly LicenseConsoleCapability[] = Object.values(
|
|
131
|
+
LICENSE_CONSOLE_CAPABILITIES,
|
|
132
|
+
),
|
|
133
|
+
): void {
|
|
134
|
+
const features = new Set<string>();
|
|
135
|
+
const permissions = new Set<string>();
|
|
136
|
+
const apis = new Set<string>();
|
|
137
|
+
for (const item of items) {
|
|
138
|
+
if (features.has(item.feature) || permissions.has(item.permission))
|
|
139
|
+
throw new Error("签发中心能力声明重复");
|
|
140
|
+
if (
|
|
141
|
+
item.permission !== item.feature &&
|
|
142
|
+
!item.permission.startsWith(`${item.feature}.`)
|
|
143
|
+
)
|
|
144
|
+
throw new Error("签发中心权限基础 Key 不匹配");
|
|
145
|
+
features.add(item.feature);
|
|
146
|
+
permissions.add(item.permission);
|
|
147
|
+
for (const api of item.apis) {
|
|
148
|
+
if (apis.has(api)) throw new Error("签发中心 API 声明重复");
|
|
149
|
+
apis.add(api);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function hasLicenseConsolePermission(
|
|
155
|
+
permissions: readonly string[],
|
|
156
|
+
permission: string,
|
|
157
|
+
): boolean {
|
|
158
|
+
return (
|
|
159
|
+
Object.values(LICENSE_CONSOLE_CAPABILITIES).some(
|
|
160
|
+
(item) => item.permission === permission,
|
|
161
|
+
) && permissions.includes(permission)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LicenseContractVersion,
|
|
3
|
+
LicenseCustomer,
|
|
4
|
+
LicenseIssueRecord,
|
|
5
|
+
LicenseNode,
|
|
6
|
+
LicenseProject,
|
|
7
|
+
LicenseReplacement,
|
|
8
|
+
LicenseV1Claims,
|
|
9
|
+
LicenseVersion,
|
|
10
|
+
} from "./license.js";
|
|
11
|
+
|
|
12
|
+
export interface LicenseIssueSummary {
|
|
13
|
+
id: string;
|
|
14
|
+
customerId: string;
|
|
15
|
+
projectId: string;
|
|
16
|
+
contractVersionId: string;
|
|
17
|
+
nodeId: string;
|
|
18
|
+
operatorId: string;
|
|
19
|
+
claims: LicenseV1Claims;
|
|
20
|
+
targetSoftwareVersion?: string;
|
|
21
|
+
predecessor?: LicenseReplacement;
|
|
22
|
+
successor?: LicenseReplacement;
|
|
23
|
+
revocationMarked: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface LicenseConsoleCollections {
|
|
26
|
+
customers: LicenseCustomer;
|
|
27
|
+
projects: LicenseProject;
|
|
28
|
+
contracts: LicenseContractVersion;
|
|
29
|
+
nodes: LicenseNode;
|
|
30
|
+
"license-versions": LicenseVersion;
|
|
31
|
+
licenses: LicenseIssueSummary;
|
|
32
|
+
}
|
|
33
|
+
export interface LicenseListQuery {
|
|
34
|
+
page: number;
|
|
35
|
+
pageSize: number;
|
|
36
|
+
q?: string;
|
|
37
|
+
customerId?: string;
|
|
38
|
+
projectId?: string;
|
|
39
|
+
status?: string;
|
|
40
|
+
contractVersionId?: string;
|
|
41
|
+
nodeId?: string;
|
|
42
|
+
machineCode?: string;
|
|
43
|
+
licenseVersionKey?: string;
|
|
44
|
+
issuedFrom?: string;
|
|
45
|
+
issuedTo?: string;
|
|
46
|
+
expiresFrom?: string;
|
|
47
|
+
expiresTo?: string;
|
|
48
|
+
expiresWithinDays?: 7 | 30 | 60;
|
|
49
|
+
}
|
|
50
|
+
export interface LicensePage<T> {
|
|
51
|
+
items: T[];
|
|
52
|
+
total: number;
|
|
53
|
+
page: number;
|
|
54
|
+
pageSize: number;
|
|
55
|
+
}
|
|
56
|
+
export interface LicenseNewNode {
|
|
57
|
+
code: string;
|
|
58
|
+
name: string;
|
|
59
|
+
purpose?: string;
|
|
60
|
+
machineCode: string;
|
|
61
|
+
}
|
|
62
|
+
export interface LicenseReviewCommand {
|
|
63
|
+
contractVersionId: string;
|
|
64
|
+
nodeId?: string;
|
|
65
|
+
newNode?: LicenseNewNode;
|
|
66
|
+
expiresAt: string;
|
|
67
|
+
softwareVersion: string;
|
|
68
|
+
replacementOfLicenseId?: string;
|
|
69
|
+
replacementReason?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface LicenseReviewConfirmation {
|
|
72
|
+
customer: LicenseCustomer;
|
|
73
|
+
project: LicenseProject;
|
|
74
|
+
contract: LicenseContractVersion;
|
|
75
|
+
licenseVersion: LicenseVersion;
|
|
76
|
+
node: LicenseNewNode;
|
|
77
|
+
keyId: string;
|
|
78
|
+
expiresAt: string;
|
|
79
|
+
softwareVersion: string;
|
|
80
|
+
replacementOfLicenseId?: string;
|
|
81
|
+
replacementReason?: string;
|
|
82
|
+
}
|
|
83
|
+
export interface LicenseReviewResult {
|
|
84
|
+
reviewDigest: string;
|
|
85
|
+
confirmation: LicenseReviewConfirmation;
|
|
86
|
+
blockingReasons: string[];
|
|
87
|
+
}
|
|
88
|
+
export interface LicenseRequestStatus {
|
|
89
|
+
status: "processing" | "failed" | "completed";
|
|
90
|
+
licenseId: string;
|
|
91
|
+
result?: LicenseIssueRecord;
|
|
92
|
+
error?: { code: string; reason: string };
|
|
93
|
+
retryAfterSeconds?: number;
|
|
94
|
+
}
|
package/src/license.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EntityId, ISODateTime } from "./primitives.js";
|
|
2
2
|
|
|
3
3
|
export * from "./license-legacy.js";
|
|
4
|
+
export * from "./license-console.js";
|
|
4
5
|
|
|
5
6
|
declare const entitlementKeyBrand: unique symbol;
|
|
6
7
|
declare const licenseVersionKeyBrand: unique symbol;
|
|
@@ -132,6 +133,8 @@ export interface LicenseProject {
|
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
export interface LicenseContractVersion {
|
|
136
|
+
predecessorId?: EntityId;
|
|
137
|
+
issuedCount?: number;
|
|
135
138
|
id: EntityId;
|
|
136
139
|
projectId: EntityId;
|
|
137
140
|
contractNumber: string;
|
|
@@ -147,6 +150,7 @@ export interface LicenseContractVersion {
|
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
export interface LicenseNode {
|
|
153
|
+
issuedCount?: number;
|
|
150
154
|
id: EntityId;
|
|
151
155
|
projectId: EntityId;
|
|
152
156
|
code: string;
|
|
@@ -200,6 +204,7 @@ export type LicenseOperatorRole =
|
|
|
200
204
|
|
|
201
205
|
export type LicenseOperatorPermission =
|
|
202
206
|
| "license.record.read"
|
|
207
|
+
| "license.audit.read"
|
|
203
208
|
| "license.issue"
|
|
204
209
|
| "license.project.manage"
|
|
205
210
|
| "license.key.rotate"
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/** 运行日志只承载排障线索;业务审计和 Agent Run 事实使用各自的持久化入口。 */
|
|
2
|
+
export const STRUCTURED_LOG_MAX_BYTES = 2048;
|
|
3
|
+
|
|
4
|
+
export type StructuredLogLevel = "info" | "warn" | "error";
|
|
5
|
+
export type StructuredLogOutcome = "success" | "failure" | "denied";
|
|
6
|
+
|
|
7
|
+
export interface StructuredLogInput {
|
|
8
|
+
event: string;
|
|
9
|
+
requestId?: string;
|
|
10
|
+
runId?: string;
|
|
11
|
+
jobId?: string;
|
|
12
|
+
traceId?: string;
|
|
13
|
+
diagnosticId?: string;
|
|
14
|
+
outcome?: StructuredLogOutcome;
|
|
15
|
+
errorCode?: string;
|
|
16
|
+
stageKey?: string;
|
|
17
|
+
handlerKey?: string;
|
|
18
|
+
queue?: string;
|
|
19
|
+
attemptNumber?: number;
|
|
20
|
+
method?: string;
|
|
21
|
+
/** 只接受框架登记的路由模板,不能传请求 URL 或实际对象路径。 */
|
|
22
|
+
route?: string;
|
|
23
|
+
statusCode?: number;
|
|
24
|
+
durationMs?: number;
|
|
25
|
+
actorType?: string;
|
|
26
|
+
authority?: string;
|
|
27
|
+
port?: number;
|
|
28
|
+
created?: number;
|
|
29
|
+
metadataUpdated?: number;
|
|
30
|
+
archived?: number;
|
|
31
|
+
unchanged?: number;
|
|
32
|
+
queued?: number;
|
|
33
|
+
dropped?: number;
|
|
34
|
+
completedUnits?: number;
|
|
35
|
+
totalUnits?: number;
|
|
36
|
+
count?: number;
|
|
37
|
+
/** 兼容现有 Logger 接口;未知字段在序列化时一律丢弃。 */
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface StructuredLogSink {
|
|
42
|
+
info(record: Record<string, unknown>): void;
|
|
43
|
+
warn(record: Record<string, unknown>): void;
|
|
44
|
+
error(record: Record<string, unknown>): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface StructuredLogger {
|
|
48
|
+
info(input: StructuredLogInput, message?: string): void;
|
|
49
|
+
warn(input: StructuredLogInput, message?: string): void;
|
|
50
|
+
error(input: StructuredLogInput, message?: string): void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const stableKey = /^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
|
|
54
|
+
const correlationKey = /^[A-Za-z0-9._-]{1,128}$/;
|
|
55
|
+
const errorKey = /^[A-Z][A-Z0-9_]{0,95}$/;
|
|
56
|
+
const methodKey = /^[A-Z]{1,12}$/;
|
|
57
|
+
const routeTemplate =
|
|
58
|
+
/^\/(?:[A-Za-z][A-Za-z0-9_-]*|:[A-Za-z][A-Za-z0-9_]*|\*)(?:\/(?:[A-Za-z][A-Za-z0-9_-]*|:[A-Za-z][A-Za-z0-9_]*|\*))*$/;
|
|
59
|
+
|
|
60
|
+
/** Adapter 可接 Pino、stdout 或测试 Sink;自由文本 message 不进入日志。 */
|
|
61
|
+
export function createStructuredLogger(
|
|
62
|
+
sink: StructuredLogSink,
|
|
63
|
+
component: string,
|
|
64
|
+
now: () => Date = () => new Date(),
|
|
65
|
+
): StructuredLogger {
|
|
66
|
+
if (!stableKey.test(component) || component.length > 64) {
|
|
67
|
+
throw new Error("运行日志 component 必须是有界稳定 Key");
|
|
68
|
+
}
|
|
69
|
+
const emit = (level: StructuredLogLevel, input: StructuredLogInput) => {
|
|
70
|
+
const record = structuredLogRecord(level, component, input, now());
|
|
71
|
+
try {
|
|
72
|
+
sink[level](record);
|
|
73
|
+
} catch {
|
|
74
|
+
// 排障日志是旁路输出;审计和业务结果由各自的可靠存储决定。
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
info: (input) => emit("info", input),
|
|
79
|
+
warn: (input) => emit("warn", input),
|
|
80
|
+
error: (input) => emit("error", input),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function structuredLogRecord(
|
|
85
|
+
level: StructuredLogLevel,
|
|
86
|
+
component: string,
|
|
87
|
+
input: StructuredLogInput,
|
|
88
|
+
at: Date,
|
|
89
|
+
): Record<string, unknown> {
|
|
90
|
+
const record: Record<string, unknown> = {
|
|
91
|
+
timestamp: at.toISOString(),
|
|
92
|
+
level,
|
|
93
|
+
component,
|
|
94
|
+
event:
|
|
95
|
+
stableKey.test(input.event) && input.event.length <= 96
|
|
96
|
+
? input.event
|
|
97
|
+
: "logging.event.invalid",
|
|
98
|
+
retentionClass: "operational",
|
|
99
|
+
};
|
|
100
|
+
for (const key of [
|
|
101
|
+
"requestId",
|
|
102
|
+
"runId",
|
|
103
|
+
"jobId",
|
|
104
|
+
"traceId",
|
|
105
|
+
"diagnosticId",
|
|
106
|
+
] as const) {
|
|
107
|
+
const value = input[key];
|
|
108
|
+
if (typeof value === "string" && correlationKey.test(value)) {
|
|
109
|
+
record[key] = value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (
|
|
113
|
+
input.outcome === "success" ||
|
|
114
|
+
input.outcome === "failure" ||
|
|
115
|
+
input.outcome === "denied"
|
|
116
|
+
)
|
|
117
|
+
record.outcome = input.outcome;
|
|
118
|
+
if (typeof input.errorCode === "string" && errorKey.test(input.errorCode)) {
|
|
119
|
+
record.errorCode = input.errorCode;
|
|
120
|
+
}
|
|
121
|
+
for (const key of [
|
|
122
|
+
"stageKey",
|
|
123
|
+
"handlerKey",
|
|
124
|
+
"queue",
|
|
125
|
+
"actorType",
|
|
126
|
+
"authority",
|
|
127
|
+
] as const) {
|
|
128
|
+
const value = input[key];
|
|
129
|
+
if (
|
|
130
|
+
typeof value === "string" &&
|
|
131
|
+
value.length <= 64 &&
|
|
132
|
+
stableKey.test(value)
|
|
133
|
+
) {
|
|
134
|
+
record[key] = value;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (typeof input.method === "string" && methodKey.test(input.method)) {
|
|
138
|
+
record.method = input.method;
|
|
139
|
+
}
|
|
140
|
+
if (
|
|
141
|
+
typeof input.route === "string" &&
|
|
142
|
+
input.route.length <= 160 &&
|
|
143
|
+
(input.route === "/" || routeTemplate.test(input.route))
|
|
144
|
+
)
|
|
145
|
+
record.route = input.route;
|
|
146
|
+
if (validNumber(input.statusCode, 100, 599, true))
|
|
147
|
+
record.statusCode = input.statusCode;
|
|
148
|
+
if (validNumber(input.durationMs, 0, 3_600_000))
|
|
149
|
+
record.durationMs = input.durationMs;
|
|
150
|
+
if (validNumber(input.port, 1, 65535, true)) record.port = input.port;
|
|
151
|
+
if (validNumber(input.attemptNumber, 1, 1_000_000_000, true))
|
|
152
|
+
record.attemptNumber = input.attemptNumber;
|
|
153
|
+
for (const key of [
|
|
154
|
+
"created",
|
|
155
|
+
"metadataUpdated",
|
|
156
|
+
"archived",
|
|
157
|
+
"unchanged",
|
|
158
|
+
"queued",
|
|
159
|
+
"dropped",
|
|
160
|
+
"completedUnits",
|
|
161
|
+
"totalUnits",
|
|
162
|
+
"count",
|
|
163
|
+
] as const) {
|
|
164
|
+
const value = input[key];
|
|
165
|
+
if (validNumber(value, 0, 1_000_000_000, true)) record[key] = value;
|
|
166
|
+
}
|
|
167
|
+
for (const key of [
|
|
168
|
+
"dropped",
|
|
169
|
+
"completedUnits",
|
|
170
|
+
"totalUnits",
|
|
171
|
+
"count",
|
|
172
|
+
"route",
|
|
173
|
+
"queued",
|
|
174
|
+
"unchanged",
|
|
175
|
+
"archived",
|
|
176
|
+
"metadataUpdated",
|
|
177
|
+
"created",
|
|
178
|
+
"port",
|
|
179
|
+
"attemptNumber",
|
|
180
|
+
"queue",
|
|
181
|
+
"handlerKey",
|
|
182
|
+
"authority",
|
|
183
|
+
"actorType",
|
|
184
|
+
"stageKey",
|
|
185
|
+
"diagnosticId",
|
|
186
|
+
"traceId",
|
|
187
|
+
"jobId",
|
|
188
|
+
"runId",
|
|
189
|
+
"requestId",
|
|
190
|
+
] as const) {
|
|
191
|
+
if (
|
|
192
|
+
new TextEncoder().encode(JSON.stringify(record)).length <=
|
|
193
|
+
STRUCTURED_LOG_MAX_BYTES
|
|
194
|
+
)
|
|
195
|
+
break;
|
|
196
|
+
delete record[key];
|
|
197
|
+
record.truncated = true;
|
|
198
|
+
}
|
|
199
|
+
return record;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function validNumber(
|
|
203
|
+
value: unknown,
|
|
204
|
+
min: number,
|
|
205
|
+
max: number,
|
|
206
|
+
integer = false,
|
|
207
|
+
): value is number {
|
|
208
|
+
return (
|
|
209
|
+
typeof value === "number" &&
|
|
210
|
+
Number.isFinite(value) &&
|
|
211
|
+
value >= min &&
|
|
212
|
+
value <= max &&
|
|
213
|
+
(!integer || Number.isInteger(value))
|
|
214
|
+
);
|
|
215
|
+
}
|
package/src/ruoyi.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type { AuditEvent, AuditLogWriter } from "./audit.js";
|
|
2
|
-
import type { LegacyTableMapping } from "./database.js";
|
|
3
|
-
import type { EntityId, ISODateTime } from "./primitives.js";
|
|
4
|
-
import type {
|
|
5
|
-
MenuNode,
|
|
6
|
-
PermissionDefinition,
|
|
7
|
-
RoleDefinition,
|
|
8
|
-
UserAccount,
|
|
9
|
-
} from "./rbac.js";
|
|
10
|
-
|
|
11
|
-
export type RuoYiCoreTable =
|
|
12
|
-
| "sys_user"
|
|
13
|
-
| "sys_role"
|
|
14
|
-
| "sys_user_role"
|
|
15
|
-
| "sys_dept"
|
|
16
|
-
| "sys_post"
|
|
17
|
-
| "sys_user_post"
|
|
18
|
-
| "sys_menu"
|
|
19
|
-
| "sys_role_menu"
|
|
20
|
-
| "sys_role_dept"
|
|
21
|
-
| "sys_dict_type"
|
|
22
|
-
| "sys_dict_data"
|
|
23
|
-
| "sys_config"
|
|
24
|
-
| "sys_notice"
|
|
25
|
-
| "sys_logininfor"
|
|
26
|
-
| "sys_oper_log"
|
|
27
|
-
| "sys_job"
|
|
28
|
-
| "sys_job_log";
|
|
29
|
-
|
|
30
|
-
export interface RuoYiConnectionConfig {
|
|
31
|
-
mysqlDsn: string;
|
|
32
|
-
redisDsn?: string;
|
|
33
|
-
readonly: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface RuoYiMigrationPlan {
|
|
37
|
-
id: EntityId;
|
|
38
|
-
createdAt: ISODateTime;
|
|
39
|
-
tables: RuoYiCoreTable[];
|
|
40
|
-
dryRun: boolean;
|
|
41
|
-
resetPasswords: boolean;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface RuoYiMigrationReport {
|
|
45
|
-
planId: EntityId;
|
|
46
|
-
startedAt: ISODateTime;
|
|
47
|
-
finishedAt?: ISODateTime;
|
|
48
|
-
importedRows: Record<RuoYiCoreTable, number>;
|
|
49
|
-
failedRows: Record<RuoYiCoreTable, number>;
|
|
50
|
-
warnings: string[];
|
|
51
|
-
auditEvents: AuditEvent[];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface RuoYiPermissionSnapshot {
|
|
55
|
-
user: UserAccount;
|
|
56
|
-
roles: RoleDefinition[];
|
|
57
|
-
menus: MenuNode[];
|
|
58
|
-
permissions: PermissionDefinition[];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface RuoYiImportAdapter {
|
|
62
|
-
dryRun(plan: RuoYiMigrationPlan): Promise<RuoYiMigrationReport>;
|
|
63
|
-
import(
|
|
64
|
-
plan: RuoYiMigrationPlan,
|
|
65
|
-
audit: AuditLogWriter,
|
|
66
|
-
): Promise<RuoYiMigrationReport>;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface RuoYiLegacyRuntimeAdapter {
|
|
70
|
-
connect(config: RuoYiConnectionConfig): Promise<void>;
|
|
71
|
-
disconnect(): Promise<void>;
|
|
72
|
-
readPermissionSnapshot(userId: EntityId): Promise<RuoYiPermissionSnapshot>;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface RuoYiReportAdapter {
|
|
76
|
-
compareUserPermissions(userId: EntityId): Promise<RuoYiMigrationReport>;
|
|
77
|
-
tableMappings(): LegacyTableMapping[];
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const ruoyiCoreTables: RuoYiCoreTable[] = [
|
|
81
|
-
"sys_user",
|
|
82
|
-
"sys_role",
|
|
83
|
-
"sys_user_role",
|
|
84
|
-
"sys_dept",
|
|
85
|
-
"sys_post",
|
|
86
|
-
"sys_user_post",
|
|
87
|
-
"sys_menu",
|
|
88
|
-
"sys_role_menu",
|
|
89
|
-
"sys_role_dept",
|
|
90
|
-
"sys_dict_type",
|
|
91
|
-
"sys_dict_data",
|
|
92
|
-
"sys_config",
|
|
93
|
-
"sys_notice",
|
|
94
|
-
"sys_logininfor",
|
|
95
|
-
"sys_oper_log",
|
|
96
|
-
"sys_job",
|
|
97
|
-
"sys_job_log",
|
|
98
|
-
];
|