@camstack/types 1.2.430 → 1.2.431
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/CHANGELOG.md +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +424 -4
- package/dist/index.mjs +420 -5
- package/dist/interfaces/config-ui.d.ts +7 -0
- package/dist/vendor-login-proxy/index.d.ts +2 -0
- package/dist/vendor-login-proxy/login-proxy.d.ts +55 -0
- package/dist/vendor-login-proxy/url-rewrite.d.ts +68 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.431] - 2026-10-27
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Addons can now offer a vendor sign-in that opens the vendor's login page through the hub and finishes on the CamStack result page.
|
|
15
|
+
|
|
10
16
|
## [1.2.430] - 2026-10-27
|
|
11
17
|
|
|
12
18
|
### Added
|
package/dist/index.d.ts
CHANGED
|
@@ -353,6 +353,7 @@ export { errMsg } from './utils/err-msg.js';
|
|
|
353
353
|
export { hfModelUrl } from './utils/hf-url.js';
|
|
354
354
|
export { asBoolean, asJsonArray, asJsonObject, asNumber, asString, parseJsonArray, parseJsonObject, parseJsonUnknown, } from './utils/json-safe.js';
|
|
355
355
|
export { maskUrlCredentials } from './utils/mask-url.js';
|
|
356
|
+
export { VendorLoginProxy, rewriteBody as rewriteLoginProxyBody, rewriteLocation as rewriteLoginProxyLocation, rewriteReferer as rewriteLoginProxyReferer, rewriteSetCookie as rewriteLoginProxySetCookie, type LoginProxyAttemptSpec, type LoginProxyResponseView, type LoginProxyRoute, type LoginProxyScheme, type LoginProxyUpstream, type LoginProxyVerdict, type VendorLoginProxyOptions, } from './vendor-login-proxy/index.js';
|
|
356
357
|
export { OAUTH_LANDING_PATH, OAUTH_LANDING_REASONS, isOauthLandingReason, oauthLandingUrl, parseOauthLandingFragment, type OauthLandingOutcome, type OauthLandingReason, } from './utils/oauth-landing.js';
|
|
357
358
|
export { commitWatchdogRestart, DEFAULT_POOL_MEMORY_POLICY, evaluatePoolMemory, initialPoolMemoryState, type PoolMemoryAction, type PoolMemoryPolicy, type PoolMemoryState, type PoolMemoryTelemetry, type PoolMemoryVerdict, PoolMemoryWatchdog, type PoolMemoryWatchdogOptions, type ProcMemory, parseProcStatus, pickRestartCandidate, poolMemoryThreshold, type RestartCandidate, resetPoolBaseline, resolvePoolMemoryPolicy, } from './utils/pool-memory-watchdog.js';
|
|
358
359
|
export { cellsToRects, type NormRect, rectsToCells } from './utils/privacy-grid-raster.js';
|
package/dist/index.js
CHANGED
|
@@ -43694,7 +43694,7 @@ function autoRankFor(role, endpoint) {
|
|
|
43694
43694
|
if (isPlausibleLan(endpoint)) return 3;
|
|
43695
43695
|
return null;
|
|
43696
43696
|
}
|
|
43697
|
-
var LOOPBACK_HOSTS = new Set([
|
|
43697
|
+
var LOOPBACK_HOSTS$1 = new Set([
|
|
43698
43698
|
"localhost",
|
|
43699
43699
|
"0.0.0.0",
|
|
43700
43700
|
"::1",
|
|
@@ -43702,9 +43702,9 @@ var LOOPBACK_HOSTS = new Set([
|
|
|
43702
43702
|
"::",
|
|
43703
43703
|
"[::]"
|
|
43704
43704
|
]);
|
|
43705
|
-
function isLoopbackHost(hostname) {
|
|
43705
|
+
function isLoopbackHost$1(hostname) {
|
|
43706
43706
|
const h = hostname.toLowerCase();
|
|
43707
|
-
if (LOOPBACK_HOSTS.has(h) || h.endsWith(".localhost")) return true;
|
|
43707
|
+
if (LOOPBACK_HOSTS$1.has(h) || h.endsWith(".localhost")) return true;
|
|
43708
43708
|
return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
|
|
43709
43709
|
}
|
|
43710
43710
|
/**
|
|
@@ -43739,7 +43739,7 @@ function validateCustomAddress(raw) {
|
|
|
43739
43739
|
ok: false,
|
|
43740
43740
|
reason: "The address has no host."
|
|
43741
43741
|
};
|
|
43742
|
-
if (isLoopbackHost(parsed.hostname)) return {
|
|
43742
|
+
if (isLoopbackHost$1(parsed.hostname)) return {
|
|
43743
43743
|
ok: false,
|
|
43744
43744
|
reason: "A loopback address names whoever dials it, never this hub — use an address other machines reach."
|
|
43745
43745
|
};
|
|
@@ -117845,6 +117845,421 @@ function parseOauthLandingFragment(hash) {
|
|
|
117845
117845
|
};
|
|
117846
117846
|
}
|
|
117847
117847
|
//#endregion
|
|
117848
|
+
//#region src/vendor-login-proxy/url-rewrite.ts
|
|
117849
|
+
var LOOPBACK_HOSTS = new Set([
|
|
117850
|
+
"127.0.0.1",
|
|
117851
|
+
"localhost",
|
|
117852
|
+
"[::1]"
|
|
117853
|
+
]);
|
|
117854
|
+
function hostWithoutPort(host) {
|
|
117855
|
+
if (host.startsWith("[")) return host.slice(0, host.indexOf("]") + 1);
|
|
117856
|
+
const colon = host.indexOf(":");
|
|
117857
|
+
return colon === -1 ? host : host.slice(0, colon);
|
|
117858
|
+
}
|
|
117859
|
+
function isLoopbackHost(host) {
|
|
117860
|
+
return LOOPBACK_HOSTS.has(hostWithoutPort(host.toLowerCase()));
|
|
117861
|
+
}
|
|
117862
|
+
/** An allow-list entry is valid when it is a bare host[:port] and `http` only targets loopback. */
|
|
117863
|
+
function isValidUpstream(upstream) {
|
|
117864
|
+
if (upstream.host.length === 0 || !/^[a-z0-9.[\]:-]+$/.test(upstream.host)) return false;
|
|
117865
|
+
return upstream.scheme === "https" || isLoopbackHost(upstream.host);
|
|
117866
|
+
}
|
|
117867
|
+
function findUpstream(route, host) {
|
|
117868
|
+
const wanted = host.toLowerCase();
|
|
117869
|
+
return route.upstreams.find((upstream) => upstream.host === wanted);
|
|
117870
|
+
}
|
|
117871
|
+
/** `<basePath>/<host><pathname><search>` — what the browser is sent to. */
|
|
117872
|
+
function proxyPathFor(route, host, pathname, search = "") {
|
|
117873
|
+
return `${route.basePath}/${host}${pathname.startsWith("/") ? pathname : `/${pathname}`}${search}`;
|
|
117874
|
+
}
|
|
117875
|
+
/** Split `/<attemptId>/<host>/<rest…>[?query]` (the data-plane view of a request). */
|
|
117876
|
+
function parseProxyPath(rawUrl) {
|
|
117877
|
+
const queryAt = rawUrl.indexOf("?");
|
|
117878
|
+
const path = queryAt === -1 ? rawUrl : rawUrl.slice(0, queryAt);
|
|
117879
|
+
const search = queryAt === -1 ? "" : rawUrl.slice(queryAt);
|
|
117880
|
+
const match = /^\/([A-Za-z0-9_-]{8,128})\/([^/]+)(\/.*)?$/.exec(path);
|
|
117881
|
+
if (match === null) return null;
|
|
117882
|
+
const attemptId = match[1];
|
|
117883
|
+
const host = match[2];
|
|
117884
|
+
if (attemptId === void 0 || host === void 0) return null;
|
|
117885
|
+
return {
|
|
117886
|
+
attemptId,
|
|
117887
|
+
host: host.toLowerCase(),
|
|
117888
|
+
pathname: match[3] ?? "/",
|
|
117889
|
+
search
|
|
117890
|
+
};
|
|
117891
|
+
}
|
|
117892
|
+
/** The vendor URL a proxied request targets, or null when the host would not stay `host`. */
|
|
117893
|
+
function upstreamUrlFor(upstream, parsed) {
|
|
117894
|
+
try {
|
|
117895
|
+
const url = new URL(`${upstream.scheme}://${upstream.host}${parsed.pathname}${parsed.search}`);
|
|
117896
|
+
return url.host.toLowerCase() === upstream.host ? url : null;
|
|
117897
|
+
} catch {
|
|
117898
|
+
return null;
|
|
117899
|
+
}
|
|
117900
|
+
}
|
|
117901
|
+
function escapeRegex(value) {
|
|
117902
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
117903
|
+
}
|
|
117904
|
+
/** A Location (or any URL value) to a vendor host becomes a proxy path; anything else is unchanged. */
|
|
117905
|
+
function rewriteLocation(location, currentUpstream, route) {
|
|
117906
|
+
let resolved;
|
|
117907
|
+
try {
|
|
117908
|
+
resolved = new URL(location, `${currentUpstream.scheme}://${currentUpstream.host}/`);
|
|
117909
|
+
} catch {
|
|
117910
|
+
return location;
|
|
117911
|
+
}
|
|
117912
|
+
if (resolved.protocol !== "https:" && resolved.protocol !== "http:") return location;
|
|
117913
|
+
const target = findUpstream(route, resolved.host);
|
|
117914
|
+
if (target === void 0) return location;
|
|
117915
|
+
return `${proxyPathFor(route, target.host, resolved.pathname, resolved.search)}${resolved.hash}`;
|
|
117916
|
+
}
|
|
117917
|
+
/** Characters that may precede a URL that STARTS a value (never `=`, `?`, `&`, `%3D`). */
|
|
117918
|
+
var VALUE_START = String.raw`(?<=^|["'(\s>,\[{])`;
|
|
117919
|
+
/**
|
|
117920
|
+
* Rewrite vendor URLs in a text body: absolute (`https://host/…`), protocol-relative
|
|
117921
|
+
* (`//host/…`) and JSON-escaped (`https:\/\/host\/…`) when they start a value; and, in
|
|
117922
|
+
* HTML/CSS, root-relative references (`href="/x"`, `url(/x)`) which would otherwise
|
|
117923
|
+
* escape the proxy mount and land on the hub.
|
|
117924
|
+
*/
|
|
117925
|
+
function rewriteBody(body, kind, currentUpstream, route) {
|
|
117926
|
+
let out = body;
|
|
117927
|
+
for (const upstream of route.upstreams) {
|
|
117928
|
+
const host = escapeRegex(upstream.host);
|
|
117929
|
+
const plain = new RegExp(`${VALUE_START}(?:https?:)?//${host}(?=[/"'?#\\s)\\]},<]|$)`, "g");
|
|
117930
|
+
out = out.replace(plain, `${route.basePath}/${upstream.host}`);
|
|
117931
|
+
const escapedBase = `${route.basePath}/${upstream.host}`.replace(/\//g, "\\/");
|
|
117932
|
+
const escaped = new RegExp(`${VALUE_START}(?:https?:)?\\\\/\\\\/${host}(?=\\\\/|["'?#\\s)\\]},<]|$)`, "g");
|
|
117933
|
+
out = out.replace(escaped, escapedBase);
|
|
117934
|
+
}
|
|
117935
|
+
const current = `${route.basePath}/${currentUpstream.host}/`;
|
|
117936
|
+
const notMounted = `(?!/)(?!${escapeRegex(route.basePath.slice(1))}/)`;
|
|
117937
|
+
if (kind === "html") {
|
|
117938
|
+
const attribute = String.raw`(\b(?:href|src|action|formaction|poster|data-href|data-src)\s*=\s*["'])/`;
|
|
117939
|
+
out = out.replace(new RegExp(`${attribute}${notMounted}`, "gi"), `$1${current}`);
|
|
117940
|
+
}
|
|
117941
|
+
if (kind === "html" || kind === "css") {
|
|
117942
|
+
const cssUrl = String.raw`(url\(\s*["']?)/`;
|
|
117943
|
+
out = out.replace(new RegExp(`${cssUrl}${notMounted}`, "gi"), `$1${current}`);
|
|
117944
|
+
}
|
|
117945
|
+
return out;
|
|
117946
|
+
}
|
|
117947
|
+
/**
|
|
117948
|
+
* A vendor `Set-Cookie` for the browser: no `Domain` (the cookie belongs to the hub
|
|
117949
|
+
* origin), `Path` narrowed to this attempt's prefix, `Secure` kept (the hub is HTTPS).
|
|
117950
|
+
*/
|
|
117951
|
+
function rewriteSetCookie(cookie, route) {
|
|
117952
|
+
const kept = cookie.split(";").map((part) => part.trim()).filter((part, index) => {
|
|
117953
|
+
if (index === 0) return true;
|
|
117954
|
+
const name = part.split("=")[0]?.trim().toLowerCase() ?? "";
|
|
117955
|
+
return name !== "domain" && name !== "path";
|
|
117956
|
+
});
|
|
117957
|
+
kept.push(`Path=${route.basePath}/`);
|
|
117958
|
+
return kept.join("; ");
|
|
117959
|
+
}
|
|
117960
|
+
/**
|
|
117961
|
+
* A browser `Referer` that points into this proxy, turned back into the vendor URL it
|
|
117962
|
+
* stands for. A Referer that is not ours is dropped (null): it names the hub.
|
|
117963
|
+
*/
|
|
117964
|
+
function rewriteReferer(referer, route) {
|
|
117965
|
+
let url;
|
|
117966
|
+
try {
|
|
117967
|
+
url = new URL(referer, "http://hub.invalid");
|
|
117968
|
+
} catch {
|
|
117969
|
+
return null;
|
|
117970
|
+
}
|
|
117971
|
+
if (!url.pathname.startsWith(`${route.basePath}/`)) return null;
|
|
117972
|
+
const rest = /^\/([^/]+)(\/.*)?$/.exec(url.pathname.slice(route.basePath.length));
|
|
117973
|
+
const host = rest?.[1]?.toLowerCase();
|
|
117974
|
+
if (host === void 0) return null;
|
|
117975
|
+
const upstream = findUpstream(route, host);
|
|
117976
|
+
if (upstream === void 0) return null;
|
|
117977
|
+
return `${upstream.scheme}://${upstream.host}${rest?.[2] ?? "/"}${url.search}`;
|
|
117978
|
+
}
|
|
117979
|
+
/** Cookies the browser holds for the hub itself must never reach a vendor. */
|
|
117980
|
+
function filterForwardedCookies(cookieHeader) {
|
|
117981
|
+
return cookieHeader.split(";").map((part) => part.trim()).filter((part) => part.length > 0 && !part.toLowerCase().startsWith("camstack_")).join("; ");
|
|
117982
|
+
}
|
|
117983
|
+
//#endregion
|
|
117984
|
+
//#region src/vendor-login-proxy/login-proxy.ts
|
|
117985
|
+
var MAX_REQUEST_BODY_BYTES = 2 * 1024 * 1024;
|
|
117986
|
+
var MAX_TEXT_BODY_BYTES = 4 * 1024 * 1024;
|
|
117987
|
+
/** Request headers that are never forwarded to the vendor. */
|
|
117988
|
+
var DROP_REQUEST_HEADERS = new Set([
|
|
117989
|
+
"host",
|
|
117990
|
+
"connection",
|
|
117991
|
+
"keep-alive",
|
|
117992
|
+
"proxy-authorization",
|
|
117993
|
+
"proxy-connection",
|
|
117994
|
+
"te",
|
|
117995
|
+
"trailer",
|
|
117996
|
+
"transfer-encoding",
|
|
117997
|
+
"upgrade",
|
|
117998
|
+
"content-length",
|
|
117999
|
+
"accept-encoding",
|
|
118000
|
+
"authorization",
|
|
118001
|
+
"cookie",
|
|
118002
|
+
"origin",
|
|
118003
|
+
"referer",
|
|
118004
|
+
"x-camstack-dataplane-secret",
|
|
118005
|
+
"x-forwarded-for",
|
|
118006
|
+
"x-forwarded-host",
|
|
118007
|
+
"x-forwarded-proto",
|
|
118008
|
+
"x-forwarded-prefix",
|
|
118009
|
+
"x-real-ip",
|
|
118010
|
+
"forwarded"
|
|
118011
|
+
]);
|
|
118012
|
+
/**
|
|
118013
|
+
* Response headers dropped: framing/CSP/HSTS that break a page under another origin, and
|
|
118014
|
+
* the ones that no longer describe the (decoded, possibly rewritten) body.
|
|
118015
|
+
*/
|
|
118016
|
+
var DROP_RESPONSE_HEADERS = new Set([
|
|
118017
|
+
"content-security-policy",
|
|
118018
|
+
"content-security-policy-report-only",
|
|
118019
|
+
"x-frame-options",
|
|
118020
|
+
"strict-transport-security",
|
|
118021
|
+
"public-key-pins",
|
|
118022
|
+
"report-to",
|
|
118023
|
+
"nel",
|
|
118024
|
+
"content-encoding",
|
|
118025
|
+
"content-length",
|
|
118026
|
+
"transfer-encoding",
|
|
118027
|
+
"connection",
|
|
118028
|
+
"keep-alive",
|
|
118029
|
+
"set-cookie",
|
|
118030
|
+
"location"
|
|
118031
|
+
]);
|
|
118032
|
+
function bodyKindOf(contentType) {
|
|
118033
|
+
const type = contentType.toLowerCase();
|
|
118034
|
+
if (type.includes("text/html") || type.includes("application/xhtml")) return "html";
|
|
118035
|
+
if (type.includes("text/css")) return "css";
|
|
118036
|
+
if (type.startsWith("text/") || type.includes("javascript") || type.includes("json") || type.includes("xml")) return "other";
|
|
118037
|
+
return null;
|
|
118038
|
+
}
|
|
118039
|
+
function firstHeader(value) {
|
|
118040
|
+
return Array.isArray(value) ? value[0] : value;
|
|
118041
|
+
}
|
|
118042
|
+
async function readRequestBody(req) {
|
|
118043
|
+
const chunks = [];
|
|
118044
|
+
let size = 0;
|
|
118045
|
+
for await (const chunk of req) {
|
|
118046
|
+
const buf = typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
118047
|
+
size += buf.length;
|
|
118048
|
+
if (size > MAX_REQUEST_BODY_BYTES) return "too-large";
|
|
118049
|
+
chunks.push(buf);
|
|
118050
|
+
}
|
|
118051
|
+
return Buffer.concat(chunks);
|
|
118052
|
+
}
|
|
118053
|
+
function sendPlain(res, status, text) {
|
|
118054
|
+
res.writeHead(status, {
|
|
118055
|
+
"content-type": "text/plain; charset=utf-8",
|
|
118056
|
+
"cache-control": "no-store"
|
|
118057
|
+
});
|
|
118058
|
+
res.end(text);
|
|
118059
|
+
}
|
|
118060
|
+
var VendorLoginProxy = class {
|
|
118061
|
+
#addonId;
|
|
118062
|
+
#prefix;
|
|
118063
|
+
#logger;
|
|
118064
|
+
#now;
|
|
118065
|
+
#fetch;
|
|
118066
|
+
#attempts = /* @__PURE__ */ new Map();
|
|
118067
|
+
constructor(options) {
|
|
118068
|
+
this.#addonId = options.addonId;
|
|
118069
|
+
this.#prefix = options.prefix;
|
|
118070
|
+
this.#logger = options.logger;
|
|
118071
|
+
this.#now = options.now ?? (() => Date.now());
|
|
118072
|
+
this.#fetch = options.fetchFn ?? globalThis.fetch.bind(globalThis);
|
|
118073
|
+
}
|
|
118074
|
+
/** The `DataPlaneHandler` to pass to `ctx.dataPlane.serve({ prefix, access: 'admin', … })`. */
|
|
118075
|
+
handler = (req, res) => this.#handle(req, res);
|
|
118076
|
+
/** Register an attempt. A one-shot capability: refused hosts are rejected up front. */
|
|
118077
|
+
open(spec) {
|
|
118078
|
+
for (const upstream of spec.upstreams) if (!isValidUpstream(upstream)) throw new Error(`vendor login proxy: upstream ${upstream.host} is not allowed`);
|
|
118079
|
+
this.#attempts.set(spec.attemptId, {
|
|
118080
|
+
expiresAtMs: spec.expiresAtMs,
|
|
118081
|
+
upstreams: spec.upstreams,
|
|
118082
|
+
evaluate: async (view) => {
|
|
118083
|
+
const verdict = await spec.terminal(view);
|
|
118084
|
+
if (verdict.kind === "continue") return verdict;
|
|
118085
|
+
return {
|
|
118086
|
+
kind: "done",
|
|
118087
|
+
landing: await spec.onDone(verdict.payload)
|
|
118088
|
+
};
|
|
118089
|
+
}
|
|
118090
|
+
});
|
|
118091
|
+
}
|
|
118092
|
+
close(attemptId) {
|
|
118093
|
+
this.#attempts.delete(attemptId);
|
|
118094
|
+
}
|
|
118095
|
+
isOpen(attemptId) {
|
|
118096
|
+
const attempt = this.#attempts.get(attemptId);
|
|
118097
|
+
return attempt !== void 0 && this.#now() <= attempt.expiresAtMs;
|
|
118098
|
+
}
|
|
118099
|
+
/**
|
|
118100
|
+
* The hub-relative path that opens `vendorUrl` through the proxy for this attempt:
|
|
118101
|
+
* the URL the browser is sent to. Null when the host is not in the attempt's list.
|
|
118102
|
+
*/
|
|
118103
|
+
entryPath(attemptId, vendorUrl) {
|
|
118104
|
+
const attempt = this.#attempts.get(attemptId);
|
|
118105
|
+
if (attempt === void 0) return null;
|
|
118106
|
+
const url = new URL(vendorUrl);
|
|
118107
|
+
if (findUpstream({
|
|
118108
|
+
basePath: "",
|
|
118109
|
+
upstreams: attempt.upstreams
|
|
118110
|
+
}, url.host) === void 0) return null;
|
|
118111
|
+
return `/addon/${this.#addonId}/${this.#prefix}/${attemptId}/${url.host}${url.pathname}${url.search}`;
|
|
118112
|
+
}
|
|
118113
|
+
#routeFor(attemptId, attempt, forwardedPrefix) {
|
|
118114
|
+
return {
|
|
118115
|
+
basePath: `${forwardedPrefix}/addon/${this.#addonId}/${this.#prefix}/${attemptId}`,
|
|
118116
|
+
upstreams: attempt.upstreams
|
|
118117
|
+
};
|
|
118118
|
+
}
|
|
118119
|
+
async #handle(req, res) {
|
|
118120
|
+
try {
|
|
118121
|
+
await this.#serve(req, res);
|
|
118122
|
+
} catch (err) {
|
|
118123
|
+
this.#logger.warn("vendor login proxy: request failed", { meta: {
|
|
118124
|
+
addonId: this.#addonId,
|
|
118125
|
+
error: err instanceof Error ? err.name : "unknown"
|
|
118126
|
+
} });
|
|
118127
|
+
if (!res.headersSent) sendPlain(res, 502, "The sign-in page could not be reached.");
|
|
118128
|
+
else res.end();
|
|
118129
|
+
}
|
|
118130
|
+
}
|
|
118131
|
+
async #serve(req, res) {
|
|
118132
|
+
const parsed = parseProxyPath(req.url ?? "/");
|
|
118133
|
+
if (parsed === null) {
|
|
118134
|
+
sendPlain(res, 404, "Not found");
|
|
118135
|
+
return;
|
|
118136
|
+
}
|
|
118137
|
+
const forwardedPrefix = (firstHeader(req.headers["x-forwarded-prefix"]) ?? "").replace(/\/+$/, "");
|
|
118138
|
+
const attempt = this.#attempts.get(parsed.attemptId);
|
|
118139
|
+
if (attempt === void 0 || this.#now() > attempt.expiresAtMs) {
|
|
118140
|
+
if (attempt !== void 0) this.#attempts.delete(parsed.attemptId);
|
|
118141
|
+
this.#logger.info("vendor login proxy: attempt refused", { meta: {
|
|
118142
|
+
addonId: this.#addonId,
|
|
118143
|
+
reason: attempt === void 0 ? "missing" : "expired"
|
|
118144
|
+
} });
|
|
118145
|
+
this.#redirect(res, forwardedPrefix, {
|
|
118146
|
+
status: "error",
|
|
118147
|
+
addonId: this.#addonId,
|
|
118148
|
+
reason: "invalid_state"
|
|
118149
|
+
});
|
|
118150
|
+
return;
|
|
118151
|
+
}
|
|
118152
|
+
const route = this.#routeFor(parsed.attemptId, attempt, forwardedPrefix);
|
|
118153
|
+
const upstream = findUpstream(route, parsed.host);
|
|
118154
|
+
const target = upstream === void 0 ? null : upstreamUrlFor(upstream, parsed);
|
|
118155
|
+
if (upstream === void 0 || target === null) {
|
|
118156
|
+
this.#logger.warn("vendor login proxy: host refused", { meta: {
|
|
118157
|
+
addonId: this.#addonId,
|
|
118158
|
+
host: parsed.host
|
|
118159
|
+
} });
|
|
118160
|
+
sendPlain(res, 404, "Not found");
|
|
118161
|
+
return;
|
|
118162
|
+
}
|
|
118163
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
118164
|
+
let body;
|
|
118165
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
118166
|
+
const read = await readRequestBody(req);
|
|
118167
|
+
if (read === "too-large") {
|
|
118168
|
+
sendPlain(res, 413, "Request too large");
|
|
118169
|
+
return;
|
|
118170
|
+
}
|
|
118171
|
+
body = read;
|
|
118172
|
+
}
|
|
118173
|
+
const headers = this.#requestHeaders(req, route, upstream);
|
|
118174
|
+
const upstreamRes = await this.#fetch(target, {
|
|
118175
|
+
method,
|
|
118176
|
+
headers,
|
|
118177
|
+
redirect: "manual",
|
|
118178
|
+
...body !== void 0 && body.length > 0 ? { body: new Uint8Array(body) } : {}
|
|
118179
|
+
});
|
|
118180
|
+
const location = upstreamRes.headers.get("location");
|
|
118181
|
+
const setCookies = upstreamRes.headers.getSetCookie();
|
|
118182
|
+
const kind = bodyKindOf(upstreamRes.headers.get("content-type") ?? "");
|
|
118183
|
+
const declaredLength = Number(upstreamRes.headers.get("content-length") ?? "0");
|
|
118184
|
+
const text = kind !== null && !(declaredLength > MAX_TEXT_BODY_BYTES) && method !== "HEAD" ? await upstreamRes.text() : null;
|
|
118185
|
+
this.#logger.debug("vendor login proxy: upstream answered", { meta: {
|
|
118186
|
+
addonId: this.#addonId,
|
|
118187
|
+
host: upstream.host,
|
|
118188
|
+
status: upstreamRes.status,
|
|
118189
|
+
method
|
|
118190
|
+
} });
|
|
118191
|
+
const settled = await attempt.evaluate({
|
|
118192
|
+
status: upstreamRes.status,
|
|
118193
|
+
url: target.toString(),
|
|
118194
|
+
location,
|
|
118195
|
+
setCookies,
|
|
118196
|
+
bodyText: text
|
|
118197
|
+
});
|
|
118198
|
+
if (settled.kind === "done") {
|
|
118199
|
+
this.#attempts.delete(parsed.attemptId);
|
|
118200
|
+
this.#logger.info("vendor login proxy: sign-in finished", { meta: {
|
|
118201
|
+
addonId: this.#addonId,
|
|
118202
|
+
status: settled.landing.status
|
|
118203
|
+
} });
|
|
118204
|
+
this.#redirect(res, forwardedPrefix, settled.landing);
|
|
118205
|
+
return;
|
|
118206
|
+
}
|
|
118207
|
+
const out = {};
|
|
118208
|
+
upstreamRes.headers.forEach((value, name) => {
|
|
118209
|
+
if (!DROP_RESPONSE_HEADERS.has(name.toLowerCase())) out[name] = value;
|
|
118210
|
+
});
|
|
118211
|
+
if (setCookies.length > 0) out["set-cookie"] = setCookies.map((c) => rewriteSetCookie(c, route));
|
|
118212
|
+
if (location !== null) out["location"] = rewriteLocation(location, upstream, route);
|
|
118213
|
+
out["cache-control"] = "no-store";
|
|
118214
|
+
if (text !== null && kind !== null) {
|
|
118215
|
+
const rewritten = rewriteBody(text, kind, upstream, route);
|
|
118216
|
+
res.writeHead(upstreamRes.status, out);
|
|
118217
|
+
res.end(rewritten);
|
|
118218
|
+
return;
|
|
118219
|
+
}
|
|
118220
|
+
res.writeHead(upstreamRes.status, out);
|
|
118221
|
+
await this.#pipeBody(upstreamRes, res);
|
|
118222
|
+
}
|
|
118223
|
+
#requestHeaders(req, route, upstream) {
|
|
118224
|
+
const headers = {};
|
|
118225
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
118226
|
+
const lower = name.toLowerCase();
|
|
118227
|
+
if (DROP_REQUEST_HEADERS.has(lower) || value === void 0) continue;
|
|
118228
|
+
headers[lower] = Array.isArray(value) ? value.join(", ") : value;
|
|
118229
|
+
}
|
|
118230
|
+
const cookie = filterForwardedCookies(firstHeader(req.headers["cookie"]) ?? "");
|
|
118231
|
+
if (cookie.length > 0) headers["cookie"] = cookie;
|
|
118232
|
+
if (req.headers["origin"] !== void 0) headers["origin"] = `${upstream.scheme}://${upstream.host}`;
|
|
118233
|
+
const referer = firstHeader(req.headers["referer"]);
|
|
118234
|
+
if (referer !== void 0) {
|
|
118235
|
+
const back = rewriteReferer(referer, route);
|
|
118236
|
+
if (back !== null) headers["referer"] = back;
|
|
118237
|
+
}
|
|
118238
|
+
return headers;
|
|
118239
|
+
}
|
|
118240
|
+
async #pipeBody(upstreamRes, res) {
|
|
118241
|
+
if (upstreamRes.body === null) {
|
|
118242
|
+
res.end();
|
|
118243
|
+
return;
|
|
118244
|
+
}
|
|
118245
|
+
for await (const chunk of upstreamRes.body) {
|
|
118246
|
+
if (!res.write(chunk)) await new Promise((resolve) => {
|
|
118247
|
+
res.once("drain", resolve);
|
|
118248
|
+
res.once("close", resolve);
|
|
118249
|
+
});
|
|
118250
|
+
if (res.destroyed) return;
|
|
118251
|
+
}
|
|
118252
|
+
res.end();
|
|
118253
|
+
}
|
|
118254
|
+
#redirect(res, forwardedPrefix, outcome) {
|
|
118255
|
+
res.writeHead(302, {
|
|
118256
|
+
location: `${forwardedPrefix}${oauthLandingUrl(null, outcome)}`,
|
|
118257
|
+
"cache-control": "no-store"
|
|
118258
|
+
});
|
|
118259
|
+
res.end();
|
|
118260
|
+
}
|
|
118261
|
+
};
|
|
118262
|
+
//#endregion
|
|
117848
118263
|
//#region src/utils/pool-memory-watchdog.ts
|
|
117849
118264
|
var MB = 1024 * 1024;
|
|
117850
118265
|
var DEFAULT_POOL_MEMORY_POLICY = {
|
|
@@ -120952,6 +121367,7 @@ exports.VectorStatsInputSchema = VectorStatsInputSchema;
|
|
|
120952
121367
|
exports.VectorStatsResultSchema = VectorStatsResultSchema;
|
|
120953
121368
|
exports.VectorUpsertInputSchema = VectorUpsertInputSchema;
|
|
120954
121369
|
exports.VectorUpsertResultSchema = VectorUpsertResultSchema;
|
|
121370
|
+
exports.VendorLoginProxy = VendorLoginProxy;
|
|
120955
121371
|
exports.VibrationStatusSchema = VibrationStatusSchema;
|
|
120956
121372
|
exports.VideoEncodeSchema = VideoEncodeSchema;
|
|
120957
121373
|
exports.ViewerDeviceAllowSchema = ViewerDeviceAllowSchema;
|
|
@@ -121849,6 +122265,10 @@ exports.resultCellKey = resultCellKey;
|
|
|
121849
122265
|
exports.resultStandings = resultStandings;
|
|
121850
122266
|
exports.retrainBoxIou = retrainBoxIou;
|
|
121851
122267
|
exports.retrainRevisionConflictMessage = retrainRevisionConflictMessage;
|
|
122268
|
+
exports.rewriteLoginProxyBody = rewriteBody;
|
|
122269
|
+
exports.rewriteLoginProxyLocation = rewriteLocation;
|
|
122270
|
+
exports.rewriteLoginProxyReferer = rewriteReferer;
|
|
122271
|
+
exports.rewriteLoginProxySetCookie = rewriteSetCookie;
|
|
121852
122272
|
exports.roleSpec = roleSpec;
|
|
121853
122273
|
exports.rollingDurationMs = require_composition.rollingDurationMs;
|
|
121854
122274
|
exports.roomCompositionExample = roomCompositionExample;
|