@webiny/api-event-handler-standalone 0.0.0-unstable.0156a4c312
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/LICENSE +21 -0
- package/README.md +11 -0
- package/bulkActions/bulkActionsServer.d.ts +6 -0
- package/bulkActions/bulkActionsServer.js +26 -0
- package/bulkActions/bulkActionsServer.js.map +1 -0
- package/config/abstractions.d.ts +36 -0
- package/config/abstractions.js +5 -0
- package/config/abstractions.js.map +1 -0
- package/config/bakedSecrets.d.ts +8 -0
- package/config/bakedSecrets.js +17 -0
- package/config/bakedSecrets.js.map +1 -0
- package/config/readBuildParams.d.ts +10 -0
- package/config/readBuildParams.js +13 -0
- package/config/readBuildParams.js.map +1 -0
- package/config/readStandaloneConfig.d.ts +12 -0
- package/config/readStandaloneConfig.js +56 -0
- package/config/readStandaloneConfig.js.map +1 -0
- package/createWebinyApiHandler.d.ts +15 -0
- package/createWebinyApiHandler.js +130 -0
- package/createWebinyApiHandler.js.map +1 -0
- package/crypto/StandaloneEncryptionConfig.d.ts +17 -0
- package/crypto/StandaloneEncryptionConfig.js +20 -0
- package/crypto/StandaloneEncryptionConfig.js.map +1 -0
- package/crypto/StandaloneHashingConfig.d.ts +16 -0
- package/crypto/StandaloneHashingConfig.js +20 -0
- package/crypto/StandaloneHashingConfig.js.map +1 -0
- package/crypto/feature.d.ts +5 -0
- package/crypto/feature.js +13 -0
- package/crypto/feature.js.map +1 -0
- package/handlers/NodeHttpAssumePermissionsDecorator.d.ts +19 -0
- package/handlers/NodeHttpAssumePermissionsDecorator.js +23 -0
- package/handlers/NodeHttpAssumePermissionsDecorator.js.map +1 -0
- package/handlers/NodeHttpIdentityLoaderDecorator.d.ts +32 -0
- package/handlers/NodeHttpIdentityLoaderDecorator.js +45 -0
- package/handlers/NodeHttpIdentityLoaderDecorator.js.map +1 -0
- package/handlers/NodeHttpTenantLoaderDecorator.d.ts +24 -0
- package/handlers/NodeHttpTenantLoaderDecorator.js +29 -0
- package/handlers/NodeHttpTenantLoaderDecorator.js.map +1 -0
- package/index.d.ts +5 -0
- package/index.js +4 -0
- package/package.json +53 -0
- package/publicUrls/ConfigPublicUrls.d.ts +13 -0
- package/publicUrls/ConfigPublicUrls.js +22 -0
- package/publicUrls/ConfigPublicUrls.js.map +1 -0
- package/publicUrls/feature.d.ts +4 -0
- package/publicUrls/feature.js +11 -0
- package/publicUrls/feature.js.map +1 -0
- package/scheduler/RecoverScheduledActionsHandler.d.ts +31 -0
- package/scheduler/RecoverScheduledActionsHandler.js +81 -0
- package/scheduler/RecoverScheduledActionsHandler.js.map +1 -0
- package/scheduler/RunScheduledActionOnceDecorator.d.ts +37 -0
- package/scheduler/RunScheduledActionOnceDecorator.js +91 -0
- package/scheduler/RunScheduledActionOnceDecorator.js.map +1 -0
- package/scheduler/ScheduledActionRecoverEventHandler.d.ts +14 -0
- package/scheduler/ScheduledActionRecoverEventHandler.js +5 -0
- package/scheduler/ScheduledActionRecoverEventHandler.js.map +1 -0
- package/scheduler/ScheduledActionRecoverEventType.d.ts +17 -0
- package/scheduler/ScheduledActionRecoverEventType.js +18 -0
- package/scheduler/ScheduledActionRecoverEventType.js.map +1 -0
- package/scheduler/abstractions/SchedulerSingleton.d.ts +10 -0
- package/scheduler/abstractions/SchedulerSingleton.js +5 -0
- package/scheduler/abstractions/SchedulerSingleton.js.map +1 -0
- package/scheduler/schedulerServer.d.ts +25 -0
- package/scheduler/schedulerServer.js +89 -0
- package/scheduler/schedulerServer.js.map +1 -0
- package/websockets/createWebsocketsAuthenticator.d.ts +24 -0
- package/websockets/createWebsocketsAuthenticator.js +22 -0
- package/websockets/createWebsocketsAuthenticator.js.map +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import { NodeHttpEventHandler } from "@webiny/event-handler-standalone";
|
|
3
|
+
import { RawAssumePermissions } from "@webiny/api-core/features/requestContext/index.js";
|
|
4
|
+
import type { EventContext } from "@webiny/event-handler-core";
|
|
5
|
+
import type { NextFunction } from "@webiny/event-handler-core";
|
|
6
|
+
/**
|
|
7
|
+
* EXTRACT (transport-specific): reads the `x-webiny-assume-permissions` header of a Node
|
|
8
|
+
* `IncomingMessage` into RawAssumePermissions. Node mirror of ApiGatewayAssumePermissionsDecorator.
|
|
9
|
+
*/
|
|
10
|
+
declare class NodeHttpAssumePermissionsDecoratorImpl implements NodeHttpEventHandler.Interface {
|
|
11
|
+
private rawAssumePermissions;
|
|
12
|
+
private decoratee;
|
|
13
|
+
constructor(rawAssumePermissions: RawAssumePermissions.Interface, decoratee: NodeHttpEventHandler.Interface);
|
|
14
|
+
execute(ctx: EventContext<IncomingMessage>, next: NextFunction): Promise<any>;
|
|
15
|
+
}
|
|
16
|
+
export declare const NodeHttpAssumePermissionsDecorator: typeof NodeHttpAssumePermissionsDecoratorImpl & {
|
|
17
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-standalone").INodeHttpEventHandler>;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NodeHttpEventHandler } from "@webiny/event-handler-standalone";
|
|
2
|
+
import { RawAssumePermissions, extractAssumePermissions } from "@webiny/api-core/features/requestContext/index.js";
|
|
3
|
+
class NodeHttpAssumePermissionsDecoratorImpl {
|
|
4
|
+
constructor(rawAssumePermissions, decoratee){
|
|
5
|
+
this.rawAssumePermissions = rawAssumePermissions;
|
|
6
|
+
this.decoratee = decoratee;
|
|
7
|
+
}
|
|
8
|
+
async execute(ctx, next) {
|
|
9
|
+
const headers = ctx.event?.headers;
|
|
10
|
+
const requested = extractAssumePermissions(headers);
|
|
11
|
+
this.rawAssumePermissions.set(requested);
|
|
12
|
+
return this.decoratee.execute(ctx, next);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const NodeHttpAssumePermissionsDecorator = NodeHttpEventHandler.createDecorator({
|
|
16
|
+
decorator: NodeHttpAssumePermissionsDecoratorImpl,
|
|
17
|
+
dependencies: [
|
|
18
|
+
RawAssumePermissions
|
|
19
|
+
]
|
|
20
|
+
});
|
|
21
|
+
export { NodeHttpAssumePermissionsDecorator };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=NodeHttpAssumePermissionsDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers/NodeHttpAssumePermissionsDecorator.js","sources":["../../src/handlers/NodeHttpAssumePermissionsDecorator.ts"],"sourcesContent":["import type { IncomingMessage } from \"node:http\";\nimport { NodeHttpEventHandler } from \"@webiny/event-handler-standalone\";\nimport { RawAssumePermissions } from \"@webiny/api-core/features/requestContext/index.js\";\nimport { extractAssumePermissions } from \"@webiny/api-core/features/requestContext/index.js\";\nimport type { EventContext } from \"@webiny/event-handler-core\";\nimport type { NextFunction } from \"@webiny/event-handler-core\";\n\n/**\n * EXTRACT (transport-specific): reads the `x-webiny-assume-permissions` header of a Node\n * `IncomingMessage` into RawAssumePermissions. Node mirror of ApiGatewayAssumePermissionsDecorator.\n */\nclass NodeHttpAssumePermissionsDecoratorImpl implements NodeHttpEventHandler.Interface {\n constructor(\n private rawAssumePermissions: RawAssumePermissions.Interface,\n private decoratee: NodeHttpEventHandler.Interface\n ) {}\n\n async execute(ctx: EventContext<IncomingMessage>, next: NextFunction): Promise<any> {\n const headers = ctx.event?.headers;\n const requested = extractAssumePermissions(headers);\n\n this.rawAssumePermissions.set(requested);\n\n return this.decoratee.execute(ctx, next);\n }\n}\n\nexport const NodeHttpAssumePermissionsDecorator = NodeHttpEventHandler.createDecorator({\n decorator: NodeHttpAssumePermissionsDecoratorImpl,\n dependencies: [RawAssumePermissions]\n});\n"],"names":["NodeHttpAssumePermissionsDecoratorImpl","rawAssumePermissions","decoratee","ctx","next","headers","requested","extractAssumePermissions","NodeHttpAssumePermissionsDecorator","NodeHttpEventHandler","RawAssumePermissions"],"mappings":";;AAWA,MAAMA;IACF,YACYC,oBAAoD,EACpDC,SAAyC,CACnD;aAFUD,oBAAoB,GAApBA;aACAC,SAAS,GAATA;IACT;IAEH,MAAM,QAAQC,GAAkC,EAAEC,IAAkB,EAAgB;QAChF,MAAMC,UAAUF,IAAI,KAAK,EAAE;QAC3B,MAAMG,YAAYC,yBAAyBF;QAE3C,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAACC;QAE9B,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAACH,KAAKC;IACvC;AACJ;AAEO,MAAMI,qCAAqCC,qBAAqB,eAAe,CAAC;IACnF,WAAWT;IACX,cAAc;QAACU;KAAqB;AACxC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import { NodeHttpEventHandler } from "@webiny/event-handler-standalone";
|
|
3
|
+
import { RawAuthToken } from "@webiny/api-core/features/requestContext/index.js";
|
|
4
|
+
import type { IRequestIdentityLoader } from "@webiny/api-core/features/requestContext/abstractions.js";
|
|
5
|
+
import type { EventContext, NextFunction } from "@webiny/event-handler-core";
|
|
6
|
+
/**
|
|
7
|
+
* EXTRACT (transport-specific): reads the auth token from a Node `IncomingMessage` —
|
|
8
|
+
* `x-webiny-authorization` first, then the bearer `Authorization` header, then the `wby-id-token`
|
|
9
|
+
* cookie — into RawAuthToken, then invokes the shared LOAD step (RequestIdentityLoader) which
|
|
10
|
+
* authenticates it (via the registered identity provider, e.g. the self-hosted JWT IdP) and sets
|
|
11
|
+
* IdentityContext.
|
|
12
|
+
*
|
|
13
|
+
* `x-webiny-authorization` exists for AWS parity: behind CloudFront with Origin Access Control, SigV4
|
|
14
|
+
* occupies the `Authorization` header, so clients that may traverse such an origin send the token in
|
|
15
|
+
* that header instead. Standalone has no such constraint, but it accepts the same header so one
|
|
16
|
+
* client works against both deployments.
|
|
17
|
+
*
|
|
18
|
+
* Node mirror of ApiGatewayIdentityLoaderDecorator. Registered BEFORE the tenant loader so identity
|
|
19
|
+
* is established before tenant.
|
|
20
|
+
*/
|
|
21
|
+
declare class NodeHttpIdentityLoaderDecoratorImpl implements NodeHttpEventHandler.Interface {
|
|
22
|
+
private rawAuthToken;
|
|
23
|
+
private identityLoader;
|
|
24
|
+
private decoratee;
|
|
25
|
+
constructor(rawAuthToken: RawAuthToken.Interface, identityLoader: IRequestIdentityLoader, decoratee: NodeHttpEventHandler.Interface);
|
|
26
|
+
execute(ctx: EventContext<IncomingMessage>, next: NextFunction): Promise<any>;
|
|
27
|
+
private extractToken;
|
|
28
|
+
}
|
|
29
|
+
export declare const NodeHttpIdentityLoaderDecorator: typeof NodeHttpIdentityLoaderDecoratorImpl & {
|
|
30
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-standalone").INodeHttpEventHandler>;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NodeHttpEventHandler } from "@webiny/event-handler-standalone";
|
|
2
|
+
import { RawAuthToken, RequestIdentityLoader } from "@webiny/api-core/features/requestContext/index.js";
|
|
3
|
+
function headerValue(value) {
|
|
4
|
+
if (Array.isArray(value)) return value[0] ?? "";
|
|
5
|
+
return value ?? "";
|
|
6
|
+
}
|
|
7
|
+
function parseCookieHeader(cookieHeader) {
|
|
8
|
+
return cookieHeader.split(";").reduce((acc, pair)=>{
|
|
9
|
+
const idx = pair.indexOf("=");
|
|
10
|
+
if (idx > 0) acc[pair.slice(0, idx).trim()] = decodeURIComponent(pair.slice(idx + 1).trim());
|
|
11
|
+
return acc;
|
|
12
|
+
}, {});
|
|
13
|
+
}
|
|
14
|
+
class NodeHttpIdentityLoaderDecoratorImpl {
|
|
15
|
+
constructor(rawAuthToken, identityLoader, decoratee){
|
|
16
|
+
this.rawAuthToken = rawAuthToken;
|
|
17
|
+
this.identityLoader = identityLoader;
|
|
18
|
+
this.decoratee = decoratee;
|
|
19
|
+
}
|
|
20
|
+
async execute(ctx, next) {
|
|
21
|
+
this.rawAuthToken.set(this.extractToken(ctx.event));
|
|
22
|
+
await this.identityLoader.establish();
|
|
23
|
+
return this.decoratee.execute(ctx, next);
|
|
24
|
+
}
|
|
25
|
+
extractToken(event) {
|
|
26
|
+
const headers = event?.headers;
|
|
27
|
+
if (!headers) return null;
|
|
28
|
+
const webinyAuth = headerValue(headers["x-webiny-authorization"] ?? headers["X-Webiny-Authorization"]).replace(/^Bearer\s+/i, "");
|
|
29
|
+
if (webinyAuth) return webinyAuth;
|
|
30
|
+
const bearer = headerValue(headers["authorization"] ?? headers["Authorization"]).replace(/^Bearer\s+/i, "");
|
|
31
|
+
if (bearer) return bearer;
|
|
32
|
+
const cookieHeader = headerValue(headers["cookie"] ?? headers["Cookie"]);
|
|
33
|
+
return parseCookieHeader(cookieHeader)["wby-id-token"] ?? null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const NodeHttpIdentityLoaderDecorator = NodeHttpEventHandler.createDecorator({
|
|
37
|
+
decorator: NodeHttpIdentityLoaderDecoratorImpl,
|
|
38
|
+
dependencies: [
|
|
39
|
+
RawAuthToken,
|
|
40
|
+
RequestIdentityLoader
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
export { NodeHttpIdentityLoaderDecorator };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=NodeHttpIdentityLoaderDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers/NodeHttpIdentityLoaderDecorator.js","sources":["../../src/handlers/NodeHttpIdentityLoaderDecorator.ts"],"sourcesContent":["import type { IncomingMessage } from \"node:http\";\nimport { NodeHttpEventHandler } from \"@webiny/event-handler-standalone\";\nimport {\n RawAuthToken,\n RequestIdentityLoader\n} from \"@webiny/api-core/features/requestContext/index.js\";\nimport type { IRequestIdentityLoader } from \"@webiny/api-core/features/requestContext/abstractions.js\";\nimport type { EventContext, NextFunction } from \"@webiny/event-handler-core\";\n\nfunction headerValue(value: string | string[] | undefined): string {\n if (Array.isArray(value)) {\n return value[0] ?? \"\";\n }\n return value ?? \"\";\n}\n\nfunction parseCookieHeader(cookieHeader: string): Record<string, string> {\n return cookieHeader.split(\";\").reduce<Record<string, string>>((acc, pair) => {\n const idx = pair.indexOf(\"=\");\n if (idx > 0) {\n acc[pair.slice(0, idx).trim()] = decodeURIComponent(pair.slice(idx + 1).trim());\n }\n return acc;\n }, {});\n}\n\n/**\n * EXTRACT (transport-specific): reads the auth token from a Node `IncomingMessage` —\n * `x-webiny-authorization` first, then the bearer `Authorization` header, then the `wby-id-token`\n * cookie — into RawAuthToken, then invokes the shared LOAD step (RequestIdentityLoader) which\n * authenticates it (via the registered identity provider, e.g. the self-hosted JWT IdP) and sets\n * IdentityContext.\n *\n * `x-webiny-authorization` exists for AWS parity: behind CloudFront with Origin Access Control, SigV4\n * occupies the `Authorization` header, so clients that may traverse such an origin send the token in\n * that header instead. Standalone has no such constraint, but it accepts the same header so one\n * client works against both deployments.\n *\n * Node mirror of ApiGatewayIdentityLoaderDecorator. Registered BEFORE the tenant loader so identity\n * is established before tenant.\n */\nclass NodeHttpIdentityLoaderDecoratorImpl implements NodeHttpEventHandler.Interface {\n constructor(\n private rawAuthToken: RawAuthToken.Interface,\n private identityLoader: IRequestIdentityLoader,\n private decoratee: NodeHttpEventHandler.Interface\n ) {}\n\n async execute(ctx: EventContext<IncomingMessage>, next: NextFunction): Promise<any> {\n this.rawAuthToken.set(this.extractToken(ctx.event));\n await this.identityLoader.establish();\n return this.decoratee.execute(ctx, next);\n }\n\n private extractToken(event: IncomingMessage): string | null {\n const headers = event?.headers;\n if (!headers) {\n return null;\n }\n const webinyAuth = headerValue(\n headers[\"x-webiny-authorization\"] ?? headers[\"X-Webiny-Authorization\"]\n ).replace(/^Bearer\\s+/i, \"\");\n if (webinyAuth) {\n return webinyAuth;\n }\n const bearer = headerValue(headers[\"authorization\"] ?? headers[\"Authorization\"]).replace(\n /^Bearer\\s+/i,\n \"\"\n );\n if (bearer) {\n return bearer;\n }\n const cookieHeader = headerValue(headers[\"cookie\"] ?? headers[\"Cookie\"]);\n return parseCookieHeader(cookieHeader)[\"wby-id-token\"] ?? null;\n }\n}\n\nexport const NodeHttpIdentityLoaderDecorator = NodeHttpEventHandler.createDecorator({\n decorator: NodeHttpIdentityLoaderDecoratorImpl,\n dependencies: [RawAuthToken, RequestIdentityLoader]\n});\n"],"names":["headerValue","value","Array","parseCookieHeader","cookieHeader","acc","pair","idx","decodeURIComponent","NodeHttpIdentityLoaderDecoratorImpl","rawAuthToken","identityLoader","decoratee","ctx","next","event","headers","webinyAuth","bearer","NodeHttpIdentityLoaderDecorator","NodeHttpEventHandler","RawAuthToken","RequestIdentityLoader"],"mappings":";;AASA,SAASA,YAAYC,KAAoC;IACrD,IAAIC,MAAM,OAAO,CAACD,QACd,OAAOA,KAAK,CAAC,EAAE,IAAI;IAEvB,OAAOA,SAAS;AACpB;AAEA,SAASE,kBAAkBC,YAAoB;IAC3C,OAAOA,aAAa,KAAK,CAAC,KAAK,MAAM,CAAyB,CAACC,KAAKC;QAChE,MAAMC,MAAMD,KAAK,OAAO,CAAC;QACzB,IAAIC,MAAM,GACNF,GAAG,CAACC,KAAK,KAAK,CAAC,GAAGC,KAAK,IAAI,GAAG,GAAGC,mBAAmBF,KAAK,KAAK,CAACC,MAAM,GAAG,IAAI;QAEhF,OAAOF;IACX,GAAG,CAAC;AACR;AAiBA,MAAMI;IACF,YACYC,YAAoC,EACpCC,cAAsC,EACtCC,SAAyC,CACnD;aAHUF,YAAY,GAAZA;aACAC,cAAc,GAAdA;aACAC,SAAS,GAATA;IACT;IAEH,MAAM,QAAQC,GAAkC,EAAEC,IAAkB,EAAgB;QAChF,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAACD,IAAI,KAAK;QACjD,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS;QACnC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAACA,KAAKC;IACvC;IAEQ,aAAaC,KAAsB,EAAiB;QACxD,MAAMC,UAAUD,OAAO;QACvB,IAAI,CAACC,SACD,OAAO;QAEX,MAAMC,aAAajB,YACfgB,OAAO,CAAC,yBAAyB,IAAIA,OAAO,CAAC,yBAAyB,EACxE,OAAO,CAAC,eAAe;QACzB,IAAIC,YACA,OAAOA;QAEX,MAAMC,SAASlB,YAAYgB,OAAO,CAAC,gBAAgB,IAAIA,OAAO,CAAC,gBAAgB,EAAE,OAAO,CACpF,eACA;QAEJ,IAAIE,QACA,OAAOA;QAEX,MAAMd,eAAeJ,YAAYgB,OAAO,CAAC,SAAS,IAAIA,OAAO,CAAC,SAAS;QACvE,OAAOb,kBAAkBC,aAAa,CAAC,eAAe,IAAI;IAC9D;AACJ;AAEO,MAAMe,kCAAkCC,qBAAqB,eAAe,CAAC;IAChF,WAAWX;IACX,cAAc;QAACY;QAAcC;KAAsB;AACvD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { IncomingMessage } from "node:http";
|
|
2
|
+
import { NodeHttpEventHandler } from "@webiny/event-handler-standalone";
|
|
3
|
+
import { RawTenantId } from "@webiny/api-core/features/requestContext/index.js";
|
|
4
|
+
import type { IRequestTenantLoader } from "@webiny/api-core/features/requestContext/abstractions.js";
|
|
5
|
+
import type { EventContext, NextFunction } from "@webiny/event-handler-core";
|
|
6
|
+
/**
|
|
7
|
+
* EXTRACT (transport-specific): reads the tenant id from the `x-tenant` header of a Node
|
|
8
|
+
* `IncomingMessage` into RawTenantId, then invokes the shared LOAD step (RequestTenantLoader) which
|
|
9
|
+
* resolves the Tenant and sets TenantContext. A missing header leaves RawTenantId null → the loader
|
|
10
|
+
* defaults to the "root" tenant.
|
|
11
|
+
*
|
|
12
|
+
* Node mirror of ApiGatewayTenantLoaderDecorator. Registered AFTER the identity loader.
|
|
13
|
+
*/
|
|
14
|
+
declare class NodeHttpTenantLoaderDecoratorImpl implements NodeHttpEventHandler.Interface {
|
|
15
|
+
private rawTenantId;
|
|
16
|
+
private tenantLoader;
|
|
17
|
+
private decoratee;
|
|
18
|
+
constructor(rawTenantId: RawTenantId.Interface, tenantLoader: IRequestTenantLoader, decoratee: NodeHttpEventHandler.Interface);
|
|
19
|
+
execute(ctx: EventContext<IncomingMessage>, next: NextFunction): Promise<any>;
|
|
20
|
+
}
|
|
21
|
+
export declare const NodeHttpTenantLoaderDecorator: typeof NodeHttpTenantLoaderDecoratorImpl & {
|
|
22
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-standalone").INodeHttpEventHandler>;
|
|
23
|
+
};
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { NodeHttpEventHandler } from "@webiny/event-handler-standalone";
|
|
2
|
+
import { RawTenantId, RequestTenantLoader } from "@webiny/api-core/features/requestContext/index.js";
|
|
3
|
+
function headerValue(value) {
|
|
4
|
+
if (Array.isArray(value)) return value[0] ?? null;
|
|
5
|
+
return value ?? null;
|
|
6
|
+
}
|
|
7
|
+
class NodeHttpTenantLoaderDecoratorImpl {
|
|
8
|
+
constructor(rawTenantId, tenantLoader, decoratee){
|
|
9
|
+
this.rawTenantId = rawTenantId;
|
|
10
|
+
this.tenantLoader = tenantLoader;
|
|
11
|
+
this.decoratee = decoratee;
|
|
12
|
+
}
|
|
13
|
+
async execute(ctx, next) {
|
|
14
|
+
const headers = ctx.event?.headers;
|
|
15
|
+
this.rawTenantId.set(headers ? headerValue(headers["x-tenant"] ?? headers["X-Tenant"]) : null);
|
|
16
|
+
await this.tenantLoader.establish();
|
|
17
|
+
return this.decoratee.execute(ctx, next);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const NodeHttpTenantLoaderDecorator = NodeHttpEventHandler.createDecorator({
|
|
21
|
+
decorator: NodeHttpTenantLoaderDecoratorImpl,
|
|
22
|
+
dependencies: [
|
|
23
|
+
RawTenantId,
|
|
24
|
+
RequestTenantLoader
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
export { NodeHttpTenantLoaderDecorator };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=NodeHttpTenantLoaderDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handlers/NodeHttpTenantLoaderDecorator.js","sources":["../../src/handlers/NodeHttpTenantLoaderDecorator.ts"],"sourcesContent":["import type { IncomingMessage } from \"node:http\";\nimport { NodeHttpEventHandler } from \"@webiny/event-handler-standalone\";\nimport {\n RawTenantId,\n RequestTenantLoader\n} from \"@webiny/api-core/features/requestContext/index.js\";\nimport type { IRequestTenantLoader } from \"@webiny/api-core/features/requestContext/abstractions.js\";\nimport type { EventContext, NextFunction } from \"@webiny/event-handler-core\";\n\nfunction headerValue(value: string | string[] | undefined): string | null {\n if (Array.isArray(value)) {\n return value[0] ?? null;\n }\n return value ?? null;\n}\n\n/**\n * EXTRACT (transport-specific): reads the tenant id from the `x-tenant` header of a Node\n * `IncomingMessage` into RawTenantId, then invokes the shared LOAD step (RequestTenantLoader) which\n * resolves the Tenant and sets TenantContext. A missing header leaves RawTenantId null → the loader\n * defaults to the \"root\" tenant.\n *\n * Node mirror of ApiGatewayTenantLoaderDecorator. Registered AFTER the identity loader.\n */\nclass NodeHttpTenantLoaderDecoratorImpl implements NodeHttpEventHandler.Interface {\n constructor(\n private rawTenantId: RawTenantId.Interface,\n private tenantLoader: IRequestTenantLoader,\n private decoratee: NodeHttpEventHandler.Interface\n ) {}\n\n async execute(ctx: EventContext<IncomingMessage>, next: NextFunction): Promise<any> {\n const headers = ctx.event?.headers;\n this.rawTenantId.set(\n headers ? headerValue(headers[\"x-tenant\"] ?? headers[\"X-Tenant\"]) : null\n );\n await this.tenantLoader.establish();\n return this.decoratee.execute(ctx, next);\n }\n}\n\nexport const NodeHttpTenantLoaderDecorator = NodeHttpEventHandler.createDecorator({\n decorator: NodeHttpTenantLoaderDecoratorImpl,\n dependencies: [RawTenantId, RequestTenantLoader]\n});\n"],"names":["headerValue","value","Array","NodeHttpTenantLoaderDecoratorImpl","rawTenantId","tenantLoader","decoratee","ctx","next","headers","NodeHttpTenantLoaderDecorator","NodeHttpEventHandler","RawTenantId","RequestTenantLoader"],"mappings":";;AASA,SAASA,YAAYC,KAAoC;IACrD,IAAIC,MAAM,OAAO,CAACD,QACd,OAAOA,KAAK,CAAC,EAAE,IAAI;IAEvB,OAAOA,SAAS;AACpB;AAUA,MAAME;IACF,YACYC,WAAkC,EAClCC,YAAkC,EAClCC,SAAyC,CACnD;aAHUF,WAAW,GAAXA;aACAC,YAAY,GAAZA;aACAC,SAAS,GAATA;IACT;IAEH,MAAM,QAAQC,GAAkC,EAAEC,IAAkB,EAAgB;QAChF,MAAMC,UAAUF,IAAI,KAAK,EAAE;QAC3B,IAAI,CAAC,WAAW,CAAC,GAAG,CAChBE,UAAUT,YAAYS,OAAO,CAAC,WAAW,IAAIA,OAAO,CAAC,WAAW,IAAI;QAExE,MAAM,IAAI,CAAC,YAAY,CAAC,SAAS;QACjC,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAACF,KAAKC;IACvC;AACJ;AAEO,MAAME,gCAAgCC,qBAAqB,eAAe,CAAC;IAC9E,WAAWR;IACX,cAAc;QAACS;QAAaC;KAAoB;AACpD"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createWebinyApiHandler } from "./createWebinyApiHandler.js";
|
|
2
|
+
export type { CreateWebinyApiHandlerConfig } from "./createWebinyApiHandler.js";
|
|
3
|
+
export { NodeHttpIdentityLoaderDecorator } from "./handlers/NodeHttpIdentityLoaderDecorator.js";
|
|
4
|
+
export { NodeHttpTenantLoaderDecorator } from "./handlers/NodeHttpTenantLoaderDecorator.js";
|
|
5
|
+
export { NodeHttpAssumePermissionsDecorator } from "./handlers/NodeHttpAssumePermissionsDecorator.js";
|
package/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { createWebinyApiHandler } from "./createWebinyApiHandler.js";
|
|
2
|
+
export { NodeHttpIdentityLoaderDecorator } from "./handlers/NodeHttpIdentityLoaderDecorator.js";
|
|
3
|
+
export { NodeHttpTenantLoaderDecorator } from "./handlers/NodeHttpTenantLoaderDecorator.js";
|
|
4
|
+
export { NodeHttpAssumePermissionsDecorator } from "./handlers/NodeHttpAssumePermissionsDecorator.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webiny/api-event-handler-standalone",
|
|
3
|
+
"version": "0.0.0-unstable.0156a4c312",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js",
|
|
8
|
+
"./*": "./*"
|
|
9
|
+
},
|
|
10
|
+
"types": "index.d.ts",
|
|
11
|
+
"description": "EXPERIMENTAL: Webiny API handler for the standalone Node HTTP server transport.",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/webiny/webiny-js.git"
|
|
15
|
+
},
|
|
16
|
+
"contributors": [
|
|
17
|
+
"Webiny Team"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@webiny/api-core": "0.0.0-unstable.0156a4c312",
|
|
22
|
+
"@webiny/api-event-handler-core": "0.0.0-unstable.0156a4c312",
|
|
23
|
+
"@webiny/api-file-manager-standalone": "0.0.0-unstable.0156a4c312",
|
|
24
|
+
"@webiny/api-graphql": "0.0.0-unstable.0156a4c312",
|
|
25
|
+
"@webiny/api-headless-cms-bulk-actions-standalone": "0.0.0-unstable.0156a4c312",
|
|
26
|
+
"@webiny/api-scheduler": "0.0.0-unstable.0156a4c312",
|
|
27
|
+
"@webiny/api-scheduler-standalone": "0.0.0-unstable.0156a4c312",
|
|
28
|
+
"@webiny/api-websockets-standalone": "0.0.0-unstable.0156a4c312",
|
|
29
|
+
"@webiny/api-websockets-standalone-redis": "0.0.0-unstable.0156a4c312",
|
|
30
|
+
"@webiny/background-tasks-standalone": "0.0.0-unstable.0156a4c312",
|
|
31
|
+
"@webiny/event-handler-core": "0.0.0-unstable.0156a4c312",
|
|
32
|
+
"@webiny/event-handler-standalone": "0.0.0-unstable.0156a4c312",
|
|
33
|
+
"@webiny/feature": "0.0.0-unstable.0156a4c312",
|
|
34
|
+
"@webiny/handler": "0.0.0-unstable.0156a4c312",
|
|
35
|
+
"@webiny/logger": "0.0.0-unstable.0156a4c312"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@webiny/di": "^2.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@webiny/build-tools": "0.0.0-unstable.0156a4c312",
|
|
42
|
+
"@webiny/di": "2.0.0",
|
|
43
|
+
"rimraf": "6.1.3",
|
|
44
|
+
"typescript": "7.0.2",
|
|
45
|
+
"vitest": "5.0.5"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"webiny": {
|
|
51
|
+
"publishFrom": "dist"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PublicUrls } from "@webiny/api-core/features/publicUrls/index.js";
|
|
2
|
+
import { StandaloneConfig } from "../config/abstractions.js";
|
|
3
|
+
/** Standalone: the URLs come from `WEBINY_API_URL` / `WEBINY_ADMIN_URL`, read at startup. */
|
|
4
|
+
declare class ConfigPublicUrlsImpl implements PublicUrls.Interface {
|
|
5
|
+
private config;
|
|
6
|
+
constructor(config: StandaloneConfig.Interface);
|
|
7
|
+
getApiUrl(): Promise<import("@webiny/api-core/features/publicUrls/PublicUrl").PublicUrl>;
|
|
8
|
+
getAdminUrl(): Promise<import("@webiny/api-core/features/publicUrls/PublicUrl").PublicUrl>;
|
|
9
|
+
}
|
|
10
|
+
export declare const ConfigPublicUrls: typeof ConfigPublicUrlsImpl & {
|
|
11
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/publicUrls/abstractions").IPublicUrls>;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PublicUrls } from "@webiny/api-core/features/publicUrls/index.js";
|
|
2
|
+
import { StandaloneConfig } from "../config/abstractions.js";
|
|
3
|
+
class ConfigPublicUrlsImpl {
|
|
4
|
+
constructor(config){
|
|
5
|
+
this.config = config;
|
|
6
|
+
}
|
|
7
|
+
async getApiUrl() {
|
|
8
|
+
return this.config.apiUrl;
|
|
9
|
+
}
|
|
10
|
+
async getAdminUrl() {
|
|
11
|
+
return this.config.adminUrl;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const ConfigPublicUrls = PublicUrls.createImplementation({
|
|
15
|
+
implementation: ConfigPublicUrlsImpl,
|
|
16
|
+
dependencies: [
|
|
17
|
+
StandaloneConfig
|
|
18
|
+
]
|
|
19
|
+
});
|
|
20
|
+
export { ConfigPublicUrls };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=ConfigPublicUrls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicUrls/ConfigPublicUrls.js","sources":["../../src/publicUrls/ConfigPublicUrls.ts"],"sourcesContent":["import { PublicUrls } from \"@webiny/api-core/features/publicUrls/index.js\";\nimport { StandaloneConfig } from \"~/config/abstractions.js\";\n\n/** Standalone: the URLs come from `WEBINY_API_URL` / `WEBINY_ADMIN_URL`, read at startup. */\nclass ConfigPublicUrlsImpl implements PublicUrls.Interface {\n constructor(private config: StandaloneConfig.Interface) {}\n\n async getApiUrl() {\n return this.config.apiUrl;\n }\n\n async getAdminUrl() {\n return this.config.adminUrl;\n }\n}\n\nexport const ConfigPublicUrls = PublicUrls.createImplementation({\n implementation: ConfigPublicUrlsImpl,\n dependencies: [StandaloneConfig]\n});\n"],"names":["ConfigPublicUrlsImpl","config","ConfigPublicUrls","PublicUrls","StandaloneConfig"],"mappings":";;AAIA,MAAMA;IACF,YAAoBC,MAAkC,CAAE;aAApCA,MAAM,GAANA;IAAqC;IAEzD,MAAM,YAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM;IAC7B;IAEA,MAAM,cAAc;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ;IAC/B;AACJ;AAEO,MAAMC,mBAAmBC,WAAW,oBAAoB,CAAC;IAC5D,gBAAgBH;IAChB,cAAc;QAACI;KAAiB;AACpC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createFeature } from "@webiny/feature/api";
|
|
2
|
+
import { ConfigPublicUrls } from "./ConfigPublicUrls.js";
|
|
3
|
+
const ConfigPublicUrlsFeature = createFeature({
|
|
4
|
+
name: "ConfigPublicUrlsFeature",
|
|
5
|
+
register (container) {
|
|
6
|
+
container.register(ConfigPublicUrls);
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
export { ConfigPublicUrlsFeature };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicUrls/feature.js","sources":["../../src/publicUrls/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { ConfigPublicUrls } from \"./ConfigPublicUrls.js\";\n\nexport const ConfigPublicUrlsFeature = createFeature({\n name: \"ConfigPublicUrlsFeature\",\n register(container) {\n container.register(ConfigPublicUrls);\n }\n});\n"],"names":["ConfigPublicUrlsFeature","createFeature","container","ConfigPublicUrls"],"mappings":";;AAGO,MAAMA,0BAA0BC,cAAc;IACjD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;IACvB;AACJ"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ListTenantsUseCase } from "@webiny/api-core/features/tenancy/ListTenants/index.js";
|
|
2
|
+
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
3
|
+
import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
|
|
4
|
+
import { Logger } from "@webiny/api-core/features/logger/abstractions.js";
|
|
5
|
+
import { ListScheduledActionsUseCase } from "@webiny/api-scheduler/features/ListScheduledActions/index.js";
|
|
6
|
+
import { ScheduledActionRecoverEventHandler } from "./ScheduledActionRecoverEventHandler.js";
|
|
7
|
+
import { SchedulerSingleton } from "./abstractions/SchedulerSingleton.js";
|
|
8
|
+
/**
|
|
9
|
+
* Re-arms every tenant's pending scheduled actions in the Bree singleton. Bree keeps its timers in
|
|
10
|
+
* memory, so a restart loses them all. Overdue actions run at once (see
|
|
11
|
+
* `BreeSchedulerService.recover`).
|
|
12
|
+
*
|
|
13
|
+
* Each tenant is recovered on its own: one that fails is logged and reported in the result, and the
|
|
14
|
+
* rest are still recovered. A fresh install has no tenants yet and recovers nothing.
|
|
15
|
+
*/
|
|
16
|
+
declare class RecoverScheduledActionsHandlerImpl implements ScheduledActionRecoverEventHandler.Interface {
|
|
17
|
+
private readonly listTenants;
|
|
18
|
+
private readonly tenantContext;
|
|
19
|
+
private readonly identityContext;
|
|
20
|
+
private readonly listScheduledActions;
|
|
21
|
+
private readonly scheduler;
|
|
22
|
+
private readonly logger;
|
|
23
|
+
constructor(listTenants: ListTenantsUseCase.Interface, tenantContext: TenantContext.Interface, identityContext: IdentityContext.Interface, listScheduledActions: ListScheduledActionsUseCase.Interface, scheduler: SchedulerSingleton.Interface, logger: Logger.Interface);
|
|
24
|
+
execute(): Promise<ScheduledActionRecoverEventHandler.Result>;
|
|
25
|
+
private recoverTenant;
|
|
26
|
+
private recoverAllPending;
|
|
27
|
+
}
|
|
28
|
+
export declare const RecoverScheduledActionsHandler: typeof RecoverScheduledActionsHandlerImpl & {
|
|
29
|
+
__abstraction: import("@webiny/di").Abstraction<import("./ScheduledActionRecoverEventHandler.js").IScheduledActionRecoverEventHandler>;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ListTenantsUseCase } from "@webiny/api-core/features/tenancy/ListTenants/index.js";
|
|
2
|
+
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
3
|
+
import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
|
|
4
|
+
import { Logger } from "@webiny/api-core/features/logger/abstractions.js";
|
|
5
|
+
import { ListScheduledActionsUseCase } from "@webiny/api-scheduler/features/ListScheduledActions/index.js";
|
|
6
|
+
import { ScheduledActionRecoverEventHandler } from "./ScheduledActionRecoverEventHandler.js";
|
|
7
|
+
import { SchedulerSingleton } from "./abstractions/SchedulerSingleton.js";
|
|
8
|
+
const PAGE_SIZE = 1000;
|
|
9
|
+
function canRecover(tenant) {
|
|
10
|
+
return tenant.isInstalled && "disabled" !== tenant.status;
|
|
11
|
+
}
|
|
12
|
+
class RecoverScheduledActionsHandlerImpl {
|
|
13
|
+
constructor(listTenants, tenantContext, identityContext, listScheduledActions, scheduler, logger){
|
|
14
|
+
this.listTenants = listTenants;
|
|
15
|
+
this.tenantContext = tenantContext;
|
|
16
|
+
this.identityContext = identityContext;
|
|
17
|
+
this.listScheduledActions = listScheduledActions;
|
|
18
|
+
this.scheduler = scheduler;
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
}
|
|
21
|
+
async execute() {
|
|
22
|
+
const tenantsResult = await this.listTenants.execute();
|
|
23
|
+
if (tenantsResult.isFail()) throw tenantsResult.error;
|
|
24
|
+
let recovered = 0;
|
|
25
|
+
const failedTenants = [];
|
|
26
|
+
for (const tenant of tenantsResult.value)if (canRecover(tenant)) try {
|
|
27
|
+
recovered += await this.recoverTenant(tenant);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
failedTenants.push(tenant.id);
|
|
30
|
+
this.logger.error({
|
|
31
|
+
error,
|
|
32
|
+
tenant: tenant.id
|
|
33
|
+
}, "Could not recover the tenant's scheduled actions.");
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
recovered,
|
|
37
|
+
failedTenants
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
recoverTenant(tenant) {
|
|
41
|
+
return this.tenantContext.withTenant(tenant, ()=>this.identityContext.withoutAuthorization(()=>this.recoverAllPending(tenant.id)));
|
|
42
|
+
}
|
|
43
|
+
async recoverAllPending(tenant) {
|
|
44
|
+
let recovered = 0;
|
|
45
|
+
let after;
|
|
46
|
+
do {
|
|
47
|
+
const listResult = await this.listScheduledActions.execute({
|
|
48
|
+
where: {},
|
|
49
|
+
limit: PAGE_SIZE,
|
|
50
|
+
after
|
|
51
|
+
});
|
|
52
|
+
if (listResult.isFail()) throw listResult.error;
|
|
53
|
+
const page = [];
|
|
54
|
+
for (const action of listResult.value.items)page.push({
|
|
55
|
+
id: action.id,
|
|
56
|
+
namespace: action.namespace,
|
|
57
|
+
tenant,
|
|
58
|
+
scheduledFor: action.scheduledFor
|
|
59
|
+
});
|
|
60
|
+
await this.scheduler.recover(page);
|
|
61
|
+
recovered += page.length;
|
|
62
|
+
const { meta } = listResult.value;
|
|
63
|
+
after = meta.hasMoreItems && meta.cursor ? meta.cursor : void 0;
|
|
64
|
+
}while (after)
|
|
65
|
+
return recovered;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const RecoverScheduledActionsHandler = ScheduledActionRecoverEventHandler.createImplementation({
|
|
69
|
+
implementation: RecoverScheduledActionsHandlerImpl,
|
|
70
|
+
dependencies: [
|
|
71
|
+
ListTenantsUseCase,
|
|
72
|
+
TenantContext,
|
|
73
|
+
IdentityContext,
|
|
74
|
+
ListScheduledActionsUseCase,
|
|
75
|
+
SchedulerSingleton,
|
|
76
|
+
Logger
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
export { RecoverScheduledActionsHandler };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=RecoverScheduledActionsHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler/RecoverScheduledActionsHandler.js","sources":["../../src/scheduler/RecoverScheduledActionsHandler.ts"],"sourcesContent":["import { ListTenantsUseCase } from \"@webiny/api-core/features/tenancy/ListTenants/index.js\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport type { Tenant } from \"@webiny/api-core/types/tenancy.js\";\nimport { IdentityContext } from \"@webiny/api-core/exports/api/security.js\";\nimport { Logger } from \"@webiny/api-core/features/logger/abstractions.js\";\nimport { ListScheduledActionsUseCase } from \"@webiny/api-scheduler/features/ListScheduledActions/index.js\";\nimport { ScheduledActionRecoverEventHandler } from \"./ScheduledActionRecoverEventHandler.js\";\nimport type { IPendingAction } from \"@webiny/api-scheduler-standalone\";\nimport { SchedulerSingleton } from \"./abstractions/SchedulerSingleton.js\";\n\n// Pending actions read per page.\nconst PAGE_SIZE = 1000;\n\n/*\n * A tenant that isn't installed has no scheduler model to read, and a disabled one must not run its\n * schedules. Both are left alone; a disabled tenant's schedules come back on the next restart after\n * it is re-enabled.\n */\nfunction canRecover(tenant: Tenant): boolean {\n return tenant.isInstalled && tenant.status !== \"disabled\";\n}\n\n/**\n * Re-arms every tenant's pending scheduled actions in the Bree singleton. Bree keeps its timers in\n * memory, so a restart loses them all. Overdue actions run at once (see\n * `BreeSchedulerService.recover`).\n *\n * Each tenant is recovered on its own: one that fails is logged and reported in the result, and the\n * rest are still recovered. A fresh install has no tenants yet and recovers nothing.\n */\nclass RecoverScheduledActionsHandlerImpl implements ScheduledActionRecoverEventHandler.Interface {\n public constructor(\n private readonly listTenants: ListTenantsUseCase.Interface,\n private readonly tenantContext: TenantContext.Interface,\n private readonly identityContext: IdentityContext.Interface,\n private readonly listScheduledActions: ListScheduledActionsUseCase.Interface,\n private readonly scheduler: SchedulerSingleton.Interface,\n private readonly logger: Logger.Interface\n ) {}\n\n public async execute(): Promise<ScheduledActionRecoverEventHandler.Result> {\n // A failed lookup is not a fresh install: it throws, so the boot step reports it.\n const tenantsResult = await this.listTenants.execute();\n if (tenantsResult.isFail()) {\n throw tenantsResult.error;\n }\n\n let recovered = 0;\n const failedTenants: string[] = [];\n\n for (const tenant of tenantsResult.value) {\n if (!canRecover(tenant)) {\n continue;\n }\n try {\n recovered += await this.recoverTenant(tenant);\n } catch (error) {\n failedTenants.push(tenant.id);\n this.logger.error(\n { error, tenant: tenant.id },\n \"Could not recover the tenant's scheduled actions.\"\n );\n }\n }\n\n return { recovered, failedTenants };\n }\n\n private recoverTenant(tenant: Tenant): Promise<number> {\n return this.tenantContext.withTenant(tenant, () => {\n /*\n * Nobody is signed in at boot. Listing without authorization is what the run side does\n * too (ExecuteScheduledActionUseCase); otherwise the anonymous identity fails the\n * permission check.\n */\n return this.identityContext.withoutAuthorization(() => {\n return this.recoverAllPending(tenant.id);\n });\n });\n }\n\n /*\n * Re-arms page by page, so a lookup that fails on a later page still leaves the earlier pages\n * armed. The failure is rethrown and logged by the boot step.\n */\n private async recoverAllPending(tenant: string): Promise<number> {\n let recovered = 0;\n let after: string | undefined;\n\n do {\n const listResult = await this.listScheduledActions.execute({\n where: {},\n limit: PAGE_SIZE,\n after\n });\n if (listResult.isFail()) {\n throw listResult.error;\n }\n\n const page: IPendingAction[] = [];\n for (const action of listResult.value.items) {\n page.push({\n id: action.id,\n namespace: action.namespace,\n tenant,\n scheduledFor: action.scheduledFor\n });\n }\n\n await this.scheduler.recover(page);\n recovered += page.length;\n\n const { meta } = listResult.value;\n after = meta.hasMoreItems && meta.cursor ? meta.cursor : undefined;\n } while (after);\n\n return recovered;\n }\n}\n\nexport const RecoverScheduledActionsHandler =\n ScheduledActionRecoverEventHandler.createImplementation({\n implementation: RecoverScheduledActionsHandlerImpl,\n dependencies: [\n ListTenantsUseCase,\n TenantContext,\n IdentityContext,\n ListScheduledActionsUseCase,\n SchedulerSingleton,\n Logger\n ]\n });\n"],"names":["PAGE_SIZE","canRecover","tenant","RecoverScheduledActionsHandlerImpl","listTenants","tenantContext","identityContext","listScheduledActions","scheduler","logger","tenantsResult","recovered","failedTenants","error","after","listResult","page","action","meta","undefined","RecoverScheduledActionsHandler","ScheduledActionRecoverEventHandler","ListTenantsUseCase","TenantContext","IdentityContext","ListScheduledActionsUseCase","SchedulerSingleton","Logger"],"mappings":";;;;;;;AAWA,MAAMA,YAAY;AAOlB,SAASC,WAAWC,MAAc;IAC9B,OAAOA,OAAO,WAAW,IAAIA,AAAkB,eAAlBA,OAAO,MAAM;AAC9C;AAUA,MAAMC;IACF,YACqBC,WAAyC,EACzCC,aAAsC,EACtCC,eAA0C,EAC1CC,oBAA2D,EAC3DC,SAAuC,EACvCC,MAAwB,CAC3C;aANmBL,WAAW,GAAXA;aACAC,aAAa,GAAbA;aACAC,eAAe,GAAfA;aACAC,oBAAoB,GAApBA;aACAC,SAAS,GAATA;aACAC,MAAM,GAANA;IAClB;IAEH,MAAa,UAA8D;QAEvE,MAAMC,gBAAgB,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO;QACpD,IAAIA,cAAc,MAAM,IACpB,MAAMA,cAAc,KAAK;QAG7B,IAAIC,YAAY;QAChB,MAAMC,gBAA0B,EAAE;QAElC,KAAK,MAAMV,UAAUQ,cAAc,KAAK,CACpC,IAAKT,WAAWC,SAGhB,IAAI;YACAS,aAAa,MAAM,IAAI,CAAC,aAAa,CAACT;QAC1C,EAAE,OAAOW,OAAO;YACZD,cAAc,IAAI,CAACV,OAAO,EAAE;YAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,CACb;gBAAEW;gBAAO,QAAQX,OAAO,EAAE;YAAC,GAC3B;QAER;QAGJ,OAAO;YAAES;YAAWC;QAAc;IACtC;IAEQ,cAAcV,MAAc,EAAmB;QACnD,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,CAACA,QAAQ,IAMlC,IAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC,IACtC,IAAI,CAAC,iBAAiB,CAACA,OAAO,EAAE;IAGnD;IAMA,MAAc,kBAAkBA,MAAc,EAAmB;QAC7D,IAAIS,YAAY;QAChB,IAAIG;QAEJ,GAAG;YACC,MAAMC,aAAa,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC;gBACvD,OAAO,CAAC;gBACR,OAAOf;gBACPc;YACJ;YACA,IAAIC,WAAW,MAAM,IACjB,MAAMA,WAAW,KAAK;YAG1B,MAAMC,OAAyB,EAAE;YACjC,KAAK,MAAMC,UAAUF,WAAW,KAAK,CAAC,KAAK,CACvCC,KAAK,IAAI,CAAC;gBACN,IAAIC,OAAO,EAAE;gBACb,WAAWA,OAAO,SAAS;gBAC3Bf;gBACA,cAAce,OAAO,YAAY;YACrC;YAGJ,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAACD;YAC7BL,aAAaK,KAAK,MAAM;YAExB,MAAM,EAAEE,IAAI,EAAE,GAAGH,WAAW,KAAK;YACjCD,QAAQI,KAAK,YAAY,IAAIA,KAAK,MAAM,GAAGA,KAAK,MAAM,GAAGC;QAC7D,QAASL,MAAO;QAEhB,OAAOH;IACX;AACJ;AAEO,MAAMS,iCACTC,mCAAmC,oBAAoB,CAAC;IACpD,gBAAgBlB;IAChB,cAAc;QACVmB;QACAC;QACAC;QACAC;QACAC;QACAC;KACH;AACL"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ScheduledActionEventHandler } from "@webiny/event-handler-core";
|
|
2
|
+
import type { IScheduledActionEvent } from "@webiny/event-handler-core";
|
|
3
|
+
import type { IScheduledActionResult } from "@webiny/event-handler-core";
|
|
4
|
+
import type { EventContext } from "@webiny/event-handler-core";
|
|
5
|
+
import type { NextFunction } from "@webiny/event-handler-core";
|
|
6
|
+
import { GetTenantByIdUseCase } from "@webiny/api-core/exports/api/tenancy.js";
|
|
7
|
+
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
8
|
+
import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
|
|
9
|
+
import { GlobalKeyValueStore } from "@webiny/api-core/features/keyValueStore/index.js";
|
|
10
|
+
import { GetScheduledActionUseCase } from "@webiny/api-scheduler/features/GetScheduledAction/index.js";
|
|
11
|
+
/**
|
|
12
|
+
* Runs a fired scheduled action at most once across all processes. Every standalone process holds its
|
|
13
|
+
* own timers for the same actions, so the same action fires in each of them; on AWS, EventBridge
|
|
14
|
+
* fires it once and this decorator is not registered.
|
|
15
|
+
*
|
|
16
|
+
* Before the action runs, it is read again. Gone means it was cancelled or already ran. Due later
|
|
17
|
+
* means another process moved it and this timer is stale. Otherwise the process takes a claim on the
|
|
18
|
+
* action at its due time, and only the process that gets it runs the action. Rescheduling changes
|
|
19
|
+
* the due time, so a moved action gets a fresh claim.
|
|
20
|
+
*/
|
|
21
|
+
declare class RunScheduledActionOnceDecoratorImpl implements ScheduledActionEventHandler.Interface {
|
|
22
|
+
private readonly getTenantById;
|
|
23
|
+
private readonly tenantContext;
|
|
24
|
+
private readonly identityContext;
|
|
25
|
+
private readonly getScheduledAction;
|
|
26
|
+
private readonly store;
|
|
27
|
+
private readonly decoratee;
|
|
28
|
+
constructor(getTenantById: GetTenantByIdUseCase.Interface, tenantContext: TenantContext.Interface, identityContext: IdentityContext.Interface, getScheduledAction: GetScheduledActionUseCase.Interface, store: GlobalKeyValueStore.Interface, decoratee: ScheduledActionEventHandler.Interface);
|
|
29
|
+
execute(eventCtx: EventContext<IScheduledActionEvent>, next: NextFunction): Promise<IScheduledActionResult>;
|
|
30
|
+
private findDueTime;
|
|
31
|
+
private takeClaim;
|
|
32
|
+
private holdAfterFailure;
|
|
33
|
+
}
|
|
34
|
+
export declare const RunScheduledActionOnceDecorator: typeof RunScheduledActionOnceDecoratorImpl & {
|
|
35
|
+
__abstraction: import("@webiny/di").Abstraction<import("@webiny/event-handler-core/features/scheduledAction/ScheduledActionEventHandler").IScheduledActionEventHandler>;
|
|
36
|
+
};
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { hostname } from "node:os";
|
|
2
|
+
import { SCHEDULED_ACTION_EVENT_IDENTIFIER, ScheduledActionEventHandler } from "@webiny/event-handler-core";
|
|
3
|
+
import { GetTenantByIdUseCase } from "@webiny/api-core/exports/api/tenancy.js";
|
|
4
|
+
import { TenantContext } from "@webiny/api-core/features/tenancy/TenantContext/index.js";
|
|
5
|
+
import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
|
|
6
|
+
import { GlobalKeyValueStore, KeyValueConditionFailedError } from "@webiny/api-core/features/keyValueStore/index.js";
|
|
7
|
+
import { GetScheduledActionUseCase } from "@webiny/api-scheduler/features/GetScheduledAction/index.js";
|
|
8
|
+
const CLAIM_SCOPE = "scheduler/claims";
|
|
9
|
+
const RUN_CLAIM_MS = 600000;
|
|
10
|
+
const RETRY_AFTER_FAILURE_MS = 86400000;
|
|
11
|
+
const EARLY_TOLERANCE_MS = 1000;
|
|
12
|
+
const SUCCESS = {
|
|
13
|
+
success: true
|
|
14
|
+
};
|
|
15
|
+
const PROCESS_NAME = `${hostname()}:${process.pid}`;
|
|
16
|
+
const expiresIn = (ms)=>{
|
|
17
|
+
const expiresAtMs = Date.now() + ms;
|
|
18
|
+
return new Date(expiresAtMs);
|
|
19
|
+
};
|
|
20
|
+
class RunScheduledActionOnceDecoratorImpl {
|
|
21
|
+
constructor(getTenantById, tenantContext, identityContext, getScheduledAction, store, decoratee){
|
|
22
|
+
this.getTenantById = getTenantById;
|
|
23
|
+
this.tenantContext = tenantContext;
|
|
24
|
+
this.identityContext = identityContext;
|
|
25
|
+
this.getScheduledAction = getScheduledAction;
|
|
26
|
+
this.store = store;
|
|
27
|
+
this.decoratee = decoratee;
|
|
28
|
+
}
|
|
29
|
+
async execute(eventCtx, next) {
|
|
30
|
+
const input = eventCtx.event[SCHEDULED_ACTION_EVENT_IDENTIFIER];
|
|
31
|
+
const tenantId = input.tenant || this.tenantContext.getTenant().id;
|
|
32
|
+
const dueAt = await this.findDueTime(tenantId, input);
|
|
33
|
+
if (!dueAt) return SUCCESS;
|
|
34
|
+
if (dueAt.getTime() > Date.now() + EARLY_TOLERANCE_MS) return SUCCESS;
|
|
35
|
+
const key = `${tenantId}/${input.id}/${dueAt.toISOString()}`;
|
|
36
|
+
const claimed = await this.takeClaim(key);
|
|
37
|
+
if (!claimed) return SUCCESS;
|
|
38
|
+
try {
|
|
39
|
+
return await this.decoratee.execute(eventCtx, next);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
await this.holdAfterFailure(key);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async findDueTime(tenantId, input) {
|
|
46
|
+
const tenantResult = await this.getTenantById.execute(tenantId);
|
|
47
|
+
if (tenantResult.isFail()) throw tenantResult.error;
|
|
48
|
+
return this.tenantContext.withTenant(tenantResult.value, ()=>this.identityContext.withoutAuthorization(async ()=>{
|
|
49
|
+
const result = await this.getScheduledAction.execute({
|
|
50
|
+
id: input.id,
|
|
51
|
+
namespace: input.namespace
|
|
52
|
+
});
|
|
53
|
+
if (result.isOk()) return result.value.scheduledFor;
|
|
54
|
+
if ("Scheduler/ScheduledAction/NotFound" === result.error.code) return null;
|
|
55
|
+
throw result.error;
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
async takeClaim(key) {
|
|
59
|
+
const expiresAt = expiresIn(RUN_CLAIM_MS);
|
|
60
|
+
const result = await this.store.set(key, PROCESS_NAME, {
|
|
61
|
+
scope: CLAIM_SCOPE,
|
|
62
|
+
expiresAt,
|
|
63
|
+
condition: {
|
|
64
|
+
exists: false
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
if (result.isOk()) return true;
|
|
68
|
+
if (result.error instanceof KeyValueConditionFailedError) return false;
|
|
69
|
+
throw result.error;
|
|
70
|
+
}
|
|
71
|
+
async holdAfterFailure(key) {
|
|
72
|
+
const expiresAt = expiresIn(RETRY_AFTER_FAILURE_MS);
|
|
73
|
+
await this.store.set(key, PROCESS_NAME, {
|
|
74
|
+
scope: CLAIM_SCOPE,
|
|
75
|
+
expiresAt
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const RunScheduledActionOnceDecorator = ScheduledActionEventHandler.createDecorator({
|
|
80
|
+
decorator: RunScheduledActionOnceDecoratorImpl,
|
|
81
|
+
dependencies: [
|
|
82
|
+
GetTenantByIdUseCase,
|
|
83
|
+
TenantContext,
|
|
84
|
+
IdentityContext,
|
|
85
|
+
GetScheduledActionUseCase,
|
|
86
|
+
GlobalKeyValueStore
|
|
87
|
+
]
|
|
88
|
+
});
|
|
89
|
+
export { RunScheduledActionOnceDecorator };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=RunScheduledActionOnceDecorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler/RunScheduledActionOnceDecorator.js","sources":["../../src/scheduler/RunScheduledActionOnceDecorator.ts"],"sourcesContent":["import { hostname } from \"node:os\";\nimport { ScheduledActionEventHandler } from \"@webiny/event-handler-core\";\nimport { SCHEDULED_ACTION_EVENT_IDENTIFIER } from \"@webiny/event-handler-core\";\nimport type { IScheduledActionEvent } from \"@webiny/event-handler-core\";\nimport type { IScheduledActionEventPayload } from \"@webiny/event-handler-core\";\nimport type { IScheduledActionResult } from \"@webiny/event-handler-core\";\nimport type { EventContext } from \"@webiny/event-handler-core\";\nimport type { NextFunction } from \"@webiny/event-handler-core\";\nimport { GetTenantByIdUseCase } from \"@webiny/api-core/exports/api/tenancy.js\";\nimport { TenantContext } from \"@webiny/api-core/features/tenancy/TenantContext/index.js\";\nimport { IdentityContext } from \"@webiny/api-core/exports/api/security.js\";\nimport { GlobalKeyValueStore } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { KeyValueConditionFailedError } from \"@webiny/api-core/features/keyValueStore/index.js\";\nimport { GetScheduledActionUseCase } from \"@webiny/api-scheduler/features/GetScheduledAction/index.js\";\n\nconst CLAIM_SCOPE = \"scheduler/claims\";\n\n/*\n * How long one run of an action holds its claim. Long enough for any action to finish; a run that\n * succeeds deletes the action, so the claim only matters while it runs.\n */\nconst RUN_CLAIM_MS = 10 * 60_000;\n\n// After a failed run, other processes leave the action alone this long before trying it again.\nconst RETRY_AFTER_FAILURE_MS = 24 * 60 * 60_000;\n\n/*\n * Timers fire slightly early or late; an action due within this much is run. One due later than that\n * belongs to a timer another process moved, and this firing is stale.\n */\nconst EARLY_TOLERANCE_MS = 1000;\n\nconst SUCCESS: IScheduledActionResult = { success: true };\n\n// Names this process in the claims it writes, for whoever reads them in the database.\nconst PROCESS_NAME = `${hostname()}:${process.pid}`;\n\nconst expiresIn = (ms: number): Date => {\n const expiresAtMs = Date.now() + ms;\n return new Date(expiresAtMs);\n};\n\n/**\n * Runs a fired scheduled action at most once across all processes. Every standalone process holds its\n * own timers for the same actions, so the same action fires in each of them; on AWS, EventBridge\n * fires it once and this decorator is not registered.\n *\n * Before the action runs, it is read again. Gone means it was cancelled or already ran. Due later\n * means another process moved it and this timer is stale. Otherwise the process takes a claim on the\n * action at its due time, and only the process that gets it runs the action. Rescheduling changes\n * the due time, so a moved action gets a fresh claim.\n */\nclass RunScheduledActionOnceDecoratorImpl implements ScheduledActionEventHandler.Interface {\n public constructor(\n private readonly getTenantById: GetTenantByIdUseCase.Interface,\n private readonly tenantContext: TenantContext.Interface,\n private readonly identityContext: IdentityContext.Interface,\n private readonly getScheduledAction: GetScheduledActionUseCase.Interface,\n private readonly store: GlobalKeyValueStore.Interface,\n private readonly decoratee: ScheduledActionEventHandler.Interface\n ) {}\n\n public async execute(\n eventCtx: EventContext<IScheduledActionEvent>,\n next: NextFunction\n ): Promise<IScheduledActionResult> {\n const input = eventCtx.event[SCHEDULED_ACTION_EVENT_IDENTIFIER];\n const tenantId = input.tenant || this.tenantContext.getTenant().id;\n\n const dueAt = await this.findDueTime(tenantId, input);\n if (!dueAt) {\n return SUCCESS;\n }\n if (dueAt.getTime() > Date.now() + EARLY_TOLERANCE_MS) {\n return SUCCESS;\n }\n\n const key = `${tenantId}/${input.id}/${dueAt.toISOString()}`;\n const claimed = await this.takeClaim(key);\n if (!claimed) {\n return SUCCESS;\n }\n\n try {\n return await this.decoratee.execute(eventCtx, next);\n } catch (error) {\n await this.holdAfterFailure(key);\n throw error;\n }\n }\n\n // The action's due time, or `null` when it no longer exists.\n private async findDueTime(\n tenantId: string,\n input: IScheduledActionEventPayload\n ): Promise<Date | null> {\n const tenantResult = await this.getTenantById.execute(tenantId);\n if (tenantResult.isFail()) {\n throw tenantResult.error;\n }\n\n return this.tenantContext.withTenant(tenantResult.value, () => {\n // GetScheduledAction checks permissions, and nobody is signed in when a timer fires.\n return this.identityContext.withoutAuthorization(async () => {\n const result = await this.getScheduledAction.execute({\n id: input.id,\n namespace: input.namespace\n });\n if (result.isOk()) {\n return result.value.scheduledFor;\n }\n if (result.error.code === \"Scheduler/ScheduledAction/NotFound\") {\n return null;\n }\n throw result.error;\n });\n });\n }\n\n private async takeClaim(key: string): Promise<boolean> {\n const expiresAt = expiresIn(RUN_CLAIM_MS);\n const result = await this.store.set(key, PROCESS_NAME, {\n scope: CLAIM_SCOPE,\n expiresAt,\n condition: { exists: false }\n });\n if (result.isOk()) {\n return true;\n }\n if (result.error instanceof KeyValueConditionFailedError) {\n return false;\n }\n throw result.error;\n }\n\n /*\n * The action stays stored after a failed run, with the error on it, and every process's\n * recovery fires overdue actions each minute. Holding the claim keeps that to one retry a day.\n */\n private async holdAfterFailure(key: string): Promise<void> {\n const expiresAt = expiresIn(RETRY_AFTER_FAILURE_MS);\n await this.store.set(key, PROCESS_NAME, { scope: CLAIM_SCOPE, expiresAt });\n }\n}\n\nexport const RunScheduledActionOnceDecorator = ScheduledActionEventHandler.createDecorator({\n decorator: RunScheduledActionOnceDecoratorImpl,\n dependencies: [\n GetTenantByIdUseCase,\n TenantContext,\n IdentityContext,\n GetScheduledActionUseCase,\n GlobalKeyValueStore\n ]\n});\n"],"names":["CLAIM_SCOPE","RUN_CLAIM_MS","RETRY_AFTER_FAILURE_MS","EARLY_TOLERANCE_MS","SUCCESS","PROCESS_NAME","hostname","process","expiresIn","ms","expiresAtMs","Date","RunScheduledActionOnceDecoratorImpl","getTenantById","tenantContext","identityContext","getScheduledAction","store","decoratee","eventCtx","next","input","SCHEDULED_ACTION_EVENT_IDENTIFIER","tenantId","dueAt","key","claimed","error","tenantResult","result","expiresAt","KeyValueConditionFailedError","RunScheduledActionOnceDecorator","ScheduledActionEventHandler","GetTenantByIdUseCase","TenantContext","IdentityContext","GetScheduledActionUseCase","GlobalKeyValueStore"],"mappings":";;;;;;;AAeA,MAAMA,cAAc;AAMpB,MAAMC,eAAe;AAGrB,MAAMC,yBAAyB;AAM/B,MAAMC,qBAAqB;AAE3B,MAAMC,UAAkC;IAAE,SAAS;AAAK;AAGxD,MAAMC,eAAe,GAAGC,WAAW,CAAC,EAAEC,QAAQ,GAAG,EAAE;AAEnD,MAAMC,YAAY,CAACC;IACf,MAAMC,cAAcC,KAAK,GAAG,KAAKF;IACjC,OAAO,IAAIE,KAAKD;AACpB;AAYA,MAAME;IACF,YACqBC,aAA6C,EAC7CC,aAAsC,EACtCC,eAA0C,EAC1CC,kBAAuD,EACvDC,KAAoC,EACpCC,SAAgD,CACnE;aANmBL,aAAa,GAAbA;aACAC,aAAa,GAAbA;aACAC,eAAe,GAAfA;aACAC,kBAAkB,GAAlBA;aACAC,KAAK,GAALA;aACAC,SAAS,GAATA;IAClB;IAEH,MAAa,QACTC,QAA6C,EAC7CC,IAAkB,EACa;QAC/B,MAAMC,QAAQF,SAAS,KAAK,CAACG,kCAAkC;QAC/D,MAAMC,WAAWF,MAAM,MAAM,IAAI,IAAI,CAAC,aAAa,CAAC,SAAS,GAAG,EAAE;QAElE,MAAMG,QAAQ,MAAM,IAAI,CAAC,WAAW,CAACD,UAAUF;QAC/C,IAAI,CAACG,OACD,OAAOpB;QAEX,IAAIoB,MAAM,OAAO,KAAKb,KAAK,GAAG,KAAKR,oBAC/B,OAAOC;QAGX,MAAMqB,MAAM,GAAGF,SAAS,CAAC,EAAEF,MAAM,EAAE,CAAC,CAAC,EAAEG,MAAM,WAAW,IAAI;QAC5D,MAAME,UAAU,MAAM,IAAI,CAAC,SAAS,CAACD;QACrC,IAAI,CAACC,SACD,OAAOtB;QAGX,IAAI;YACA,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAACe,UAAUC;QAClD,EAAE,OAAOO,OAAO;YACZ,MAAM,IAAI,CAAC,gBAAgB,CAACF;YAC5B,MAAME;QACV;IACJ;IAGA,MAAc,YACVJ,QAAgB,EAChBF,KAAmC,EACf;QACpB,MAAMO,eAAe,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAACL;QACtD,IAAIK,aAAa,MAAM,IACnB,MAAMA,aAAa,KAAK;QAG5B,OAAO,IAAI,CAAC,aAAa,CAAC,UAAU,CAACA,aAAa,KAAK,EAAE,IAE9C,IAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC;gBAC7C,MAAMC,SAAS,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC;oBACjD,IAAIR,MAAM,EAAE;oBACZ,WAAWA,MAAM,SAAS;gBAC9B;gBACA,IAAIQ,OAAO,IAAI,IACX,OAAOA,OAAO,KAAK,CAAC,YAAY;gBAEpC,IAAIA,AAAsB,yCAAtBA,OAAO,KAAK,CAAC,IAAI,EACjB,OAAO;gBAEX,MAAMA,OAAO,KAAK;YACtB;IAER;IAEA,MAAc,UAAUJ,GAAW,EAAoB;QACnD,MAAMK,YAAYtB,UAAUP;QAC5B,MAAM4B,SAAS,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAACJ,KAAKpB,cAAc;YACnD,OAAOL;YACP8B;YACA,WAAW;gBAAE,QAAQ;YAAM;QAC/B;QACA,IAAID,OAAO,IAAI,IACX,OAAO;QAEX,IAAIA,OAAO,KAAK,YAAYE,8BACxB,OAAO;QAEX,MAAMF,OAAO,KAAK;IACtB;IAMA,MAAc,iBAAiBJ,GAAW,EAAiB;QACvD,MAAMK,YAAYtB,UAAUN;QAC5B,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAACuB,KAAKpB,cAAc;YAAE,OAAOL;YAAa8B;QAAU;IAC5E;AACJ;AAEO,MAAME,kCAAkCC,4BAA4B,eAAe,CAAC;IACvF,WAAWrB;IACX,cAAc;QACVsB;QACAC;QACAC;QACAC;QACAC;KACH;AACL"}
|