@webiny/api-event-handler-standalone 0.0.0-unstable.33e4fd9647
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 +22 -0
- package/bulkActions/bulkActionsServer.js.map +1 -0
- package/config/abstractions.d.ts +26 -0
- package/config/abstractions.js +5 -0
- package/config/abstractions.js.map +1 -0
- package/config/readStandaloneConfig.d.ts +12 -0
- package/config/readStandaloneConfig.js +38 -0
- package/config/readStandaloneConfig.js.map +1 -0
- package/createWebinyApiHandler.d.ts +15 -0
- package/createWebinyApiHandler.js +120 -0
- package/createWebinyApiHandler.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/logger/consoleLogger.d.ts +2 -0
- package/logger/consoleLogger.js +12 -0
- package/logger/consoleLogger.js.map +1 -0
- package/package.json +52 -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 +87 -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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Webiny
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @webiny/api-event-handler-standalone
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> This package is part of the [Webiny](https://www.webiny.com) monorepo.
|
|
5
|
+
> Itβs **included in every Webiny project by default** and is not meant to be used as a standalone package.
|
|
6
|
+
|
|
7
|
+
π **Documentation:** [https://www.webiny.com/docs](https://www.webiny.com/docs)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
_This README file is automatically generated during the publish process._
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Container } from "@webiny/di";
|
|
2
|
+
/**
|
|
3
|
+
* Empties the trash bins on a timer by dispatching an `EmptyTrashBinsEvent`. The standalone
|
|
4
|
+
* counterpart of the EventBridge rule that does it on AWS.
|
|
5
|
+
*/
|
|
6
|
+
export declare function startBulkActionsServer(rootContainer: Container): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { EventDispatcher } from "@webiny/event-handler-core";
|
|
2
|
+
import { EMPTY_TRASH_BINS_EVENT_IDENTIFIER } from "@webiny/api-headless-cms-bulk-actions-standalone";
|
|
3
|
+
const EMPTY_TRASH_INTERVAL_MS = 21600000;
|
|
4
|
+
const FIRST_RUN_DELAY_MS = 5000;
|
|
5
|
+
function startBulkActionsServer(rootContainer) {
|
|
6
|
+
const dispatcher = rootContainer.resolve(EventDispatcher);
|
|
7
|
+
const event = {
|
|
8
|
+
[EMPTY_TRASH_BINS_EVENT_IDENTIFIER]: true
|
|
9
|
+
};
|
|
10
|
+
const trigger = async ()=>{
|
|
11
|
+
try {
|
|
12
|
+
await dispatcher.dispatch(event);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
console.error("[bulk-actions] failed to start emptying the trash bins:", err);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
setTimeout(trigger, FIRST_RUN_DELAY_MS);
|
|
18
|
+
setInterval(trigger, EMPTY_TRASH_INTERVAL_MS);
|
|
19
|
+
}
|
|
20
|
+
export { startBulkActionsServer };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=bulkActionsServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulkActions/bulkActionsServer.js","sources":["../../src/bulkActions/bulkActionsServer.ts"],"sourcesContent":["import type { Container } from \"@webiny/di\";\nimport { EventDispatcher } from \"@webiny/event-handler-core\";\nimport { EMPTY_TRASH_BINS_EVENT_IDENTIFIER } from \"@webiny/api-headless-cms-bulk-actions-standalone\";\nimport type { IEmptyTrashBinsEvent } from \"@webiny/api-headless-cms-bulk-actions-standalone\";\n\n// Every 6 hours.\nconst EMPTY_TRASH_INTERVAL_MS = 6 * 60 * 60 * 1000;\n\n// Shortly after boot, so the first run doesn't compete with startup.\nconst FIRST_RUN_DELAY_MS = 5000;\n\n/**\n * Empties the trash bins on a timer by dispatching an `EmptyTrashBinsEvent`. The standalone\n * counterpart of the EventBridge rule that does it on AWS.\n */\nexport function startBulkActionsServer(rootContainer: Container): void {\n const dispatcher = rootContainer.resolve(EventDispatcher);\n const event: IEmptyTrashBinsEvent = { [EMPTY_TRASH_BINS_EVENT_IDENTIFIER]: true };\n\n const trigger = async () => {\n try {\n await dispatcher.dispatch(event);\n } catch (err) {\n console.error(\"[bulk-actions] failed to start emptying the trash bins:\", err);\n }\n };\n\n setTimeout(trigger, FIRST_RUN_DELAY_MS);\n setInterval(trigger, EMPTY_TRASH_INTERVAL_MS);\n}\n"],"names":["EMPTY_TRASH_INTERVAL_MS","FIRST_RUN_DELAY_MS","startBulkActionsServer","rootContainer","dispatcher","EventDispatcher","event","EMPTY_TRASH_BINS_EVENT_IDENTIFIER","trigger","err","console","setTimeout","setInterval"],"mappings":";;AAMA,MAAMA,0BAA0B;AAGhC,MAAMC,qBAAqB;AAMpB,SAASC,uBAAuBC,aAAwB;IAC3D,MAAMC,aAAaD,cAAc,OAAO,CAACE;IACzC,MAAMC,QAA8B;QAAE,CAACC,kCAAkC,EAAE;IAAK;IAEhF,MAAMC,UAAU;QACZ,IAAI;YACA,MAAMJ,WAAW,QAAQ,CAACE;QAC9B,EAAE,OAAOG,KAAK;YACVC,QAAQ,KAAK,CAAC,2DAA2DD;QAC7E;IACJ;IAEAE,WAAWH,SAASP;IACpBW,YAAYJ,SAASR;AACzB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PublicUrl } from "@webiny/api-core/features/publicUrls/index.js";
|
|
2
|
+
export interface IStandaloneConfig {
|
|
3
|
+
/** `WEBINY_API_URL`. Required. */
|
|
4
|
+
readonly apiUrl: PublicUrl;
|
|
5
|
+
/** `WEBINY_ADMIN_URL`. Optional: unknown when not set. */
|
|
6
|
+
readonly adminUrl: PublicUrl;
|
|
7
|
+
readonly fileStorage: {
|
|
8
|
+
/** `WEBINY_LOCAL_STORAGE_PATH`, resolved to an absolute path. Required. */
|
|
9
|
+
readonly storagePath: string;
|
|
10
|
+
/** `WEBINY_UPLOAD_SECRET`. Required. */
|
|
11
|
+
readonly uploadSecret: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* `WEBINY_REDIS_URL`. Optional. When set, websocket messages travel between API processes
|
|
15
|
+
* through this Redis or Valkey server; required when more than one API process runs.
|
|
16
|
+
*/
|
|
17
|
+
readonly redisUrl: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Deployment-specific settings of a standalone API, read from env once when the server starts.
|
|
21
|
+
* Nothing here is baked into the build, so one build runs in every environment.
|
|
22
|
+
*/
|
|
23
|
+
export declare const StandaloneConfig: import("@webiny/di").Abstraction<IStandaloneConfig>;
|
|
24
|
+
export declare namespace StandaloneConfig {
|
|
25
|
+
type Interface = IStandaloneConfig;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config/abstractions.js","sources":["../../src/config/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { PublicUrl } from \"@webiny/api-core/features/publicUrls/index.js\";\n\nexport interface IStandaloneConfig {\n /** `WEBINY_API_URL`. Required. */\n readonly apiUrl: PublicUrl;\n /** `WEBINY_ADMIN_URL`. Optional: unknown when not set. */\n readonly adminUrl: PublicUrl;\n readonly fileStorage: {\n /** `WEBINY_LOCAL_STORAGE_PATH`, resolved to an absolute path. Required. */\n readonly storagePath: string;\n /** `WEBINY_UPLOAD_SECRET`. Required. */\n readonly uploadSecret: string;\n };\n /**\n * `WEBINY_REDIS_URL`. Optional. When set, websocket messages travel between API processes\n * through this Redis or Valkey server; required when more than one API process runs.\n */\n readonly redisUrl: string | undefined;\n}\n\n/**\n * Deployment-specific settings of a standalone API, read from env once when the server starts.\n * Nothing here is baked into the build, so one build runs in every environment.\n */\nexport const StandaloneConfig = createAbstraction<IStandaloneConfig>(\"Standalone/Config\");\n\nexport namespace StandaloneConfig {\n export type Interface = IStandaloneConfig;\n}\n"],"names":["StandaloneConfig","createAbstraction"],"mappings":";AAyBO,MAAMA,mBAAmBC,kBAAqC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StandaloneConfig } from "./abstractions.js";
|
|
2
|
+
type Env = Record<string, string | undefined>;
|
|
3
|
+
/**
|
|
4
|
+
* Reads the standalone API's settings from env. Throws one error naming every missing or invalid
|
|
5
|
+
* variable, so a misconfigured container fails at boot instead of on the first upload or login.
|
|
6
|
+
*
|
|
7
|
+
* Nothing falls back to a default: `webiny watch` and `webiny serve` get their values from
|
|
8
|
+
* `<Infra.ApiUrl>`, `<Infra.AdminUrl>` and `<Infra.FileStorage>` in webiny.config, and a deployed
|
|
9
|
+
* container must set them explicitly.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readStandaloneConfig(env: Env): StandaloneConfig.Interface;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import { PublicUrl } from "@webiny/api-core/features/publicUrls/index.js";
|
|
3
|
+
function read(env, name) {
|
|
4
|
+
return env[name]?.trim() ?? "";
|
|
5
|
+
}
|
|
6
|
+
function readStandaloneConfig(env) {
|
|
7
|
+
const problems = [];
|
|
8
|
+
const apiUrlValue = read(env, "WEBINY_API_URL");
|
|
9
|
+
const apiUrl = PublicUrl.from(apiUrlValue);
|
|
10
|
+
if (apiUrlValue) {
|
|
11
|
+
if (!apiUrl.isKnown()) problems.push(`WEBINY_API_URL is not an absolute http(s) URL: "${apiUrlValue}".`);
|
|
12
|
+
} else problems.push("WEBINY_API_URL is not set.");
|
|
13
|
+
const adminUrlValue = read(env, "WEBINY_ADMIN_URL");
|
|
14
|
+
const adminUrl = PublicUrl.from(adminUrlValue);
|
|
15
|
+
if (adminUrlValue && !adminUrl.isKnown()) problems.push(`WEBINY_ADMIN_URL is not an absolute http(s) URL: "${adminUrlValue}".`);
|
|
16
|
+
const storagePath = read(env, "WEBINY_LOCAL_STORAGE_PATH");
|
|
17
|
+
if (!storagePath) problems.push("WEBINY_LOCAL_STORAGE_PATH is not set.");
|
|
18
|
+
const uploadSecret = read(env, "WEBINY_UPLOAD_SECRET");
|
|
19
|
+
if (!uploadSecret) problems.push("WEBINY_UPLOAD_SECRET is not set.");
|
|
20
|
+
const redisUrl = read(env, "WEBINY_REDIS_URL");
|
|
21
|
+
if (redisUrl && !/^rediss?:\/\//.test(redisUrl)) problems.push("WEBINY_REDIS_URL must start with redis:// or rediss://.");
|
|
22
|
+
if (problems.length > 0) throw new Error([
|
|
23
|
+
"The standalone API is missing required configuration:",
|
|
24
|
+
...problems
|
|
25
|
+
].join("\n "));
|
|
26
|
+
return {
|
|
27
|
+
apiUrl,
|
|
28
|
+
adminUrl,
|
|
29
|
+
fileStorage: {
|
|
30
|
+
storagePath: node_path.resolve(storagePath),
|
|
31
|
+
uploadSecret
|
|
32
|
+
},
|
|
33
|
+
redisUrl: redisUrl || void 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export { readStandaloneConfig };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=readStandaloneConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config/readStandaloneConfig.js","sources":["../../src/config/readStandaloneConfig.ts"],"sourcesContent":["import nodePath from \"node:path\";\nimport { PublicUrl } from \"@webiny/api-core/features/publicUrls/index.js\";\nimport type { StandaloneConfig } from \"./abstractions.js\";\n\ntype Env = Record<string, string | undefined>;\n\nfunction read(env: Env, name: string): string {\n return env[name]?.trim() ?? \"\";\n}\n\n/**\n * Reads the standalone API's settings from env. Throws one error naming every missing or invalid\n * variable, so a misconfigured container fails at boot instead of on the first upload or login.\n *\n * Nothing falls back to a default: `webiny watch` and `webiny serve` get their values from\n * `<Infra.ApiUrl>`, `<Infra.AdminUrl>` and `<Infra.FileStorage>` in webiny.config, and a deployed\n * container must set them explicitly.\n */\nexport function readStandaloneConfig(env: Env): StandaloneConfig.Interface {\n const problems: string[] = [];\n\n const apiUrlValue = read(env, \"WEBINY_API_URL\");\n const apiUrl = PublicUrl.from(apiUrlValue);\n if (!apiUrlValue) {\n problems.push(\"WEBINY_API_URL is not set.\");\n } else if (!apiUrl.isKnown()) {\n problems.push(`WEBINY_API_URL is not an absolute http(s) URL: \"${apiUrlValue}\".`);\n }\n\n const adminUrlValue = read(env, \"WEBINY_ADMIN_URL\");\n const adminUrl = PublicUrl.from(adminUrlValue);\n if (adminUrlValue && !adminUrl.isKnown()) {\n problems.push(`WEBINY_ADMIN_URL is not an absolute http(s) URL: \"${adminUrlValue}\".`);\n }\n\n const storagePath = read(env, \"WEBINY_LOCAL_STORAGE_PATH\");\n if (!storagePath) {\n problems.push(\"WEBINY_LOCAL_STORAGE_PATH is not set.\");\n }\n\n const uploadSecret = read(env, \"WEBINY_UPLOAD_SECRET\");\n if (!uploadSecret) {\n problems.push(\"WEBINY_UPLOAD_SECRET is not set.\");\n }\n\n const redisUrl = read(env, \"WEBINY_REDIS_URL\");\n if (redisUrl && !/^rediss?:\\/\\//.test(redisUrl)) {\n problems.push(\"WEBINY_REDIS_URL must start with redis:// or rediss://.\");\n }\n\n if (problems.length > 0) {\n throw new Error(\n [\"The standalone API is missing required configuration:\", ...problems].join(\"\\n \")\n );\n }\n\n return {\n apiUrl,\n adminUrl,\n fileStorage: {\n storagePath: nodePath.resolve(storagePath),\n uploadSecret\n },\n redisUrl: redisUrl || undefined\n };\n}\n"],"names":["read","env","name","readStandaloneConfig","problems","apiUrlValue","apiUrl","PublicUrl","adminUrlValue","adminUrl","storagePath","uploadSecret","redisUrl","Error","nodePath","undefined"],"mappings":";;AAMA,SAASA,KAAKC,GAAQ,EAAEC,IAAY;IAChC,OAAOD,GAAG,CAACC,KAAK,EAAE,UAAU;AAChC;AAUO,SAASC,qBAAqBF,GAAQ;IACzC,MAAMG,WAAqB,EAAE;IAE7B,MAAMC,cAAcL,KAAKC,KAAK;IAC9B,MAAMK,SAASC,UAAU,IAAI,CAACF;IAC9B,IAAKA,aAEE;QAAA,IAAI,CAACC,OAAO,OAAO,IACtBF,SAAS,IAAI,CAAC,CAAC,gDAAgD,EAAEC,YAAY,EAAE,CAAC;IACpF,OAHID,SAAS,IAAI,CAAC;IAKlB,MAAMI,gBAAgBR,KAAKC,KAAK;IAChC,MAAMQ,WAAWF,UAAU,IAAI,CAACC;IAChC,IAAIA,iBAAiB,CAACC,SAAS,OAAO,IAClCL,SAAS,IAAI,CAAC,CAAC,kDAAkD,EAAEI,cAAc,EAAE,CAAC;IAGxF,MAAME,cAAcV,KAAKC,KAAK;IAC9B,IAAI,CAACS,aACDN,SAAS,IAAI,CAAC;IAGlB,MAAMO,eAAeX,KAAKC,KAAK;IAC/B,IAAI,CAACU,cACDP,SAAS,IAAI,CAAC;IAGlB,MAAMQ,WAAWZ,KAAKC,KAAK;IAC3B,IAAIW,YAAY,CAAC,gBAAgB,IAAI,CAACA,WAClCR,SAAS,IAAI,CAAC;IAGlB,IAAIA,SAAS,MAAM,GAAG,GAClB,MAAM,IAAIS,MACN;QAAC;WAA4DT;KAAS,CAAC,IAAI,CAAC;IAIpF,OAAO;QACHE;QACAG;QACA,aAAa;YACT,aAAaK,UAAAA,OAAgB,CAACJ;YAC9BC;QACJ;QACA,UAAUC,YAAYG;IAC1B;AACJ"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Container } from "@webiny/di";
|
|
2
|
+
import { registerExtensions } from "@webiny/handler";
|
|
3
|
+
export interface CreateWebinyApiHandlerConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Project-defined extensions, applied at register() time.
|
|
6
|
+
*/
|
|
7
|
+
extensions: () => Parameters<typeof registerExtensions>[1];
|
|
8
|
+
/**
|
|
9
|
+
* Register ALL root-container storage for this deployment (the DB feature + CMS storage ops +
|
|
10
|
+
* registries) AND the identity provider. Unlike the AWS base, the server base bakes in no DB or
|
|
11
|
+
* IdP assumption β the variant supplies the complete storage + auth wiring.
|
|
12
|
+
*/
|
|
13
|
+
registerRootStorage: (container: Container) => void | Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function createWebinyApiHandler(config: CreateWebinyApiHandlerConfig): Promise<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { GraphQLSchemaCacheFeature } from "@webiny/api-graphql";
|
|
2
|
+
import { EncryptionKeyCacheFeature } from "@webiny/api-core/features/encryption/index.js";
|
|
3
|
+
import { JwksStoreFeature } from "@webiny/api-core/idp/index.js";
|
|
4
|
+
import { NodeHttpFeature, ProcessOwner, RandomProcessOwner, createServerHandler } from "@webiny/event-handler-standalone";
|
|
5
|
+
import { registerApiRequestStack } from "@webiny/api-event-handler-core";
|
|
6
|
+
import { InProcessMessageBus, NodeWsAdapter, ServerConnectionManager, WebsocketsConnectionManager, WebsocketsMessageBus, WebsocketsServerAdapter, WebsocketsStandaloneFeature, attachWebsocketsServer, createBusMessageHandler } from "@webiny/api-websockets-standalone";
|
|
7
|
+
import { WebsocketsRedisFeature } from "@webiny/api-websockets-standalone-redis";
|
|
8
|
+
import { BackgroundTasksStandaloneFeature, startTaskRecovery, stopBackgroundTasks } from "@webiny/background-tasks-standalone";
|
|
9
|
+
import { FileManagerStandaloneFeature } from "@webiny/api-file-manager-standalone";
|
|
10
|
+
import { registerSchedulerServer, startSchedulerServer } from "./scheduler/schedulerServer.js";
|
|
11
|
+
import { startBulkActionsServer } from "./bulkActions/bulkActionsServer.js";
|
|
12
|
+
import { NodeHttpIdentityLoaderDecorator } from "./handlers/NodeHttpIdentityLoaderDecorator.js";
|
|
13
|
+
import { NodeHttpTenantLoaderDecorator } from "./handlers/NodeHttpTenantLoaderDecorator.js";
|
|
14
|
+
import { NodeHttpAssumePermissionsDecorator } from "./handlers/NodeHttpAssumePermissionsDecorator.js";
|
|
15
|
+
import { createWebsocketsAuthenticator } from "./websockets/createWebsocketsAuthenticator.js";
|
|
16
|
+
import { EmptyTrashBinsFeature } from "@webiny/api-headless-cms-bulk-actions-standalone";
|
|
17
|
+
import { mkdirSync } from "node:fs";
|
|
18
|
+
import { ConfigPublicUrlsFeature } from "./publicUrls/feature.js";
|
|
19
|
+
import { StandaloneConfig } from "./config/abstractions.js";
|
|
20
|
+
import { readStandaloneConfig } from "./config/readStandaloneConfig.js";
|
|
21
|
+
import { consoleLogger } from "./logger/consoleLogger.js";
|
|
22
|
+
const SHUTDOWN_WAIT_MS = 6000;
|
|
23
|
+
function createWebinyApiHandler(config) {
|
|
24
|
+
let stopTaskRecovery;
|
|
25
|
+
let stopScheduler;
|
|
26
|
+
let stopWebsockets;
|
|
27
|
+
let closeMessageBus;
|
|
28
|
+
const standaloneConfig = readStandaloneConfig(process.env);
|
|
29
|
+
return createServerHandler({
|
|
30
|
+
root: async (rootContainer)=>{
|
|
31
|
+
rootContainer.registerInstance(StandaloneConfig, standaloneConfig);
|
|
32
|
+
rootContainer.register(RandomProcessOwner).inSingletonScope();
|
|
33
|
+
mkdirSync(standaloneConfig.fileStorage.storagePath, {
|
|
34
|
+
recursive: true
|
|
35
|
+
});
|
|
36
|
+
NodeHttpFeature.register(rootContainer);
|
|
37
|
+
rootContainer.registerDecorator(NodeHttpIdentityLoaderDecorator);
|
|
38
|
+
rootContainer.registerDecorator(NodeHttpTenantLoaderDecorator);
|
|
39
|
+
rootContainer.registerDecorator(NodeHttpAssumePermissionsDecorator);
|
|
40
|
+
await config.registerRootStorage(rootContainer);
|
|
41
|
+
rootContainer.register(ServerConnectionManager).inSingletonScope();
|
|
42
|
+
rootContainer.register(NodeWsAdapter).inSingletonScope();
|
|
43
|
+
if (standaloneConfig.redisUrl) WebsocketsRedisFeature.register(rootContainer, {
|
|
44
|
+
url: standaloneConfig.redisUrl,
|
|
45
|
+
logger: consoleLogger
|
|
46
|
+
});
|
|
47
|
+
else rootContainer.register(InProcessMessageBus).inSingletonScope();
|
|
48
|
+
GraphQLSchemaCacheFeature.register(rootContainer);
|
|
49
|
+
EncryptionKeyCacheFeature.register(rootContainer);
|
|
50
|
+
JwksStoreFeature.register(rootContainer);
|
|
51
|
+
BackgroundTasksStandaloneFeature.register(rootContainer);
|
|
52
|
+
registerSchedulerServer(rootContainer);
|
|
53
|
+
EmptyTrashBinsFeature.register(rootContainer);
|
|
54
|
+
},
|
|
55
|
+
child: async (container)=>{
|
|
56
|
+
await registerApiRequestStack(container, {
|
|
57
|
+
extensions: config.extensions,
|
|
58
|
+
transports: {
|
|
59
|
+
publicUrls: (requestContainer)=>{
|
|
60
|
+
ConfigPublicUrlsFeature.register(requestContainer);
|
|
61
|
+
},
|
|
62
|
+
realtime: (requestContainer)=>{
|
|
63
|
+
WebsocketsStandaloneFeature.register(requestContainer);
|
|
64
|
+
},
|
|
65
|
+
fileManager: (requestContainer)=>{
|
|
66
|
+
FileManagerStandaloneFeature.register(requestContainer, standaloneConfig.fileStorage);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
onServer: async (server, rootContainer)=>{
|
|
72
|
+
const connectionManager = rootContainer.resolve(WebsocketsConnectionManager);
|
|
73
|
+
const websockets = attachWebsocketsServer({
|
|
74
|
+
server,
|
|
75
|
+
connectionManager,
|
|
76
|
+
authenticate: createWebsocketsAuthenticator(rootContainer, config.extensions)
|
|
77
|
+
});
|
|
78
|
+
await websockets.start();
|
|
79
|
+
stopWebsockets = ()=>websockets.stop();
|
|
80
|
+
const messageBus = rootContainer.resolve(WebsocketsMessageBus);
|
|
81
|
+
const processOwner = rootContainer.resolve(ProcessOwner);
|
|
82
|
+
const adapter = rootContainer.resolve(WebsocketsServerAdapter);
|
|
83
|
+
const handleBusMessage = createBusMessageHandler({
|
|
84
|
+
connectionManager,
|
|
85
|
+
adapter,
|
|
86
|
+
logger: consoleLogger
|
|
87
|
+
});
|
|
88
|
+
await messageBus.subscribe(processOwner.id, handleBusMessage);
|
|
89
|
+
closeMessageBus = ()=>messageBus.close();
|
|
90
|
+
consoleLogger.info({
|
|
91
|
+
messageBus: standaloneConfig.redisUrl ? "redis" : "in-process"
|
|
92
|
+
}, "[websockets] using message bus");
|
|
93
|
+
stopScheduler = await startSchedulerServer(rootContainer);
|
|
94
|
+
startBulkActionsServer(rootContainer);
|
|
95
|
+
stopTaskRecovery = startTaskRecovery(rootContainer);
|
|
96
|
+
},
|
|
97
|
+
onShutdown: async (rootContainer)=>{
|
|
98
|
+
stopTaskRecovery?.();
|
|
99
|
+
const tasks = stopBackgroundTasks(rootContainer, SHUTDOWN_WAIT_MS);
|
|
100
|
+
const scheduledActions = stopScheduler?.(SHUTDOWN_WAIT_MS);
|
|
101
|
+
const websockets = stopWebsockets?.();
|
|
102
|
+
const workDone = Promise.allSettled([
|
|
103
|
+
tasks,
|
|
104
|
+
scheduledActions
|
|
105
|
+
]);
|
|
106
|
+
const messageBus = workDone.then(()=>closeMessageBus?.());
|
|
107
|
+
const results = await Promise.allSettled([
|
|
108
|
+
tasks,
|
|
109
|
+
scheduledActions,
|
|
110
|
+
websockets,
|
|
111
|
+
messageBus
|
|
112
|
+
]);
|
|
113
|
+
const failed = results.find((result)=>"rejected" === result.status);
|
|
114
|
+
if (failed) throw failed.reason;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export { createWebinyApiHandler };
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=createWebinyApiHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createWebinyApiHandler.js","sources":["../src/createWebinyApiHandler.ts"],"sourcesContent":["/**\n * Webiny API handler for the standalone Node HTTP server transport β storage-agnostic BASE.\n *\n * The ROOT container wires the Node HTTP transport (NodeHttpFeature = event type + router + HttpFeature)\n * plus the auth/tenant loader decorators (extract token / x-tenant from the IncomingMessage β shared\n * RequestIdentityLoader / RequestTenantLoader). The per-request feature stack is the transport-agnostic\n * `registerApiRequestStack` from `@webiny/api-event-handler-core` β the SAME stack the AWS handler uses.\n * The realtime transport hook installs the server WebSockets transport (vs AWS's API Gateway Management\n * API) with a SINGLE-PROCESS, in-process implementation. Background tasks and the Bree scheduler are\n * ROOT-container singletons (below) β mirroring how the AWS handler registers background tasks at root,\n * and the WebSockets connection manager β rather than per-request transports; the scheduler fires timers\n * in-process (vs AWS's EventBridge). The storage variant (and identity provider) is injected via\n * `registerRootStorage`.\n *\n * The identity provider (e.g. `@webiny/self-hosted-auth`'s JWT IdP) must be registered by the variant\n * in `registerRootStorage`, so the RequestIdentityLoader driven by the identity decorator can resolve it.\n */\nimport { GraphQLSchemaCacheFeature } from \"@webiny/api-graphql\";\nimport { EncryptionKeyCacheFeature } from \"@webiny/api-core/features/encryption/index.js\";\nimport { JwksStoreFeature } from \"@webiny/api-core/idp/index.js\";\nimport type { Container } from \"@webiny/di\";\nimport { createServerHandler, NodeHttpFeature } from \"@webiny/event-handler-standalone\";\nimport { RandomProcessOwner } from \"@webiny/event-handler-standalone\";\nimport { registerExtensions } from \"@webiny/handler\";\nimport { registerApiRequestStack } from \"@webiny/api-event-handler-core\";\nimport {\n ServerConnectionManager,\n NodeWsAdapter,\n WebsocketsStandaloneFeature,\n WebsocketsConnectionManager,\n attachWebsocketsServer\n} from \"@webiny/api-websockets-standalone\";\nimport { WebsocketsServerAdapter } from \"@webiny/api-websockets-standalone\";\nimport { WebsocketsMessageBus } from \"@webiny/api-websockets-standalone\";\nimport { InProcessMessageBus } from \"@webiny/api-websockets-standalone\";\nimport { createBusMessageHandler } from \"@webiny/api-websockets-standalone\";\nimport { WebsocketsRedisFeature } from \"@webiny/api-websockets-standalone-redis\";\nimport { ProcessOwner } from \"@webiny/event-handler-standalone\";\nimport { BackgroundTasksStandaloneFeature } from \"@webiny/background-tasks-standalone\";\nimport { startTaskRecovery } from \"@webiny/background-tasks-standalone\";\nimport { stopBackgroundTasks } from \"@webiny/background-tasks-standalone\";\nimport { FileManagerStandaloneFeature } from \"@webiny/api-file-manager-standalone\";\nimport { registerSchedulerServer, startSchedulerServer } from \"~/scheduler/schedulerServer.js\";\nimport { startBulkActionsServer } from \"~/bulkActions/bulkActionsServer.js\";\nimport { NodeHttpIdentityLoaderDecorator } from \"~/handlers/NodeHttpIdentityLoaderDecorator.js\";\nimport { NodeHttpTenantLoaderDecorator } from \"~/handlers/NodeHttpTenantLoaderDecorator.js\";\nimport { NodeHttpAssumePermissionsDecorator } from \"~/handlers/NodeHttpAssumePermissionsDecorator.js\";\nimport { createWebsocketsAuthenticator } from \"~/websockets/createWebsocketsAuthenticator.js\";\nimport { EmptyTrashBinsFeature } from \"@webiny/api-headless-cms-bulk-actions-standalone\";\nimport { mkdirSync } from \"node:fs\";\nimport { ConfigPublicUrlsFeature } from \"~/publicUrls/feature.js\";\nimport { StandaloneConfig } from \"~/config/abstractions.js\";\nimport { readStandaloneConfig } from \"~/config/readStandaloneConfig.js\";\nimport { consoleLogger } from \"~/logger/consoleLogger.js\";\n\nexport interface CreateWebinyApiHandlerConfig {\n /**\n * Project-defined extensions, applied at register() time.\n */\n extensions: () => Parameters<typeof registerExtensions>[1];\n /**\n * Register ALL root-container storage for this deployment (the DB feature + CMS storage ops +\n * registries) AND the identity provider. Unlike the AWS base, the server base bakes in no DB or\n * IdP assumption β the variant supplies the complete storage + auth wiring.\n */\n registerRootStorage: (container: Container) => void | Promise<void>;\n}\n\n/*\n * The part of the 8 seconds a shutdown gets that running tasks and scheduled actions may spend\n * finishing or handing back; both wait at the same time. The rest leaves room to exit before a\n * process manager loses patience.\n */\nconst SHUTDOWN_WAIT_MS = 6000;\n\nexport function createWebinyApiHandler(config: CreateWebinyApiHandlerConfig) {\n let stopTaskRecovery: (() => void) | undefined;\n let stopScheduler: ((timeoutMs: number) => Promise<void>) | undefined;\n let stopWebsockets: (() => Promise<void>) | undefined;\n let closeMessageBus: (() => Promise<void>) | undefined;\n\n // Deployment-specific settings (public URLs, file storage) come from env, read once here so a\n // missing value stops the server at boot. Nothing deployment-specific is baked into the build.\n const standaloneConfig = readStandaloneConfig(process.env);\n\n return createServerHandler({\n root: async rootContainer => {\n rootContainer.registerInstance(StandaloneConfig, standaloneConfig);\n\n // Names this process in shared storage: task claims and websocket connections.\n rootContainer.register(RandomProcessOwner).inSingletonScope();\n mkdirSync(standaloneConfig.fileStorage.storagePath, { recursive: true });\n\n // ββ Transport (Node HTTP) ββββββββββββββββββββββββββββββββββ\n // NodeHttpFeature registers the event type + HttpFeature (router) + the routing terminal.\n NodeHttpFeature.register(rootContainer);\n\n // ββ Tenant + auth (extract β shared load) ββββββββββββββββββ\n // registerDecorator applies LATER registrations as the OUTER wrapper (whose execute()\n // runs first). TENANT must be established before IDENTITY: API-key authentication resolves\n // the key by tenant partition (ApiKeysRepository reads TenantContext.getTenant()), so\n // identity depends on the tenant; the reverse is not true (RequestTenantLoader has no\n // identity dependency). Register identity first (inner) and tenant last (outer) β tenant\n // runs, then identity, then the router.\n rootContainer.registerDecorator(NodeHttpIdentityLoaderDecorator);\n rootContainer.registerDecorator(NodeHttpTenantLoaderDecorator);\n\n // Preview-as. Mirrors the AWS handler.\n rootContainer.registerDecorator(NodeHttpAssumePermissionsDecorator);\n\n // ββ Storage + identity provider (variant-supplied) βββββββββ\n await config.registerRootStorage(rootContainer);\n\n // ββ WebSockets transport (root) ββββββββββββββββββββββββββββ\n // Live-socket registry + adapter live in the root as singletons: the connection manager\n // is shared between the upgrade acceptor (attachWebsocketsServer, below) and the\n // per-request ServerWebsocketsTransport that sends to those live sockets. The persistent\n // connection registry (ConnectionRegistry) is the storage variant's job (e.g. sql).\n rootContainer.register(ServerConnectionManager).inSingletonScope();\n rootContainer.register(NodeWsAdapter).inSingletonScope();\n\n /*\n * How a send reaches a socket another API process holds. Without a Redis URL there is\n * one process, and every socket is in it.\n */\n if (standaloneConfig.redisUrl) {\n WebsocketsRedisFeature.register(rootContainer, {\n url: standaloneConfig.redisUrl,\n logger: consoleLogger\n });\n } else {\n rootContainer.register(InProcessMessageBus).inSingletonScope();\n }\n\n // ββ GraphQL schema cache (root) ββββββββββββββββββββββββββββ\n // Root, so a built schema outlives the request that built it.\n GraphQLSchemaCacheFeature.register(rootContainer);\n\n // ββ Encryption key cache (root) ββββββββββββββββββββββββββββ\n // Root, so the scrypt-derived key is computed once per process instead of per request.\n EncryptionKeyCacheFeature.register(rootContainer);\n // Root, so identity providers' signing keys are fetched once per process, not per request.\n JwksStoreFeature.register(rootContainer);\n\n // ββ Background tasks (root) ββββββββββββββββββββββββββββββββ\n // Mirrors the AWS handler registering its background-task transport at root. Where AWS\n // has Step Functions invoke the Lambda once per iteration, a root task loop dispatches a\n // BackgroundTaskEvent per iteration through the EventDispatcher (registered by\n // createServerHandler), and each one runs in a fresh request container.\n BackgroundTasksStandaloneFeature.register(rootContainer);\n\n // ββ Scheduler (root) βββββββββββββββββββββββββββββββββββββββ\n // The Bree scheduler is a single long-lived instance for ALL tenants, started once at boot\n // (onServer, below) β the counterpart of the WebSockets connection manager, NOT a per-request\n // transport. Registered here as SchedulerService (per-request create/update/delete during\n // mutations manipulate this one live timer set) plus the run/recover event handlers. When a\n // timer fires (outside any request) it dispatches a ScheduledActionEvent, handled in a fresh\n // request container by the same handler AWS uses.\n registerSchedulerServer(rootContainer);\n\n // ββ Bulk actions (root) βββββββββββββββββββββββββββββββββββ\n // Registers the EmptyTrashBinsEvent handler that the periodic trigger\n // (startBulkActionsServer, onServer below) dispatches.\n EmptyTrashBinsFeature.register(rootContainer);\n },\n\n child: async container => {\n // The transport-agnostic per-request stack. The realtime hook installs the server\n // WebSockets transport (overriding the domain's NullWebsocketsTransport); it resolves the\n // shared connection manager + adapter from the root. Scheduler is NOT a per-request\n // transport here β it's a root singleton wired in `root` + `onServer` (see above).\n await registerApiRequestStack(container, {\n extensions: config.extensions,\n // Why hooks (and not just registering the transports ourselves): `.register()` calls\n // are otherwise order-independent β you can register Features in any order, because\n // they only REGISTER, they don't RESOLVE during registration (resolution happens\n // later, in Initializers / SchemaFactories). The one thing that makes order matter is\n // a DEFAULT registration: e.g. `WebsocketsFeature` registers `NullWebsocketsTransport`\n // so the abstraction is always resolvable. Overriding it (last-registration-wins) means\n // our transport MUST be registered AFTER that Feature. These hooks are the seams\n // `registerApiRequestStack` provides for exactly that β each runs right after its Null\n // default, so the override is guaranteed without the caller knowing the internal order.\n transports: {\n // Public URLs: no deploy step to discover them from, so they come from\n // `WEBINY_API_URL` / `WEBINY_ADMIN_URL` (StandaloneConfig, registered on the root).\n publicUrls: requestContainer => {\n ConfigPublicUrlsFeature.register(requestContainer);\n },\n // Server WebSockets transport; resolves the shared connection manager + adapter\n // from the root (registered as singletons above).\n realtime: requestContainer => {\n WebsocketsStandaloneFeature.register(requestContainer);\n },\n // File-manager storage transport: local disk. Where AWS uses S3 (+ a separate asset-\n // delivery Lambda), the single-process server stores files on disk and serves them\n // in-process β FileManagerStandaloneFeature registers local asset delivery (overriding\n // the domain's null impls), the upload/multipart HTTP routes, and disk file ops.\n fileManager: requestContainer => {\n FileManagerStandaloneFeature.register(\n requestContainer,\n standaloneConfig.fileStorage\n );\n }\n }\n });\n },\n\n onServer: async (server, rootContainer) => {\n // Attach the WebSockets upgrade handler to the running HTTP server, backed by the shared\n // (root) connection manager so request-time sends reach the live sockets.\n //\n // Authenticate each WebSocket connection from its `?token` JWT so it's registered under\n // the real identity (targeted serverβclient sends match by identity id). It's a callback\n // the handler supplies rather than something the WS server resolves itself β see\n // createWebsocketsAuthenticator for why (AuthenticationContext is per-request, unreachable\n // from the root container the WS server is attached to).\n const connectionManager = rootContainer.resolve(WebsocketsConnectionManager);\n const websockets = attachWebsocketsServer({\n server,\n connectionManager,\n authenticate: createWebsocketsAuthenticator(rootContainer, config.extensions)\n });\n await websockets.start();\n stopWebsockets = () => websockets.stop();\n\n // Receive sends and disconnects other API processes publish for this one's sockets.\n const messageBus = rootContainer.resolve(WebsocketsMessageBus);\n const processOwner = rootContainer.resolve(ProcessOwner);\n const adapter = rootContainer.resolve(WebsocketsServerAdapter);\n const handleBusMessage = createBusMessageHandler({\n connectionManager,\n adapter,\n logger: consoleLogger\n });\n await messageBus.subscribe(processOwner.id, handleBusMessage);\n closeMessageBus = () => messageBus.close();\n consoleLogger.info(\n { messageBus: standaloneConfig.redisUrl ? \"redis\" : \"in-process\" },\n \"[websockets] using message bus\"\n );\n\n /*\n * Start the in-process scheduler timers, then re-arm persisted schedules in the\n * background, at boot and then every minute (see startSchedulerServer).\n */\n stopScheduler = await startSchedulerServer(rootContainer);\n\n // Start the periodic empty-trash-bin trigger.\n startBulkActionsServer(rootContainer);\n\n /*\n * Take over tasks a restart or another process left behind, at boot and then every\n * minute (see startTaskRecovery).\n */\n stopTaskRecovery = startTaskRecovery(rootContainer);\n },\n\n onShutdown: async rootContainer => {\n /*\n * Stop starting new work first: task recovery, and the scheduler's timers and recovery.\n * Then wait for what is running: tasks hand themselves back, so another process picks\n * them up at its next sweep, and scheduled actions already running get to finish.\n * Websockets close at the same time, with 1012 so clients reconnect to a process that\n * is up, and their connections leave the registry; that takes about a second. The\n * message bus closes last, once tasks and scheduled actions are done: what they still\n * send to browsers on other processes goes through it.\n */\n stopTaskRecovery?.();\n const tasks = stopBackgroundTasks(rootContainer, SHUTDOWN_WAIT_MS);\n const scheduledActions = stopScheduler?.(SHUTDOWN_WAIT_MS);\n const websockets = stopWebsockets?.();\n const workDone = Promise.allSettled([tasks, scheduledActions]);\n const messageBus = workDone.then(() => closeMessageBus?.());\n\n // One failing part must not cut the others short; the first failure is reported after.\n const results = await Promise.allSettled([\n tasks,\n scheduledActions,\n websockets,\n messageBus\n ]);\n const failed = results.find((result): result is PromiseRejectedResult => {\n return result.status === \"rejected\";\n });\n if (failed) {\n throw failed.reason;\n }\n }\n });\n}\n"],"names":["SHUTDOWN_WAIT_MS","createWebinyApiHandler","config","stopTaskRecovery","stopScheduler","stopWebsockets","closeMessageBus","standaloneConfig","readStandaloneConfig","process","createServerHandler","rootContainer","StandaloneConfig","RandomProcessOwner","mkdirSync","NodeHttpFeature","NodeHttpIdentityLoaderDecorator","NodeHttpTenantLoaderDecorator","NodeHttpAssumePermissionsDecorator","ServerConnectionManager","NodeWsAdapter","WebsocketsRedisFeature","consoleLogger","InProcessMessageBus","GraphQLSchemaCacheFeature","EncryptionKeyCacheFeature","JwksStoreFeature","BackgroundTasksStandaloneFeature","registerSchedulerServer","EmptyTrashBinsFeature","container","registerApiRequestStack","requestContainer","ConfigPublicUrlsFeature","WebsocketsStandaloneFeature","FileManagerStandaloneFeature","server","connectionManager","WebsocketsConnectionManager","websockets","attachWebsocketsServer","createWebsocketsAuthenticator","messageBus","WebsocketsMessageBus","processOwner","ProcessOwner","adapter","WebsocketsServerAdapter","handleBusMessage","createBusMessageHandler","startSchedulerServer","startBulkActionsServer","startTaskRecovery","tasks","stopBackgroundTasks","scheduledActions","workDone","Promise","results","failed","result"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyEA,MAAMA,mBAAmB;AAElB,SAASC,uBAAuBC,MAAoC;IACvE,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAIJ,MAAMC,mBAAmBC,qBAAqBC,QAAQ,GAAG;IAEzD,OAAOC,oBAAoB;QACvB,MAAM,OAAMC;YACRA,cAAc,gBAAgB,CAACC,kBAAkBL;YAGjDI,cAAc,QAAQ,CAACE,oBAAoB,gBAAgB;YAC3DC,UAAUP,iBAAiB,WAAW,CAAC,WAAW,EAAE;gBAAE,WAAW;YAAK;YAItEQ,gBAAgB,QAAQ,CAACJ;YASzBA,cAAc,iBAAiB,CAACK;YAChCL,cAAc,iBAAiB,CAACM;YAGhCN,cAAc,iBAAiB,CAACO;YAGhC,MAAMhB,OAAO,mBAAmB,CAACS;YAOjCA,cAAc,QAAQ,CAACQ,yBAAyB,gBAAgB;YAChER,cAAc,QAAQ,CAACS,eAAe,gBAAgB;YAMtD,IAAIb,iBAAiB,QAAQ,EACzBc,uBAAuB,QAAQ,CAACV,eAAe;gBAC3C,KAAKJ,iBAAiB,QAAQ;gBAC9B,QAAQe;YACZ;iBAEAX,cAAc,QAAQ,CAACY,qBAAqB,gBAAgB;YAKhEC,0BAA0B,QAAQ,CAACb;YAInCc,0BAA0B,QAAQ,CAACd;YAEnCe,iBAAiB,QAAQ,CAACf;YAO1BgB,iCAAiC,QAAQ,CAAChB;YAS1CiB,wBAAwBjB;YAKxBkB,sBAAsB,QAAQ,CAAClB;QACnC;QAEA,OAAO,OAAMmB;YAKT,MAAMC,wBAAwBD,WAAW;gBACrC,YAAY5B,OAAO,UAAU;gBAU7B,YAAY;oBAGR,YAAY8B,CAAAA;wBACRC,wBAAwB,QAAQ,CAACD;oBACrC;oBAGA,UAAUA,CAAAA;wBACNE,4BAA4B,QAAQ,CAACF;oBACzC;oBAKA,aAAaA,CAAAA;wBACTG,6BAA6B,QAAQ,CACjCH,kBACAzB,iBAAiB,WAAW;oBAEpC;gBACJ;YACJ;QACJ;QAEA,UAAU,OAAO6B,QAAQzB;YASrB,MAAM0B,oBAAoB1B,cAAc,OAAO,CAAC2B;YAChD,MAAMC,aAAaC,uBAAuB;gBACtCJ;gBACAC;gBACA,cAAcI,8BAA8B9B,eAAeT,OAAO,UAAU;YAChF;YACA,MAAMqC,WAAW,KAAK;YACtBlC,iBAAiB,IAAMkC,WAAW,IAAI;YAGtC,MAAMG,aAAa/B,cAAc,OAAO,CAACgC;YACzC,MAAMC,eAAejC,cAAc,OAAO,CAACkC;YAC3C,MAAMC,UAAUnC,cAAc,OAAO,CAACoC;YACtC,MAAMC,mBAAmBC,wBAAwB;gBAC7CZ;gBACAS;gBACA,QAAQxB;YACZ;YACA,MAAMoB,WAAW,SAAS,CAACE,aAAa,EAAE,EAAEI;YAC5C1C,kBAAkB,IAAMoC,WAAW,KAAK;YACxCpB,cAAc,IAAI,CACd;gBAAE,YAAYf,iBAAiB,QAAQ,GAAG,UAAU;YAAa,GACjE;YAOJH,gBAAgB,MAAM8C,qBAAqBvC;YAG3CwC,uBAAuBxC;YAMvBR,mBAAmBiD,kBAAkBzC;QACzC;QAEA,YAAY,OAAMA;YAUdR;YACA,MAAMkD,QAAQC,oBAAoB3C,eAAeX;YACjD,MAAMuD,mBAAmBnD,gBAAgBJ;YACzC,MAAMuC,aAAalC;YACnB,MAAMmD,WAAWC,QAAQ,UAAU,CAAC;gBAACJ;gBAAOE;aAAiB;YAC7D,MAAMb,aAAac,SAAS,IAAI,CAAC,IAAMlD;YAGvC,MAAMoD,UAAU,MAAMD,QAAQ,UAAU,CAAC;gBACrCJ;gBACAE;gBACAhB;gBACAG;aACH;YACD,MAAMiB,SAASD,QAAQ,IAAI,CAAC,CAACE,SAClBA,AAAkB,eAAlBA,OAAO,MAAM;YAExB,IAAID,QACA,MAAMA,OAAO,MAAM;QAE3B;IACJ;AACJ"}
|
|
@@ -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";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const consoleLogger = {
|
|
2
|
+
trace: (...args)=>console.trace(...args),
|
|
3
|
+
debug: (...args)=>console.debug(...args),
|
|
4
|
+
info: (...args)=>console.info(...args),
|
|
5
|
+
warn: (...args)=>console.warn(...args),
|
|
6
|
+
error: (...args)=>console.error(...args),
|
|
7
|
+
fatal: (...args)=>console.error(...args),
|
|
8
|
+
log: (...args)=>console.log(...args)
|
|
9
|
+
};
|
|
10
|
+
export { consoleLogger };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=consoleLogger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger/consoleLogger.js","sources":["../../src/logger/consoleLogger.ts"],"sourcesContent":["import type { Logger } from \"@webiny/api-core/features/logger/abstractions.js\";\n\n/*\n * Minimal console-backed logger for root singletons: the scheduler and the websocket message bus.\n * The real DI Logger is registered per-request (via ApiCoreFeature/LoggerFeature), so it isn't\n * resolvable at the root where these singletons are built. And even if we bridged to it, they run\n * outside any request, so there's no request context for a context-aware logger to enrich, and\n * console is functionally equivalent, not a downgrade. Revisit (a shared boot-time logger for all\n * root singletons) tracked in https://github.com/webiny/webiny-js/issues/5446.\n */\nexport const consoleLogger: Logger.Interface = {\n trace: (...args: any[]) => console.trace(...args),\n debug: (...args: any[]) => console.debug(...args),\n info: (...args: any[]) => console.info(...args),\n warn: (...args: any[]) => console.warn(...args),\n error: (...args: any[]) => console.error(...args),\n fatal: (...args: any[]) => console.error(...args),\n log: (...args: any[]) => console.log(...args)\n};\n"],"names":["consoleLogger","args","console"],"mappings":"AAUO,MAAMA,gBAAkC;IAC3C,OAAO,CAAC,GAAGC,OAAgBC,QAAQ,KAAK,IAAID;IAC5C,OAAO,CAAC,GAAGA,OAAgBC,QAAQ,KAAK,IAAID;IAC5C,MAAM,CAAC,GAAGA,OAAgBC,QAAQ,IAAI,IAAID;IAC1C,MAAM,CAAC,GAAGA,OAAgBC,QAAQ,IAAI,IAAID;IAC1C,OAAO,CAAC,GAAGA,OAAgBC,QAAQ,KAAK,IAAID;IAC5C,OAAO,CAAC,GAAGA,OAAgBC,QAAQ,KAAK,IAAID;IAC5C,KAAK,CAAC,GAAGA,OAAgBC,QAAQ,GAAG,IAAID;AAC5C"}
|