@openhi/constructs 0.0.133 → 0.0.134
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/lib/index.d.mts +139 -72
- package/lib/index.d.ts +139 -72
- package/lib/index.js +174 -68
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +177 -67
- package/lib/index.mjs.map +1 -1
- package/lib/static-hosting.origin-request-handler.d.mts +62 -0
- package/lib/static-hosting.origin-request-handler.d.ts +62 -0
- package/lib/static-hosting.origin-request-handler.js +90 -0
- package/lib/static-hosting.origin-request-handler.js.map +1 -0
- package/lib/static-hosting.origin-request-handler.mjs +61 -0
- package/lib/static-hosting.origin-request-handler.mjs.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CloudFrontRequest, CloudFrontRequestEvent } from 'aws-lambda';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Custom header populated by the viewer-request CloudFront Function with
|
|
5
|
+
* the viewer's `Host` value. CloudFront strips the literal `Host` header
|
|
6
|
+
* before reaching the origin-request stage (per the
|
|
7
|
+
* `ALL_VIEWER_EXCEPT_HOST_HEADER` origin-request policy), so the per-PR
|
|
8
|
+
* host gets shuttled through this side channel instead.
|
|
9
|
+
*/
|
|
10
|
+
declare const VIEWER_HOST_HEADER = "x-viewer-host";
|
|
11
|
+
/**
|
|
12
|
+
* Host-prefix pair the construct bakes into this bundle via esbuild
|
|
13
|
+
* `define` at synth time (Lambda@Edge forbids runtime env vars). At
|
|
14
|
+
* bundle time these `process.env.*` references are replaced with the
|
|
15
|
+
* literal strings the consumer configured on `StaticHostingProps.restApi.hostMapping`;
|
|
16
|
+
* under jest (no bundling) the `??` fallbacks supply the OpenHI defaults.
|
|
17
|
+
*/
|
|
18
|
+
declare const VIEWER_HOST_PREFIX: string;
|
|
19
|
+
declare const API_HOST_PREFIX: string;
|
|
20
|
+
/**
|
|
21
|
+
* Origin-request Lambda@Edge handler. Picks the per-PR REST API origin
|
|
22
|
+
* based on the viewer host that the viewer-request stage copied into
|
|
23
|
+
* {@link VIEWER_HOST_HEADER}.
|
|
24
|
+
*
|
|
25
|
+
* With the default OpenHI mapping (`admin` -> `api`):
|
|
26
|
+
* `admin.dev.openhi.org` -> `api.dev.openhi.org`
|
|
27
|
+
* `admin-feat-foo.dev.openhi.org` -> `api-feat-foo.dev.openhi.org`
|
|
28
|
+
*
|
|
29
|
+
* When the header is missing or the host does not start with the
|
|
30
|
+
* configured viewer-host prefix, the request falls through unchanged
|
|
31
|
+
* (the static `HttpOrigin` domain resolves the request).
|
|
32
|
+
*/
|
|
33
|
+
declare const originRequestHandler: (event: CloudFrontRequestEvent) => Promise<CloudFrontRequest>;
|
|
34
|
+
/**
|
|
35
|
+
* Reads the `x-viewer-host` header value from the request. Returns
|
|
36
|
+
* `null` when the header is absent or has no entries.
|
|
37
|
+
*/
|
|
38
|
+
declare const readViewerHost: (request: CloudFrontRequest) => string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Translates a viewer host like `admin-feat-foo.dev.openhi.org` to its
|
|
41
|
+
* matching API host (`api-feat-foo.dev.openhi.org`) by swapping
|
|
42
|
+
* `viewerPrefix` for `apiPrefix` at the start of the host. Returns
|
|
43
|
+
* `null` when the input does not start with `viewerPrefix`.
|
|
44
|
+
*
|
|
45
|
+
* `viewerPrefix` and `apiPrefix` default to the module-level
|
|
46
|
+
* {@link VIEWER_HOST_PREFIX} / {@link API_HOST_PREFIX} constants
|
|
47
|
+
* (baked in by esbuild at bundle time). The positional args are
|
|
48
|
+
* primarily there so unit tests can exercise custom mappings without
|
|
49
|
+
* mutating module state.
|
|
50
|
+
*/
|
|
51
|
+
declare const computeApiHost: (viewerHost: string, viewerPrefix?: string, apiPrefix?: string) => string | null;
|
|
52
|
+
/**
|
|
53
|
+
* Mutates the request so CloudFront forwards it to `apiHost` instead
|
|
54
|
+
* of the construct-configured static API domain. Both the custom-origin
|
|
55
|
+
* `domainName` and the `Host` header are overridden — API Gateway's
|
|
56
|
+
* custom-domain mapping routes on the upstream Host header, so updating
|
|
57
|
+
* just the origin domain without the header would leave API Gateway
|
|
58
|
+
* unable to match the request.
|
|
59
|
+
*/
|
|
60
|
+
declare const applyOriginOverride: (request: CloudFrontRequest, apiHost: string) => CloudFrontRequest;
|
|
61
|
+
|
|
62
|
+
export { API_HOST_PREFIX, VIEWER_HOST_HEADER, VIEWER_HOST_PREFIX, applyOriginOverride, computeApiHost, originRequestHandler, readViewerHost };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CloudFrontRequest, CloudFrontRequestEvent } from 'aws-lambda';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Custom header populated by the viewer-request CloudFront Function with
|
|
5
|
+
* the viewer's `Host` value. CloudFront strips the literal `Host` header
|
|
6
|
+
* before reaching the origin-request stage (per the
|
|
7
|
+
* `ALL_VIEWER_EXCEPT_HOST_HEADER` origin-request policy), so the per-PR
|
|
8
|
+
* host gets shuttled through this side channel instead.
|
|
9
|
+
*/
|
|
10
|
+
declare const VIEWER_HOST_HEADER = "x-viewer-host";
|
|
11
|
+
/**
|
|
12
|
+
* Host-prefix pair the construct bakes into this bundle via esbuild
|
|
13
|
+
* `define` at synth time (Lambda@Edge forbids runtime env vars). At
|
|
14
|
+
* bundle time these `process.env.*` references are replaced with the
|
|
15
|
+
* literal strings the consumer configured on `StaticHostingProps.restApi.hostMapping`;
|
|
16
|
+
* under jest (no bundling) the `??` fallbacks supply the OpenHI defaults.
|
|
17
|
+
*/
|
|
18
|
+
declare const VIEWER_HOST_PREFIX: string;
|
|
19
|
+
declare const API_HOST_PREFIX: string;
|
|
20
|
+
/**
|
|
21
|
+
* Origin-request Lambda@Edge handler. Picks the per-PR REST API origin
|
|
22
|
+
* based on the viewer host that the viewer-request stage copied into
|
|
23
|
+
* {@link VIEWER_HOST_HEADER}.
|
|
24
|
+
*
|
|
25
|
+
* With the default OpenHI mapping (`admin` -> `api`):
|
|
26
|
+
* `admin.dev.openhi.org` -> `api.dev.openhi.org`
|
|
27
|
+
* `admin-feat-foo.dev.openhi.org` -> `api-feat-foo.dev.openhi.org`
|
|
28
|
+
*
|
|
29
|
+
* When the header is missing or the host does not start with the
|
|
30
|
+
* configured viewer-host prefix, the request falls through unchanged
|
|
31
|
+
* (the static `HttpOrigin` domain resolves the request).
|
|
32
|
+
*/
|
|
33
|
+
declare const originRequestHandler: (event: CloudFrontRequestEvent) => Promise<CloudFrontRequest>;
|
|
34
|
+
/**
|
|
35
|
+
* Reads the `x-viewer-host` header value from the request. Returns
|
|
36
|
+
* `null` when the header is absent or has no entries.
|
|
37
|
+
*/
|
|
38
|
+
declare const readViewerHost: (request: CloudFrontRequest) => string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Translates a viewer host like `admin-feat-foo.dev.openhi.org` to its
|
|
41
|
+
* matching API host (`api-feat-foo.dev.openhi.org`) by swapping
|
|
42
|
+
* `viewerPrefix` for `apiPrefix` at the start of the host. Returns
|
|
43
|
+
* `null` when the input does not start with `viewerPrefix`.
|
|
44
|
+
*
|
|
45
|
+
* `viewerPrefix` and `apiPrefix` default to the module-level
|
|
46
|
+
* {@link VIEWER_HOST_PREFIX} / {@link API_HOST_PREFIX} constants
|
|
47
|
+
* (baked in by esbuild at bundle time). The positional args are
|
|
48
|
+
* primarily there so unit tests can exercise custom mappings without
|
|
49
|
+
* mutating module state.
|
|
50
|
+
*/
|
|
51
|
+
declare const computeApiHost: (viewerHost: string, viewerPrefix?: string, apiPrefix?: string) => string | null;
|
|
52
|
+
/**
|
|
53
|
+
* Mutates the request so CloudFront forwards it to `apiHost` instead
|
|
54
|
+
* of the construct-configured static API domain. Both the custom-origin
|
|
55
|
+
* `domainName` and the `Host` header are overridden — API Gateway's
|
|
56
|
+
* custom-domain mapping routes on the upstream Host header, so updating
|
|
57
|
+
* just the origin domain without the header would leave API Gateway
|
|
58
|
+
* unable to match the request.
|
|
59
|
+
*/
|
|
60
|
+
declare const applyOriginOverride: (request: CloudFrontRequest, apiHost: string) => CloudFrontRequest;
|
|
61
|
+
|
|
62
|
+
export { API_HOST_PREFIX, VIEWER_HOST_HEADER, VIEWER_HOST_PREFIX, applyOriginOverride, computeApiHost, originRequestHandler, readViewerHost };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/components/static-hosting/static-hosting.origin-request-handler.ts
|
|
21
|
+
var static_hosting_origin_request_handler_exports = {};
|
|
22
|
+
__export(static_hosting_origin_request_handler_exports, {
|
|
23
|
+
API_HOST_PREFIX: () => API_HOST_PREFIX,
|
|
24
|
+
VIEWER_HOST_HEADER: () => VIEWER_HOST_HEADER,
|
|
25
|
+
VIEWER_HOST_PREFIX: () => VIEWER_HOST_PREFIX,
|
|
26
|
+
applyOriginOverride: () => applyOriginOverride,
|
|
27
|
+
computeApiHost: () => computeApiHost,
|
|
28
|
+
originRequestHandler: () => originRequestHandler,
|
|
29
|
+
readViewerHost: () => readViewerHost
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(static_hosting_origin_request_handler_exports);
|
|
32
|
+
var isTestEnv = process.env.JEST_WORKER_ID !== void 0;
|
|
33
|
+
var VIEWER_HOST_HEADER = "x-viewer-host";
|
|
34
|
+
var VIEWER_HOST_PREFIX = process.env.VIEWER_HOST_PREFIX ?? "admin";
|
|
35
|
+
var API_HOST_PREFIX = process.env.API_HOST_PREFIX ?? "api";
|
|
36
|
+
var originRequestHandler = async (event) => {
|
|
37
|
+
if (!isTestEnv) {
|
|
38
|
+
console.log("Origin Request Event: ", JSON.stringify(event, null, 2));
|
|
39
|
+
}
|
|
40
|
+
const request = event.Records[0].cf.request;
|
|
41
|
+
const viewerHost = readViewerHost(request);
|
|
42
|
+
if (viewerHost === null) {
|
|
43
|
+
if (!isTestEnv) {
|
|
44
|
+
console.log("Missing x-viewer-host header; passing through.");
|
|
45
|
+
}
|
|
46
|
+
return request;
|
|
47
|
+
}
|
|
48
|
+
const apiHost = computeApiHost(viewerHost);
|
|
49
|
+
if (apiHost === null) {
|
|
50
|
+
if (!isTestEnv) {
|
|
51
|
+
console.log(
|
|
52
|
+
`x-viewer-host "${viewerHost}" does not start with "${VIEWER_HOST_PREFIX}"; passing through.`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return request;
|
|
56
|
+
}
|
|
57
|
+
return applyOriginOverride(request, apiHost);
|
|
58
|
+
};
|
|
59
|
+
var readViewerHost = (request) => {
|
|
60
|
+
const header = request.headers[VIEWER_HOST_HEADER];
|
|
61
|
+
if (!header || header.length === 0) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const value = header[0]?.value;
|
|
65
|
+
return value && value.length > 0 ? value : null;
|
|
66
|
+
};
|
|
67
|
+
var computeApiHost = (viewerHost, viewerPrefix = VIEWER_HOST_PREFIX, apiPrefix = API_HOST_PREFIX) => {
|
|
68
|
+
if (!viewerHost.startsWith(viewerPrefix)) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return apiPrefix + viewerHost.slice(viewerPrefix.length);
|
|
72
|
+
};
|
|
73
|
+
var applyOriginOverride = (request, apiHost) => {
|
|
74
|
+
if (request.origin?.custom) {
|
|
75
|
+
request.origin.custom.domainName = apiHost;
|
|
76
|
+
}
|
|
77
|
+
request.headers.host = [{ key: "Host", value: apiHost }];
|
|
78
|
+
return request;
|
|
79
|
+
};
|
|
80
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
81
|
+
0 && (module.exports = {
|
|
82
|
+
API_HOST_PREFIX,
|
|
83
|
+
VIEWER_HOST_HEADER,
|
|
84
|
+
VIEWER_HOST_PREFIX,
|
|
85
|
+
applyOriginOverride,
|
|
86
|
+
computeApiHost,
|
|
87
|
+
originRequestHandler,
|
|
88
|
+
readViewerHost
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=static-hosting.origin-request-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/static-hosting/static-hosting.origin-request-handler.ts"],"sourcesContent":["import { CloudFrontRequest, CloudFrontRequestEvent } from \"aws-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/static-hosting/static-hosting.origin-request-handler.md\n */\n\nconst isTestEnv = process.env.JEST_WORKER_ID !== undefined;\n\n/**\n * Custom header populated by the viewer-request CloudFront Function with\n * the viewer's `Host` value. CloudFront strips the literal `Host` header\n * before reaching the origin-request stage (per the\n * `ALL_VIEWER_EXCEPT_HOST_HEADER` origin-request policy), so the per-PR\n * host gets shuttled through this side channel instead.\n */\nexport const VIEWER_HOST_HEADER = \"x-viewer-host\";\n\n/**\n * Host-prefix pair the construct bakes into this bundle via esbuild\n * `define` at synth time (Lambda@Edge forbids runtime env vars). At\n * bundle time these `process.env.*` references are replaced with the\n * literal strings the consumer configured on `StaticHostingProps.restApi.hostMapping`;\n * under jest (no bundling) the `??` fallbacks supply the OpenHI defaults.\n */\nexport const VIEWER_HOST_PREFIX = process.env.VIEWER_HOST_PREFIX ?? \"admin\";\nexport const API_HOST_PREFIX = process.env.API_HOST_PREFIX ?? \"api\";\n\n/**\n * Origin-request Lambda@Edge handler. Picks the per-PR REST API origin\n * based on the viewer host that the viewer-request stage copied into\n * {@link VIEWER_HOST_HEADER}.\n *\n * With the default OpenHI mapping (`admin` -> `api`):\n * `admin.dev.openhi.org` -> `api.dev.openhi.org`\n * `admin-feat-foo.dev.openhi.org` -> `api-feat-foo.dev.openhi.org`\n *\n * When the header is missing or the host does not start with the\n * configured viewer-host prefix, the request falls through unchanged\n * (the static `HttpOrigin` domain resolves the request).\n */\nexport const originRequestHandler = async (\n event: CloudFrontRequestEvent,\n): Promise<CloudFrontRequest> => {\n if (!isTestEnv) {\n console.log(\"Origin Request Event: \", JSON.stringify(event, null, 2));\n }\n\n const request = event.Records[0].cf.request;\n const viewerHost = readViewerHost(request);\n\n if (viewerHost === null) {\n if (!isTestEnv) {\n console.log(\"Missing x-viewer-host header; passing through.\");\n }\n return request;\n }\n\n const apiHost = computeApiHost(viewerHost);\n if (apiHost === null) {\n if (!isTestEnv) {\n console.log(\n `x-viewer-host \"${viewerHost}\" does not start with \"${VIEWER_HOST_PREFIX}\"; passing through.`,\n );\n }\n return request;\n }\n\n return applyOriginOverride(request, apiHost);\n};\n\n/**\n * Reads the `x-viewer-host` header value from the request. Returns\n * `null` when the header is absent or has no entries.\n */\nexport const readViewerHost = (request: CloudFrontRequest): string | null => {\n const header = request.headers[VIEWER_HOST_HEADER];\n if (!header || header.length === 0) {\n return null;\n }\n const value = header[0]?.value;\n return value && value.length > 0 ? value : null;\n};\n\n/**\n * Translates a viewer host like `admin-feat-foo.dev.openhi.org` to its\n * matching API host (`api-feat-foo.dev.openhi.org`) by swapping\n * `viewerPrefix` for `apiPrefix` at the start of the host. Returns\n * `null` when the input does not start with `viewerPrefix`.\n *\n * `viewerPrefix` and `apiPrefix` default to the module-level\n * {@link VIEWER_HOST_PREFIX} / {@link API_HOST_PREFIX} constants\n * (baked in by esbuild at bundle time). The positional args are\n * primarily there so unit tests can exercise custom mappings without\n * mutating module state.\n */\nexport const computeApiHost = (\n viewerHost: string,\n viewerPrefix: string = VIEWER_HOST_PREFIX,\n apiPrefix: string = API_HOST_PREFIX,\n): string | null => {\n if (!viewerHost.startsWith(viewerPrefix)) {\n return null;\n }\n return apiPrefix + viewerHost.slice(viewerPrefix.length);\n};\n\n/**\n * Mutates the request so CloudFront forwards it to `apiHost` instead\n * of the construct-configured static API domain. Both the custom-origin\n * `domainName` and the `Host` header are overridden — API Gateway's\n * custom-domain mapping routes on the upstream Host header, so updating\n * just the origin domain without the header would leave API Gateway\n * unable to match the request.\n */\nexport const applyOriginOverride = (\n request: CloudFrontRequest,\n apiHost: string,\n): CloudFrontRequest => {\n if (request.origin?.custom) {\n request.origin.custom.domainName = apiHost;\n }\n request.headers.host = [{ key: \"Host\", value: apiHost }];\n return request;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,IAAM,YAAY,QAAQ,IAAI,mBAAmB;AAS1C,IAAM,qBAAqB;AAS3B,IAAM,qBAAqB,QAAQ,IAAI,sBAAsB;AAC7D,IAAM,kBAAkB,QAAQ,IAAI,mBAAmB;AAevD,IAAM,uBAAuB,OAClC,UAC+B;AAC/B,MAAI,CAAC,WAAW;AACd,YAAQ,IAAI,0BAA0B,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,EACtE;AAEA,QAAM,UAAU,MAAM,QAAQ,CAAC,EAAE,GAAG;AACpC,QAAM,aAAa,eAAe,OAAO;AAEzC,MAAI,eAAe,MAAM;AACvB,QAAI,CAAC,WAAW;AACd,cAAQ,IAAI,gDAAgD;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,eAAe,UAAU;AACzC,MAAI,YAAY,MAAM;AACpB,QAAI,CAAC,WAAW;AACd,cAAQ;AAAA,QACN,kBAAkB,UAAU,0BAA0B,kBAAkB;AAAA,MAC1E;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO,oBAAoB,SAAS,OAAO;AAC7C;AAMO,IAAM,iBAAiB,CAAC,YAA8C;AAC3E,QAAM,SAAS,QAAQ,QAAQ,kBAAkB;AACjD,MAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,CAAC,GAAG;AACzB,SAAO,SAAS,MAAM,SAAS,IAAI,QAAQ;AAC7C;AAcO,IAAM,iBAAiB,CAC5B,YACA,eAAuB,oBACvB,YAAoB,oBACF;AAClB,MAAI,CAAC,WAAW,WAAW,YAAY,GAAG;AACxC,WAAO;AAAA,EACT;AACA,SAAO,YAAY,WAAW,MAAM,aAAa,MAAM;AACzD;AAUO,IAAM,sBAAsB,CACjC,SACA,YACsB;AACtB,MAAI,QAAQ,QAAQ,QAAQ;AAC1B,YAAQ,OAAO,OAAO,aAAa;AAAA,EACrC;AACA,UAAQ,QAAQ,OAAO,CAAC,EAAE,KAAK,QAAQ,OAAO,QAAQ,CAAC;AACvD,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import "./chunk-LZOMFHX3.mjs";
|
|
2
|
+
|
|
3
|
+
// src/components/static-hosting/static-hosting.origin-request-handler.ts
|
|
4
|
+
var isTestEnv = process.env.JEST_WORKER_ID !== void 0;
|
|
5
|
+
var VIEWER_HOST_HEADER = "x-viewer-host";
|
|
6
|
+
var VIEWER_HOST_PREFIX = process.env.VIEWER_HOST_PREFIX ?? "admin";
|
|
7
|
+
var API_HOST_PREFIX = process.env.API_HOST_PREFIX ?? "api";
|
|
8
|
+
var originRequestHandler = async (event) => {
|
|
9
|
+
if (!isTestEnv) {
|
|
10
|
+
console.log("Origin Request Event: ", JSON.stringify(event, null, 2));
|
|
11
|
+
}
|
|
12
|
+
const request = event.Records[0].cf.request;
|
|
13
|
+
const viewerHost = readViewerHost(request);
|
|
14
|
+
if (viewerHost === null) {
|
|
15
|
+
if (!isTestEnv) {
|
|
16
|
+
console.log("Missing x-viewer-host header; passing through.");
|
|
17
|
+
}
|
|
18
|
+
return request;
|
|
19
|
+
}
|
|
20
|
+
const apiHost = computeApiHost(viewerHost);
|
|
21
|
+
if (apiHost === null) {
|
|
22
|
+
if (!isTestEnv) {
|
|
23
|
+
console.log(
|
|
24
|
+
`x-viewer-host "${viewerHost}" does not start with "${VIEWER_HOST_PREFIX}"; passing through.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return request;
|
|
28
|
+
}
|
|
29
|
+
return applyOriginOverride(request, apiHost);
|
|
30
|
+
};
|
|
31
|
+
var readViewerHost = (request) => {
|
|
32
|
+
const header = request.headers[VIEWER_HOST_HEADER];
|
|
33
|
+
if (!header || header.length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const value = header[0]?.value;
|
|
37
|
+
return value && value.length > 0 ? value : null;
|
|
38
|
+
};
|
|
39
|
+
var computeApiHost = (viewerHost, viewerPrefix = VIEWER_HOST_PREFIX, apiPrefix = API_HOST_PREFIX) => {
|
|
40
|
+
if (!viewerHost.startsWith(viewerPrefix)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return apiPrefix + viewerHost.slice(viewerPrefix.length);
|
|
44
|
+
};
|
|
45
|
+
var applyOriginOverride = (request, apiHost) => {
|
|
46
|
+
if (request.origin?.custom) {
|
|
47
|
+
request.origin.custom.domainName = apiHost;
|
|
48
|
+
}
|
|
49
|
+
request.headers.host = [{ key: "Host", value: apiHost }];
|
|
50
|
+
return request;
|
|
51
|
+
};
|
|
52
|
+
export {
|
|
53
|
+
API_HOST_PREFIX,
|
|
54
|
+
VIEWER_HOST_HEADER,
|
|
55
|
+
VIEWER_HOST_PREFIX,
|
|
56
|
+
applyOriginOverride,
|
|
57
|
+
computeApiHost,
|
|
58
|
+
originRequestHandler,
|
|
59
|
+
readViewerHost
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=static-hosting.origin-request-handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/static-hosting/static-hosting.origin-request-handler.ts"],"sourcesContent":["import { CloudFrontRequest, CloudFrontRequestEvent } from \"aws-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/static-hosting/static-hosting.origin-request-handler.md\n */\n\nconst isTestEnv = process.env.JEST_WORKER_ID !== undefined;\n\n/**\n * Custom header populated by the viewer-request CloudFront Function with\n * the viewer's `Host` value. CloudFront strips the literal `Host` header\n * before reaching the origin-request stage (per the\n * `ALL_VIEWER_EXCEPT_HOST_HEADER` origin-request policy), so the per-PR\n * host gets shuttled through this side channel instead.\n */\nexport const VIEWER_HOST_HEADER = \"x-viewer-host\";\n\n/**\n * Host-prefix pair the construct bakes into this bundle via esbuild\n * `define` at synth time (Lambda@Edge forbids runtime env vars). At\n * bundle time these `process.env.*` references are replaced with the\n * literal strings the consumer configured on `StaticHostingProps.restApi.hostMapping`;\n * under jest (no bundling) the `??` fallbacks supply the OpenHI defaults.\n */\nexport const VIEWER_HOST_PREFIX = process.env.VIEWER_HOST_PREFIX ?? \"admin\";\nexport const API_HOST_PREFIX = process.env.API_HOST_PREFIX ?? \"api\";\n\n/**\n * Origin-request Lambda@Edge handler. Picks the per-PR REST API origin\n * based on the viewer host that the viewer-request stage copied into\n * {@link VIEWER_HOST_HEADER}.\n *\n * With the default OpenHI mapping (`admin` -> `api`):\n * `admin.dev.openhi.org` -> `api.dev.openhi.org`\n * `admin-feat-foo.dev.openhi.org` -> `api-feat-foo.dev.openhi.org`\n *\n * When the header is missing or the host does not start with the\n * configured viewer-host prefix, the request falls through unchanged\n * (the static `HttpOrigin` domain resolves the request).\n */\nexport const originRequestHandler = async (\n event: CloudFrontRequestEvent,\n): Promise<CloudFrontRequest> => {\n if (!isTestEnv) {\n console.log(\"Origin Request Event: \", JSON.stringify(event, null, 2));\n }\n\n const request = event.Records[0].cf.request;\n const viewerHost = readViewerHost(request);\n\n if (viewerHost === null) {\n if (!isTestEnv) {\n console.log(\"Missing x-viewer-host header; passing through.\");\n }\n return request;\n }\n\n const apiHost = computeApiHost(viewerHost);\n if (apiHost === null) {\n if (!isTestEnv) {\n console.log(\n `x-viewer-host \"${viewerHost}\" does not start with \"${VIEWER_HOST_PREFIX}\"; passing through.`,\n );\n }\n return request;\n }\n\n return applyOriginOverride(request, apiHost);\n};\n\n/**\n * Reads the `x-viewer-host` header value from the request. Returns\n * `null` when the header is absent or has no entries.\n */\nexport const readViewerHost = (request: CloudFrontRequest): string | null => {\n const header = request.headers[VIEWER_HOST_HEADER];\n if (!header || header.length === 0) {\n return null;\n }\n const value = header[0]?.value;\n return value && value.length > 0 ? value : null;\n};\n\n/**\n * Translates a viewer host like `admin-feat-foo.dev.openhi.org` to its\n * matching API host (`api-feat-foo.dev.openhi.org`) by swapping\n * `viewerPrefix` for `apiPrefix` at the start of the host. Returns\n * `null` when the input does not start with `viewerPrefix`.\n *\n * `viewerPrefix` and `apiPrefix` default to the module-level\n * {@link VIEWER_HOST_PREFIX} / {@link API_HOST_PREFIX} constants\n * (baked in by esbuild at bundle time). The positional args are\n * primarily there so unit tests can exercise custom mappings without\n * mutating module state.\n */\nexport const computeApiHost = (\n viewerHost: string,\n viewerPrefix: string = VIEWER_HOST_PREFIX,\n apiPrefix: string = API_HOST_PREFIX,\n): string | null => {\n if (!viewerHost.startsWith(viewerPrefix)) {\n return null;\n }\n return apiPrefix + viewerHost.slice(viewerPrefix.length);\n};\n\n/**\n * Mutates the request so CloudFront forwards it to `apiHost` instead\n * of the construct-configured static API domain. Both the custom-origin\n * `domainName` and the `Host` header are overridden — API Gateway's\n * custom-domain mapping routes on the upstream Host header, so updating\n * just the origin domain without the header would leave API Gateway\n * unable to match the request.\n */\nexport const applyOriginOverride = (\n request: CloudFrontRequest,\n apiHost: string,\n): CloudFrontRequest => {\n if (request.origin?.custom) {\n request.origin.custom.domainName = apiHost;\n }\n request.headers.host = [{ key: \"Host\", value: apiHost }];\n return request;\n};\n"],"mappings":";;;AAMA,IAAM,YAAY,QAAQ,IAAI,mBAAmB;AAS1C,IAAM,qBAAqB;AAS3B,IAAM,qBAAqB,QAAQ,IAAI,sBAAsB;AAC7D,IAAM,kBAAkB,QAAQ,IAAI,mBAAmB;AAevD,IAAM,uBAAuB,OAClC,UAC+B;AAC/B,MAAI,CAAC,WAAW;AACd,YAAQ,IAAI,0BAA0B,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,EACtE;AAEA,QAAM,UAAU,MAAM,QAAQ,CAAC,EAAE,GAAG;AACpC,QAAM,aAAa,eAAe,OAAO;AAEzC,MAAI,eAAe,MAAM;AACvB,QAAI,CAAC,WAAW;AACd,cAAQ,IAAI,gDAAgD;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,eAAe,UAAU;AACzC,MAAI,YAAY,MAAM;AACpB,QAAI,CAAC,WAAW;AACd,cAAQ;AAAA,QACN,kBAAkB,UAAU,0BAA0B,kBAAkB;AAAA,MAC1E;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO,oBAAoB,SAAS,OAAO;AAC7C;AAMO,IAAM,iBAAiB,CAAC,YAA8C;AAC3E,QAAM,SAAS,QAAQ,QAAQ,kBAAkB;AACjD,MAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,OAAO,CAAC,GAAG;AACzB,SAAO,SAAS,MAAM,SAAS,IAAI,QAAQ;AAC7C;AAcO,IAAM,iBAAiB,CAC5B,YACA,eAAuB,oBACvB,YAAoB,oBACF;AAClB,MAAI,CAAC,WAAW,WAAW,YAAY,GAAG;AACxC,WAAO;AAAA,EACT;AACA,SAAO,YAAY,WAAW,MAAM,aAAa,MAAM;AACzD;AAUO,IAAM,sBAAsB,CACjC,SACA,YACsB;AACtB,MAAI,QAAQ,QAAQ,QAAQ;AAC1B,YAAQ,OAAO,OAAO,aAAa;AAAA,EACrC;AACA,UAAQ,QAAQ,OAAO,CAAC,EAAE,KAAK,QAAQ,OAAO,QAAQ,CAAC;AACvD,SAAO;AACT;","names":[]}
|
package/package.json
CHANGED
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"type-fest": "^4",
|
|
57
57
|
"ulid": "^3.0.2",
|
|
58
58
|
"@openhi/config": "0.0.0",
|
|
59
|
-
"@openhi/
|
|
60
|
-
"@openhi/
|
|
59
|
+
"@openhi/workflows": "0.0.0",
|
|
60
|
+
"@openhi/types": "0.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devEngines": {
|
|
63
63
|
"packageManager": {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"publishConfig": {
|
|
72
72
|
"access": "public"
|
|
73
73
|
},
|
|
74
|
-
"version": "0.0.
|
|
74
|
+
"version": "0.0.134",
|
|
75
75
|
"types": "lib/index.d.ts",
|
|
76
76
|
"//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm exec projen\".",
|
|
77
77
|
"scripts": {
|