Skip to main content

Request Pipeline

@drumee/server-core is the middleware foundation for all Drumee backend services. Every HTTP request — file management, sharing, messaging, user administration — flows through the same ordered pipeline before any service code runs.

Pipeline Stages

Each stage is a class that communicates with the next via named events. No stage calls the next one directly.

HTTP Request


┌──────────────────────────────────────┐
│ Input │ Parse URL, headers, cookies,
│ lib/input.js │ multipart uploads, body.
│ │ Emits: INPUT_READY
└──────────────────┬───────────────────┘
│ INPUT_READY

┌──────────────────────────────────────┐
│ Session │ Identify hub, resolve user
│ lib/session.js │ from session cookie, handle
│ │ login / OTP / mimic flows.
│ │ Emits: READY / START
└──────────────────┬───────────────────┘
│ READY

┌──────────────────────────────────────┐
│ Acl │ Check bitwise permissions on
│ lib/acl.js │ the requested service, source
│ │ node, and destination node.
│ │ Emits: GRANTED / DENIED
└──────────────────┬───────────────────┘
│ GRANTED

┌──────────────────────────────────────┐
│ Entity (extends Acl) │ Execute service logic; send
│ lib/entity.js │ hub / user / email notifications;
│ │ spawn background tasks.
└──────────────┬───────────────────────┘

┌───────┴──────────────────┐
▼ ▼
┌───────────────┐ ┌──────────────────────┐
│ Mfs / FileIo │ │ Generator / │
│ (file ops) │ │ Document │
│ │ │ (media conversion) │
└──────┬────────┘ └────────┬─────────────┘
└────────────┬─────────────┘

┌──────────────────────────────────────┐
│ Output │ Serialize response (JSON / HTML /
│ lib/output.js │ media), set headers, cookies, CORS.
│ │ Emits: SENT
└──────────────────────────────────────┘


HTTP Response

If any stage encounters an error it delegates to Exception (lib/exception.js), which sends a formatted error response and terminates the pipeline.

Module Responsibilities

ModuleFileRole
Inputlib/input.jsParse raw HTTP request into a normalised data structure
Sessionlib/session.jsIdentify hub; authenticate user; load context
Acllib/acl.jsEnforce bitwise access control rules
Entitylib/entity.jsExtend Acl with notification and service execution utilities
Outputlib/output.jsFormat and write the HTTP response
Datalib/data.jsWrap request payload; extract service name, module, method, recipient
Userlib/user.jsExpose user profile, locale, identity helpers
Mfslib/mfs.jsVirtual filesystem abstraction backed by MariaDB
FileIolib/file-io.jsStream physical files; serve media with proper format headers
Exceptionlib/exception.jsEmit structured HTTP error responses
RuntimeEnvlib/runtimeEnv.jsBuild client-facing runtime configuration (bundles, locale, hub settings)
Pagelib/page.jsRender Lodash HTML templates with runtime context injected
Generatorlib/utils/generator.jsConvert media files (images, video, audio, documents) into derived formats
Documentlib/utils/document.jsIndex and rebuild document previews in background subprocesses

Pipeline Events

Stages communicate exclusively through named events. This decoupling means any stage can be replaced or short-circuited without modifying upstream code.

EventEmitted byConsumed by
INPUT_READYInputSession
READYSessionAcl / calling code
STARTSessionAcl / calling code
GRANTEDAclEntity / service handler
DENIEDAclException
ERRORAny stageException
SENTOutputCleanup / connection tracking
END_OF_SESSIONSessionCleanup
precondition_failedInputException

Session Lifecycle

1. Input emits INPUT_READY
2. Session._selectSession()
├─ _initHub() → load hub config from YP (by Host header)
└─ _initUser() → resolve user from session cookie
├─ Normal user → load profile + settings JSON
├─ Guest / DMZ → dmz_login()
├─ OTP pending → send_otp()
└─ Mimic → validate impersonation window
3. Session emits READY (with hub + user context set)

User States

StateMeaning
activeNormal login
newFirst-time user
otpAwaiting one-time password
onlineAlready connected
offlineInactive
frozen / locked / archivedAccess denied
systemInternal service identity

Mimic (impersonation): A privileged user can mimic another. The session validates that the mimic window is still open on every request; if expired, access is denied.

Response Envelope

Every JSON response from Output is wrapped in a standard envelope:

{
"__ack__": "<service name>",
"__status__": "<ok | error | …>",
"__expiry__": "<cache TTL>",
"__timestamp__": "<ms since epoch>",
"data": { }
}

Convenience methods on the output object:

MethodUse
output.data(obj)JSON data response
output.row(obj)Single database record
output.rows(arr)Multiple records
output.list(arr)Array payload
output.html(str)HTML page
output.redirect(url)302 redirect

Cookies are always set httpOnly, SameSite=Strict, and scoped to the hub domain.

Error Handling

Exception maps semantic errors to HTTP status codes:

MethodHTTP code
exception.server()500
exception.user()400
exception.unauthorized()401
exception.forbiden()403
exception.not_found()404
exception.reject()405
exception.precondition()412
exception.fatal()512

All methods accept an optional reason field for context that is logged server-side but not exposed to the client.

YP — Yellow Pages Service Registry

YP is Drumee's internal RPC and registry abstraction backed by MariaDB stored procedures. It is injected into each pipeline stage via initialize(opt) and used throughout.

// Typical YP call pattern
const rows = await this.yp.await_proc("procedure_name", arg1, arg2);

YP is the primary mechanism for:

  • Loading hub and user data during session initialisation
  • Resolving MFS nodes and permissions in the ACL layer
  • Any database read/write from service handlers

Multi-tenancy is enforced at the YP level: each hub has its own database schema, and YP routes calls to the correct schema automatically. See Database Sharding & the Entity Pool for how those per-entity databases are provisioned.

Plugin / Extensibility Model

New backend services can be added without modifying server-core. A plugin is a separate Node.js package that:

  1. Extends Entity (or Acl) from this library for its service handlers.
  2. Declares its services in a JSON config file (same format as the ACL spec).
  3. Is registered in the Drumee service loader alongside core services.

The permission and pipeline infrastructure is inherited automatically. The plugin only needs to implement service-specific business logic. server-core is an infrastructure boundary — features live in plugins, core provides the contract.

Key Conventions

Service naming: module.method — e.g. yp.get_env, media.copy, share.link. The ACL and input parsing both rely on this dot-separated format.

initialize(opt) pattern: No class uses a traditional constructor for setup. All wiring happens in initialize(opt), making it straightforward to inject mocks or reconfigure a stage in tests.

Physical file paths follow a predictable template:

{mfs_root}/{node_path}/orig.{ext}          ← original
{mfs_root}/{node_path}/{format}.{ext} ← derived (preview, slide, …)
{mfs_root}/{node_path}/info.json ← metadata cache

Async strategy: Database and I/O calls use async/await (e.g. await this.yp.await_proc(...)). Media generation also uses async/await. Long-running calls fork child processes in fire-and-forget mode (detached: true) or queue them; results are pushed back through WebSocket.