report-engine (logger)

One repo, two names

The repository is report-engine, but the actual Node.js application lives in the logger/ subdirectory and is named logger in its package.json (report-engine/logger/package.json:2). Docker artifacts call the service/container logger too. In this doc “the app” = report-engine/logger.

1. Purpose & role in FaceKom

A small TypeScript service that ingests system/audit events into a TimescaleDB hypertable and produces call-center analytics reports for the FaceKom video-call platform. It is driven entirely over RabbitMQ (no HTTP API of its own).

Two responsibilities, both wired through RabbitMQ (report-engine/logger/src/entities/RabbitMQService.ts):

  1. Event sink — consumes events on the logger queue and inserts each into the system_events hypertable (report-engine/logger/src/queue/queue_server/LoggerQueueServer.ts:8, report-engine/logger/src/entities/TimescaleDBService.ts:203).
  2. Report generator — consumes report requests on logger-report-request, runs SQL aggregations, and pushes the result back out on logger-report-response (report-engine/logger/src/queue/queue_server/LoggerQueueReportRequestServer.ts, .../queue_client/LoggerReportResponseQueueClient.ts).

The only implemented report is the Calls report (CallsReportServiceCallsReportStrategy, report-engine/logger/src/entities/report-strategies/CallsReportStrategy.ts). The report’s metric catalog (with the original Hungarian product labels) is documented inline in report-engine/logger/reportGenerator.ts:24-38: video-chat count, average/max waiting time, customers who left the waiting room without a call, customers switched to self-service, late answers, service level, incoming calls, average/total call time, and callback requests.

package.json author is "Facekom"; repository.url is git+https://github.com/TechTeamer/report-engine.git (report-engine/logger/package.json:14,19). The Docker image is published to harbor.techteamer.com/facekom-devel/logger (report-engine/docker/report-engine.yml:13).

Repo state

Single commit on master (fb3b9f5 update image). The README ## TODO still lists “test install & setup” and “remove supervisor” (report-engine/README.md:65-69), and source files are peppered with TODO: markers. Treat this as an early/in-progress service.

2. Tech stack & runtime

AspectValueSource
LanguageTypeScript (ESM, run directly via tsx, no precompile step in dev)report-engine/logger/package.json:6,8
Node version>=22.7.0 (engines)report-engine/logger/package.json:16-18
Node in imageNode 24.x (install-nodejs-24.sh)report-engine/docker/install/install-nodejs-24.sh:7
Package managerYarn (yarn.lock present; README uses yarn run …)report-engine/logger/yarn.lock, README.md:21
TS runnertsx (tsx logger.ts, tsx watch)report-engine/logger/package.json:8-9
Module system"type": "module", module/moduleResolution: nodenext, verbatimModuleSyntax, decorators enabledreport-engine/logger/package.json:6, tsconfig.json:5-13
Configconfig (node-config)report-engine/logger/package.json:24
Messaging@techteamer/mq ^7.2.0 (FaceKom RabbitMQ wrapper)report-engine/logger/package.json:23
DB driverpg ^8 (raw client, not a pool — see gotchas)report-engine/logger/package.json:27, TimescaleDBService.ts:24-40
DB engineTimescaleDB (Postgres 18) hypertablesreport-engine/docker/report-engine.yml:22
Datesdayjs (+ utc/timezone/isoWeek/customParseFormat/isSameOrBefore plugins)report-engine/logger/src/helpers/timeRange.ts:1-13
Loggingpino + pino-prettyreport-engine/logger/src/entities/LoggerService.ts:1,20-25
ELK plugin@techteamer/ecs-plugin (declared dep, no first-party import found)report-engine/logger/package.json:22
reflect-metadatadeclared dep (no first-party import found — DI is hand-rolled)report-engine/logger/package.json:29
Lint/formatBiome 2.4.12 (not ESLint/Prettier)report-engine/logger/biome.json, package.json:34

TypeScript version

package.json pins typescript: "^6.0.2" and @types/node: "^25.3.3" (report-engine/logger/package.json:31,35) — versions ahead of what was generally available at the knowledge cutoff. Recorded as-is from the manifest; not independently validated.

3. Build & run

package.json scripts (verbatim)

report-engine/logger/package.json:7-11:

ScriptCommandWhat it does
starttsx logger.tsRuns the service entrypoint directly with tsx (no build).
devtsx watch logger.tsSame, with file-watch auto-reload.
lintyarn exec biome check --max-diagnostics=unlimited --writeRuns Biome check + autofix across the project.

There is no build / test script despite tsconfig.json having noEmit:false + outDir:./dist and package.json main: dist/logger.js (report-engine/logger/package.json:5, tsconfig.json:14). dist/ is gitignored (report-engine/.gitignore:5).

Entrypoints (first-party top-level)

  • report-engine/logger/logger.ts — the service. Sets TZ=Etc/UTC, optionally disables TLS verification when settings.allowSelfSignedCerts is set, then connectDB()createViews()setupQueueConnection(). On init failure it disconnects DB, logs, and process.exit(2) (logger.ts:14-35).
  • report-engine/logger/reportGenerator.ts — standalone CLI/manual harness: connects DB, runs callsReportStrategy.generateReport(...) for a hard-coded param (day 18/05/2026), console.logs the result + elapsed ms, disconnects. Not part of the service runtime (reportGenerator.ts:40-47).
  • report-engine/logger/bin/db/create_tables.ts — DB bootstrap (see §5).

Start commands (from README, report-engine/README.md)

# build & start containers
cd /workspace/report-engine/docker
docker compose -f report-engine.yml build
docker compose -f dev.yml up -d            # NOTE: dev.yml is NOT in this repo (gap §11)
docker compose -f report-engine.yml up -d
 
# run app (inside container / workspace)
yarn run start          # or: yarn run dev  (auto-reload)
 
# bootstrap DB schema
docker exec -it logger bash
node bin/db/create_tables.ts
 
tail -f /var/log/logger.log    # watch logs

README run mismatch

README says node bin/db/create_tables.ts and the supervisor configs use command=node logger.ts, but plain node cannot run a .ts file without a loader. The package scripts use tsx. In practice these must run under tsx/--import tsx (Node 22.7+ has experimental TS stripping, but .ts import specifiers like ./container.ts typically require tsx). Verify the actual invocation before relying on the README literally. (README.md:40, report-engine/logger/supervisor_dev.conf:8)

Dockerfile

Single Dockerfile: report-engine/docker/logger/Dockerfile.

  • Base registry.access.redhat.com/ubi10/ubi-minimal (:1).
  • Installs OS tools → creates techteamer user (UID/GID default 1000) → installs supervisor 4.2.5 (pip) → Node 24.xyarn → symlinks $APP_HOME/supervisor_dev.conf into /etc/supervisor/conf.d/ → installs dev tools (Dockerfile:24-50).
  • APP_HOME=/workspace/$APP_NAME = /workspace/logger; WORKDIR = $APP_HOME (Dockerfile:15,52).
  • HEALTHCHECK runs /usr/local/bin/supervisor-health-check.sh every 60s (Dockerfile:46-47).
  • CMD runs supervisord -n -c /etc/supervisor/supervisord.conf (foreground) (Dockerfile:55).
  • Runs as non-root USER techteamer (Dockerfile:54).

Dev container mounts the source live

report-engine.yml bind-mounts host /workspace/report-engine/logger → container /workspace/logger (report-engine/docker/report-engine.yml:17), so the image ships only the runtime; the actual code comes from the host mount. network_mode: host.

Compose services (report-engine/docker/report-engine.yml)

  • logger — built from ./logger/Dockerfile, image harbor.techteamer.com/facekom-devel/logger:${LOGGER_VERSION}.${LOGGER_BUILD_NUMBER}-${SECURITY_NUMBER}, restart: always, host networking, mounts logs/source/MQ-certs (:2-18).
  • timescaledbtimescale/timescaledb:latest-pg18, DB metrics, user/pass postgres/postgres, host port 5433→5432, data volume /workspace/tsdb_data (:20-31).

.env (report-engine/docker/.env) supplies PROJECT_NAME=report-engine, LOGGER_VERSION=DEV, LOGGER_BUILD_NUMBER=DEV, DEV_UID/GID=1000, SECURITY_NUMBER=20260121.

4. Top-level structure

report-engine/
├── README.md            install/run/config notes
├── .editorconfig        2-space, LF, max line 240
├── .gitignore           ignores .idea, *.log*, node_modules, dist
├── docker/              container build + dev infra (see below)
└── logger/              ← the application (see below)

report-engine/docker/:

PathPurpose
logger/DockerfileUBI10 image for the logger service
report-engine.ymlCompose: logger + timescaledb
.envCompose build/version vars
install/Provisioning shell scripts + yum/dnf repos + nginx/supervisor confs (mostly shared template; only a subset used by the logger image — §5/§11)
workspace/cert/vuer_mq_cert/RabbitMQ mTLS CA + client/server certs & key material (committed, see §10)
workspace/log/....gitkeep placeholders for bind-mounted log dirs

report-engine/logger/:

PathPurpose
logger.tsService entrypoint (§3)
reportGenerator.tsManual report-run harness (§3)
bin/db/create_tables.tsDB schema bootstrap (§5)
config/node-config files: default.json, dev.json({}), prod.json({})
src/container.tsHand-rolled DI: instantiates all services/singletons
src/Emitter.tsTyped EventEmitter bridging report-gen → response queue
src/bootstrap/process-listeners.tsGlobal process signal/error handlers
src/entities/LoggerService, RabbitMQService, TimescaleDBService + report-strategies/CallsReportStrategy
src/queue/queue_server/LoggerQueueServer (event sink), LoggerQueueReportRequestServer
src/queue/queue_client/LoggerReportResponseQueueClient (report output)
src/interfaces/IReportStrategy.tsReport strategy contract + request param types
src/helpers/timeRange.ts (date-range math), utils.ts (zip)
docs/logging.md; inversion_of_control.md (effectively empty, 1 line)
biome.json, tsconfig.json, supervisor_dev.conftooling/runtime config

5. First-party bin/ scripts

Only one first-party bin/ directory exists: report-engine/logger/bin/.

  • report-engine/logger/bin/db/create_tables.ts — Standalone DB bootstrap. Instantiates TimescaleDBService, then connectDB()createSystemEventsTable()disconnectDB(); on error logs and process.exit(1) (bin/db/create_tables.ts:1-12). createSystemEventsTable() creates the system_events table, converts it to a hypertable on ts, builds 5 indices, and creates the 4 views (delegates to createIndices() + createViews()TimescaleDBService.ts:53-79).

Other shell "scripts" are container-provisioning, not app bin/

report-engine/docker/install/*.sh and docker/install/tools/*.sh are image-build scripts. Those the logger Dockerfile actually invokes are covered in §3 (install-os.sh, install-supervisor.sh, install-nodejs-24.sh, install-yarn.sh, install-devtools.sh) plus the healthcheck (tools/supervisor-health-check.sh). Unused-by-logger scripts are listed in §11.

6. Key modules / services / entities

DI container — src/container.ts

Hand-rolled (no DI framework despite reflect-metadata dep). Eagerly constructs singletons and exports them: loggerService, rabbitMQService, timescaleDBService, callsReportStrategy, emitter. CallsReportStrategy is injected with loggerService + timescaleDBService (container.ts:7-13).

TimescaleDBServicesrc/entities/TimescaleDBService.ts

The data layer. Key points:

  • Wraps a single pg.Client built from config.get('db') (:39). Not pooledTODO: we should use a pool (:24).
  • SystemEvent type (:6-22) defines the audit-event shape: ts, log_id, actor_type, actor_id, action, event, target_type, target_id, system, source_ip, business_context, result_status, result_code, tags[], searchFieldValues. Its action union only declares four values — 'systemCheck:results' | 'callBackRequest' | 'leaveWaitingRoom' | 'pageVisit' (:11) — even though the SQL views/queries below filter on additional undeclared action literals (room_created, room_closed, selfservice_room_created, and the snake_case callback_request_created — which does not match the type’s camelCase callBackRequest). The type is narrower than what the schema/queries actually use.
  • Schema (createSystemEventsTable, :53-79): system_events table → create_hypertable('system_events','ts') → indices (ts+system, ts+action, ts+actor, ts+result, GIN on tags) → views.
  • Views (createViews, :90-198), recreated on every service start (logger.ts:16):
    • customer_page_events — heavy 6-CTE view reconstructing per-customer page transitions (waiting-room ↔ videochat), de-noising bounces (<20s) and sub-second transitions, with duration per segment (well-documented JSDoc at :91-112).
    • roomssystem_events WHERE action='room_created'.
    • selfservice_roomsaction='selfservice_room_created'.
    • room_durations — self-join room_createdroom_closed per target_id with duration.
  • insertSystemEvent() — parameterized 15-column INSERT (:203-245).

CallsReportStrategysrc/entities/report-strategies/CallsReportStrategy.ts

The single implemented report (implements IReportStrategy). generateReport(params) (:66-103):

  1. Resolves {startDate,endDate} via getStartEndDates (timeRange.ts).
  2. Computes scalar report (CallReportResult): calls/avg_waiting_time/max_waiting_time/late_answers, derived service_level = (calls-late)/max(calls,1)*100, exits/exitTimes, avg_call_time/sum_call_time, waiting_calls, selfservice, callback.
  3. Computes per-bucket chartData (hourly when rangeType==='day', else daily) via the *Range query variants + Postgres generate_series + date_bin (:507-541).
  4. Builds ranges labels via getPartDefinitions.
  • lateAnswerTime threshold comes from config (:25). Numerous dbg* methods exist as debug-only single-aggregation queries (annotated @privateRemarks ... use the combined query).

SQL string-interpolated date format

Queries interpolate sqlDateFormat ('YYYY-MM-DD HH24:MI:ss') directly into SQL template strings (e.g. CallsReportStrategy.ts:140), while the values (dates, thresholds) are passed as bound params ($1..$3). The format token is a constant, not user input, but the pattern is worth knowing.

Queue layer — src/queue/*

  • LoggerQueueServer (event sink): _callback(event)insertSystemEvent, errors logged not thrown (queue_server/LoggerQueueServer.ts).
  • LoggerQueueReportRequestServer: _callback(reportRequest) switches on reporterService; for CallsReportService runs the strategy and emitter.emit('LoggerQueueServer:generatedReport', …) (queue_server/LoggerQueueReportRequestServer.ts).
  • LoggerReportResponseQueueClient: on initEventHandlers() subscribes to the emitter event and this.send({action:'reportEngine:data', message: event}) back onto the response queue (queue_client/LoggerReportResponseQueueClient.ts).
  • Emitter (src/Emitter.ts): typed EventEmitter whose real event is LoggerQueueServer:generatedReport (plus unused test/test1).

RabbitMQServicesrc/entities/RabbitMQService.ts

Builds a @techteamer/mq ConnectionPool from config.get('queue'), registers the two queue servers + one queue client, and connects. Any connection close event → process.exit(2) (:32-35) — hard-fail on broker disconnect.

LoggerServicesrc/entities/LoggerService.ts

Thin pino wrapper (level debug, pino-pretty transport), adds a .log alias and .child(). Transport is hard-coded (TODO: make configurable, :17-18).

process-listeners.tssrc/bootstrap/

Registers uncaughtException, unhandledRejection, exit, warning handlers (all via safe-log wrappers) and SIGINT(130)/SIGTERM(143) handlers with a 1s graceful-shutdown delay (:30-56).

7. Configuration

node-config (config pkg). Layering per README: default.json ← env-specific (dev.json/prod.json, selected by NODE_ENV) ← environment variables override everything (README.md:43-49).

report-engine/logger/config/default.json (the only populated config; dev.json and prod.json are both {}):

KeyValueConsumed by
db.host/port/user/password/databaselocalhost / 5433 / postgres / postgres / metricsTimescaleDBService ctor (config.get('db'))
timezoneEurope/BudapesttimeRange.ts shiftUTC/unshiftUTC (config.get('timezone'))
lateAnswerTime60 (seconds)CallsReportStrategy.ts:25 (config.get('lateAnswerTime'))
queue.urlamqps://localhost:5671RabbitMQService (config.get('queue'))
queue.optionsrejectUnauthorized:false, mTLS cert/key/ca under /workspace/vuer_mq_cert/...RabbitMQ TLS
queue.rpcTimeoutMs / rpcQueueMaxSize10000 / 100@techteamer/mq
logging.settings.format/defaultLevelconsole / debug(present in config; LoggerService currently hard-codes pino-pretty/debug and does not read this — see gotchas)
logging.file.maxLogSize/backups20480000 / 10(config only; not read by LoggerService)

Runtime-set env: logger.ts sets process.env.TZ='Etc/UTC' and (conditionally) NODE_TLS_REJECT_UNAUTHORIZED='0' when config.has('settings.allowSelfSignedCerts') is truthy (logger.ts:6-12). Note settings.* is not present in default.json.

No AJV / getconfig schema

There is no JSON-schema/AJV validation of config and no getconfig. Config is plain node-config with raw config.get(...) calls. The only schema validation in the repo is Biome’s $schema for its own config file.

8. Tests

No tests

No test framework, no test files, and no test script. There is no jest/mocha/vitest/tap/ava dependency in package.json, no *.test.* / *.spec.* files, and no test//__tests__ directory. reportGenerator.ts is a manual run harness, not an automated test (report-engine/logger/reportGenerator.ts). README ## TODO includes “test install & setup” (README.md:67).

9. Dependencies on other FaceKom services

DependencyEvidence
RabbitMQ (amqps, mTLS) via @techteamer/mqconfig/default.json queue.url=amqps://localhost:5671 + client/CA certs; RabbitMQService.setupQueueConnection()
Queue logger (consumed, QueueServer) — event ingestion sinkRabbitMQService.ts:9,52
Queue logger-report-request (consumed, QueueServer) — report requests inRabbitMQService.ts:60
Queue logger-report-response (produced, QueueClient) — report results out, message {action:'reportEngine:data'}RabbitMQService.ts:68, LoggerReportResponseQueueClient.ts:15
TimescaleDB / Postgres metrics DB on localhost:5433config/default.json db.*; compose timescaledb service
MQ TLS certs from FaceKom vuer ecosystem (vuer_mq_cert)cert dir name + paths in queue.options

Producers/consumers of these queues live in other FaceKom repos. The system_events.action values referenced from this repo fall into two groups, which do not form one consistent set: the SystemEvent TypeScript type (TimescaleDBService.ts:11) declares only pageVisit, leaveWaitingRoom, callBackRequest, systemCheck:results, whereas the SQL views/queries additionally filter on room_created, room_closed, selfservice_room_created (TimescaleDBService.ts:185-195) and callback_request_created (CallsReportStrategy.ts:470,489) — none of which are in the type, and the SQL’s snake_case callback_request_created does not match the type’s camelCase callBackRequest. Together with the CallsReportService request shape these imply the FaceKom OSS/customer + admin web apps; the system='oss', actor_id='oss' literals point at the OSS app. See architecture-overview, vuer_oss. (Cross-repo producers not verified from this repo — they are not in this tree.)

10. Verified gotchas

Committed RabbitMQ key material

The repo commits real private keys and a CA under docker/workspace/cert/vuer_mq_cert/ (ca/private/cakey.pem, client/key.pem, server/key.pem, *.p12, rabbitstore) — confirmed as regular blobs via git ls-files -s (NOT LFS pointers, NOT placeholders). These are mounted into the container as the MQ client identity (report-engine.yml:18). Treat as dev-only secrets; do not reuse in prod.

rejectUnauthorized:false + optional global TLS bypass

queue.options.rejectUnauthorized:false (config/default.json:14) and, if settings.allowSelfSignedCerts is set, NODE_TLS_REJECT_UNAUTHORIZED='0' process-wide (logger.ts:9-11). Self-signed-friendly by design; insecure for hardened deployments.

Broker disconnect and init failure both hard-exit

RabbitMQ connection closeprocess.exit(2) (RabbitMQService.ts:34); startup failure → process.exit(2) (logger.ts:31). Exit code 2 is in supervisor’s exitcodes=0,2 (supervisor_dev.conf:17), so supervisor treats it as a clean exit and restarts — relies on supervisor for resilience (README TODO wants supervisor removed).

Views dropped/recreated on every boot

logger.ts calls timescaleDBService.createViews() on every start (logger.ts:16), issuing CREATE OR REPLACE VIEW ... for customer_page_events, rooms, selfservice_rooms, room_durations. Schema drift in those view bodies takes effect on restart.

default.json DB/queue point at localhost

Defaults assume co-located TimescaleDB (localhost:5433) and RabbitMQ (localhost:5671) — consistent with network_mode: host in compose. Any non-localhost deployment must override via env or local.json (README mentions config/local.json, which is not committed).

Single un-pooled pg.Client

TimescaleDBService uses one pg.Client (TODO: we should use a pool, :24). Concurrent report requests share one connection.

logging.* config is currently dead

LoggerService hard-codes pino level debug + pino-pretty and does not read config.logging.* (LoggerService.ts:17-25); those keys in default.json have no effect yet.

bin/db/create_tables.ts vs the README path

README runs it from inside the container as node bin/db/create_tables.ts (README.md:39-41); see the §3 warning about node vs tsx for .ts files.

11. Unverified / gaps

  • docker/dev.yml — referenced by README (README.md:14) but absent from the repo (git ls-files + history show no docker/dev.yml). The “dev” infra (likely RabbitMQ/Redis) it would start is therefore not defined here.
  • config/local.json — referenced by README (README.md:47) for local overrides; not committed (expected — it’s an override file).
  • docs/inversion_of_control.md — exists but effectively empty (1 line). docs/logging.md describes a container.get<LoggerService>(SERVICE_IDENTIFIER.LOGGER)/container.bind(...) DI API that does not match the actual hand-rolled container.ts (no SERVICE_IDENTIFIER, no bind); the doc appears aspirational/stale.
  • Install scripts not invoked by the logger Dockerfile (read structurally, present under docker/install/ but not referenced by docker/logger/Dockerfile): install-avast.sh, install-java-11.sh, install-nginx.sh, install-redis.sh, install-puppeteer-centos.sh, and tools/install-nodejs-22.sh. Likewise the install/nginx/**, install/yum/**, install/dnf/dnf.conf repo/conf files are shared-template provisioning assets; only dnf/dnf.conf is used (via install-os.sh). I confirmed their existence and read their headers but did not treat them as part of the logger runtime since the Dockerfile does not call them.
  • @techteamer/mq, @techteamer/ecs-plugin internals — third-party FaceKom packages, not read (vendored). Queue semantics inferred from this repo’s usage only.
  • Cross-repo queue producers/consumers — the upstream services that publish to logger/logger-report-request and consume logger-report-response are not in this tree; not verified here.
  • yarn.lock — present; not parsed line-by-line.

Sources

Files read in full unless noted:

  • report-engine/README.md, .gitignore, .editorconfig
  • report-engine/logger/package.json, tsconfig.json, biome.json, supervisor_dev.conf
  • report-engine/logger/logger.ts, reportGenerator.ts, bin/db/create_tables.ts
  • report-engine/logger/src/container.ts, Emitter.ts, bootstrap/process-listeners.ts
  • report-engine/logger/src/entities/LoggerService.ts, RabbitMQService.ts, TimescaleDBService.ts, report-strategies/CallsReportStrategy.ts
  • report-engine/logger/src/queue/queue_server/LoggerQueueServer.ts, LoggerQueueReportRequestServer.ts; queue/queue_client/LoggerReportResponseQueueClient.ts
  • report-engine/logger/src/interfaces/IReportStrategy.ts; src/helpers/timeRange.ts, utils.ts
  • report-engine/logger/config/default.json, dev.json, prod.json
  • report-engine/logger/docs/logging.md, docs/inversion_of_control.md (1 line)
  • report-engine/docker/logger/Dockerfile, report-engine.yml, .env
  • report-engine/docker/install/install-os.sh, install-supervisor.sh, install-nodejs-24.sh, install-yarn.sh, install-devtools.sh; install/tools/supervisor-health-check.sh, install/tools/install-nodejs-22.sh; install/supervisor/supervisord.conf
  • Structure/inventory via git -C … ls-files, git log, git ls-files -s (cert blob types), ls