Skip to content

feat(self-hosted): background tasks, scheduler & file manager for the server flavour#5408

Merged
adrians5j merged 9 commits into
nextfrom
adrian/self-hosted-server-bg-scheduler-fm
Jul 15, 2026
Merged

feat(self-hosted): background tasks, scheduler & file manager for the server flavour#5408
adrians5j merged 9 commits into
nextfrom
adrian/self-hosted-server-bg-scheduler-fm

Conversation

@adrians5j

Copy link
Copy Markdown
Member

Ports the AWS-only background tasks, scheduler, and file manager capabilities to the self-hosted Node server flavour, following the transport-hook pattern established by the WebSockets work (#5393). Builds on the already-shipped server packages (background-tasks-server, api-scheduler-server, api-file-manager-server) — this PR wires them into the event handler and fixes the gaps that surfaced running them in a single long-running process.

Background tasks

  • Register BackgroundTasksServerFeature in the server handler root (in-process worker_thread runner + /background-task route). Root, not per-request — the shared InternalToken that gates the route must be a stable singleton, else the worker's callback 403s.
  • WorkerService reads the runtime PORT env (what runApiServer injects and the server listens on) instead of the unset SERVER_PORT build param.

Scheduler

  • Wire registerSchedulerServerExtension via the registerSchedulerTransport hook (Bree/in-process, vs EventBridge).
  • Register its RequestContextInitializer via register() rather than registerInstance()@webiny/di runs registerInstance initializers first, so it was running before SchedulerModelContextualSchema registered ScheduledActionModel ("No registration found for ScheduledActionModel").

File manager

  • Add a registerFileManagerTransport hook to the shared request stack, replacing the hardcoded FileManagerS3Feature — AWS passes S3, server passes FileManagerServerFeature. (Third transport hook alongside realtime + scheduler.)
  • FileManagerServerFeature now also registers: local asset delivery (createLocalAssetDeliveryFeature), the upload GraphQL schema (mirrors S3 query names so the SDK is unchanged), and the domain Read/WriteFileMetadata features.
  • FileManagerServerConfig resolves lazily — build params aren't registered at register() time (they land with project extensions later in the request stack).
  • New Infra.FileStorage extension bakes WEBINY_LOCAL_STORAGE_PATH + WEBINY_UPLOAD_SECRET build params (mirrors Infra.Sqlite / Infra.Crypto.Encryption).
  • resolveServerUrl derives the URL from WEBINY_API_URL/PORT — the DI-native gql context exposes no request headers to resolvers.
  • SettingsInstaller falls back to WEBINY_API_URL for srcPrefix when there's no CloudFront manifest (AWS-safe: AWS still uses the manifest domain).

Transport (event-handler-server)

  • Read binary request bodies as raw Buffers — the previous raw += chunk utf8-corrupted multipart uploads.
  • Write Buffer response bodies raw — image delivery was JSON.stringify'd, so browsers ERR_BLOCKED_BY_ORB'd it.

Verified

Manually: background-task dispatch, scheduler bind, and the full file-manager loop (upload → disk → metadata → transform → deliver, image renders in admin). All changed packages typecheck; adio + oxlint clean.

Follow-ups (not in this PR)

  • The three request-seam hooks (registerRealtimeTransport, registerSchedulerTransport, registerFileManagerTransport) are structurally identical — candidate to collapse into a single transports: {} config.
  • Scheduler builds a new Bree service per request; should be a boot-time root singleton (like WebSockets onServer).
  • No automated tests yet for the server upload/deliver loop.

🤖 Generated with Claude Code

adrians5j and others added 5 commits July 13, 2026 16:42
… to the server flavour

Wire the AWS-only background-tasks, scheduler, and file-manager capabilities into the
self-hosted Node server flavour, plus the transport fixes needed to run them in a single
long-running process.

Background tasks:
- Register BackgroundTasksServerFeature in the server handler root (in-process worker_thread
  runner + /background-task route). Root, not per-request, so the shared InternalToken gate
  is a stable singleton.
- WorkerService reads the runtime PORT env instead of the (unset) SERVER_PORT build param.

Scheduler:
- Wire registerSchedulerServerExtension via the registerSchedulerTransport hook.
- Register its RequestContextInitializer via register() (not registerInstance) so it runs
  after SchedulerModelContextualSchema registers ScheduledActionModel.

File manager:
- Add a registerFileManagerTransport hook to the shared request stack (replaces the
  hardcoded FileManagerS3Feature); AWS passes S3, server passes FileManagerServerFeature.
- FileManagerServerFeature: register local asset delivery, the upload GraphQL schema
  (mirrors S3 query names so the SDK is unchanged), and the domain Read/WriteFileMetadata
  features; resolve config lazily (build params are not registered at register() time).
- Add Infra.FileStorage (bakes WEBINY_LOCAL_STORAGE_PATH + WEBINY_UPLOAD_SECRET build params).
- resolveServerUrl derives the URL from WEBINY_API_URL/PORT (the DI-native gql context
  exposes no request headers to resolvers).
- SettingsInstaller falls back to WEBINY_API_URL for srcPrefix when there is no CloudFront
  manifest (AWS-safe).

Transport (event-handler-server):
- Read binary request bodies as raw Buffers (multipart uploads were utf8-corrupted).
- Write Buffer response bodies raw (image delivery was JSON-serialized -> ORB-blocked).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nsports`

Collapse the three structurally-identical hooks (registerRealtimeTransport /
registerSchedulerTransport / registerFileManagerTransport) into a single
`transports: { realtime?, scheduler?, fileManager? }` object on
RegisterApiRequestStackConfig, plus a shared `TransportRegistrar` type. Behaviour
unchanged — the adapters still run at the same interleave points. Makes "these are all
transport adapters" one named concept and adding a future request-seam transport a single
key rather than a new top-level param. Updates the AWS and server handlers to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `assetDelivery` config knob was never set (both S3 and server callers pass no
config), so it was speculative parity. Remove the interface + param; register with
`createLocalAssetDeliveryFeature()` defaults.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rver-bg-scheduler-fm

# Conflicts:
#	packages/project-server/src/infra.ts
@adrians5j

Copy link
Copy Markdown
Member Author

/vitest

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

Vitest tests have been initiated (for more information, click here). ✨

Group Status
No storage ✅ 53/53 passed
DDB ✅ 17/17 passed
DDB+OS ❌ 18/20 passed
SQL ✅ 10/10 passed
PGlite ❌ 9/10 passed
❌ Failed packages

DDB+OS

  • api-file-manager
  • api-headless-cms-es-tasks

PGlite

  • api-websockets

@adrians5j

Copy link
Copy Markdown
Member Author

/vitest

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

Vitest tests have been initiated (for more information, click here). ✨

Group Status
No storage ✅ 53/53 passed
DDB ✅ 17/17 passed
DDB+OS ✅ 20/20 passed
SQL ✅ 10/10 passed
PGlite ❌ 0/3 passed
❌ Failed packages

PGlite

  • api-websockets

@adrians5j
adrians5j merged commit 911e026 into next Jul 15, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant