@molecule/api-resource-status-page
v1.0.2
Published
System status page resource for molecule.dev.
Maintainers
Readme
@molecule/api-resource-status-page
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Status page resource for molecule.dev.
Public status-page backend: monitored services, incidents with lifecycle updates, health-check history, and uptime windows. The read endpoints power a public status page; the mutation endpoints are for operators.
Quick Start
import { createRequestHandler } from '@molecule/api-resource'
import { createRequestHandlerMap, routes } from '@molecule/api-resource-status-page'
// The handler map is a FACTORY (like api-resource-device) — build it with
// the createRequestHandler from @molecule/api-resource (mlcl inject does this):
const requestHandlerMap = createRequestHandlerMap(createRequestHandler)
// Public reads: GET /status, GET /status/services, GET /status/services/:id,
// GET /status/incidents, GET /status/uptime
// Admin-only: POST/PATCH/DELETE /status/services(/:id),
// POST/PATCH /status/incidents(/:id)Type
resource
Installation
npm install @molecule/api-resource-status-page @molecule/api-bond @molecule/api-database @molecule/api-i18n @molecule/api-locales-status-page @molecule/api-monitoring @molecule/api-notifications @molecule/api-permissions @molecule/api-resource @molecule/api-scheduler zodAPI
Types
CheckProps
Health check result properties.
type CheckProps = z.infer<typeof checkPropsSchema>CreateIncidentProps
Fields accepted when creating a new incident.
type CreateIncidentProps = z.infer<typeof createIncidentPropsSchema>CreateServiceProps
Fields accepted when creating a new service.
type CreateServiceProps = z.infer<typeof createServicePropsSchema>IncidentProps
Full incident record properties.
type IncidentProps = z.infer<typeof incidentPropsSchema>ServiceProps
Full service record properties.
type ServiceProps = z.infer<typeof servicePropsSchema>UpdateIncidentProps
Updatable incident fields.
type UpdateIncidentProps = z.infer<typeof updateIncidentPropsSchema>UpdateServiceProps
Updatable service fields.
type UpdateServiceProps = z.infer<typeof updateServicePropsSchema>UptimeWindowProps
Uptime window statistics properties.
type UptimeWindowProps = z.infer<typeof uptimeWindowPropsSchema>Functions
createRequestHandlerMap(createRequestHandler)
Creates the full request handler map for the Status resource. Maps handler names
(matching route definitions) to Express middleware via createRequestHandler.
requireAdmin is the status-management authorizer middleware referenced by the
mutating routes (createService/updateService/deleteService/createIncident/
updateIncident). It must live here as a real handler-map key so the mlcl
injector's route scanner preserves it — a bare middleware string that isn't a
handler-map key is silently dropped (which is why the previous 'auth' gate was
inert and the mutating routes shipped public). It is already an Express
middleware, so it is NOT wrapped in createRequestHandler.
function createRequestHandlerMap(
createRequestHandler: (
handler: Handler,
) => (req: MoleculeRequest, res: MoleculeResponse, next: MoleculeNextFunction) => Promise<void>,
): Record<string, MoleculeRequestHandler>createRequestHandler— Factory from@molecule/api-resourcethat wraps handler configs into Express middleware.
Returns: A record mapping handler names to Express middleware functions.
createResource()
Creates a new status resource definition.
function createResource(): types.Resource<unknown>Returns: The status resource descriptor with name, tableName, and schema.
isStatusAdmin(res)
Resolves whether the current request's session belongs to an actor authorized
to administer the status page (create/update/delete services + incidents).
Fail-closed: returns false when there is no authenticated session, and
otherwise only true when the session carries an admin claim or a bonded
permissions provider grants the manage status permission.
Use this for in-handler defense-in-depth (it does not depend on the route middleware being preserved by the injector).
function isStatusAdmin(res: MoleculeResponse): Promise<boolean>res— The response whoselocals.sessionis inspected.
Returns: true when the session is an authorized status page admin.
requireAdmin()
Route middleware that gates the admin-only status page mutation routes. Calls
next() only for an authenticated status page admin; otherwise forwards an
error to the framework error handler — Unauthorized when no session is
present, Forbidden when the session is authenticated but not authorized to
manage the status page.
Exposed as a request-handler-map key so the injector's route scanner keeps it
(unlike the inert global 'auth' string, which is dropped).
function requireAdmin(): MoleculeRequestHandlerReturns: An Express-compatible middleware function.
Constants
checkPropsSchema
Schema for a single health check result.
const checkPropsSchema: z.ZodObject<
{
id: z.ZodString
serviceId: z.ZodString
status: z.ZodEnum<{ up: 'up'; down: 'down'; degraded: 'degraded' }>
httpStatus: z.ZodOptional<z.ZodNumber>
latencyMs: z.ZodOptional<z.ZodNumber>
error: z.ZodOptional<z.ZodString>
checkedAt: z.ZodString
},
z.core.$strip
>createIncidentPropsSchema
Schema for creating a new incident.
const createIncidentPropsSchema: z.ZodObject<
{
status: z.ZodEnum<{
investigating: 'investigating'
identified: 'identified'
monitoring: 'monitoring'
resolved: 'resolved'
}>
serviceId: z.ZodString
title: z.ZodString
description: z.ZodOptional<z.ZodString>
severity: z.ZodEnum<{ minor: 'minor'; major: 'major'; critical: 'critical' }>
autoDetected: z.ZodDefault<z.ZodBoolean>
startedAt: z.ZodString
},
z.core.$strip
>createServicePropsSchema
Schema for creating a new service.
const createServicePropsSchema: z.ZodObject<
{
name: z.ZodString
url: z.ZodString
method: z.ZodDefault<z.ZodEnum<{ GET: 'GET'; HEAD: 'HEAD'; POST: 'POST' }>>
expectedStatus: z.ZodDefault<z.ZodNumber>
timeoutMs: z.ZodDefault<z.ZodNumber>
intervalMs: z.ZodDefault<z.ZodNumber>
groupName: z.ZodOptional<z.ZodString>
enabled: z.ZodDefault<z.ZodBoolean>
},
z.core.$strip
>i18nNamespace
The i18n namespace for the status resource.
const i18nNamespace: 'status'i18nRegistered
The i18n registered.
const i18nRegistered: trueincidentPropsSchema
Full schema for a service incident.
const incidentPropsSchema: z.ZodObject<
{
id: z.ZodString
createdAt: z.ZodString
updatedAt: z.ZodString
serviceId: z.ZodString
title: z.ZodString
description: z.ZodOptional<z.ZodString>
severity: z.ZodEnum<{ minor: 'minor'; major: 'major'; critical: 'critical' }>
status: z.ZodEnum<{
investigating: 'investigating'
identified: 'identified'
monitoring: 'monitoring'
resolved: 'resolved'
}>
autoDetected: z.ZodDefault<z.ZodBoolean>
startedAt: z.ZodString
resolvedAt: z.ZodOptional<z.ZodString>
},
z.core.$strip
>resource
The status resource definition.
const resource: types.Resource<unknown>routes
Route array for status page endpoints: public read routes and admin-gated mutation routes.
const routes: (
| { method: 'get'; path: string; middlewares: never[]; handler: string }
| { method: 'post'; path: string; middlewares: string[]; handler: string }
| { method: 'patch'; path: string; middlewares: string[]; handler: string }
| { method: 'delete'; path: string; middlewares: string[]; handler: string }
)[]servicePropsSchema
Full schema for a monitored service.
const servicePropsSchema: z.ZodObject<
{
id: z.ZodString
createdAt: z.ZodString
updatedAt: z.ZodString
name: z.ZodString
url: z.ZodString
method: z.ZodDefault<z.ZodEnum<{ GET: 'GET'; HEAD: 'HEAD'; POST: 'POST' }>>
expectedStatus: z.ZodDefault<z.ZodNumber>
timeoutMs: z.ZodDefault<z.ZodNumber>
intervalMs: z.ZodDefault<z.ZodNumber>
groupName: z.ZodOptional<z.ZodString>
enabled: z.ZodDefault<z.ZodBoolean>
},
z.core.$strip
>STATUS_ADMIN_PERMISSION
Session-claim permission string ('status:manage') that, when present in a
session's permissions array, grants status page administration without a
bonded permissions provider.
const STATUS_ADMIN_PERMISSION: 'status:manage'STATUS_PERMISSION_ACTION
Permission action checked against @molecule/api-permissions for status page
administration.
const STATUS_PERMISSION_ACTION: 'manage'STATUS_PERMISSION_RESOURCE
Permission resource checked against @molecule/api-permissions for status page
administration.
const STATUS_PERMISSION_RESOURCE: 'status'updateIncidentPropsSchema
Schema for updating an existing incident (all fields optional).
const updateIncidentPropsSchema: z.ZodObject<
{
status: z.ZodOptional<
z.ZodEnum<{
investigating: 'investigating'
identified: 'identified'
monitoring: 'monitoring'
resolved: 'resolved'
}>
>
title: z.ZodOptional<z.ZodString>
description: z.ZodOptional<z.ZodOptional<z.ZodString>>
severity: z.ZodOptional<z.ZodEnum<{ minor: 'minor'; major: 'major'; critical: 'critical' }>>
resolvedAt: z.ZodOptional<z.ZodOptional<z.ZodString>>
},
z.core.$strip
>updateServicePropsSchema
Schema for updating an existing service (all fields optional).
const updateServicePropsSchema: z.ZodObject<
{
name: z.ZodOptional<z.ZodString>
url: z.ZodOptional<z.ZodString>
method: z.ZodOptional<z.ZodDefault<z.ZodEnum<{ GET: 'GET'; HEAD: 'HEAD'; POST: 'POST' }>>>
expectedStatus: z.ZodOptional<z.ZodDefault<z.ZodNumber>>
timeoutMs: z.ZodOptional<z.ZodDefault<z.ZodNumber>>
intervalMs: z.ZodOptional<z.ZodDefault<z.ZodNumber>>
groupName: z.ZodOptional<z.ZodOptional<z.ZodString>>
enabled: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>
},
z.core.$strip
>uptimeWindowPropsSchema
Schema for a pre-computed uptime statistics window.
const uptimeWindowPropsSchema: z.ZodObject<
{
id: z.ZodString
serviceId: z.ZodString
window: z.ZodEnum<{ '1h': '1h'; '24h': '24h'; '7d': '7d'; '30d': '30d'; '90d': '90d' }>
uptimePct: z.ZodNumber
totalChecks: z.ZodNumber
upChecks: z.ZodNumber
avgLatencyMs: z.ZodNumber
},
z.core.$strip
>Namespaces
handlers
Members:
handlers.createIncident— function: Creates a new incident record. Validates the request body againsthandlers.createService— function: Creates a new monitored service. Validates the request body againsthandlers.deleteService— function: Deletes a monitored service by ID. Returns 404 if the service does not exist.handlers.getService— function: Returns a single service by ID along with the last 50 check results.handlers.getStatus— function: Returns the aggregated system health status.handlers.getUptime— function: Returns uptime window statistics for all services, or filtered byhandlers.listIncidents— function: Lists incidents with support for?status=filtering and paginationhandlers.listServices— function: Lists all services along with each service's latest check result.handlers.updateIncident— function: Updates an existing incident by ID. Validates the request body againsthandlers.updateService— function: Updates an existing monitored service by ID. Validates the request body
types
Members:
types.CheckProps— type: Health check result properties.types.CreateIncidentProps— type: Fields accepted when creating a new incident.types.CreateServiceProps— type: Fields accepted when creating a new service.types.IncidentProps— type: Full incident record properties.types.ServiceProps— type: Full service record properties.types.UpdateIncidentProps— type: Updatable incident fields.types.UpdateServiceProps— type: Updatable service fields.types.UptimeWindowProps— type: Uptime window statistics properties.types.Resource— type: An object describing thestatusresource.
z
Members:
z.core— namespacez.infer— typez.output— typez.input— typez.JSONType— typez.globalRegistry— constz.GlobalMeta— interfacez.registry— functionz.config— functionz.$output— constz.$input— constz.$brand— constz.clone— functionz.regexes— namespacez.treeifyError— functionz.prettifyError— functionz.formatError— functionz.flattenError— functionz.TimePrecision— constz.util— namespacez.NEVER— const: A special constant with typeneverz.toJSONSchema— functionz.fromJSONSchema— function: Converts a JSON Schema to a Zod schema. This function should be considered semi-experimental. It's behavior is liable to change.z.locales— namespacez.ZodISODateTime— interfacez.ZodISODate— interfacez.ZodISOTime— interfacez.ZodISODuration— interfacez.iso— namespacez.ZodCoercedString— interfacez.ZodCoercedNumber— interfacez.ZodCoercedBigInt— interfacez.ZodCoercedBoolean— interfacez.ZodCoercedDate— interfacez.coerce— namespacez.string— functionz.email— functionz.guid— functionz.uuid— functionz.uuidv4— functionz.uuidv6— functionz.uuidv7— functionz.url— functionz.httpUrl— functionz.emoji— functionz.nanoid— functionz.cuid— function: Validates a CUID v1 string.z.cuid2— functionz.ulid— functionz.xid— functionz.ksuid— functionz.ipv4— functionz.mac— functionz.ipv6— functionz.cidrv4— functionz.cidrv6— functionz.base64— functionz.base64url— functionz.e164— functionz.jwt— functionz.stringFormat— functionz.hostname— functionz.hex— functionz.hash— functionz.number— functionz.int— functionz.float32— functionz.float64— functionz.int32— functionz.uint32— functionz.boolean— functionz.bigint— functionz.int64— functionz.uint64— functionz.symbol— functionz.any— functionz.unknown— functionz.never— functionz.date— functionz.array— functionz.keyof— functionz.object— functionz.strictObject— functionz.looseObject— functionz.union— functionz.xor— function: Creates an exclusive union (XOR) where exactly one option must match.z.discriminatedUnion— functionz.intersection— functionz.tuple— functionz.record— functionz.partialRecord— functionz.looseRecord— functionz.map— functionz.set— functionz.nativeEnum— functionz.literal— functionz.file— functionz.transform— functionz.optional— functionz.exactOptional— functionz.nullable— functionz.nullish— functionz._default— functionz.prefault— functionz.nonoptional— functionz.success— functionz.nan— functionz.pipe— functionz.codec— functionz.invertCodec— functionz.readonly— functionz.templateLiteral— functionz.lazy— functionz.promise— functionz._function— functionz.check— functionz.custom— functionz.refine— functionz.superRefine— functionz.json— functionz.preprocess— functionz.ZodStandardSchemaWithJSON— typez.ZodType— interfacez._ZodType— interfacez._ZodString— interfacez.ZodString— interfacez.ZodStringFormat— interfacez.ZodEmail— interfacez.ZodGUID— interfacez.ZodUUID— interfacez.ZodURL— interfacez.ZodEmoji— interfacez.ZodNanoID— interfacez.ZodCUID— interfacez.ZodCUID2— interfacez.ZodULID— interfacez.ZodXID— interfacez.ZodKSUID— interfacez.ZodIPv4— interfacez.ZodMAC— interfacez.ZodIPv6— interfacez.ZodCIDRv4— interfacez.ZodCIDRv6— interfacez.ZodBase64— interfacez.ZodBase64URL— interfacez.ZodE164— interfacez.ZodJWT— interfacez.ZodCustomStringFormat— interfacez._ZodNumber— interfacez.ZodNumber— interfacez.ZodNumberFormat— interfacez.ZodInt— interfacez.ZodFloat32— interfacez.ZodFloat64— interfacez.ZodInt32— interfacez.ZodUInt32— interfacez._ZodBoolean— interfacez.ZodBoolean— interfacez._ZodBigInt— interfacez.ZodBigInt— interfacez.ZodBigIntFormat— interfacez.ZodSymbol— interfacez.ZodUndefined— interfacez.undefined— functionz.ZodNull— interfacez.null— functionz.ZodAny— interfacez.ZodUnknown— interfacez.ZodNever— interfacez.ZodVoid— interfacez.void— functionz._ZodDate— interfacez.ZodDate— interfacez.ZodArray— interfacez.SafeExtendShape— typez.ZodObject— interfacez.ZodUnion— interfacez.ZodXor— interfacez.ZodDiscriminatedUnion— interfacez.ZodIntersection— interfacez.ZodTuple— interfacez.ZodRecord— interfacez.ZodMap— interfacez.ZodSet— interfacez.ZodEnum— interfacez.enum— functionz.ZodLiteral— interfacez.ZodFile— interfacez.ZodTransform— interfacez.ZodOptional— interfacez.ZodExactOptional— interfacez.ZodNullable— interfacez.ZodDefault— interfacez.ZodPrefault— interfacez.ZodNonOptional— interfacez.ZodSuccess— interfacez.ZodCatch— interfacez.catch— functionz.ZodNaN— interfacez.ZodPipe— interfacez.ZodCodec— interfacez.ZodPreprocess— interfacez.ZodReadonly— interfacez.ZodTemplateLiteral— interfacez.ZodLazy— interfacez.ZodPromise— interfacez.ZodFunction— interfacez.function— functionz.ZodCustom— interfacez.describe— constz.meta— constz.instanceof— functionz.stringbool— constz.ZodJSONSchemaInternals— interfacez.ZodJSONSchema— interfacez.lt— functionz.lte— functionz.gt— functionz.gte— functionz.positive— functionz.negative— functionz.nonpositive— functionz.nonnegative— functionz.multipleOf— functionz.maxSize— functionz.minSize— functionz.size— functionz.maxLength— functionz.minLength— functionz.length— functionz.regex— functionz.lowercase— functionz.uppercase— functionz.includes— functionz.startsWith— functionz.endsWith— functionz.property— functionz.mime— functionz.overwrite— functionz.normalize— functionz.trim— functionz.toLowerCase— functionz.toUpperCase— functionz.slugify— functionz.RefinementCtx— interfacez.ZodIssue— typez.ZodError— interface: An Error-like class used to store Zod validation issues.z.ZodRealError— constz.ZodFlattenedError— typez.ZodFormattedError— typez.ZodErrorMap— interfacez.IssueData— typez.ZodSafeParseResult— typez.ZodSafeParseSuccess— typez.ZodSafeParseError— typez.parse— constz.parseAsync— constz.safeParse— constz.safeParseAsync— constz.encode— constz.decode— constz.encodeAsync— constz.decodeAsync— constz.safeEncode— constz.safeDecode— constz.safeEncodeAsync— constz.safeDecodeAsync— constz.setErrorMap— functionz.getErrorMap— functionz.TypeOf— typez.Infer— typez.ZodFirstPartySchemaTypes— typez.ZodIssueCode— constz.inferFlattenedErrors— typez.inferFormattedError— typez.BRAND— type: Usez.$brandinsteadz.ZodTypeAny— interfacez.ZodSchema— interfacez.Schema— interfacez.ZodRawShape— type: Included for Zod 3 compatibilityz.ZodFirstPartyTypeKind— enum
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-database^1.0.1@molecule/api-i18n^1.0.1@molecule/api-monitoring^1.0.1@molecule/api-notifications^1.0.1@molecule/api-permissions^1.0.1@molecule/api-resource^1.0.1@molecule/api-scheduler^1.0.1@molecule/api-locales-status-page^1.0.1
Runtime Dependencies
@molecule/api-bond@molecule/api-database@molecule/api-i18n@molecule/api-locales-status-page@molecule/api-monitoring@molecule/api-notifications@molecule/api-permissions@molecule/api-resource@molecule/api-schedulerzod
The GET routes are PUBLIC by design — a status page is a public surface; do
not put them behind auth. The mutating routes are ADMIN-ONLY and DENY by
default: they are gated by the requireAdmin middleware AND re-checked
inside every mutation handler via isStatusAdmin — fail-closed
defense-in-depth that holds even if a route scanner drops the middleware.
"Admin" resolves as: an admin session claim (isAdmin: true,
role: 'admin', roles containing 'admin', or a 'status:manage' /
'admin' entry in session.permissions) OR a bonded
@molecule/api-permissions grant of manage on status. Until the app
grants one of those, every mutation is denied — grant the claim/permission
at startup, do NOT strip the gate: an open mutation surface lets any caller
deface the public status page (fabricate outages, delete services).
Handler text flows through t() with English defaults; the companion
locale bond @molecule/api-locales-status-page provides translations.
Tables: setup/*.sql creates services, incidents, checks, and
uptimeWindows. An mlcl-scaffolded API replays these setup files
automatically on migrate; anywhere else run them once — nothing at runtime
creates them.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual status-page screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. Verify BEHAVIOR and the public/admin split, not just that CRUD compiles:
- [ ] The PUBLIC status page renders every monitored service (component)
with its current status and an OVERALL banner that is DERIVED, never
hardcoded: GET /status returns
operationalwhen no enabled service's latestcheckisdownordegraded("All systems operational"),downif ANY latest check isdown, elsedegradedif any isdegraded— the banner reflects the WORST service, and a service with no check readsunknown(which never turns the banner red). GET /status/services lists each service with its latest check. - [ ] An admin creating an INCIDENT (POST /status/incidents with serviceId,
title, severity minor|major|critical, status
investigating, startedAt) persists it and it shows on the public GET /status/incidents. The affected service reads down/degraded on the page because its latestchecksays so (checks are monitor-written, never typed in), so the overall banner goes non-green while the incident is open. - [ ] Updating the incident (PATCH /status/incidents/:id) advances its
timeline through the real lifecycle investigating -> identified ->
monitoring -> resolved (bumping updatedAt), and each stage shows on the
public page. Marking it
resolved(with resolvedAt) moves it to history — the public list filtered?status=resolvedincludes it while the active incidents drop it — and once the affected service's latest check returns toup, GET /status recovers to "All systems operational" (the banner tracks live check state, so a still-down check keeps it red). - [ ] AUTHORIZATION — reads are PUBLIC: with NO session, every GET (/status, /status/services, /status/services/:id, /status/incidents, /status/uptime) returns 200 — the status page is meant to be seen without signing in.
- [ ] AUTHORIZATION — writes are ADMIN-ONLY, deny by default: every mutation
(POST/PATCH/DELETE /status/services(/:id), POST/PATCH
/status/incidents(/:id)) is refused for an anonymous caller (401) and for
a normal signed-in user with no admin claim /
manage statusgrant (403), and nothing changes — enforced twice (therequireAdminroute middleware AND the in-handlerisStatusAdminre-check). A non-admin has NO path to fabricate an outage, delete a service, or post a fake incident, and no endpoint sets a service up/down at all (that is monitor-written).
