@h-ai/kit 1.0.0-beta.18 → 1.0.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +53 -33
- package/README.ja.md +53 -33
- package/README.md +54 -33
- package/dist/adapter/index.d.ts +8 -7
- package/dist/adapter/index.js.map +1 -1
- package/dist/browser.d.ts +9 -26
- package/dist/browser.js +4 -4
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-JRQPMPQC.js → chunk-BCEBXG4X.js} +2 -2
- package/dist/chunk-BCEBXG4X.js.map +1 -0
- package/dist/{chunk-7EH47MTW.js → chunk-BVGXHIXR.js} +30 -9
- package/dist/chunk-BVGXHIXR.js.map +1 -0
- package/dist/{chunk-WCMEGLBL.js → chunk-GQZCU6PC.js} +11 -5
- package/dist/chunk-GQZCU6PC.js.map +1 -0
- package/dist/{chunk-5JW3VW4Q.js → chunk-JL2C5ZGQ.js} +38 -11
- package/dist/chunk-JL2C5ZGQ.js.map +1 -0
- package/dist/{chunk-6KS67QUS.js → chunk-WYGIKXHE.js} +20 -7
- package/dist/chunk-WYGIKXHE.js.map +1 -0
- package/dist/client/index.d.ts +6 -5
- package/dist/client/index.js +5 -5
- package/dist/client/index.js.map +1 -1
- package/dist/index.d.ts +18 -711
- package/dist/index.js +264 -149
- package/dist/index.js.map +1 -1
- package/dist/{kit-a2a-types-ikgvWS3T.d.ts → kit-a2a-types-CeDikRAX.d.ts} +3 -2
- package/dist/kit-config-CCksTmi5.d.ts +182 -0
- package/dist/kit-crypto-types-CqFENBqm.d.ts +47 -0
- package/dist/kit-types-BUDaKGB0.d.ts +2057 -0
- package/dist/modules/a2a/index.d.ts +9 -5
- package/dist/modules/a2a/index.js +1 -1
- package/dist/modules/crypto/index.d.ts +8 -105
- package/dist/modules/crypto/index.js +3 -149
- package/dist/modules/crypto/index.js.map +1 -1
- package/package.json +22 -22
- package/dist/chunk-5JW3VW4Q.js.map +0 -1
- package/dist/chunk-6KS67QUS.js.map +0 -1
- package/dist/chunk-7EH47MTW.js.map +0 -1
- package/dist/chunk-JRQPMPQC.js.map +0 -1
- package/dist/chunk-WCMEGLBL.js.map +0 -1
- package/dist/kit-client-auth-DeWmc-1u.d.ts +0 -406
- package/dist/kit-crypto-types-Brd0l8Hv.d.ts +0 -204
- package/dist/kit-i18n-CjwdGeKY.d.ts +0 -506
- package/dist/kit-types-CnfBC89D.d.ts +0 -832
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { defineCrud } from './chunk-
|
|
2
|
-
export { KitConfigSchema, defineCrud, resolveKitConfig, resolveKitTransportConfig, resolveOptions, resolveText } from './chunk-
|
|
3
|
-
import { clearBrowserToken, setBrowserToken, logout, registerAndLogin, loginWithApiKey, loginWithLdap, loginWithOtp, login, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient, getAccessToken, configureAuth } from './chunk-
|
|
4
|
-
import { resolveA2AConfig, handleA2ARequest } from './chunk-
|
|
5
|
-
import { transportEncryptionMiddleware } from './chunk-
|
|
6
|
-
import './chunk-
|
|
1
|
+
import { defineCrud } from './chunk-GQZCU6PC.js';
|
|
2
|
+
export { KitConfigSchema, defineCrud, resolveKitConfig, resolveKitTransportConfig, resolveOptions, resolveText } from './chunk-GQZCU6PC.js';
|
|
3
|
+
import { clearBrowserToken, setBrowserToken, logout, registerAndLogin, loginWithApiKey, loginWithLdap, loginWithOtp, login, installBrowserTransportFetch, installBrowserTransport, createKitAuthActions, createKitClient, getAccessToken, configureAuth } from './chunk-JL2C5ZGQ.js';
|
|
4
|
+
import { resolveA2AConfig, handleA2ARequest } from './chunk-BVGXHIXR.js';
|
|
5
|
+
import { transportEncryptionMiddleware } from './chunk-WYGIKXHE.js';
|
|
6
|
+
import './chunk-BCEBXG4X.js';
|
|
7
7
|
import { kitM, setAllModulesLocale } from './chunk-USFJN7IO.js';
|
|
8
8
|
import { core, HaiCommonError, err } from '@h-ai/core';
|
|
9
|
+
import process from 'process';
|
|
9
10
|
import { audit } from '@h-ai/audit';
|
|
10
11
|
import { TRANSPORT_PROTOCOL } from '@h-ai/crypto';
|
|
11
|
-
import {
|
|
12
|
+
import { isHttpError, isRedirect } from '@sveltejs/kit';
|
|
13
|
+
import { createHash, hkdfSync, timingSafeEqual, createHmac } from 'crypto';
|
|
12
14
|
import { isIP } from 'net';
|
|
13
|
-
import
|
|
15
|
+
import { Buffer } from 'buffer';
|
|
14
16
|
import { IamConfirmPasswordResetInputSchema, IamRequestPasswordResetInputSchema, IamSendOtpInputSchema, IamRegisterInputSchema, IamOtpLoginInputSchema, IamLdapLoginInputSchema, IamLoginInputSchema, IamRenewPasswordInputSchema, IamReauthenticateInputSchema, IamCreateCaptchaInputSchema, IamLogoutInputSchema, FeedbackSubmitInputSchema, FeedbackListInputSchema, FeedbackIdInputSchema, FeedbackEvidenceQuerySchema, FeedbackAppendEvidenceInputSchema, FeedbackSetStatusInputSchema, FeedbackRetryInputSchema, FeedbackCompleteInputSchema, FeedbackExportInputSchema, FeedbackUploadInputSchema, FeedbackAttachmentQuerySchema, FeedbackExportQuerySchema } from '@h-ai/api-contract';
|
|
15
17
|
import { z } from 'zod';
|
|
16
18
|
|
|
@@ -40,14 +42,28 @@ function hasPermission(session, permission) {
|
|
|
40
42
|
function assertPermission(session, permission) {
|
|
41
43
|
if (!session) {
|
|
42
44
|
return new Response(
|
|
43
|
-
JSON.stringify({
|
|
44
|
-
|
|
45
|
+
JSON.stringify({
|
|
46
|
+
success: false,
|
|
47
|
+
error: kitM("kit_unauthorized")
|
|
48
|
+
}),
|
|
49
|
+
{
|
|
50
|
+
status: 401,
|
|
51
|
+
headers: { "Content-Type": "application/json" }
|
|
52
|
+
}
|
|
45
53
|
);
|
|
46
54
|
}
|
|
47
55
|
if (!matchPermission(permission, session.permissions ?? [])) {
|
|
48
56
|
return new Response(
|
|
49
|
-
JSON.stringify({
|
|
50
|
-
|
|
57
|
+
JSON.stringify({
|
|
58
|
+
success: false,
|
|
59
|
+
error: kitM("kit_forbidden", {
|
|
60
|
+
params: { permission }
|
|
61
|
+
})
|
|
62
|
+
}),
|
|
63
|
+
{
|
|
64
|
+
status: 403,
|
|
65
|
+
headers: { "Content-Type": "application/json" }
|
|
66
|
+
}
|
|
51
67
|
);
|
|
52
68
|
}
|
|
53
69
|
return void 0;
|
|
@@ -85,42 +101,57 @@ function noContent() {
|
|
|
85
101
|
}
|
|
86
102
|
function error(code, message, status = 400, requestId, details) {
|
|
87
103
|
const safe = core.error.toPublic({ code, message, httpStatus: status }, requestId);
|
|
88
|
-
|
|
104
|
+
const safeStatus = safe.httpStatus ?? 500;
|
|
105
|
+
core.logger.error("API request failed", {
|
|
106
|
+
traceId: safe.traceId,
|
|
107
|
+
error: {
|
|
108
|
+
code,
|
|
109
|
+
message,
|
|
110
|
+
httpStatus: status,
|
|
111
|
+
details
|
|
112
|
+
}
|
|
113
|
+
});
|
|
89
114
|
const response = {
|
|
90
115
|
success: false,
|
|
91
116
|
error: {
|
|
92
|
-
code,
|
|
117
|
+
code: String(safe.code),
|
|
93
118
|
message: safe.message,
|
|
94
119
|
traceId: safe.traceId,
|
|
95
|
-
|
|
120
|
+
httpStatus: safeStatus,
|
|
121
|
+
details: safeStatus < 500 ? core.sanitize.sanitizeSensitiveFields(details) : void 0
|
|
96
122
|
},
|
|
97
|
-
requestId:
|
|
123
|
+
requestId: safe.traceId
|
|
98
124
|
};
|
|
99
125
|
return new Response(JSON.stringify(response), {
|
|
100
|
-
status,
|
|
126
|
+
status: safeStatus,
|
|
101
127
|
headers: { "Content-Type": "application/json", "X-Trace-Id": safe.traceId }
|
|
102
128
|
});
|
|
103
129
|
}
|
|
104
130
|
function badRequest(message, requestId, details) {
|
|
105
|
-
return error(
|
|
131
|
+
return error(HaiCommonError.INVALID_REQUEST.code, message, 400, requestId, details);
|
|
106
132
|
}
|
|
107
133
|
function unauthorized(message, requestId) {
|
|
108
|
-
return error(
|
|
134
|
+
return error(HaiCommonError.UNAUTHORIZED.code, message ?? kitM("kit_authRequired"), 401, requestId);
|
|
109
135
|
}
|
|
110
136
|
function forbidden(message, requestId) {
|
|
111
|
-
return error(
|
|
137
|
+
return error(HaiCommonError.FORBIDDEN.code, message ?? kitM("kit_accessDenied"), 403, requestId);
|
|
112
138
|
}
|
|
113
139
|
function notFound(message, requestId) {
|
|
114
|
-
return error(
|
|
140
|
+
return error(HaiCommonError.NOT_FOUND.code, message ?? kitM("kit_resourceNotFound"), 404, requestId);
|
|
115
141
|
}
|
|
116
142
|
function conflict(message, requestId) {
|
|
117
|
-
return error(
|
|
143
|
+
return error(HaiCommonError.CONFLICT.code, message, 409, requestId);
|
|
118
144
|
}
|
|
119
145
|
function validationError(errors, requestId) {
|
|
120
|
-
return
|
|
146
|
+
return fromError({
|
|
147
|
+
...HaiCommonError.VALIDATION_ERROR,
|
|
148
|
+
message: kitM("kit_validationFailed"),
|
|
149
|
+
httpStatus: 422,
|
|
150
|
+
cause: errors
|
|
151
|
+
}, requestId);
|
|
121
152
|
}
|
|
122
153
|
function internalError(message, requestId) {
|
|
123
|
-
return error(
|
|
154
|
+
return error(HaiCommonError.INTERNAL_ERROR.code, message ?? kitM("kit_internalError"), 500, requestId);
|
|
124
155
|
}
|
|
125
156
|
function redirect(url, status = 302) {
|
|
126
157
|
return new Response(null, {
|
|
@@ -132,13 +163,25 @@ function fromResult(result, httpStatusMap, requestId) {
|
|
|
132
163
|
if (result.success) {
|
|
133
164
|
return ok(result.data, requestId);
|
|
134
165
|
}
|
|
135
|
-
const status = httpStatusMap?.[result.error.code] ?? (typeof result.error.httpStatus === "number" ? result.error.httpStatus :
|
|
166
|
+
const status = httpStatusMap?.[result.error.code] ?? (typeof result.error.httpStatus === "number" ? result.error.httpStatus : 500);
|
|
136
167
|
return fromError({ ...result.error, httpStatus: status }, requestId);
|
|
137
168
|
}
|
|
138
169
|
function fromError(haiError, requestId) {
|
|
139
170
|
const safe = core.error.toPublic(haiError, requestId);
|
|
140
171
|
core.logger.error("API operation failed", { traceId: safe.traceId, error: haiError });
|
|
141
|
-
|
|
172
|
+
const response = {
|
|
173
|
+
success: false,
|
|
174
|
+
error: {
|
|
175
|
+
...safe,
|
|
176
|
+
code: String(safe.code),
|
|
177
|
+
httpStatus: safe.httpStatus ?? 500
|
|
178
|
+
},
|
|
179
|
+
requestId: safe.traceId
|
|
180
|
+
};
|
|
181
|
+
return new Response(JSON.stringify(response), {
|
|
182
|
+
status: safe.httpStatus ?? 500,
|
|
183
|
+
headers: { "Content-Type": "application/json", "X-Trace-Id": safe.traceId }
|
|
184
|
+
});
|
|
142
185
|
}
|
|
143
186
|
|
|
144
187
|
// src/guards/kit-sensitive.ts
|
|
@@ -150,8 +193,6 @@ async function requireSensitiveOperation(iam, event, operation) {
|
|
|
150
193
|
if (!checked.success)
|
|
151
194
|
throw fromError(checked.error);
|
|
152
195
|
}
|
|
153
|
-
|
|
154
|
-
// src/kit-utils.ts
|
|
155
196
|
function isResponseLike(value) {
|
|
156
197
|
if (value instanceof Response)
|
|
157
198
|
return true;
|
|
@@ -161,16 +202,19 @@ function isResponseLike(value) {
|
|
|
161
202
|
return false;
|
|
162
203
|
}
|
|
163
204
|
function isSvelteKitControlFlow(value) {
|
|
164
|
-
|
|
165
|
-
return false;
|
|
166
|
-
if (typeof value === "object" && value !== null && "status" in value) {
|
|
167
|
-
const status = value.status;
|
|
168
|
-
return typeof status === "number";
|
|
169
|
-
}
|
|
170
|
-
return false;
|
|
205
|
+
return isHttpError(value) || isRedirect(value);
|
|
171
206
|
}
|
|
172
207
|
function loggingMiddleware(config = {}) {
|
|
173
|
-
const { logBody = false, logResponse = false, redactFields = [
|
|
208
|
+
const { logBody = false, logResponse = false, redactFields = [
|
|
209
|
+
"password",
|
|
210
|
+
"token",
|
|
211
|
+
"secret",
|
|
212
|
+
"authorization",
|
|
213
|
+
"apikey",
|
|
214
|
+
"api_key",
|
|
215
|
+
"creditcard",
|
|
216
|
+
"credit_card"
|
|
217
|
+
] } = config;
|
|
174
218
|
return async (context, next) => {
|
|
175
219
|
const { event, requestId } = context;
|
|
176
220
|
const startTime = Date.now();
|
|
@@ -215,7 +259,7 @@ function redactObject(obj, fields) {
|
|
|
215
259
|
return core.sanitize.sanitizeSensitiveFields(obj, { matcher: fields });
|
|
216
260
|
}
|
|
217
261
|
function permitsMemory() {
|
|
218
|
-
return !core.module.requiresSharedState(
|
|
262
|
+
return !core.module.requiresSharedState(process.env);
|
|
219
263
|
}
|
|
220
264
|
function createCacheRateLimitStore(backend, options) {
|
|
221
265
|
if (!options.namespace || options.namespace.length > 128)
|
|
@@ -230,14 +274,14 @@ function createCacheRateLimitStore(backend, options) {
|
|
|
230
274
|
for (let attempt = 0; attempt < 64; attempt++) {
|
|
231
275
|
const read = await backend.kv.get(cacheKey);
|
|
232
276
|
if (!read.success)
|
|
233
|
-
throw new Error("Rate limit cache read failed");
|
|
277
|
+
throw new Error("Rate limit cache read failed", { cause: read.error });
|
|
234
278
|
const old = read.data ?? void 0;
|
|
235
279
|
if (old && (!Number.isSafeInteger(old.count) || old.count <= 0 || old.count >= Number.MAX_SAFE_INTEGER || !Number.isSafeInteger(old.resetAt)))
|
|
236
280
|
throw new Error("Invalid rate limit state");
|
|
237
281
|
const next = old ? { count: old.count + 1, resetAt: old.resetAt } : { count: 1, resetAt: Date.now() + windowMs };
|
|
238
282
|
const written = await backend.kv.compareAndSwap(cacheKey, old, next, old ? { keepTtl: true } : { px: windowMs });
|
|
239
283
|
if (!written.success)
|
|
240
|
-
throw new Error("Rate limit cache commit failed");
|
|
284
|
+
throw new Error("Rate limit cache commit failed", { cause: written.error });
|
|
241
285
|
if (written.data)
|
|
242
286
|
return next;
|
|
243
287
|
}
|
|
@@ -258,7 +302,12 @@ function clientKey(address) {
|
|
|
258
302
|
if (!mapped)
|
|
259
303
|
return normalized;
|
|
260
304
|
const value = Number.parseInt(mapped[1], 16) * 65536 + Number.parseInt(mapped[2], 16);
|
|
261
|
-
return [
|
|
305
|
+
return [
|
|
306
|
+
value >>> 24,
|
|
307
|
+
value >>> 16 & 255,
|
|
308
|
+
value >>> 8 & 255,
|
|
309
|
+
value & 255
|
|
310
|
+
].join(".");
|
|
262
311
|
}
|
|
263
312
|
var MemoryRateLimitStore = class {
|
|
264
313
|
scope = "memory";
|
|
@@ -331,37 +380,29 @@ function rateLimitMiddleware(config) {
|
|
|
331
380
|
entry = await store.increment(keyGenerator(event), windowMs);
|
|
332
381
|
if (!Number.isSafeInteger(entry.count) || entry.count <= 0 || !Number.isSafeInteger(entry.resetAt))
|
|
333
382
|
throw new TypeError("Invalid atomic rate limit result");
|
|
334
|
-
} catch {
|
|
335
|
-
|
|
336
|
-
|
|
383
|
+
} catch (cause) {
|
|
384
|
+
return fromError({
|
|
385
|
+
...HaiCommonError.SERVICE_UNAVAILABLE,
|
|
386
|
+
message: kitM("kit_internalError"),
|
|
387
|
+
cause
|
|
388
|
+
}, requestId);
|
|
337
389
|
}
|
|
338
390
|
const remaining = Math.max(0, maxRequests - entry.count);
|
|
339
391
|
const resetTime = Math.ceil(entry.resetAt / 1e3);
|
|
340
392
|
if (entry.count > maxRequests) {
|
|
341
|
-
core.logger.warn("Rate limit exceeded", { requestId });
|
|
342
393
|
if (onLimitReached) {
|
|
394
|
+
core.logger.warn("Rate limit exceeded", { requestId });
|
|
343
395
|
return onLimitReached(event);
|
|
344
396
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
{
|
|
355
|
-
status: 429,
|
|
356
|
-
headers: {
|
|
357
|
-
"Content-Type": "application/json",
|
|
358
|
-
"X-RateLimit-Limit": String(maxRequests),
|
|
359
|
-
"X-RateLimit-Remaining": "0",
|
|
360
|
-
"X-RateLimit-Reset": String(resetTime),
|
|
361
|
-
"Retry-After": String(Math.max(1, Math.ceil((entry.resetAt - now) / 1e3)))
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
);
|
|
397
|
+
const response2 = fromError({
|
|
398
|
+
...HaiCommonError.RATE_LIMIT_EXCEEDED,
|
|
399
|
+
message: kitM("kit_rateLimitExceeded")
|
|
400
|
+
}, requestId);
|
|
401
|
+
response2.headers.set("X-RateLimit-Limit", String(maxRequests));
|
|
402
|
+
response2.headers.set("X-RateLimit-Remaining", "0");
|
|
403
|
+
response2.headers.set("X-RateLimit-Reset", String(resetTime));
|
|
404
|
+
response2.headers.set("Retry-After", String(Math.max(1, Math.ceil((entry.resetAt - now) / 1e3))));
|
|
405
|
+
return response2;
|
|
365
406
|
}
|
|
366
407
|
const response = await next();
|
|
367
408
|
response.headers.set("X-RateLimit-Limit", String(maxRequests));
|
|
@@ -370,10 +411,16 @@ function rateLimitMiddleware(config) {
|
|
|
370
411
|
return response;
|
|
371
412
|
};
|
|
372
413
|
}
|
|
373
|
-
var ENCRYPTED_PREFIX = "enc:";
|
|
414
|
+
var ENCRYPTED_PREFIX = "enc:v2:";
|
|
374
415
|
function createEncryptedCookieProxy(cookies, config) {
|
|
375
416
|
const { names, symmetric, encryptionKey } = config;
|
|
376
417
|
const logger = core.logger.child({ module: "kit", scope: "cookie-proxy" });
|
|
418
|
+
if (!/^[\da-f]{32}$/i.test(encryptionKey))
|
|
419
|
+
throw new Error("Cookie encryption requires a 32-character hexadecimal key");
|
|
420
|
+
const keys = Buffer.from(hkdfSync("sha256", Buffer.from(encryptionKey, "hex"), Buffer.alloc(0), "hai:kit:cookie:v2", 48));
|
|
421
|
+
const cipherKey = keys.subarray(0, 16).toString("hex");
|
|
422
|
+
const macKey = keys.subarray(16);
|
|
423
|
+
const authenticate = (name, iv, ciphertext) => createHmac("sha256", macKey).update(JSON.stringify(["v2", name, iv, ciphertext])).digest();
|
|
377
424
|
return new Proxy(cookies, {
|
|
378
425
|
get(target, prop, receiver) {
|
|
379
426
|
if (prop === "get") {
|
|
@@ -387,14 +434,17 @@ function createEncryptedCookieProxy(cookies, config) {
|
|
|
387
434
|
}
|
|
388
435
|
try {
|
|
389
436
|
const ciphertext = raw.slice(ENCRYPTED_PREFIX.length);
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
437
|
+
const parts = ciphertext.split(":");
|
|
438
|
+
if (parts.length !== 3 || !/^[\da-f]{64}$/i.test(parts[2])) {
|
|
392
439
|
logger.warn("Encrypted cookie has invalid format", { name });
|
|
393
440
|
return void 0;
|
|
394
441
|
}
|
|
395
|
-
const iv =
|
|
396
|
-
|
|
397
|
-
|
|
442
|
+
const [iv, encrypted, signature] = parts;
|
|
443
|
+
if (!timingSafeEqual(authenticate(name, iv, encrypted), Buffer.from(signature, "hex"))) {
|
|
444
|
+
logger.warn("Cookie authentication failed", { name });
|
|
445
|
+
return void 0;
|
|
446
|
+
}
|
|
447
|
+
const result = symmetric.decryptWithIV(encrypted, cipherKey, iv);
|
|
398
448
|
if (!result.success || typeof result.data !== "string") {
|
|
399
449
|
logger.warn("Cookie decryption failed", { name });
|
|
400
450
|
return void 0;
|
|
@@ -409,12 +459,13 @@ function createEncryptedCookieProxy(cookies, config) {
|
|
|
409
459
|
if (prop === "set") {
|
|
410
460
|
return (name, value, opts) => {
|
|
411
461
|
if (names.has(name)) {
|
|
412
|
-
const result = symmetric.encryptWithIV(value,
|
|
462
|
+
const result = symmetric.encryptWithIV(value, cipherKey);
|
|
413
463
|
if (!result.success || !result.data) {
|
|
414
464
|
logger.error("Cookie encryption failed", { name });
|
|
415
465
|
throw new Error("Cookie encryption failed");
|
|
416
466
|
}
|
|
417
|
-
|
|
467
|
+
const { iv, ciphertext } = result.data;
|
|
468
|
+
value = `${ENCRYPTED_PREFIX}${iv}:${ciphertext}:${authenticate(name, iv, ciphertext).toString("hex")}`;
|
|
418
469
|
}
|
|
419
470
|
return target.set(name, value, opts);
|
|
420
471
|
};
|
|
@@ -475,7 +526,10 @@ function createHandle(config = {}) {
|
|
|
475
526
|
maxRequests: rateLimitConfig.maxRequests ?? 100
|
|
476
527
|
}));
|
|
477
528
|
}
|
|
478
|
-
const allMiddleware = [
|
|
529
|
+
const allMiddleware = [
|
|
530
|
+
...builtinMiddleware,
|
|
531
|
+
...customMiddleware
|
|
532
|
+
];
|
|
479
533
|
const transportMiddleware = buildTransportMiddleware(cryptoConfig, checkAccess ? checkAccess.encryption : void 0);
|
|
480
534
|
const cookieProxyConfig = buildCookieProxyConfig(cryptoConfig);
|
|
481
535
|
return async ({ event, resolve }) => {
|
|
@@ -488,15 +542,23 @@ function createHandle(config = {}) {
|
|
|
488
542
|
}
|
|
489
543
|
const resolvedAddress = resolveIp(event.request, clientAddress);
|
|
490
544
|
if (resolvedAddress !== clientAddress) {
|
|
491
|
-
event
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
545
|
+
event = {
|
|
546
|
+
...event,
|
|
547
|
+
getClientAddress: () => {
|
|
548
|
+
if (!resolvedAddress)
|
|
549
|
+
throw new Error("Trusted client address is unavailable");
|
|
550
|
+
return resolvedAddress;
|
|
551
|
+
}
|
|
495
552
|
};
|
|
496
553
|
}
|
|
497
554
|
clientAddress = resolvedAddress;
|
|
498
555
|
const accessDecision = checkAccess ? checkAccess(event.url.pathname, event.request.method, clientAddress) : void 0;
|
|
499
|
-
return core.context.run({
|
|
556
|
+
return core.context.run({
|
|
557
|
+
requestId,
|
|
558
|
+
userId: void 0,
|
|
559
|
+
scopeId: void 0,
|
|
560
|
+
jobId: void 0
|
|
561
|
+
}, () => audit.withContext({
|
|
500
562
|
actor: null,
|
|
501
563
|
requestId,
|
|
502
564
|
traceId: requestId,
|
|
@@ -519,33 +581,49 @@ function createHandle(config = {}) {
|
|
|
519
581
|
category: /\/(?:auth|iam|audit)(?:\/|$)/.test(resource) ? "system" : "business",
|
|
520
582
|
outcome: guardDenied || [401, 403, 423, 429].includes(status) ? "denied" : requestFailed || status >= 400 ? "failure" : "success",
|
|
521
583
|
details: { method: event.request.method, status }
|
|
522
|
-
}, {
|
|
584
|
+
}, {
|
|
585
|
+
read: ["GET", "HEAD", "OPTIONS"].includes(event.request.method)
|
|
586
|
+
});
|
|
523
587
|
if (!written.success)
|
|
524
588
|
core.logger.error("Request audit failed", { requestId, resource, error: written.error });
|
|
525
589
|
};
|
|
590
|
+
if (cookieProxyConfig) {
|
|
591
|
+
event = {
|
|
592
|
+
...event,
|
|
593
|
+
cookies: createEncryptedCookieProxy(event.cookies, cookieProxyConfig)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
526
596
|
const context = {
|
|
527
597
|
event,
|
|
528
598
|
requestId
|
|
529
599
|
};
|
|
530
600
|
const locals = event.locals;
|
|
531
601
|
locals.requestId = requestId;
|
|
532
|
-
if (cookieProxyConfig) {
|
|
533
|
-
const proxiedCookies = createEncryptedCookieProxy(event.cookies, cookieProxyConfig);
|
|
534
|
-
Object.defineProperty(event, "cookies", {
|
|
535
|
-
value: proxiedCookies,
|
|
536
|
-
writable: true,
|
|
537
|
-
configurable: true
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
602
|
const runRequest = async () => {
|
|
541
603
|
try {
|
|
542
604
|
if (accessDecision && !accessDecision.allowed) {
|
|
543
605
|
guardDenied = true;
|
|
544
|
-
core.logger.info("Request access policy denied", {
|
|
545
|
-
|
|
606
|
+
core.logger.info("Request access policy denied", {
|
|
607
|
+
ruleId: accessDecision.ruleId,
|
|
608
|
+
reason: accessDecision.reason,
|
|
609
|
+
method: event.request.method,
|
|
610
|
+
ipAddress: clientAddress
|
|
611
|
+
});
|
|
612
|
+
return Response.json({
|
|
613
|
+
success: false,
|
|
614
|
+
error: {
|
|
615
|
+
...HaiCommonError.REQUEST_ACCESS_DENIED,
|
|
616
|
+
message: core.i18n.coreM("core_requestAccessDenied"),
|
|
617
|
+
traceId: requestId
|
|
618
|
+
}
|
|
619
|
+
}, { status: 403 });
|
|
546
620
|
}
|
|
547
621
|
if (!transportMiddleware && event.request.method !== "OPTIONS" && (event.request.headers.has(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER) || event.request.headers.has(TRANSPORT_PROTOCOL.ENVELOPE_HEADER)) && checkAccess && checkAccess.encryption(event.url.pathname, event.request.method) === "disabled") {
|
|
548
|
-
return Response.json(err({
|
|
622
|
+
return Response.json(err({
|
|
623
|
+
...HaiCommonError.VALIDATION_ERROR,
|
|
624
|
+
message: kitM("kit_transportPlaintextRequired"),
|
|
625
|
+
traceId: requestId
|
|
626
|
+
}), { status: 400 });
|
|
549
627
|
}
|
|
550
628
|
if (a2aResolved) {
|
|
551
629
|
const a2aResponse = await handleA2ARequest(event, requestId, a2aResolved);
|
|
@@ -576,20 +654,11 @@ function createHandle(config = {}) {
|
|
|
576
654
|
headers: { Location: guardResult.redirect }
|
|
577
655
|
});
|
|
578
656
|
}
|
|
579
|
-
return
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
message: guardResult.message ?? kitM("kit_accessDenied")
|
|
585
|
-
},
|
|
586
|
-
requestId
|
|
587
|
-
}),
|
|
588
|
-
{
|
|
589
|
-
status: guardResult.status ?? 403,
|
|
590
|
-
headers: { "Content-Type": "application/json" }
|
|
591
|
-
}
|
|
592
|
-
);
|
|
657
|
+
return fromError({
|
|
658
|
+
...guardResult.status === 401 ? HaiCommonError.UNAUTHORIZED : HaiCommonError.FORBIDDEN,
|
|
659
|
+
message: guardResult.message ?? kitM("kit_accessDenied"),
|
|
660
|
+
httpStatus: guardResult.status ?? 403
|
|
661
|
+
}, requestId);
|
|
593
662
|
}
|
|
594
663
|
}
|
|
595
664
|
const response2 = await executeMiddlewareChain(
|
|
@@ -606,25 +675,15 @@ function createHandle(config = {}) {
|
|
|
606
675
|
throw error2;
|
|
607
676
|
}
|
|
608
677
|
requestFailed = true;
|
|
609
|
-
core.logger.error("Request failed", { requestId, error: error2 });
|
|
610
678
|
if (onError) {
|
|
679
|
+
core.logger.error("Request failed", { requestId, error: error2 });
|
|
611
680
|
return onError(error2, event);
|
|
612
681
|
}
|
|
613
|
-
return
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
message: core.error.formatMessage({ message: kitM("kit_internalError"), traceId: requestId }),
|
|
619
|
-
traceId: requestId
|
|
620
|
-
},
|
|
621
|
-
requestId
|
|
622
|
-
}),
|
|
623
|
-
{
|
|
624
|
-
status: 500,
|
|
625
|
-
headers: { "Content-Type": "application/json" }
|
|
626
|
-
}
|
|
627
|
-
);
|
|
682
|
+
return fromError({
|
|
683
|
+
...HaiCommonError.INTERNAL_ERROR,
|
|
684
|
+
message: kitM("kit_internalError"),
|
|
685
|
+
cause: error2
|
|
686
|
+
}, requestId);
|
|
628
687
|
}
|
|
629
688
|
};
|
|
630
689
|
const keyExchange = cryptoConfig?.transport ? cryptoConfig.transport.keyExchangePath ?? "/api/_hai/key-exchange" : void 0;
|
|
@@ -716,10 +775,12 @@ function buildTransportMiddleware(cryptoConfig, encryption) {
|
|
|
716
775
|
function buildCookieProxyConfig(cryptoConfig) {
|
|
717
776
|
if (!cryptoConfig?.encryptedCookies?.length)
|
|
718
777
|
return null;
|
|
719
|
-
const key = cryptoConfig.cookieEncryptionKey ??
|
|
778
|
+
const key = cryptoConfig.cookieEncryptionKey ?? process.env.HAI_KIT_COOKIE_KEY;
|
|
720
779
|
if (!key) {
|
|
721
780
|
throw new Error("Cookie encryption requires crypto.cookieEncryptionKey or HAI_KIT_COOKIE_KEY");
|
|
722
781
|
}
|
|
782
|
+
if (!/^[\da-f]{32}$/i.test(key))
|
|
783
|
+
throw new Error("Cookie encryption requires a 32-character hexadecimal key");
|
|
723
784
|
return {
|
|
724
785
|
names: new Set(cryptoConfig.encryptedCookies),
|
|
725
786
|
symmetric: cryptoConfig.crypto.symmetric,
|
|
@@ -774,18 +835,13 @@ function handler(fn) {
|
|
|
774
835
|
if (isSvelteKitControlFlow(error2)) {
|
|
775
836
|
throw error2;
|
|
776
837
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
message:
|
|
780
|
-
|
|
781
|
-
cause: error2
|
|
782
|
-
|
|
783
|
-
core.logger.error("Request handler error", {
|
|
784
|
-
error: normalizedError,
|
|
785
|
-
path: event.url.pathname,
|
|
786
|
-
method: event.request.method
|
|
838
|
+
return fromError({
|
|
839
|
+
...HaiCommonError.INTERNAL_ERROR,
|
|
840
|
+
message: kitM("kit_internalError"),
|
|
841
|
+
// 由 Core 统一规范化原始异常,保留驱动码和 AggregateError 的子错误。
|
|
842
|
+
cause: error2,
|
|
843
|
+
ext: { path: event.url.pathname, method: event.request.method }
|
|
787
844
|
});
|
|
788
|
-
return internalError();
|
|
789
845
|
}
|
|
790
846
|
};
|
|
791
847
|
}
|
|
@@ -823,14 +879,24 @@ async function validateForm(request, schema) {
|
|
|
823
879
|
} else {
|
|
824
880
|
return {
|
|
825
881
|
valid: false,
|
|
826
|
-
errors: [
|
|
882
|
+
errors: [
|
|
883
|
+
{
|
|
884
|
+
field: "_",
|
|
885
|
+
message: kitM("kit_unsupportedContentType")
|
|
886
|
+
}
|
|
887
|
+
]
|
|
827
888
|
};
|
|
828
889
|
}
|
|
829
890
|
return validateParsedData(data, schema);
|
|
830
891
|
} catch {
|
|
831
892
|
return {
|
|
832
893
|
valid: false,
|
|
833
|
-
errors: [
|
|
894
|
+
errors: [
|
|
895
|
+
{
|
|
896
|
+
field: "_",
|
|
897
|
+
message: kitM("kit_parseBodyFailed")
|
|
898
|
+
}
|
|
899
|
+
]
|
|
834
900
|
};
|
|
835
901
|
}
|
|
836
902
|
}
|
|
@@ -861,7 +927,20 @@ var PaginationQuerySchema = z.object({
|
|
|
861
927
|
});
|
|
862
928
|
|
|
863
929
|
// src/kit-auth-api.ts
|
|
864
|
-
var authApiPublicPaths = [
|
|
930
|
+
var authApiPublicPaths = [
|
|
931
|
+
"/api/auth/session/close",
|
|
932
|
+
"/api/auth/logout",
|
|
933
|
+
"/api/auth/captcha",
|
|
934
|
+
"/api/auth/config",
|
|
935
|
+
"/api/auth/login",
|
|
936
|
+
"/api/auth/login/ldap",
|
|
937
|
+
"/api/auth/login/otp",
|
|
938
|
+
"/api/auth/register",
|
|
939
|
+
"/api/auth/otp/send",
|
|
940
|
+
"/api/auth/password-reset/request",
|
|
941
|
+
"/api/auth/password-reset/confirm",
|
|
942
|
+
"/api/auth/password/renew"
|
|
943
|
+
];
|
|
865
944
|
function requestContext(event) {
|
|
866
945
|
let ip;
|
|
867
946
|
try {
|
|
@@ -869,7 +948,11 @@ function requestContext(event) {
|
|
|
869
948
|
} catch {
|
|
870
949
|
}
|
|
871
950
|
const terminal = event.request.headers.get("x-hai-terminal");
|
|
872
|
-
return {
|
|
951
|
+
return {
|
|
952
|
+
ip,
|
|
953
|
+
userAgent: event.request.headers.get("user-agent") ?? void 0,
|
|
954
|
+
terminalType: terminal === "app" || terminal === "desktop" || terminal === "api" ? terminal : "web"
|
|
955
|
+
};
|
|
873
956
|
}
|
|
874
957
|
function createAuthApi(iam) {
|
|
875
958
|
function response(result) {
|
|
@@ -932,18 +1015,33 @@ function createAuthApi(iam) {
|
|
|
932
1015
|
};
|
|
933
1016
|
}
|
|
934
1017
|
function createFeedbackHandler(options) {
|
|
935
|
-
const
|
|
1018
|
+
const configuredPath = options.basePath ?? "/api/feedback";
|
|
1019
|
+
let pathEnd = configuredPath.length;
|
|
1020
|
+
while (pathEnd > 0 && configuredPath[pathEnd - 1] === "/")
|
|
1021
|
+
pathEnd--;
|
|
1022
|
+
const basePath = configuredPath.slice(0, pathEnd);
|
|
936
1023
|
return async (event) => {
|
|
937
1024
|
let actor;
|
|
938
1025
|
try {
|
|
939
1026
|
actor = await options.resolveContext(event);
|
|
940
1027
|
} catch {
|
|
941
|
-
return fromError({
|
|
1028
|
+
return fromError({
|
|
1029
|
+
...HaiCommonError.INTERNAL_ERROR,
|
|
1030
|
+
message: kitM("kit_internalError")
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
if (!actor?.scopeId || !actor.userId) {
|
|
1034
|
+
return fromError({
|
|
1035
|
+
...HaiCommonError.UNAUTHORIZED,
|
|
1036
|
+
message: kitM("kit_unauthorized")
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
if (options.feedback.isInitialized === false) {
|
|
1040
|
+
return fromError({
|
|
1041
|
+
...HaiCommonError.SERVICE_UNAVAILABLE,
|
|
1042
|
+
message: kitM("kit_feedbackUnavailable")
|
|
1043
|
+
});
|
|
942
1044
|
}
|
|
943
|
-
if (!actor?.scopeId || !actor.userId)
|
|
944
|
-
return fromError({ ...HaiCommonError.UNAUTHORIZED, message: kitM("kit_unauthorized") });
|
|
945
|
-
if (options.feedback.isInitialized === false)
|
|
946
|
-
return fromError({ ...HaiCommonError.SERVICE_UNAVAILABLE, message: kitM("kit_feedbackUnavailable") });
|
|
947
1045
|
const suffix = event.url.pathname.startsWith(`${basePath}/`) ? event.url.pathname.slice(basePath.length + 1) : "";
|
|
948
1046
|
let parts;
|
|
949
1047
|
try {
|
|
@@ -953,8 +1051,12 @@ function createFeedbackHandler(options) {
|
|
|
953
1051
|
}
|
|
954
1052
|
if (parts.length === 1 && parts[0] === "policy" && event.request.method === "GET")
|
|
955
1053
|
return ok(options.feedback.budget);
|
|
956
|
-
if (parts[0] !== "reports" || parts.length > 4)
|
|
957
|
-
return fromError({
|
|
1054
|
+
if (parts[0] !== "reports" || parts.length > 4) {
|
|
1055
|
+
return fromError({
|
|
1056
|
+
...HaiCommonError.NOT_FOUND,
|
|
1057
|
+
message: core.i18n.coreM("core_errorNotFound")
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
958
1060
|
let body = {};
|
|
959
1061
|
if (!["GET", "HEAD"].includes(event.request.method)) {
|
|
960
1062
|
if (event.request.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() !== "application/json")
|
|
@@ -970,7 +1072,11 @@ function createFeedbackHandler(options) {
|
|
|
970
1072
|
const bodyRecord = body;
|
|
971
1073
|
if (parts[1] && Object.hasOwn(bodyRecord, "id") && bodyRecord.id !== parts[1])
|
|
972
1074
|
return invalid(400);
|
|
973
|
-
const input = {
|
|
1075
|
+
const input = {
|
|
1076
|
+
...Object.fromEntries(event.url.searchParams),
|
|
1077
|
+
...bodyRecord,
|
|
1078
|
+
...parts[1] ? { id: parts[1] } : {}
|
|
1079
|
+
};
|
|
974
1080
|
const method = event.request.method;
|
|
975
1081
|
const service = options.feedback;
|
|
976
1082
|
async function execute(schema, operation) {
|
|
@@ -981,7 +1087,10 @@ function createFeedbackHandler(options) {
|
|
|
981
1087
|
const result = await core.context.run(actor, () => operation(parsed.data));
|
|
982
1088
|
return result.success ? ok(result.data) : fromError(result.error);
|
|
983
1089
|
} catch {
|
|
984
|
-
return fromError({
|
|
1090
|
+
return fromError({
|
|
1091
|
+
...HaiCommonError.INTERNAL_ERROR,
|
|
1092
|
+
message: kitM("kit_internalError")
|
|
1093
|
+
});
|
|
985
1094
|
}
|
|
986
1095
|
}
|
|
987
1096
|
if (parts.length === 1) {
|
|
@@ -1017,11 +1126,18 @@ function createFeedbackHandler(options) {
|
|
|
1017
1126
|
Object.assign(input, { jobId: parts[3] });
|
|
1018
1127
|
return execute(FeedbackExportQuerySchema, (value) => service.getExport(value, actor));
|
|
1019
1128
|
}
|
|
1020
|
-
return fromError({
|
|
1129
|
+
return fromError({
|
|
1130
|
+
...HaiCommonError.NOT_FOUND,
|
|
1131
|
+
message: core.i18n.coreM("core_errorNotFound")
|
|
1132
|
+
});
|
|
1021
1133
|
};
|
|
1022
1134
|
}
|
|
1023
1135
|
function invalid(status) {
|
|
1024
|
-
return fromError({
|
|
1136
|
+
return fromError({
|
|
1137
|
+
...HaiCommonError.INVALID_REQUEST,
|
|
1138
|
+
httpStatus: status,
|
|
1139
|
+
message: kitM("kit_validationFailed")
|
|
1140
|
+
});
|
|
1025
1141
|
}
|
|
1026
1142
|
async function readBoundedJson(request, maximum) {
|
|
1027
1143
|
const reader = request.body?.getReader();
|
|
@@ -1155,7 +1271,6 @@ var kit = {
|
|
|
1155
1271
|
setBrowserToken,
|
|
1156
1272
|
/** 清除浏览器端 Access Token(客户端 logout 用) */
|
|
1157
1273
|
clearBrowserToken
|
|
1158
|
-
/** 创建浏览器端 Token 存储器(自定义 key 时使用) */
|
|
1159
1274
|
},
|
|
1160
1275
|
// ─── CRUD ───
|
|
1161
1276
|
crud: {
|