@h-ai/serv 0.1.0-alpha.75 → 1.0.0-beta.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/README.en.md +22 -9
- package/README.ja.md +22 -9
- package/README.md +22 -9
- package/dist/{chunk-JAQIEQJS.js → chunk-I3F7RN4I.js} +8 -2
- package/dist/chunk-I3F7RN4I.js.map +1 -0
- package/dist/{chunk-65XNI7TH.js → chunk-TWMOZPGU.js} +3 -3
- package/dist/{chunk-65XNI7TH.js.map → chunk-TWMOZPGU.js.map} +1 -1
- package/dist/features/ai.js +1 -1
- package/dist/features/feedback.js +1 -1
- package/dist/features/iam.d.ts +42 -44
- package/dist/features/iam.js +1 -9
- package/dist/features/iam.js.map +1 -1
- package/dist/features/storage.js +2 -2
- package/dist/index.d.ts +85 -13
- package/dist/index.js +154 -157
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
- package/dist/chunk-JAQIEQJS.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { validateInputOrFail, resolveRequestLocale, localizeZodError } from './chunk-
|
|
2
|
-
import { servM, WILDCARD_ROLE, WILDCARD_PERMISSION, implement, buildHaiErrorBody } from './chunk-
|
|
1
|
+
import { validateInputOrFail, resolveRequestLocale, localizeZodError } from './chunk-TWMOZPGU.js';
|
|
2
|
+
import { servM, WILDCARD_ROLE, WILDCARD_PERMISSION, implement, buildHaiErrorBody } from './chunk-I3F7RN4I.js';
|
|
3
3
|
import process from 'process';
|
|
4
4
|
import { upgradeWebSocket, serve } from '@hono/node-server';
|
|
5
5
|
import { apiContract, AUDIO_WS_PATH, AudioWsClientMessageSchema } from '@h-ai/api-contract';
|
|
6
6
|
import { audit } from '@h-ai/audit';
|
|
7
|
-
import { core, HaiCommonError, err, ok } from '@h-ai/core';
|
|
7
|
+
import { core, RequestAccessConfigSchema, TrustedProxiesSchema, HaiCommonError, err, ok } from '@h-ai/core';
|
|
8
8
|
import { TRANSPORT_PLAINTEXT_RANGE, createTransportBodyPolicy, TRANSPORT_PROTOCOL, authenticateTransportRequest, TransportTimeError, createTransportErrorResponse, TransportBodyTooLargeError, readTransportBody, TRANSPORT_BODY_LIMITS, isValidSM2PublicKey } from '@h-ai/crypto';
|
|
9
9
|
import { OpenAPIHandler } from '@orpc/openapi/fetch';
|
|
10
10
|
import { RPCHandler } from '@orpc/server/fetch';
|
|
11
11
|
import { Hono } from 'hono';
|
|
12
12
|
import { WebSocketServer } from 'ws';
|
|
13
13
|
import { Buffer } from 'buffer';
|
|
14
|
-
import { BlockList, isIP } from 'net';
|
|
15
14
|
import { z } from 'zod';
|
|
16
15
|
import { getCookie } from 'hono/cookie';
|
|
17
16
|
import { readFile } from 'fs/promises';
|
|
@@ -552,66 +551,7 @@ function toArrayBuffer(data) {
|
|
|
552
551
|
function readRemoteAddress(metadata) {
|
|
553
552
|
if (!metadata || typeof metadata !== "object" || !("remoteAddress" in metadata) || typeof metadata.remoteAddress !== "string")
|
|
554
553
|
return void 0;
|
|
555
|
-
return normalizeClientIp(metadata.remoteAddress);
|
|
556
|
-
}
|
|
557
|
-
function normalizeClientIp(value) {
|
|
558
|
-
if (!value)
|
|
559
|
-
return void 0;
|
|
560
|
-
const address = value.trim();
|
|
561
|
-
if (!isIP(address))
|
|
562
|
-
return void 0;
|
|
563
|
-
const mapped = address.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
|
|
564
|
-
return mapped?.[1] ?? address;
|
|
565
|
-
}
|
|
566
|
-
function addProxy(blockList, entry) {
|
|
567
|
-
const [address, prefix, extra] = entry.split("/");
|
|
568
|
-
const family = address ? isIP(address) : 0;
|
|
569
|
-
if (!family || extra !== void 0)
|
|
570
|
-
throw new Error("trustedProxies entries must be IP addresses or CIDRs");
|
|
571
|
-
const type = family === 4 ? "ipv4" : "ipv6";
|
|
572
|
-
if (prefix === void 0) {
|
|
573
|
-
blockList.addAddress(address, type);
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
const bits = Number(prefix);
|
|
577
|
-
if (!/^\d+$/.test(prefix) || bits < 0 || bits > (family === 4 ? 32 : 128))
|
|
578
|
-
throw new Error("Invalid trustedProxies CIDR prefix");
|
|
579
|
-
blockList.addSubnet(address, bits, type);
|
|
580
|
-
}
|
|
581
|
-
function isTrustedProxyEntry(entry) {
|
|
582
|
-
try {
|
|
583
|
-
addProxy(new BlockList(), entry);
|
|
584
|
-
return true;
|
|
585
|
-
} catch {
|
|
586
|
-
return false;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
function createClientIpResolver(trustedProxies) {
|
|
590
|
-
const proxies = new BlockList();
|
|
591
|
-
for (const entry of trustedProxies)
|
|
592
|
-
addProxy(proxies, entry);
|
|
593
|
-
const trusted = (address) => proxies.check(address, isIP(address) === 4 ? "ipv4" : "ipv6");
|
|
594
|
-
return (request, remoteAddress) => {
|
|
595
|
-
const peer = normalizeClientIp(remoteAddress);
|
|
596
|
-
if (!peer || !trusted(peer))
|
|
597
|
-
return peer;
|
|
598
|
-
const forwarded = request.headers.get("x-forwarded-for");
|
|
599
|
-
if (forwarded === null) {
|
|
600
|
-
const realIp = request.headers.get("x-real-ip");
|
|
601
|
-
return realIp === null ? peer : normalizeClientIp(realIp);
|
|
602
|
-
}
|
|
603
|
-
const chain = forwarded.split(",");
|
|
604
|
-
if (chain.length > 32)
|
|
605
|
-
return void 0;
|
|
606
|
-
let current = peer;
|
|
607
|
-
for (let index = chain.length - 1; index >= 0 && trusted(current); index--) {
|
|
608
|
-
const address = normalizeClientIp(chain[index]);
|
|
609
|
-
if (!address)
|
|
610
|
-
return void 0;
|
|
611
|
-
current = address;
|
|
612
|
-
}
|
|
613
|
-
return current;
|
|
614
|
-
};
|
|
554
|
+
return core.access.normalizeClientIp(metadata.remoteAddress);
|
|
615
555
|
}
|
|
616
556
|
|
|
617
557
|
// src/pipelines/serv-pipeline-require-internal-rpc.ts
|
|
@@ -743,7 +683,8 @@ var CorsRuntimeConfigInputSchema = z.object({
|
|
|
743
683
|
credentials: z.boolean().default(false)
|
|
744
684
|
});
|
|
745
685
|
var ServHttpConfigInputSchema = z.object({
|
|
746
|
-
trustedProxies:
|
|
686
|
+
trustedProxies: TrustedProxiesSchema.default("auto"),
|
|
687
|
+
access: RequestAccessConfigSchema.default(false),
|
|
747
688
|
apiPrefix: ApiPrefixSchema.default("/api/v1"),
|
|
748
689
|
openapi: OpenAPIHttpConfigInputSchema.default(false),
|
|
749
690
|
docs: DocsHttpConfigInputSchema.default(false),
|
|
@@ -803,7 +744,7 @@ function extractBearerToken(value) {
|
|
|
803
744
|
function parseRequestContext(input) {
|
|
804
745
|
const headers = input.request.headers;
|
|
805
746
|
const requestId = core.context.get().requestId ?? resolveRequestId(headers.get(HEADER_REQUEST_ID));
|
|
806
|
-
const ip = normalizeClientIp(Object.hasOwn(input, "ip") ? input.ip : input.remoteAddress);
|
|
747
|
+
const ip = core.access.normalizeClientIp(Object.hasOwn(input, "ip") ? input.ip : input.remoteAddress);
|
|
807
748
|
const locale = resolveRequestLocale(headers);
|
|
808
749
|
const accessToken = extractBearerToken(headers.get(HEADER_AUTHORIZATION));
|
|
809
750
|
const terminal = headers.get("x-hai-terminal");
|
|
@@ -999,6 +940,96 @@ async function readRefreshTokenFromBody(request) {
|
|
|
999
940
|
const body = await request.json().catch(() => void 0);
|
|
1000
941
|
return typeof body?.refreshToken === "string" && body.refreshToken.length > 0 ? body.refreshToken : void 0;
|
|
1001
942
|
}
|
|
943
|
+
|
|
944
|
+
// src/serv-cors.ts
|
|
945
|
+
var DEFAULT_CORS_METHODS = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE", "OPTIONS"];
|
|
946
|
+
var DEFAULT_CORS_ALLOWED_HEADERS = ["Authorization", "Content-Type", "X-Requested-With", "X-Hai-Reauthentication", "X-Hai-Terminal"];
|
|
947
|
+
var DEFAULT_CORS_MAX_AGE = 86400;
|
|
948
|
+
var corsMiddlewares = /* @__PURE__ */ new WeakSet();
|
|
949
|
+
function isCorsMiddleware(middleware) {
|
|
950
|
+
return corsMiddlewares.has(middleware);
|
|
951
|
+
}
|
|
952
|
+
function cors(config = {}) {
|
|
953
|
+
const methods = [...config.methods ?? DEFAULT_CORS_METHODS];
|
|
954
|
+
const allowedHeaders = [...config.allowedHeaders ?? DEFAULT_CORS_ALLOWED_HEADERS];
|
|
955
|
+
const exposedHeaders = [.../* @__PURE__ */ new Set(["X-Request-Id", "X-Trace-Id", ...config.exposedHeaders ?? []])];
|
|
956
|
+
const credentials = config.credentials ?? false;
|
|
957
|
+
const maxAge = config.maxAge ?? DEFAULT_CORS_MAX_AGE;
|
|
958
|
+
const originConfig = config.origin ?? "*";
|
|
959
|
+
const middleware = async (c, next) => {
|
|
960
|
+
const allowedOrigin = resolveCorsOrigin(c.req.header("Origin"), originConfig);
|
|
961
|
+
if (c.req.method === "OPTIONS") {
|
|
962
|
+
return new Response(null, {
|
|
963
|
+
status: 204,
|
|
964
|
+
headers: buildCorsHeaders({
|
|
965
|
+
allowedOrigin,
|
|
966
|
+
allowedHeaders,
|
|
967
|
+
credentials,
|
|
968
|
+
exposedHeaders,
|
|
969
|
+
maxAge,
|
|
970
|
+
methods,
|
|
971
|
+
request: c.req.raw
|
|
972
|
+
})
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
await next();
|
|
976
|
+
const corsHeaders = buildCorsHeaders({
|
|
977
|
+
allowedOrigin,
|
|
978
|
+
allowedHeaders,
|
|
979
|
+
credentials,
|
|
980
|
+
exposedHeaders,
|
|
981
|
+
maxAge,
|
|
982
|
+
methods,
|
|
983
|
+
request: c.req.raw
|
|
984
|
+
});
|
|
985
|
+
for (const [key, value] of corsHeaders) {
|
|
986
|
+
if (key.toLowerCase() === "vary") {
|
|
987
|
+
c.res.headers.append(key, value);
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
c.res.headers.set(key, value);
|
|
991
|
+
}
|
|
992
|
+
return void 0;
|
|
993
|
+
};
|
|
994
|
+
corsMiddlewares.add(middleware);
|
|
995
|
+
return middleware;
|
|
996
|
+
}
|
|
997
|
+
function resolveCorsOrigin(origin, configOrigin) {
|
|
998
|
+
if (!origin)
|
|
999
|
+
return null;
|
|
1000
|
+
if (configOrigin === "*")
|
|
1001
|
+
return "*";
|
|
1002
|
+
if (typeof configOrigin === "string")
|
|
1003
|
+
return origin === configOrigin ? origin : null;
|
|
1004
|
+
if (Array.isArray(configOrigin))
|
|
1005
|
+
return configOrigin.includes(origin) ? origin : null;
|
|
1006
|
+
if (typeof configOrigin === "function")
|
|
1007
|
+
return configOrigin(origin) ? origin : null;
|
|
1008
|
+
return null;
|
|
1009
|
+
}
|
|
1010
|
+
function buildCorsHeaders(options) {
|
|
1011
|
+
const headers = new Headers();
|
|
1012
|
+
if (!options.allowedOrigin)
|
|
1013
|
+
return headers;
|
|
1014
|
+
const requestedHeaders = options.request.headers.get("Access-Control-Request-Headers");
|
|
1015
|
+
headers.set("Access-Control-Allow-Origin", options.allowedOrigin);
|
|
1016
|
+
if (options.credentials)
|
|
1017
|
+
headers.set("Access-Control-Allow-Credentials", "true");
|
|
1018
|
+
if (options.allowedOrigin !== "*") {
|
|
1019
|
+
headers.set(
|
|
1020
|
+
"Vary",
|
|
1021
|
+
requestedHeaders ? "Origin, Access-Control-Request-Headers" : "Origin"
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
if (options.exposedHeaders.length > 0)
|
|
1025
|
+
headers.set("Access-Control-Expose-Headers", options.exposedHeaders.join(", "));
|
|
1026
|
+
if (options.request.method === "OPTIONS") {
|
|
1027
|
+
headers.set("Access-Control-Allow-Methods", options.methods.join(", "));
|
|
1028
|
+
headers.set("Access-Control-Allow-Headers", requestedHeaders ?? options.allowedHeaders.join(", "));
|
|
1029
|
+
headers.set("Access-Control-Max-Age", String(options.maxAge));
|
|
1030
|
+
}
|
|
1031
|
+
return headers;
|
|
1032
|
+
}
|
|
1002
1033
|
var SCALAR_ROUTE = "/_hai/scalar.js";
|
|
1003
1034
|
var SCALAR_BROWSER_ENTRY_FALLBACK = "./dist/browser/standalone.js";
|
|
1004
1035
|
var scalarScriptCache;
|
|
@@ -1063,12 +1094,20 @@ function readScalarBrowserEntry(packageInfo) {
|
|
|
1063
1094
|
}
|
|
1064
1095
|
var logger2 = core.logger.child({ module: "serv", scope: "transport" });
|
|
1065
1096
|
var ENCRYPTABLE_RESPONSE_CONTENT_TYPES = ["application/json"];
|
|
1066
|
-
function createTransportMiddleware(manager, keyExchangePath, excludePaths = [], policy = createTransportBodyPolicy(manager.bodyLimits.plaintextBytes)) {
|
|
1097
|
+
function createTransportMiddleware(manager, keyExchangePath, excludePaths = [], policy = createTransportBodyPolicy(manager.bodyLimits.plaintextBytes), encryption) {
|
|
1067
1098
|
return async (c, next) => {
|
|
1068
1099
|
const pathname = new URL(c.req.url).pathname;
|
|
1100
|
+
if (c.req.method === "OPTIONS")
|
|
1101
|
+
return next();
|
|
1069
1102
|
if (pathname === keyExchangePath && c.req.method === "POST")
|
|
1070
1103
|
return handleKeyExchange(c.req.raw, manager);
|
|
1071
|
-
|
|
1104
|
+
const mode = encryption?.(pathname, c.req.method);
|
|
1105
|
+
if (mode === "disabled") {
|
|
1106
|
+
if (c.req.raw.headers.has(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER) || c.req.raw.headers.has(TRANSPORT_PROTOCOL.ENVELOPE_HEADER))
|
|
1107
|
+
return Response.json(err({ ...HaiCommonError.VALIDATION_ERROR, message: servM("serv_transportPlaintextRequired"), traceId: core.context.get().traceId ?? core.context.get().requestId }), { status: 400 });
|
|
1108
|
+
return next();
|
|
1109
|
+
}
|
|
1110
|
+
if (mode !== "required" && shouldExclude(pathname, excludePaths, keyExchangePath))
|
|
1072
1111
|
return next();
|
|
1073
1112
|
const clientId = c.req.header(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER);
|
|
1074
1113
|
if (!clientId)
|
|
@@ -1222,7 +1261,10 @@ var API_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
|
|
|
1222
1261
|
var audioWsServers = /* @__PURE__ */ new WeakMap();
|
|
1223
1262
|
function createApp(options) {
|
|
1224
1263
|
const http = resolveServHttpConfig(options.http);
|
|
1225
|
-
const resolveIp = createClientIpResolver(http.trustedProxies);
|
|
1264
|
+
const resolveIp = core.access.createClientIpResolver(http.trustedProxies);
|
|
1265
|
+
const checkAccess = core.access.createRequestCheck(http.access);
|
|
1266
|
+
if (checkAccess.requiresEncryption && !options.transport)
|
|
1267
|
+
throw new Error("Request access rules require transport encryption, but Serv transport is not configured");
|
|
1226
1268
|
const app = new Hono();
|
|
1227
1269
|
const requestAuditContexts = /* @__PURE__ */ new WeakMap();
|
|
1228
1270
|
app.onError((error, c) => {
|
|
@@ -1280,8 +1322,45 @@ function createApp(options) {
|
|
|
1280
1322
|
return cachedSpec;
|
|
1281
1323
|
};
|
|
1282
1324
|
app.use("*", securityHeaders());
|
|
1283
|
-
|
|
1284
|
-
|
|
1325
|
+
const accessDenied = (c) => {
|
|
1326
|
+
const ipAddress = resolveIp(c.req.raw, readRemoteAddress(c.env));
|
|
1327
|
+
const decision = checkAccess(c.req.path, c.req.method, ipAddress);
|
|
1328
|
+
if (decision.allowed) {
|
|
1329
|
+
if (!options.transport && c.req.method !== "OPTIONS" && (c.req.raw.headers.has(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER) || c.req.raw.headers.has(TRANSPORT_PROTOCOL.ENVELOPE_HEADER)) && checkAccess.encryption(c.req.path, c.req.method) === "disabled") {
|
|
1330
|
+
return c.json(buildHaiErrorBody(HaiCommonError.VALIDATION_ERROR, servM("serv_transportPlaintextRequired")), 400);
|
|
1331
|
+
}
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
core.logger.info("Request access policy denied", { ruleId: decision.ruleId, reason: decision.reason, method: c.req.method, ipAddress });
|
|
1335
|
+
return c.json(buildHaiErrorBody(HaiCommonError.REQUEST_ACCESS_DENIED, core.i18n.coreM("core_requestAccessDenied")), 403);
|
|
1336
|
+
};
|
|
1337
|
+
const cors2 = options.middlewares?.filter((entry) => isCorsMiddleware(entry.middleware)) ?? [];
|
|
1338
|
+
const custom = options.middlewares?.filter((entry) => !isCorsMiddleware(entry.middleware)) ?? [];
|
|
1339
|
+
for (const entry of cors2) {
|
|
1340
|
+
if (http.access === false) {
|
|
1341
|
+
app.use(entry.path ?? "*", entry.middleware);
|
|
1342
|
+
continue;
|
|
1343
|
+
}
|
|
1344
|
+
app.use(entry.path ?? "*", async (c, next) => {
|
|
1345
|
+
if (c.req.method !== "OPTIONS")
|
|
1346
|
+
return entry.middleware(c, next);
|
|
1347
|
+
const rejected = accessDenied(c);
|
|
1348
|
+
const preflight = await entry.middleware(c, async () => {
|
|
1349
|
+
});
|
|
1350
|
+
if (!rejected)
|
|
1351
|
+
return preflight;
|
|
1352
|
+
if (preflight instanceof Response) {
|
|
1353
|
+
for (const [key, value] of preflight.headers)
|
|
1354
|
+
rejected.headers.set(key, value);
|
|
1355
|
+
rejected.headers.set("content-type", "application/json; charset=utf-8");
|
|
1356
|
+
}
|
|
1357
|
+
return rejected;
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
const keyExchangePath = options.transport ? `${http.apiPrefix}${options.transport.keyExchangePath ?? "/_hai/key-exchange"}` : void 0;
|
|
1361
|
+
if (http.access !== false && keyExchangePath) {
|
|
1362
|
+
app.use("*", async (c, next) => c.req.path === keyExchangePath && c.req.method === "POST" ? accessDenied(c) ?? next() : next());
|
|
1363
|
+
}
|
|
1285
1364
|
if (options.transport) {
|
|
1286
1365
|
const policy = createTransportBodyPolicy(options.transport.maxPlaintextBytes, options.transport.routePlaintextBytes);
|
|
1287
1366
|
const mgr = options.transport.crypto.transport.createServer({
|
|
@@ -1296,9 +1375,11 @@ function createApp(options) {
|
|
|
1296
1375
|
mgr.error
|
|
1297
1376
|
);
|
|
1298
1377
|
}
|
|
1299
|
-
|
|
1300
|
-
app.use("*", createTransportMiddleware(mgr.data, keyExchangePath, options.transport.excludePaths, policy));
|
|
1378
|
+
app.use("*", createTransportMiddleware(mgr.data, keyExchangePath, options.transport.excludePaths, policy, checkAccess.encryption));
|
|
1301
1379
|
}
|
|
1380
|
+
if (http.access !== false)
|
|
1381
|
+
app.use("*", async (c, next) => accessDenied(c) ?? next());
|
|
1382
|
+
mountCustomMiddlewares(app, custom);
|
|
1302
1383
|
if (http.health !== false)
|
|
1303
1384
|
mountHealthRoutes(app, http.health);
|
|
1304
1385
|
if (options.refreshCookie)
|
|
@@ -1478,90 +1559,6 @@ function listen(app, options = {}) {
|
|
|
1478
1559
|
return handle;
|
|
1479
1560
|
}
|
|
1480
1561
|
|
|
1481
|
-
// src/serv-cors.ts
|
|
1482
|
-
var DEFAULT_CORS_METHODS = ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE", "OPTIONS"];
|
|
1483
|
-
var DEFAULT_CORS_ALLOWED_HEADERS = ["Authorization", "Content-Type", "X-Requested-With", "X-Hai-Reauthentication", "X-Hai-Terminal"];
|
|
1484
|
-
var DEFAULT_CORS_MAX_AGE = 86400;
|
|
1485
|
-
function cors(config = {}) {
|
|
1486
|
-
const methods = [...config.methods ?? DEFAULT_CORS_METHODS];
|
|
1487
|
-
const allowedHeaders = [...config.allowedHeaders ?? DEFAULT_CORS_ALLOWED_HEADERS];
|
|
1488
|
-
const exposedHeaders = [.../* @__PURE__ */ new Set(["X-Request-Id", "X-Trace-Id", ...config.exposedHeaders ?? []])];
|
|
1489
|
-
const credentials = config.credentials ?? false;
|
|
1490
|
-
const maxAge = config.maxAge ?? DEFAULT_CORS_MAX_AGE;
|
|
1491
|
-
const originConfig = config.origin ?? "*";
|
|
1492
|
-
return async (c, next) => {
|
|
1493
|
-
const allowedOrigin = resolveCorsOrigin(c.req.header("Origin"), originConfig);
|
|
1494
|
-
if (c.req.method === "OPTIONS") {
|
|
1495
|
-
return new Response(null, {
|
|
1496
|
-
status: 204,
|
|
1497
|
-
headers: buildCorsHeaders({
|
|
1498
|
-
allowedOrigin,
|
|
1499
|
-
allowedHeaders,
|
|
1500
|
-
credentials,
|
|
1501
|
-
exposedHeaders,
|
|
1502
|
-
maxAge,
|
|
1503
|
-
methods,
|
|
1504
|
-
request: c.req.raw
|
|
1505
|
-
})
|
|
1506
|
-
});
|
|
1507
|
-
}
|
|
1508
|
-
await next();
|
|
1509
|
-
const corsHeaders = buildCorsHeaders({
|
|
1510
|
-
allowedOrigin,
|
|
1511
|
-
allowedHeaders,
|
|
1512
|
-
credentials,
|
|
1513
|
-
exposedHeaders,
|
|
1514
|
-
maxAge,
|
|
1515
|
-
methods,
|
|
1516
|
-
request: c.req.raw
|
|
1517
|
-
});
|
|
1518
|
-
for (const [key, value] of corsHeaders) {
|
|
1519
|
-
if (key.toLowerCase() === "vary") {
|
|
1520
|
-
c.res.headers.append(key, value);
|
|
1521
|
-
continue;
|
|
1522
|
-
}
|
|
1523
|
-
c.res.headers.set(key, value);
|
|
1524
|
-
}
|
|
1525
|
-
return void 0;
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1528
|
-
function resolveCorsOrigin(origin, configOrigin) {
|
|
1529
|
-
if (!origin)
|
|
1530
|
-
return null;
|
|
1531
|
-
if (configOrigin === "*")
|
|
1532
|
-
return "*";
|
|
1533
|
-
if (typeof configOrigin === "string")
|
|
1534
|
-
return origin === configOrigin ? origin : null;
|
|
1535
|
-
if (Array.isArray(configOrigin))
|
|
1536
|
-
return configOrigin.includes(origin) ? origin : null;
|
|
1537
|
-
if (typeof configOrigin === "function")
|
|
1538
|
-
return configOrigin(origin) ? origin : null;
|
|
1539
|
-
return null;
|
|
1540
|
-
}
|
|
1541
|
-
function buildCorsHeaders(options) {
|
|
1542
|
-
const headers = new Headers();
|
|
1543
|
-
if (!options.allowedOrigin)
|
|
1544
|
-
return headers;
|
|
1545
|
-
const requestedHeaders = options.request.headers.get("Access-Control-Request-Headers");
|
|
1546
|
-
headers.set("Access-Control-Allow-Origin", options.allowedOrigin);
|
|
1547
|
-
if (options.credentials)
|
|
1548
|
-
headers.set("Access-Control-Allow-Credentials", "true");
|
|
1549
|
-
if (options.allowedOrigin !== "*") {
|
|
1550
|
-
headers.set(
|
|
1551
|
-
"Vary",
|
|
1552
|
-
requestedHeaders ? "Origin, Access-Control-Request-Headers" : "Origin"
|
|
1553
|
-
);
|
|
1554
|
-
}
|
|
1555
|
-
if (options.exposedHeaders.length > 0)
|
|
1556
|
-
headers.set("Access-Control-Expose-Headers", options.exposedHeaders.join(", "));
|
|
1557
|
-
if (options.request.method === "OPTIONS") {
|
|
1558
|
-
headers.set("Access-Control-Allow-Methods", options.methods.join(", "));
|
|
1559
|
-
headers.set("Access-Control-Allow-Headers", requestedHeaders ?? options.allowedHeaders.join(", "));
|
|
1560
|
-
headers.set("Access-Control-Max-Age", String(options.maxAge));
|
|
1561
|
-
}
|
|
1562
|
-
return headers;
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
1562
|
// src/serv-runtime-security.ts
|
|
1566
1563
|
function createRuntimeSecurityPolicy(input) {
|
|
1567
1564
|
const production = input.environment?.trim().toLowerCase() === "production";
|