openvidu/openvidu-dashboard/dashboard-ce/backend/AGENTS.md

76 lines
5.9 KiB
Markdown
Raw Normal View History

# Backend (`@openvidu-dashboard/backend`)
Express 5, Inversify, Zod and the official `mongodb` driver; TypeScript 6 strict ESM, relative imports end in `.js`.
Behaviour is specified in `openspec/specs/*-api`, `api-conventions`, `auth`, `status-api` and `editions-info`.
## Structure
- `src/lifecycle/startup.ts`: `startDashboard(options)` reads the configuration, connects to MongoDB, applies the
writer schema gate, builds the container, calls `configure` and the startup hooks, then listens. `TOKENS`
(`src/di/tokens.ts`) are bound with `toResolvedValue` factories, without decorators, so esbuild and Vitest need no
metadata. Exit codes go through `Shutdown.run(code)`.
- Routes are declared once in the `RouteRegistry` (`src/api/route-registry.ts`): method, path under `/api/v1`, edition,
`feature` of a PRO route, security (`public`, `bearer`, `optional-bearer`), Zod schemas of params, query, body and
responses, and the handler. It drives validation, authentication and the OpenAPI document (only when `NODE_ENV` is
not `production`).
- PRO routes are only locks here: `registry.lock({ method, path, feature })` answers `403 PRO_FEATURE` after
authentication and stays out of the OpenAPI document. An edition that extends CE registers the full definition in the
lock's place (mount order kept) and swaps a CE route for its extension with `registry.replace()`; a `pro` startup
that leaves a lock without a definition exits with code 1, naming it.
- Extension tokens that `configure` can rebind: `Features`, `LicenseInfo`, `AnalyticsCatalogue` (metrics and
dimensions the analytics endpoints compute) and `OverviewExtension` (extra overview fields, merged before the
`ETag`). A `configure` that throws stops the startup with code 1.
- A resource slice is `src/<resource>/`: `*-schemas.ts` (Zod), `*-query.ts` (pure filter builders), `*-repository.ts`
(MongoDB), `*-service.ts` (paging, mapping), `*-responses.ts` (OpenAPI response schemas, checked against the mappers
in unit tests) and `*-routes.ts` (`register<Resource>Routes`: the CE routes and the locks of the PRO ones).
- Zod schemas live here because typings has no runtime dependencies; typings declares the matching types in
`src/api/`. Errors use `{ error: { code, message, details? } }` with `ErrorCode`.
- Shared query pieces in `src/api/`: `rangedQuery(now, shape)` for ranged queries (relative grammar resolved at parse
time) and `optionalRangeQuery(now, shape)` when the bounds are optional, `pageQuery()` for keyset pages on an index
ending in `_id`, `indexOrderedPage()` when the index does not order by `_id`, and `rankedPage()` with
`assertRankedRange()` (`src/api/ranked.ts`) for metric sorts ranked in memory at the cursor's `asOf`. Calendar math in
a time zone uses date-fns and `@date-fns/tz`.
- `src/stats/stats-mode.ts`: `StatsModeReader` reads the writer's `stats_detail` for `GET /info`.
- `src/analytics/` answers from the rollup collections only: `analytics-planner.ts` picks the granularity, source
collection and bucket grid, `analytics-counters.ts` folds the documents in process with the definitions of the
catalogue, `AnalyticsCache` keeps the folds of completed documents and `AnalyticsFeatureGate` rejects PRO metrics,
dimensions and node scopes on CE.
- `src/status/freshness.ts` marks active rooms and participants `stale` with the backend clock, from the typings
`freshnessWindows()`.
- `src/index.ts` exports what an extending backend builds on: the startup, tokens and registry, the generic query,
cursor, response and freshness pieces, and the list pieces that entity details extend.
- Exact identifiers (`RM_`, `PA_`, `EG_`, `IN_`, `TR_`) are matched with `ENTITY_ID_PATTERNS`, `isEntityId()` and
`entityIdKind()` from typings.
- Configuration variables are new `DASHBOARD_*`, or `DATABASE_*` for database connection settings; legacy names keep
their 3.9 meaning (`src/config/variables.ts`, unit-tested). The README lists them all.
## Rules
- Repositories own the queries. Every list, detail and time-series query is backed by an index and asserted with
`expectIndexBacked` (`test/support/query-plan.ts`: no COLLSCAN above 1 000 documents, bounded `totalDocsExamined`).
## Tests
- Unit tests: Vitest (`pnpm test`).
- Integration tests: `test/integration/`, Vitest projects `integration` and `integration-plans` (the `*query-plan`
suites, run after the rest so their data generation does not skew the latency budgets). `test/support/` starts a
replica set with Testcontainers on `openvidu/mongodb:8.0.29-r1` (the official `mongo:8.0` refuses Linux kernels 6.19
and newer), or uses `DASHBOARD_TEST_MONGO_URI`; `DASHBOARD_TEST_MONGO_IMAGE` overrides the image. Through pnpm
without Docker the suites report themselves skipped; `./dashboard.sh test-integration` fails instead.
- They also need `openssl` (test CA of the TLS case) and build the package to run `dist/server.js` as a process.
`test/support/boot.ts` starts the dashboard in-process on a fresh database with a controllable clock.
- `test/support/` is exported to the suites of extending packages as `@openvidu-dashboard/backend/testing` (source
only, never in `dist`), and the MongoDB global setup as `@openvidu-dashboard/backend/testing/global-mongo`.
- Query-plan suites generate data with `dashboard-ce/tools/data-generator` and report the p95 of their budget. Design
figures come from scenario M over 7 days on a MongoDB with room for it, run alone:
```bash
DASHBOARD_TEST_MONGO_URI=<uri> ROOMS_PLAN_SCENARIO=M ROOMS_PLAN_DAYS=7 ROOMS_PLAN_REPORT=<file> \
pnpm --filter @openvidu-dashboard/backend exec vitest run --project integration-plans \
test/integration/rooms-query-plan.test.ts
```
The other suites take the prefixes `PARTICIPANTS_PLAN_`, `EGRESS_PLAN_`, `INGRESS_PLAN_`, `OVERVIEW_PLAN_` and
`ANALYTICS_PLAN_`. `DASHBOARD_TEST_LATENCY_BUDGETS=report` (set in CI) turns p95 overruns into warnings; plan
assertions fail either way.