report-engine (logger)
One repo, two names
The repository is
report-engine, but the actual Node.js application lives in thelogger/subdirectory and is namedloggerin itspackage.json(report-engine/logger/package.json:2). Docker artifacts call the service/containerloggertoo. 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):
- Event sink — consumes events on the
loggerqueue and inserts each into thesystem_eventshypertable (report-engine/logger/src/queue/queue_server/LoggerQueueServer.ts:8,report-engine/logger/src/entities/TimescaleDBService.ts:203). - Report generator — consumes report requests on
logger-report-request, runs SQL aggregations, and pushes the result back out onlogger-report-response(report-engine/logger/src/queue/queue_server/LoggerQueueReportRequestServer.ts,.../queue_client/LoggerReportResponseQueueClient.ts).
The only implemented report is the Calls report (CallsReportService → CallsReportStrategy, 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## TODOstill lists “test install & setup” and “remove supervisor” (report-engine/README.md:65-69), and source files are peppered withTODO:markers. Treat this as an early/in-progress service.
2. Tech stack & runtime
| Aspect | Value | Source |
|---|---|---|
| Language | TypeScript (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 image | Node 24.x (install-nodejs-24.sh) | report-engine/docker/install/install-nodejs-24.sh:7 |
| Package manager | Yarn (yarn.lock present; README uses yarn run …) | report-engine/logger/yarn.lock, README.md:21 |
| TS runner | tsx (tsx logger.ts, tsx watch) | report-engine/logger/package.json:8-9 |
| Module system | "type": "module", module/moduleResolution: nodenext, verbatimModuleSyntax, decorators enabled | report-engine/logger/package.json:6, tsconfig.json:5-13 |
| Config | config (node-config) | report-engine/logger/package.json:24 |
| Messaging | @techteamer/mq ^7.2.0 (FaceKom RabbitMQ wrapper) | report-engine/logger/package.json:23 |
| DB driver | pg ^8 (raw client, not a pool — see gotchas) | report-engine/logger/package.json:27, TimescaleDBService.ts:24-40 |
| DB engine | TimescaleDB (Postgres 18) hypertables | report-engine/docker/report-engine.yml:22 |
| Dates | dayjs (+ utc/timezone/isoWeek/customParseFormat/isSameOrBefore plugins) | report-engine/logger/src/helpers/timeRange.ts:1-13 |
| Logging | pino + pino-pretty | report-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-metadata | declared dep (no first-party import found — DI is hand-rolled) | report-engine/logger/package.json:29 |
| Lint/format | Biome 2.4.12 (not ESLint/Prettier) | report-engine/logger/biome.json, package.json:34 |
TypeScript version
package.jsonpinstypescript: "^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:
| Script | Command | What it does |
|---|---|---|
start | tsx logger.ts | Runs the service entrypoint directly with tsx (no build). |
dev | tsx watch logger.ts | Same, with file-watch auto-reload. |
lint | yarn exec biome check --max-diagnostics=unlimited --write | Runs 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. SetsTZ=Etc/UTC, optionally disables TLS verification whensettings.allowSelfSignedCertsis set, thenconnectDB()→createViews()→setupQueueConnection(). On init failure it disconnects DB, logs, andprocess.exit(2)(logger.ts:14-35).report-engine/logger/reportGenerator.ts— standalone CLI/manual harness: connects DB, runscallsReportStrategy.generateReport(...)for a hard-codedparam(day18/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 logsREADME run mismatch
README says
node bin/db/create_tables.tsand the supervisor configs usecommand=node logger.ts, but plainnodecannot run a.tsfile without a loader. The packagescriptsusetsx. In practice these must run undertsx/--import tsx(Node 22.7+ has experimental TS stripping, but.tsimport specifiers like./container.tstypically requiretsx). 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
techteameruser (UID/GID default 1000) → installs supervisor 4.2.5 (pip) → Node 24.x → yarn → symlinks$APP_HOME/supervisor_dev.confinto/etc/supervisor/conf.d/→ installs dev tools (Dockerfile:24-50). APP_HOME=/workspace/$APP_NAME=/workspace/logger;WORKDIR=$APP_HOME(Dockerfile:15,52).HEALTHCHECKruns/usr/local/bin/supervisor-health-check.shevery 60s (Dockerfile:46-47).CMDrunssupervisord -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.ymlbind-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, imageharbor.techteamer.com/facekom-devel/logger:${LOGGER_VERSION}.${LOGGER_BUILD_NUMBER}-${SECURITY_NUMBER},restart: always, host networking, mounts logs/source/MQ-certs (:2-18).timescaledb—timescale/timescaledb:latest-pg18, DBmetrics, user/passpostgres/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/:
| Path | Purpose |
|---|---|
logger/Dockerfile | UBI10 image for the logger service |
report-engine.yml | Compose: logger + timescaledb |
.env | Compose 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/:
| Path | Purpose |
|---|---|
logger.ts | Service entrypoint (§3) |
reportGenerator.ts | Manual report-run harness (§3) |
bin/db/create_tables.ts | DB schema bootstrap (§5) |
config/ | node-config files: default.json, dev.json({}), prod.json({}) |
src/container.ts | Hand-rolled DI: instantiates all services/singletons |
src/Emitter.ts | Typed EventEmitter bridging report-gen → response queue |
src/bootstrap/process-listeners.ts | Global 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.ts | Report 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.conf | tooling/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. InstantiatesTimescaleDBService, thenconnectDB()→createSystemEventsTable()→disconnectDB(); on error logs andprocess.exit(1)(bin/db/create_tables.ts:1-12).createSystemEventsTable()creates thesystem_eventstable, converts it to a hypertable onts, builds 5 indices, and creates the 4 views (delegates tocreateIndices()+createViews()—TimescaleDBService.ts:53-79).
Other shell "scripts" are container-provisioning, not app
bin/
report-engine/docker/install/*.shanddocker/install/tools/*.share 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).
TimescaleDBService — src/entities/TimescaleDBService.ts
The data layer. Key points:
- Wraps a single
pg.Clientbuilt fromconfig.get('db')(:39). Not pooled —TODO: we should use a pool(:24). SystemEventtype (: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. Itsactionunion only declares four values —'systemCheck:results' | 'callBackRequest' | 'leaveWaitingRoom' | 'pageVisit'(:11) — even though the SQL views/queries below filter on additional undeclaredactionliterals (room_created,room_closed,selfservice_room_created, and the snake_casecallback_request_created— which does not match the type’s camelCasecallBackRequest). The type is narrower than what the schema/queries actually use.- Schema (
createSystemEventsTable,:53-79):system_eventstable →create_hypertable('system_events','ts')→ indices (ts+system,ts+action,ts+actor,ts+result, GIN ontags) → 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, withdurationper segment (well-documented JSDoc at:91-112).rooms—system_events WHERE action='room_created'.selfservice_rooms—action='selfservice_room_created'.room_durations— self-joinroom_created→room_closedpertarget_idwithduration.
insertSystemEvent()— parameterized 15-column INSERT (:203-245).
CallsReportStrategy — src/entities/report-strategies/CallsReportStrategy.ts
The single implemented report (implements IReportStrategy). generateReport(params) (:66-103):
- Resolves
{startDate,endDate}viagetStartEndDates(timeRange.ts). - Computes scalar
report(CallReportResult):calls/avg_waiting_time/max_waiting_time/late_answers, derivedservice_level = (calls-late)/max(calls,1)*100,exits/exitTimes,avg_call_time/sum_call_time,waiting_calls,selfservice,callback. - Computes per-bucket
chartData(hourly whenrangeType==='day', else daily) via the*Rangequery variants + Postgresgenerate_series+date_bin(:507-541). - Builds
rangeslabels viagetPartDefinitions.
lateAnswerTimethreshold comes from config (:25). Numerousdbg*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 onreporterService; forCallsReportServiceruns the strategy andemitter.emit('LoggerQueueServer:generatedReport', …)(queue_server/LoggerQueueReportRequestServer.ts).LoggerReportResponseQueueClient: oninitEventHandlers()subscribes to the emitter event andthis.send({action:'reportEngine:data', message: event})back onto the response queue (queue_client/LoggerReportResponseQueueClient.ts).Emitter(src/Emitter.ts): typedEventEmitterwhose real event isLoggerQueueServer:generatedReport(plus unusedtest/test1).
RabbitMQService — src/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.
LoggerService — src/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.ts — src/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 {}):
| Key | Value | Consumed by |
|---|---|---|
db.host/port/user/password/database | localhost / 5433 / postgres / postgres / metrics | TimescaleDBService ctor (config.get('db')) |
timezone | Europe/Budapest | timeRange.ts shiftUTC/unshiftUTC (config.get('timezone')) |
lateAnswerTime | 60 (seconds) | CallsReportStrategy.ts:25 (config.get('lateAnswerTime')) |
queue.url | amqps://localhost:5671 | RabbitMQService (config.get('queue')) |
queue.options | rejectUnauthorized:false, mTLS cert/key/ca under /workspace/vuer_mq_cert/... | RabbitMQ TLS |
queue.rpcTimeoutMs / rpcQueueMaxSize | 10000 / 100 | @techteamer/mq |
logging.settings.format/defaultLevel | console / debug | (present in config; LoggerService currently hard-codes pino-pretty/debug and does not read this — see gotchas) |
logging.file.maxLogSize/backups | 20480000 / 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 rawconfig.get(...)calls. The only schema validation in the repo is Biome’s$schemafor its own config file.
8. Tests
No tests
No test framework, no test files, and no
testscript. There is nojest/mocha/vitest/tap/avadependency inpackage.json, no*.test.*/*.spec.*files, and notest//__tests__directory.reportGenerator.tsis a manual run harness, not an automated test (report-engine/logger/reportGenerator.ts). README## TODOincludes “test install & setup” (README.md:67).
9. Dependencies on other FaceKom services
| Dependency | Evidence |
|---|---|
RabbitMQ (amqps, mTLS) via @techteamer/mq | config/default.json queue.url=amqps://localhost:5671 + client/CA certs; RabbitMQService.setupQueueConnection() |
Queue logger (consumed, QueueServer) — event ingestion sink | RabbitMQService.ts:9,52 |
Queue logger-report-request (consumed, QueueServer) — report requests in | RabbitMQService.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:5433 | config/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 viagit 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, ifsettings.allowSelfSignedCertsis 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
close→process.exit(2)(RabbitMQService.ts:34); startup failure →process.exit(2)(logger.ts:31). Exit code 2 is in supervisor’sexitcodes=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.tscallstimescaleDBService.createViews()on every start (logger.ts:16), issuingCREATE OR REPLACE VIEW ...forcustomer_page_events,rooms,selfservice_rooms,room_durations. Schema drift in those view bodies takes effect on restart.
default.jsonDB/queue point atlocalhostDefaults assume co-located TimescaleDB (
localhost:5433) and RabbitMQ (localhost:5671) — consistent withnetwork_mode: hostin compose. Any non-localhost deployment must override via env orlocal.json(README mentionsconfig/local.json, which is not committed).
Single un-pooled
pg.Client
TimescaleDBServiceuses onepg.Client(TODO: we should use a pool,:24). Concurrent report requests share one connection.
logging.*config is currently dead
LoggerServicehard-codes pino leveldebug+pino-prettyand does not readconfig.logging.*(LoggerService.ts:17-25); those keys indefault.jsonhave no effect yet.
bin/db/create_tables.tsvs the README pathREADME runs it from inside the container as
node bin/db/create_tables.ts(README.md:39-41); see the §3 warning aboutnodevstsxfor.tsfiles.
11. Unverified / gaps
docker/dev.yml— referenced by README (README.md:14) but absent from the repo (git ls-files+ history show nodocker/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.mddescribes acontainer.get<LoggerService>(SERVICE_IDENTIFIER.LOGGER)/container.bind(...)DI API that does not match the actual hand-rolledcontainer.ts(noSERVICE_IDENTIFIER, nobind); the doc appears aspirational/stale.- Install scripts not invoked by the logger Dockerfile (read structurally, present under
docker/install/but not referenced bydocker/logger/Dockerfile):install-avast.sh,install-java-11.sh,install-nginx.sh,install-redis.sh,install-puppeteer-centos.sh, andtools/install-nodejs-22.sh. Likewise theinstall/nginx/**,install/yum/**,install/dnf/dnf.confrepo/conf files are shared-template provisioning assets; onlydnf/dnf.confis used (viainstall-os.sh). I confirmed their existence and read their headers but did not treat them as part of theloggerruntime since the Dockerfile does not call them. @techteamer/mq,@techteamer/ecs-plugininternals — 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-requestand consumelogger-report-responseare 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,.editorconfigreport-engine/logger/package.json,tsconfig.json,biome.json,supervisor_dev.confreport-engine/logger/logger.ts,reportGenerator.ts,bin/db/create_tables.tsreport-engine/logger/src/container.ts,Emitter.ts,bootstrap/process-listeners.tsreport-engine/logger/src/entities/LoggerService.ts,RabbitMQService.ts,TimescaleDBService.ts,report-strategies/CallsReportStrategy.tsreport-engine/logger/src/queue/queue_server/LoggerQueueServer.ts,LoggerQueueReportRequestServer.ts;queue/queue_client/LoggerReportResponseQueueClient.tsreport-engine/logger/src/interfaces/IReportStrategy.ts;src/helpers/timeRange.ts,utils.tsreport-engine/logger/config/default.json,dev.json,prod.jsonreport-engine/logger/docs/logging.md,docs/inversion_of_control.md(1 line)report-engine/docker/logger/Dockerfile,report-engine.yml,.envreport-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