Merge remote-tracking branch 'origin/release/v2' into feat/pr-6691-hardening

# Conflicts:
#	apps/nextjs/src/app/[locale]/layout.tsx
#	packages/api/src/router/widgets/anchor-notes.ts
#	packages/api/src/router/widgets/archive-team-warrior.ts
#	packages/api/src/router/widgets/audio-stats.ts
#	packages/api/src/router/widgets/bazarr.ts
#	packages/api/src/router/widgets/coolify.ts
#	packages/api/src/router/widgets/firewall.ts
#	packages/api/src/router/widgets/immich.ts
#	packages/api/src/router/widgets/paperless-ngx.ts
#	packages/api/src/router/widgets/patchmon.ts
#	packages/api/src/router/widgets/speedtest-tracker.ts
#	packages/api/src/router/widgets/tracearr.ts
#	packages/api/src/router/widgets/traefik.ts
#	packages/api/src/router/widgets/umami.ts
#	packages/api/src/router/widgets/uptime-kuma.ts
#	packages/api/src/router/widgets/vpn.ts
#	packages/api/src/router/widgets/wud.ts
#	packages/definitions/src/widget-integration-map.ts
#	packages/widgets/src/anchor-note/index.ts
#	packages/widgets/src/archive-team-warrior/index.ts
#	packages/widgets/src/audio-stats/index.ts
#	packages/widgets/src/bazarr/index.ts
#	packages/widgets/src/coolify/index.ts
#	packages/widgets/src/firewall/index.ts
#	packages/widgets/src/immich/album-carousel/index.ts
#	packages/widgets/src/immich/server-stats/index.ts
#	packages/widgets/src/paperless-ngx/index.ts
#	packages/widgets/src/patchmon/index.ts
#	packages/widgets/src/speedtest-tracker/index.ts
#	packages/widgets/src/system-disks/index.ts
#	packages/widgets/src/system-resources/index.ts
#	packages/widgets/src/tracearr/index.ts
#	packages/widgets/src/traefik/index.ts
#	packages/widgets/src/umami/index.ts
#	packages/widgets/src/uptime-kuma/index.ts
#	packages/widgets/src/vpn/index.ts
#	packages/widgets/src/wud/index.ts
This commit is contained in:
ajnart committed 2026-08-26 15:12:49 +02:00
commit ac7e3f2ef4
201 files changed
+13795 -981

No files matched your search

@@ -34,7 +34,7 @@ const getIntegrationCacheIdentity = <TKind extends IntegrationKind, TInput exten
integration: IntegrationOfKind<TKind>;
options: TInput;
}) => {
const secretRevisions = integration.decryptedSecrets
const secretRevisions = (integration.decryptedSecrets ?? [])
.map((secret) => ({
kind: secret.kind,
fingerprint: createKeyedFingerprint(JSON.stringify([secret.kind, secret.value])),
@@ -322,7 +322,7 @@ describe("createRequestHandler", () => {
});
describe("createIntegrationRequestHandler", () => {
it("keys requests by integration id and options without retaining secret values", async () => {
it("does not deduplicate requests across rotated integration secrets", async () => {
let calls = 0;
const request = deferred<string>();
const handler = createIntegrationRequestHandler<string, "sonarr", { page: number }>({
@@ -332,11 +332,16 @@ describe("createIntegrationRequestHandler", () => {
},
});
const first = handler.handler(createIntegration("first-secret"), { page: 1 }).getDataAsync();
const duplicate = handler.handler(createIntegration("first-secret"), { page: 1 }).getDataAsync();
const second = handler.handler(createIntegration("rotated-secret"), { page: 1 }).getDataAsync();
expect(calls).toBe(1);
expect(calls).toBe(2);
request.resolve("value");
await expect(Promise.all([first, second])).resolves.toMatchObject([{ data: "value" }, { data: "value" }]);
await expect(Promise.all([first, duplicate, second])).resolves.toMatchObject([
{ data: "value" },
{ data: "value" },
{ data: "value" },
]);
});
});
@@ -162,7 +162,12 @@ export const createRequestHandler = <TData, TInput extends Record<string, unknow
const controller = new AbortController();
const timeoutError = new RequestHandlerTimeoutError();
const remainingMs = deadlineAt - Date.now();
const upstreamRequest = Promise.resolve().then(async () => await options.requestAsync(input, controller.signal));
let upstreamRequest: Promise<TData>;
try {
upstreamRequest = Promise.resolve(options.requestAsync(input, controller.signal));
} catch (error) {
upstreamRequest = Promise.reject(error);
}
const settledAsync = upstreamRequest.then(
() => undefined,
() => undefined,
@@ -239,8 +244,9 @@ export const createRequestHandler = <TData, TInput extends Record<string, unknow
const baseKey = options.getCacheKey?.(input) ?? JSON.stringify(input);
const requestGeneration = generation;
let sharedCache: SharedCacheAdapter<TData> | undefined;
if (ttl > 0) {
sharedCache = await resolveSharedCacheAsync(input, baseKey, requestGeneration);
const sharedCacheResolution = ttl > 0 ? resolveSharedCacheAsync(input, baseKey, requestGeneration) : undefined;
if (sharedCacheResolution) {
sharedCache = await sharedCacheResolution;
throwIfDeadlineExceeded(deadlineAt);
}