@h-ai/serv 0.1.0-alpha.67 → 0.1.0-alpha.69
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 +85 -19
- package/README.ja.md +85 -19
- package/README.md +86 -20
- package/dist/chunk-BGWE4S6Z.js +37 -0
- package/dist/chunk-BGWE4S6Z.js.map +1 -0
- package/dist/{chunk-R354OMWL.js → chunk-TWHCU6HT.js} +48 -6
- package/dist/chunk-TWHCU6HT.js.map +1 -0
- package/dist/{chunk-27PCPE37.js → chunk-XKCLD42I.js} +2 -2
- package/dist/chunk-XKCLD42I.js.map +1 -0
- package/dist/features/ai.d.ts +139 -57
- package/dist/features/ai.js +57 -46
- package/dist/features/ai.js.map +1 -1
- package/dist/features/feedback.d.ts +688 -0
- package/dist/features/feedback.js +82 -0
- package/dist/features/feedback.js.map +1 -0
- package/dist/features/iam.d.ts +29 -4
- package/dist/features/iam.js +3 -3
- package/dist/features/iam.js.map +1 -1
- package/dist/features/storage.d.ts +94 -9
- package/dist/features/storage.js +48 -12
- package/dist/features/storage.js.map +1 -1
- package/dist/index.d.ts +594 -187
- package/dist/index.js +109 -112
- package/dist/index.js.map +1 -1
- package/dist/{serv-context-CWzyrsdq.d.ts → serv-context-B-AxU8hj.d.ts} +129 -7
- package/dist/serv-router-C8dSz2k-.d.ts +322 -0
- package/package.json +24 -11
- package/dist/chunk-27PCPE37.js.map +0 -1
- package/dist/chunk-R354OMWL.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,26 +1,53 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { validateInputOrFail, resolveRequestLocale, localizeZodError } from './chunk-BGWE4S6Z.js';
|
|
2
|
+
import { servM, WILDCARD_ROLE, WILDCARD_PERMISSION, implement, buildHaiErrorBody } from './chunk-TWHCU6HT.js';
|
|
2
3
|
import process from 'process';
|
|
3
4
|
import { upgradeWebSocket, serve } from '@hono/node-server';
|
|
4
5
|
import { apiContract, AUDIO_WS_PATH, AudioWsClientMessageSchema } from '@h-ai/api-contract';
|
|
5
|
-
import { core,
|
|
6
|
+
import { core, HaiCommonError, err, ok } from '@h-ai/core';
|
|
6
7
|
import { OpenAPIHandler } from '@orpc/openapi/fetch';
|
|
7
8
|
import { RPCHandler } from '@orpc/server/fetch';
|
|
8
9
|
import { Hono } from 'hono';
|
|
9
10
|
import { WebSocketServer } from 'ws';
|
|
10
11
|
import { Buffer } from 'buffer';
|
|
11
12
|
import { BlockList, isIP } from 'net';
|
|
13
|
+
import { TRANSPORT_PLAINTEXT_RANGE, TRANSPORT_BODY_LIMITS, TRANSPORT_PROTOCOL, authenticateTransportRequest, TransportTimeError, createTransportErrorResponse, TransportBodyTooLargeError, readTransportBody, isValidSM2PublicKey } from '@h-ai/crypto';
|
|
12
14
|
import { z } from 'zod';
|
|
13
15
|
import { getCookie } from 'hono/cookie';
|
|
14
16
|
import { readFile } from 'fs/promises';
|
|
15
17
|
import { OpenAPIGenerator } from '@orpc/openapi';
|
|
16
18
|
import { ZodToJsonSchemaConverter } from '@orpc/zod/zod4';
|
|
17
|
-
import { TRANSPORT_PROTOCOL } from '@h-ai/crypto';
|
|
18
19
|
|
|
19
20
|
// src/adapters/serv-adapter-fetch.ts
|
|
20
21
|
function toFetch(app) {
|
|
21
|
-
const handler = (input, init) => {
|
|
22
|
-
const request =
|
|
23
|
-
|
|
22
|
+
const handler = async (input, init) => {
|
|
23
|
+
const request = new Request(input, init);
|
|
24
|
+
const { signal } = request;
|
|
25
|
+
signal.throwIfAborted();
|
|
26
|
+
let onAbort;
|
|
27
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
28
|
+
onAbort = () => reject(signal.reason);
|
|
29
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
30
|
+
});
|
|
31
|
+
try {
|
|
32
|
+
return await Promise.race([
|
|
33
|
+
Promise.resolve().then(async () => {
|
|
34
|
+
signal.throwIfAborted();
|
|
35
|
+
const response = await app.fetch(request);
|
|
36
|
+
if (signal.aborted) {
|
|
37
|
+
try {
|
|
38
|
+
await response.body?.cancel(signal.reason);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
signal.throwIfAborted();
|
|
42
|
+
}
|
|
43
|
+
return response;
|
|
44
|
+
}),
|
|
45
|
+
cancelled
|
|
46
|
+
]);
|
|
47
|
+
} finally {
|
|
48
|
+
if (onAbort)
|
|
49
|
+
signal.removeEventListener("abort", onAbort);
|
|
50
|
+
}
|
|
24
51
|
};
|
|
25
52
|
return handler;
|
|
26
53
|
}
|
|
@@ -585,36 +612,6 @@ function createClientIpResolver(trustedProxies) {
|
|
|
585
612
|
return current;
|
|
586
613
|
};
|
|
587
614
|
}
|
|
588
|
-
var DEFAULT_LOCALE = "zh-CN";
|
|
589
|
-
function resolveRequestLocale(headers) {
|
|
590
|
-
const get = (name) => {
|
|
591
|
-
if (headers instanceof Headers)
|
|
592
|
-
return headers.get(name) ?? void 0;
|
|
593
|
-
return headers[name] ?? headers[name.toLowerCase()];
|
|
594
|
-
};
|
|
595
|
-
const explicit = get("x-hai-locale");
|
|
596
|
-
if (explicit)
|
|
597
|
-
return normalizeServLocale(explicit);
|
|
598
|
-
return normalizeServLocale(get("accept-language") ?? get("Accept-Language") ?? DEFAULT_LOCALE);
|
|
599
|
-
}
|
|
600
|
-
function localizeZodError(zodError, locale) {
|
|
601
|
-
const resolvedLocale = normalizeServLocale(locale);
|
|
602
|
-
return core.zodValidation.mapZodErrorToFormErrors(
|
|
603
|
-
zodError,
|
|
604
|
-
core.zodValidation.createPrefixedZodMessageGetter(
|
|
605
|
-
"serv",
|
|
606
|
-
(messageKey, params) => servM(messageKey, { locale: resolvedLocale, params })
|
|
607
|
-
)
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
function validateInputOrFail(schema, input, locale = DEFAULT_LOCALE) {
|
|
611
|
-
const resolvedLocale = normalizeServLocale(locale);
|
|
612
|
-
const result = schema.safeParse(input);
|
|
613
|
-
if (result.success)
|
|
614
|
-
return ok(result.data);
|
|
615
|
-
const errors = localizeZodError(result.error, resolvedLocale);
|
|
616
|
-
return err(HaiCommonError.VALIDATION_ERROR, servM("serv_validationFailed", { locale: resolvedLocale }), errors);
|
|
617
|
-
}
|
|
618
615
|
|
|
619
616
|
// src/pipelines/serv-pipeline-require-internal-rpc.ts
|
|
620
617
|
var pipelineLogger = core.logger.child({ module: "serv", scope: "pipeline" });
|
|
@@ -727,9 +724,9 @@ var RpcHttpConfigInputSchema = z.union([
|
|
|
727
724
|
var TransportRuntimeConfigInputSchema = z.union([
|
|
728
725
|
z.literal(false),
|
|
729
726
|
z.object({
|
|
727
|
+
maxPlaintextBytes: z.number().int().min(TRANSPORT_PLAINTEXT_RANGE.min).max(TRANSPORT_PLAINTEXT_RANGE.max).optional().describe("\u53CC\u5411\u4E1A\u52A1\u660E\u6587 UTF-8 \u5B57\u8282\u4E0A\u9650\uFF1B\u9ED8\u8BA4 1 MiB\uFF0C\u5BC6\u6587\u4E0E\u4EE3\u7406\u9884\u7B97\u81EA\u52A8\u63A8\u5BFC").meta({ default: TRANSPORT_BODY_LIMITS.plaintextBytes }),
|
|
730
728
|
keyExchangePath: AbsolutePathSchema.default("/_hai/key-exchange"),
|
|
731
|
-
excludePaths: z.array(AbsolutePathSchema).default([])
|
|
732
|
-
maxClients: z.number().int().positive().default(1e4)
|
|
729
|
+
excludePaths: z.array(AbsolutePathSchema).default([])
|
|
733
730
|
})
|
|
734
731
|
]);
|
|
735
732
|
var ServerConfigInputSchema = z.object({
|
|
@@ -790,6 +787,9 @@ function resolveServTransportRuntimeConfig(input = false) {
|
|
|
790
787
|
var HEADER_REQUEST_ID = "x-request-id";
|
|
791
788
|
var HEADER_AUTHORIZATION = "authorization";
|
|
792
789
|
var HEADER_USER_AGENT = "user-agent";
|
|
790
|
+
function resolveRequestId(value) {
|
|
791
|
+
return value && /^[\w.:-]{1,128}$/.test(value) ? value : core.id.uuid();
|
|
792
|
+
}
|
|
793
793
|
function extractBearerToken(value) {
|
|
794
794
|
if (!value)
|
|
795
795
|
return void 0;
|
|
@@ -800,7 +800,7 @@ function extractBearerToken(value) {
|
|
|
800
800
|
}
|
|
801
801
|
function parseRequestContext(input) {
|
|
802
802
|
const headers = input.request.headers;
|
|
803
|
-
const requestId =
|
|
803
|
+
const requestId = core.context.get().requestId ?? resolveRequestId(headers.get(HEADER_REQUEST_ID));
|
|
804
804
|
const ip = normalizeClientIp(Object.hasOwn(input, "ip") ? input.ip : input.remoteAddress);
|
|
805
805
|
const locale = resolveRequestLocale(headers);
|
|
806
806
|
const accessToken = extractBearerToken(headers.get(HEADER_AUTHORIZATION));
|
|
@@ -848,7 +848,7 @@ function resolveRefreshCookieConfig(apiPrefix, config, iam) {
|
|
|
848
848
|
return {
|
|
849
849
|
cookieName: config.cookieName ?? "hai_refresh_token",
|
|
850
850
|
maxAge: config.maxAge ?? 30 * 24 * 3600,
|
|
851
|
-
isSecure: config.secure ?? process.env.NODE_ENV === "production",
|
|
851
|
+
isSecure: config.secure ?? (process.env.NODE_ENV === "production" || process.env.HAI_CORE_ENV === "production"),
|
|
852
852
|
refreshCookiePath: `${apiPrefix}${IAM_AUTH_PATHS.refresh}`,
|
|
853
853
|
onRefresh,
|
|
854
854
|
nativeTokenTransport: config.nativeTokenTransport
|
|
@@ -1058,7 +1058,6 @@ function readScalarBrowserEntry(packageInfo) {
|
|
|
1058
1058
|
return typeof browser === "string" ? browser : void 0;
|
|
1059
1059
|
}
|
|
1060
1060
|
var logger2 = core.logger.child({ module: "serv", scope: "transport" });
|
|
1061
|
-
var MAX_ENCRYPTED_BODY = 1048576;
|
|
1062
1061
|
var ENCRYPTABLE_RESPONSE_CONTENT_TYPES = ["application/json"];
|
|
1063
1062
|
function createTransportMiddleware(manager, keyExchangePath, excludePaths = []) {
|
|
1064
1063
|
return async (c, next) => {
|
|
@@ -1070,55 +1069,72 @@ function createTransportMiddleware(manager, keyExchangePath, excludePaths = [])
|
|
|
1070
1069
|
const clientId = c.req.header(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER);
|
|
1071
1070
|
if (!clientId)
|
|
1072
1071
|
return jsonError(servM("serv_transportClientIdRequired"), 400);
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
return
|
|
1072
|
+
let requestContext;
|
|
1073
|
+
try {
|
|
1074
|
+
const verified = await authenticateTransportRequest(manager, c.req.raw);
|
|
1075
|
+
if (!verified.success) {
|
|
1076
|
+
if (verified.error.cause instanceof TransportTimeError && verified.error.cause.context)
|
|
1077
|
+
return createTransportErrorResponse(verified.error.cause, { manager, context: verified.error.cause.context });
|
|
1078
|
+
return jsonError(servM("serv_transportDecryptFailed"), 400);
|
|
1079
|
+
}
|
|
1080
|
+
requestContext = verified.data.context;
|
|
1081
|
+
if (c.req.method !== "GET" && c.req.method !== "HEAD")
|
|
1082
|
+
replaceRequestBody(c, verified.data.plaintext);
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
if (error instanceof TransportBodyTooLargeError)
|
|
1085
|
+
return createTransportErrorResponse(error);
|
|
1086
|
+
return jsonError(servM("serv_transportInvalidPayload"), error instanceof RangeError ? 413 : 400);
|
|
1080
1087
|
}
|
|
1081
1088
|
await next();
|
|
1082
1089
|
const response = c.res;
|
|
1083
1090
|
if (!response)
|
|
1084
1091
|
return;
|
|
1085
1092
|
const contentType = response.headers.get("Content-Type") ?? "";
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1093
|
+
const bodyless = c.req.method === "HEAD" || isEmptyResponse(response);
|
|
1094
|
+
if (bodyless)
|
|
1095
|
+
void response.body?.cancel().catch(() => {
|
|
1096
|
+
});
|
|
1097
|
+
c.header("Content-Length", void 0);
|
|
1098
|
+
c.header("Content-Encoding", void 0);
|
|
1099
|
+
c.header(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, void 0);
|
|
1100
|
+
c.header(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, void 0);
|
|
1101
|
+
if (!bodyless && !canEncryptResponseContentType(contentType)) {
|
|
1102
|
+
void response.body?.cancel().catch(() => {
|
|
1103
|
+
});
|
|
1094
1104
|
c.res = jsonError(servM("serv_transportEncryptFailed"), 500);
|
|
1095
1105
|
return;
|
|
1096
1106
|
}
|
|
1097
1107
|
let bodyText;
|
|
1098
1108
|
try {
|
|
1099
|
-
bodyText = await response.
|
|
1109
|
+
bodyText = bodyless ? "" : await readTransportBody(response.body, {
|
|
1110
|
+
maxBytes: manager.bodyLimits.plaintextBytes,
|
|
1111
|
+
direction: "response",
|
|
1112
|
+
signal: c.req.raw.signal
|
|
1113
|
+
});
|
|
1100
1114
|
} catch (cause) {
|
|
1115
|
+
if (cause instanceof TransportBodyTooLargeError) {
|
|
1116
|
+
c.res = await createTransportErrorResponse(cause, { manager, context: requestContext });
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1101
1119
|
logger2.warn("Failed to read response body for encryption", { error: cause });
|
|
1102
1120
|
c.res = jsonError(servM("serv_transportEncryptFailed"), 500);
|
|
1103
1121
|
return;
|
|
1104
1122
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
}
|
|
1111
|
-
const encResult = await manager.encryptResponse(clientId, bodyText);
|
|
1123
|
+
const headers = new Headers(response.headers);
|
|
1124
|
+
headers.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
|
|
1125
|
+
headers.delete("Content-Length");
|
|
1126
|
+
headers.delete("Content-Encoding");
|
|
1127
|
+
const encResult = await manager.encryptResponse(requestContext, bodyText, response.status);
|
|
1112
1128
|
if (!encResult.success) {
|
|
1113
1129
|
logger2.warn("Failed to encrypt response", { error: encResult.error });
|
|
1114
1130
|
c.res = jsonError(servM("serv_transportEncryptFailed"), 500);
|
|
1115
1131
|
return;
|
|
1116
1132
|
}
|
|
1117
|
-
const headers = new Headers(response.headers);
|
|
1118
1133
|
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
1119
1134
|
headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE);
|
|
1120
|
-
|
|
1121
|
-
|
|
1135
|
+
if (bodyless)
|
|
1136
|
+
headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encResult.data));
|
|
1137
|
+
c.res = new Response(bodyless ? null : JSON.stringify(encResult.data), {
|
|
1122
1138
|
status: response.status,
|
|
1123
1139
|
statusText: response.statusText,
|
|
1124
1140
|
headers
|
|
@@ -1128,16 +1144,22 @@ function createTransportMiddleware(manager, keyExchangePath, excludePaths = [])
|
|
|
1128
1144
|
async function handleKeyExchange(request, manager) {
|
|
1129
1145
|
let body;
|
|
1130
1146
|
try {
|
|
1131
|
-
body = await request.
|
|
1132
|
-
|
|
1133
|
-
|
|
1147
|
+
body = JSON.parse(await readTransportBody(request.body, {
|
|
1148
|
+
maxBytes: TRANSPORT_BODY_LIMITS.keyExchangeBytes,
|
|
1149
|
+
signal: request.signal
|
|
1150
|
+
}));
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
return jsonError(servM("serv_transportInvalidPayload"), error instanceof RangeError ? 413 : 400);
|
|
1134
1153
|
}
|
|
1135
|
-
if (typeof body
|
|
1154
|
+
if (!body || typeof body !== "object" || !("clientPublicKey" in body) || !isValidSM2PublicKey(body.clientPublicKey) || new TextEncoder().encode(body.clientPublicKey).byteLength > TRANSPORT_BODY_LIMITS.publicKeyBytes) {
|
|
1136
1155
|
return jsonError(servM("serv_transportInvalidPayload"), 400);
|
|
1156
|
+
}
|
|
1137
1157
|
try {
|
|
1138
|
-
const clientId = await manager.registerClientKey(body.clientPublicKey);
|
|
1139
1158
|
const serverPublicKey = manager.getServerPublicKey();
|
|
1140
|
-
|
|
1159
|
+
if (new TextEncoder().encode(serverPublicKey).byteLength > TRANSPORT_BODY_LIMITS.publicKeyBytes)
|
|
1160
|
+
return jsonError(servM("serv_transportKeyExchangeFailed"), 500);
|
|
1161
|
+
const clientId = await manager.registerClientKey(body.clientPublicKey);
|
|
1162
|
+
return new Response(JSON.stringify({ serverKeyId: manager.getServerKeyId(), serverPublicKey, clientId, maxPlaintextBytes: manager.bodyLimits.plaintextBytes }), {
|
|
1141
1163
|
status: 200,
|
|
1142
1164
|
headers: { "Content-Type": "application/json; charset=utf-8" }
|
|
1143
1165
|
});
|
|
@@ -1146,37 +1168,23 @@ async function handleKeyExchange(request, manager) {
|
|
|
1146
1168
|
return jsonError(servM("serv_transportKeyExchangeFailed"), 500);
|
|
1147
1169
|
}
|
|
1148
1170
|
}
|
|
1149
|
-
|
|
1150
|
-
const isEncrypted = c.req.header(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER) === TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE;
|
|
1151
|
-
if (!isEncrypted)
|
|
1152
|
-
return jsonError(servM("serv_transportInvalidPayload"), 400);
|
|
1153
|
-
let payload;
|
|
1154
|
-
try {
|
|
1155
|
-
payload = await c.req.raw.clone().json();
|
|
1156
|
-
} catch {
|
|
1157
|
-
return jsonError(servM("serv_transportInvalidPayload"), 400);
|
|
1158
|
-
}
|
|
1159
|
-
if (!isEncryptedPayloadShape(payload))
|
|
1160
|
-
return jsonError(servM("serv_transportInvalidPayload"), 400);
|
|
1161
|
-
const decResult = manager.decryptRequest(payload);
|
|
1162
|
-
if (!decResult.success) {
|
|
1163
|
-
logger2.warn("Failed to decrypt request", { error: decResult.error });
|
|
1164
|
-
return jsonError(servM("serv_transportDecryptFailed"), 400);
|
|
1165
|
-
}
|
|
1171
|
+
function replaceRequestBody(c, body) {
|
|
1166
1172
|
const original = c.req.raw;
|
|
1167
1173
|
const newHeaders = new Headers(original.headers);
|
|
1168
1174
|
newHeaders.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
|
|
1169
1175
|
newHeaders.delete("Content-Length");
|
|
1176
|
+
newHeaders.delete("Content-Encoding");
|
|
1170
1177
|
newHeaders.set("Content-Type", "application/json; charset=utf-8");
|
|
1171
1178
|
const newRequest = new Request(original.url, {
|
|
1172
1179
|
method: original.method,
|
|
1173
1180
|
headers: newHeaders,
|
|
1174
|
-
body
|
|
1181
|
+
body,
|
|
1182
|
+
signal: original.signal,
|
|
1175
1183
|
// @ts-expect-error Node fetch 需要 duplex 才能携带流式 body。
|
|
1176
1184
|
duplex: "half"
|
|
1177
1185
|
});
|
|
1178
1186
|
c.req.raw = newRequest;
|
|
1179
|
-
|
|
1187
|
+
c.req.bodyCache = {};
|
|
1180
1188
|
}
|
|
1181
1189
|
function shouldExclude(pathname, excludePaths, keyExchangePath) {
|
|
1182
1190
|
if (pathname === keyExchangePath)
|
|
@@ -1189,18 +1197,7 @@ function canEncryptResponseContentType(contentType) {
|
|
|
1189
1197
|
function isEmptyResponse(response) {
|
|
1190
1198
|
if ([204, 304].includes(response.status))
|
|
1191
1199
|
return true;
|
|
1192
|
-
return response.
|
|
1193
|
-
}
|
|
1194
|
-
function hasBody(method) {
|
|
1195
|
-
return ["POST", "PUT", "PATCH", "DELETE"].includes(method.toUpperCase());
|
|
1196
|
-
}
|
|
1197
|
-
function isEmptyBody(req) {
|
|
1198
|
-
if (req.body === null)
|
|
1199
|
-
return true;
|
|
1200
|
-
const cl = req.headers.get("Content-Length");
|
|
1201
|
-
if (cl !== null)
|
|
1202
|
-
return Number.parseInt(cl, 10) === 0;
|
|
1203
|
-
return !req.headers.get("Content-Type");
|
|
1200
|
+
return response.body === null;
|
|
1204
1201
|
}
|
|
1205
1202
|
function jsonError(message, status) {
|
|
1206
1203
|
return new Response(JSON.stringify({ error: message }), {
|
|
@@ -1208,12 +1205,6 @@ function jsonError(message, status) {
|
|
|
1208
1205
|
headers: { "Content-Type": "application/json; charset=utf-8" }
|
|
1209
1206
|
});
|
|
1210
1207
|
}
|
|
1211
|
-
function isEncryptedPayloadShape(value) {
|
|
1212
|
-
if (!value || typeof value !== "object")
|
|
1213
|
-
return false;
|
|
1214
|
-
const p = value;
|
|
1215
|
-
return typeof p.encryptedKey === "string" && typeof p.ciphertext === "string" && typeof p.iv === "string";
|
|
1216
|
-
}
|
|
1217
1208
|
|
|
1218
1209
|
// src/serv-app.ts
|
|
1219
1210
|
var API_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
|
|
@@ -1221,6 +1212,11 @@ var audioWsServers = /* @__PURE__ */ new WeakMap();
|
|
|
1221
1212
|
function createApp(options) {
|
|
1222
1213
|
const http = resolveServHttpConfig(options.http);
|
|
1223
1214
|
const app = new Hono();
|
|
1215
|
+
app.use("*", async (c, next) => {
|
|
1216
|
+
const requestId = resolveRequestId(c.req.header("x-request-id") ?? null);
|
|
1217
|
+
await core.context.run({ requestId, userId: void 0, scopeId: void 0, jobId: void 0 }, () => core.i18n.runWithLocale(resolveRequestLocale(c.req.raw.headers), next));
|
|
1218
|
+
c.header("x-request-id", requestId);
|
|
1219
|
+
});
|
|
1224
1220
|
app.notFound((c) => c.json(buildHaiErrorBody(
|
|
1225
1221
|
HaiCommonError.NOT_FOUND,
|
|
1226
1222
|
core.i18n.coreM("core_errorNotFound", { locale: resolveRequestLocale(c.req.raw.headers) })
|
|
@@ -1240,8 +1236,9 @@ function createApp(options) {
|
|
|
1240
1236
|
mountCustomMiddlewares(app, options.middlewares);
|
|
1241
1237
|
if (options.transport) {
|
|
1242
1238
|
const mgr = options.transport.crypto.transport.createServer({
|
|
1239
|
+
serverKeyRing: options.transport.serverKeyRing,
|
|
1243
1240
|
keyStore: options.transport.keyStore,
|
|
1244
|
-
|
|
1241
|
+
maxPlaintextBytes: options.transport.maxPlaintextBytes
|
|
1245
1242
|
});
|
|
1246
1243
|
if (!mgr.success) {
|
|
1247
1244
|
throw core.error.buildHaiErrorInst(
|
|
@@ -1323,7 +1320,7 @@ function mountRPCRoutes(app, procedures, rpcConfig, createContext) {
|
|
|
1323
1320
|
}
|
|
1324
1321
|
async function handleORPC(c, next, prefix, handler, createContext) {
|
|
1325
1322
|
const context = await createContext({ request: c.req.raw, remoteAddress: readRemoteAddress(c.env) });
|
|
1326
|
-
const { matched, response } = await handler.handle(c.req.raw, { prefix, context });
|
|
1323
|
+
const { matched, response } = await core.context.run({ requestId: context.requestId, userId: context.session?.userId, scopeId: context.scopeId }, () => handler.handle(c.req.raw, { prefix, context }));
|
|
1327
1324
|
if (!matched) {
|
|
1328
1325
|
await next();
|
|
1329
1326
|
return;
|