Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/api-event-handler-aws/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"dependencies": {
"@webiny/api-core": "0.0.0",
"@webiny/api-event-handler-core": "0.0.0",
"@webiny/api-file-manager-s3": "0.0.0",
"@webiny/api-scheduler-aws": "0.0.0",
"@webiny/api-websockets": "0.0.0",
"@webiny/api-websockets-aws": "0.0.0",
Expand Down
31 changes: 19 additions & 12 deletions packages/api-event-handler-aws/src/createWebinyApiHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { DynamoDBCoreFeature } from "@webiny/db-dynamodb";
import { registerApiRequestStack } from "@webiny/api-event-handler-core";
import { WebsocketsAwsFeature } from "@webiny/api-websockets-aws";
import { registerSchedulerAwsExtension } from "@webiny/api-scheduler-aws";
import { FileManagerS3Feature } from "@webiny/api-file-manager-s3";
import { WebSocketLambdaHandler } from "@webiny/api-websockets";
// CognitoIdpFeature must be in the root container so the request auth step
// (ApiGatewayIdentityLoaderDecorator → RequestIdentityLoader) sees CognitoIdentityProvider
Expand Down Expand Up @@ -107,21 +108,27 @@ export function createWebinyApiHandler(config: CreateWebinyApiHandlerConfig) {
},

request: async container => {
// The per-request feature stack is transport-agnostic (shared with the future server
// transport). The two AWS-specific interleave points are supplied as hooks.
// The per-request feature stack is transport-agnostic (shared with the server transport).
// The AWS-specific interleave points are supplied as the `transports` adapters.
await registerApiRequestStack(container, {
extensions: config.extensions,
registerRequestStorage: config.registerRequestStorage,
// Real AWS WebSocket transport (API Gateway Management API), registered right after
// WebsocketsFeature so it overrides the NullWebsocketsTransport.
registerRealtimeTransport: c => {
WebsocketsAwsFeature.register(c);
},
// Scheduler transport: the scheduler-aws extension (EventBridge Scheduler).
registerSchedulerTransport: c => {
registerSchedulerAwsExtension(c, {
getClient: schedulerConfig => createSchedulerClient(schedulerConfig)
});
transports: {
// Real AWS WebSocket transport (API Gateway Management API), registered right after
// WebsocketsFeature so it overrides the NullWebsocketsTransport.
realtime: c => {
WebsocketsAwsFeature.register(c);
},
// Scheduler transport: the scheduler-aws extension (EventBridge Scheduler).
scheduler: c => {
registerSchedulerAwsExtension(c, {
getClient: schedulerConfig => createSchedulerClient(schedulerConfig)
});
},
// File-manager storage transport: S3 (asset delivery + S3 file operations + schema).
fileManager: c => {
FileManagerS3Feature.register(c, {});
}
}
});
}
Expand Down
3 changes: 3 additions & 0 deletions packages/api-event-handler-aws/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"references": [
{ "path": "../api-core/tsconfig.build.json" },
{ "path": "../api-event-handler-core/tsconfig.build.json" },
{ "path": "../api-file-manager-s3/tsconfig.build.json" },
{ "path": "../api-scheduler-aws/tsconfig.build.json" },
{ "path": "../api-websockets/tsconfig.build.json" },
{ "path": "../api-websockets-aws/tsconfig.build.json" },
Expand All @@ -26,6 +27,8 @@
"@webiny/api-core": ["../api-core/src"],
"@webiny/api-event-handler-core/*": ["../api-event-handler-core/src/*"],
"@webiny/api-event-handler-core": ["../api-event-handler-core/src"],
"@webiny/api-file-manager-s3/*": ["../api-file-manager-s3/src/*"],
"@webiny/api-file-manager-s3": ["../api-file-manager-s3/src"],
"@webiny/api-scheduler-aws/*": ["../api-scheduler-aws/src/*"],
"@webiny/api-scheduler-aws": ["../api-scheduler-aws/src"],
"@webiny/api-websockets/*": ["../api-websockets/src/*"],
Expand Down
3 changes: 3 additions & 0 deletions packages/api-event-handler-aws/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"references": [
{ "path": "../api-core" },
{ "path": "../api-event-handler-core" },
{ "path": "../api-file-manager-s3" },
{ "path": "../api-scheduler-aws" },
{ "path": "../api-websockets" },
{ "path": "../api-websockets-aws" },
Expand All @@ -26,6 +27,8 @@
"@webiny/api-core": ["../api-core/src"],
"@webiny/api-event-handler-core/*": ["../api-event-handler-core/src/*"],
"@webiny/api-event-handler-core": ["../api-event-handler-core/src"],
"@webiny/api-file-manager-s3/*": ["../api-file-manager-s3/src/*"],
"@webiny/api-file-manager-s3": ["../api-file-manager-s3/src"],
"@webiny/api-scheduler-aws/*": ["../api-scheduler-aws/src/*"],
"@webiny/api-scheduler-aws": ["../api-scheduler-aws/src"],
"@webiny/api-websockets/*": ["../api-websockets/src/*"],
Expand Down
1 change: 0 additions & 1 deletion packages/api-event-handler-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"@webiny/api-core": "0.0.0",
"@webiny/api-file-manager": "0.0.0",
"@webiny/api-file-manager-aco": "0.0.0",
"@webiny/api-file-manager-s3": "0.0.0",
"@webiny/api-headless-cms": "0.0.0",
"@webiny/api-headless-cms-aco": "0.0.0",
"@webiny/api-headless-cms-scheduler": "0.0.0",
Expand Down
46 changes: 30 additions & 16 deletions packages/api-event-handler-core/src/registerApiRequestStack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ import { AcoFeature } from "@webiny/api-aco";
import { BackgroundTasksFeature } from "@webiny/background-tasks/api";
import { FileManagerAppFeature } from "@webiny/api-file-manager";
import { FileManagerAcoFeature } from "@webiny/api-file-manager-aco";
import { FileManagerS3Feature } from "@webiny/api-file-manager-s3";
import { WebsiteBuilderFeature, setupWebsiteBuilderModels } from "@webiny/api-website-builder";
import { WebsiteBuilderWorkflowsFeature } from "@webiny/api-website-builder-workflows";
import { WebsiteBuilderSchedulerFeature } from "@webiny/api-website-builder-scheduler";
import { WebsocketsFeature } from "@webiny/api-websockets";
import { WorkflowsFeature } from "@webiny/api-workflows";
import { SchedulerFeature } from "@webiny/api-scheduler";

/** Installs a flavour-specific transport adapter into the per-request container at its interleave point. */
export type TransportRegistrar = (container: Container) => void | Promise<void>;

export interface RegisterApiRequestStackConfig {
/**
* Project-defined extensions, applied at register() time (so extension features — including
Expand All @@ -36,18 +38,30 @@ export interface RegisterApiRequestStackConfig {
*/
registerRequestStorage?: (container: Container) => void | Promise<void>;
/**
* Register the real-time (websockets) transport, run immediately AFTER the transport-agnostic
* `WebsocketsFeature`. On AWS this is `WebsocketsAwsFeature` (API Gateway Management API); it
* MUST register after WebsocketsFeature so it overrides the NullWebsocketsTransport
* (nearest-container-last-wins), otherwise every server→client send() is a silent no-op.
* Optional — omit for transports with no real-time push.
*/
registerRealtimeTransport?: (container: Container) => void | Promise<void>;
/**
* Register the scheduler transport, run AFTER `SchedulerFeature` and BEFORE `CmsSchedulerFeature`.
* On AWS this bridges the scheduler-aws extension (EventBridge Scheduler). Optional.
* Flavour-specific transport adapters, each installed at its exact interleave point in the stack.
* Every entry follows the same shape: it runs immediately AFTER the transport-agnostic domain
* Feature has registered its NULL default, and overrides that default (nearest-container-last-wins)
* with the real adapter. Each is optional — omit one for a deployment/transport that lacks that
* capability. AWS supplies AWS adapters (API Gateway Management API / EventBridge / S3); the
* self-hosted server supplies in-process adapters (server WebSockets / Bree / local disk).
*/
registerSchedulerTransport?: (container: Container) => void | Promise<void>;
transports?: {
/**
* Real-time (WebSockets) transport, run right after `WebsocketsFeature`. Overrides the
* NullWebsocketsTransport, otherwise every server→client send() is a silent no-op.
*/
realtime?: TransportRegistrar;
/**
* Scheduler transport, run AFTER `SchedulerFeature` and BEFORE `CmsSchedulerFeature`.
*/
scheduler?: TransportRegistrar;
/**
* File-manager storage transport, run AFTER `FileManagerAppFeature` (which registers the
* AssetDeliveryRoute + NULL asset-delivery impls). Overrides those nulls with real impls and
* adds the file-operation features.
*/
fileManager?: TransportRegistrar;
};
}

/**
Expand Down Expand Up @@ -86,10 +100,10 @@ export async function registerApiRequestStack(
AcoHcmsFeature.register(container);
HcmsTasksFeature.register(container);

// ── File Manager ───────────────────────────────────────────
// ── File Manager (domain) + storage transport ──────────────
FileManagerAppFeature.register(container);
FileManagerAcoFeature.register(container);
FileManagerS3Feature.register(container, {});
await config.transports?.fileManager?.(container);

// ── Website Builder ────────────────────────────────────────
WebsiteBuilderFeature.register(container);
Expand All @@ -99,7 +113,7 @@ export async function registerApiRequestStack(

// ── Websockets (domain) + real-time transport ──────────────
WebsocketsFeature.register(container);
await config.registerRealtimeTransport?.(container);
await config.transports?.realtime?.(container);

// ── Supporting services ────────────────────────────────────
MailerFeature.register(container);
Expand All @@ -114,7 +128,7 @@ export async function registerApiRequestStack(

// ── Scheduler + scheduler transport ────────────────────────
SchedulerFeature.register(container);
await config.registerSchedulerTransport?.(container);
await config.transports?.scheduler?.(container);
CmsSchedulerFeature.register(container);

// ── Extensions ─────────────────────────────────────────────
Expand Down
3 changes: 0 additions & 3 deletions packages/api-event-handler-core/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
{ "path": "../api-core/tsconfig.build.json" },
{ "path": "../api-file-manager/tsconfig.build.json" },
{ "path": "../api-file-manager-aco/tsconfig.build.json" },
{ "path": "../api-file-manager-s3/tsconfig.build.json" },
{ "path": "../api-headless-cms/tsconfig.build.json" },
{ "path": "../api-headless-cms-aco/tsconfig.build.json" },
{ "path": "../api-headless-cms-scheduler/tsconfig.build.json" },
Expand Down Expand Up @@ -44,8 +43,6 @@
"@webiny/api-file-manager": ["../api-file-manager/src"],
"@webiny/api-file-manager-aco/*": ["../api-file-manager-aco/src/*"],
"@webiny/api-file-manager-aco": ["../api-file-manager-aco/src"],
"@webiny/api-file-manager-s3/*": ["../api-file-manager-s3/src/*"],
"@webiny/api-file-manager-s3": ["../api-file-manager-s3/src"],
"@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"],
"@webiny/api-headless-cms": ["../api-headless-cms/src"],
"@webiny/api-headless-cms-aco/*": ["../api-headless-cms-aco/src/*"],
Expand Down
3 changes: 0 additions & 3 deletions packages/api-event-handler-core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
{ "path": "../api-core" },
{ "path": "../api-file-manager" },
{ "path": "../api-file-manager-aco" },
{ "path": "../api-file-manager-s3" },
{ "path": "../api-headless-cms" },
{ "path": "../api-headless-cms-aco" },
{ "path": "../api-headless-cms-scheduler" },
Expand Down Expand Up @@ -44,8 +43,6 @@
"@webiny/api-file-manager": ["../api-file-manager/src"],
"@webiny/api-file-manager-aco/*": ["../api-file-manager-aco/src/*"],
"@webiny/api-file-manager-aco": ["../api-file-manager-aco/src"],
"@webiny/api-file-manager-s3/*": ["../api-file-manager-s3/src/*"],
"@webiny/api-file-manager-s3": ["../api-file-manager-s3/src"],
"@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"],
"@webiny/api-headless-cms": ["../api-headless-cms/src"],
"@webiny/api-headless-cms-aco/*": ["../api-headless-cms-aco/src/*"],
Expand Down
3 changes: 3 additions & 0 deletions packages/api-event-handler-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
"dependencies": {
"@webiny/api-core": "0.0.0",
"@webiny/api-event-handler-core": "0.0.0",
"@webiny/api-file-manager-server": "0.0.0",
"@webiny/api-scheduler-server": "0.0.0",
"@webiny/api-websockets-server": "0.0.0",
"@webiny/background-tasks-server": "0.0.0",
"@webiny/event-handler-core": "0.0.0",
"@webiny/event-handler-server": "0.0.0",
"@webiny/handler": "0.0.0"
Expand Down
57 changes: 46 additions & 11 deletions packages/api-event-handler-server/src/createWebinyApiHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
* plus the auth/tenant loader decorators (extract token / x-tenant from the IncomingMessage → shared
* RequestIdentityLoader / RequestTenantLoader). The per-request feature stack is the transport-agnostic
* `registerApiRequestStack` from `@webiny/api-event-handler-core` — the SAME stack the AWS handler uses.
* It is called with NEITHER the realtime nor the scheduler hook: those are AWS-specific (API Gateway
* WebSockets / EventBridge Scheduler) and this transport has no equivalent — the hooks being optional
* is the point. The storage variant (and its identity provider) is injected via `registerRootStorage`.
* Both interleave hooks are supplied with SINGLE-PROCESS, in-process equivalents of the AWS transports:
* the realtime hook installs the server WebSockets transport (vs AWS's API Gateway Management API), and
* the scheduler hook installs the Bree/in-process scheduler (vs AWS's EventBridge Scheduler). Background
* tasks are wired in the ROOT container (below), mirroring how the AWS handler registers its background-
* task transport at root. The storage variant (and its identity provider) is injected via `registerRootStorage`.
*
* The identity provider (e.g. `@webiny/self-hosted-auth`'s JWT IdP) must be registered by the variant
* in `registerRootStorage`, so the RequestIdentityLoader driven by the identity decorator can resolve it.
Expand All @@ -23,6 +25,9 @@ import {
WebsocketsConnectionManager,
attachWebsocketsServer
} from "@webiny/api-websockets-server";
import { BackgroundTasksServerFeature } from "@webiny/background-tasks-server";
import { registerSchedulerServerExtension } from "@webiny/api-scheduler-server";
import { FileManagerServerFeature } from "@webiny/api-file-manager-server";
import { NodeHttpIdentityLoaderDecorator } from "~/handlers/NodeHttpIdentityLoaderDecorator.js";
import { NodeHttpTenantLoaderDecorator } from "~/handlers/NodeHttpTenantLoaderDecorator.js";

Expand Down Expand Up @@ -63,25 +68,55 @@ export function createWebinyApiHandler(config: CreateWebinyApiHandlerConfig) {
// connection registry (ConnectionRegistry) is the storage variant's job (e.g. sql).
container.register(ServerConnectionManager).inSingletonScope();
container.register(NodeWsAdapter).inSingletonScope();

// ── Background tasks (root) ────────────────────────────────
// Mirrors the AWS handler registering its background-task transport at root. There is no
// Step Functions / Lambda re-invocation in a single process, so the transport is in-process:
// WorkerService (the BackgroundTasks/TaskService dispatch abstraction) runs each triggered
// task in a Node worker_thread that POSTs back to this server's `/background-task` HTTP route
// (BackgroundTaskRoute), which runs the task loop (continue/timeout/abort) in-process. Root,
// not per-request, is load-bearing: the shared InternalToken (registered here as a singleton)
// gates the route against the worker's callback, so dispatcher and route MUST see the SAME
// token — a per-request registration would mint a fresh token per request and always 403.
// The route is an HttpRoute; the per-request HttpRouter collects it via the parent chain.
BackgroundTasksServerFeature.register(container);
},

request: async container => {
// The transport-agnostic per-request stack. The realtime hook installs the server
// WebSockets transport (overriding the domain's NullWebsocketsTransport); it resolves the
// shared connection manager + adapter from the root. No scheduler hook — that's AWS-only.
// shared connection manager + adapter from the root. The scheduler hook installs the
// Bree/in-process scheduler transport (the single-process equivalent of EventBridge).
await registerApiRequestStack(container, {
extensions: config.extensions,
// Why a hook (and not just registering the transport ourselves): `.register()` calls
// Why hooks (and not just registering the transports ourselves): `.register()` calls
// are otherwise order-independent — you can register Features in any order, because
// they only REGISTER, they don't RESOLVE during registration (resolution happens
// later, in Initializers / SchemaFactories). The one thing that makes order matter is
// a DEFAULT registration: `WebsocketsFeature` registers `NullWebsocketsTransport` so
// the abstraction is always resolvable. Overriding it (last-registration-wins) means
// our transport MUST be registered AFTER `WebsocketsFeature`. This hook is the seam
// `registerApiRequestStack` provides for exactly that — it runs right after the Null
// a DEFAULT registration: e.g. `WebsocketsFeature` registers `NullWebsocketsTransport`
// so the abstraction is always resolvable. Overriding it (last-registration-wins) means
// our transport MUST be registered AFTER that Feature. These hooks are the seams
// `registerApiRequestStack` provides for exactly that — each runs right after its Null
// default, so the override is guaranteed without the caller knowing the internal order.
registerRealtimeTransport: requestContainer => {
requestContainer.register(ServerWebsocketsTransport);
transports: {
// Server WebSockets transport; resolves the shared connection manager + adapter
// from the root (registered as singletons above).
realtime: requestContainer => {
requestContainer.register(ServerWebsocketsTransport);
},
// Scheduler transport: the Bree/in-process extension. Where AWS bridges EventBridge
// Scheduler, the single-process server drives delayed/scheduled action triggers with
// in-process timers (Bree).
scheduler: requestContainer => {
registerSchedulerServerExtension(requestContainer);
},
// File-manager storage transport: local disk. Where AWS uses S3 (+ a separate asset-
// delivery Lambda), the single-process server stores files on disk and serves them
// in-process — FileManagerServerFeature registers local asset delivery (overriding
// the domain's null impls), the upload/multipart HTTP routes, and disk file ops.
fileManager: requestContainer => {
FileManagerServerFeature.register(requestContainer);
}
}
});
},
Expand Down
Loading
Loading