Skip to content

AdminStatsService returns a hardcoded zero snapshot: the admin dashboard always shows zeros #522

Description

@Xhristin3

Problem

GET /admin/stats always returns zeros. AdminStatsService.compute() (api/src/admin/admin-stats.service.ts) does not query the database at all — it returns a hardcoded snapshot:

export class AdminStatsService {
  async compute(): Promise<AdminStats> {
    // Placeholder zero snapshot. The query above replaces this body
    // once the DB module is wired.
    return {
      totalUsers: 0,
      totalStreams: 0,
      activeStreams: 0,
      eventsLast24h: 0,
      generatedAt: new Date().toISOString(),
    }
  }
}

The comment's premise is stale: the DB module is wired, and the repository layer for every table it would count already exists (UsersRepository, StreamsDbRepository, stream_events queries in streams-db.repository.ts). The documented replacement query — four aggregate subqueries over users, streams, and stream_events — is written in the file's JSDoc but never executed. The admin dashboard (app/src/app/admin/admin-dashboard.tsx) therefore renders "0" for total users, total streams, active streams, and events last 24h, with a plausible-looking generatedAt timestamp, in every deployment.

Root cause

// api/src/admin/admin-stats.service.ts — compute()
return { totalUsers: 0, totalStreams: 0, activeStreams: 0, eventsLast24h: 0, ... }

The service injects nothing (no PG_POOL, no repository), so there is no path to real data.

Why this is architecturally hard

  1. The service is constructed with no dependencies, and AdminController already wraps it in a 60s cache (CacheInterceptor plus an explicit cache.set). The fix must add a data source without breaking the caching contract — the aggregate queries are cheap and index-friendly (the JSDoc cites idx_streams_user_id and idx_stream_events_created_at), so the design question is which layer owns the query: a new admin repository, or direct PG_POOL access consistent with the codebase's repository pattern.
  2. AdminStats (the wire shape) is consumed by the app (app/lib/api/admin-stats.ts) and the contract is stable; the change is internal. But the definition of "active streams" and "events last 24h" must be pinned (status = 'active' at query time; stream_events.created_at > NOW() - interval '24 hours') so the dashboard's labels match the query.
  3. This is a trust boundary: admin dashboards showing plausible zeros can mask a dead system (e.g. a failing pipeline that produces no events). The acceptance criteria should treat "non-zero when data exists" as the observable outcome, not just "the query runs".

Acceptance criteria

Service

  • compute() returns real counts: totalUsers = row count of users, totalStreams = row count of streams, activeStreams = streams with status = 'active', eventsLast24h = stream_events rows with created_at within the last 24 hours.
  • The implementation follows the repository pattern used elsewhere in api/src (parameterized queries, handleDbError-style error handling) and keeps the 60-second cache behaviour of AdminController intact.
  • The placeholder comment and JSDoc in admin-stats.service.ts are removed or rewritten to describe the implemented queries.

Tests

  • An integration test against the test database (see api/src/database.integration.spec.ts) seeds users/streams/events and asserts the returned counts match, including a stream with status = 'inactive' that must not count toward activeStreams.
  • The existing admin controller spec still passes with the cached behaviour.

Documentation

  • The Swagger description of GET /admin/stats matches the implemented definitions (which counters, which time windows).

Out of scope

New admin metrics beyond the four existing fields, and the admin role/access story (separate issue).

Getting started

Real files in scope: api/src/admin/admin-stats.service.ts, api/src/admin/admin.controller.ts, api/src/database/database.module.ts (PG_POOL token), api/src/auth/users.repository.ts (pattern), api/src/database.integration.spec.ts (test harness), app/src/app/admin/admin-dashboard.tsx (consumer).

Verify with:

cd api && npm run typecheck && npm test

Good first files to read: api/src/admin/admin-stats.service.ts, api/src/streams/repository/streams-db.repository.ts (query style), api/src/database.integration.spec.ts.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignapiREST API design and endpointsbugSomething isn't workingobservability

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions