Skip to content

implements OT tracing with Jaeger UI - #184

Open
jacksayshi wants to merge 42 commits into
mainfrom
tracing
Open

implements OT tracing with Jaeger UI#184
jacksayshi wants to merge 42 commits into
mainfrom
tracing

Conversation

@jacksayshi

Copy link
Copy Markdown
Collaborator

feat: Open Telemetry Tracing with Jaeger UI

resolves (partly) : #171

Description

Adds OpenTelemetry distributed tracing to the backend so we can see where request time is actually going.

The backend is instrumented with the OpenTelemetry Node SDK plus auto-instrumentations, which traces incoming HTTP requests, outgoing HTTP calls, and Postgres queries without any per-route code changes. Spans are exported over OTLP/HTTP to a Jaeger all-in-one container added to docker-compose.yml, so traces are viewable in the Jaeger UI at http://localhost:16686/.
Tracing is loaded via Node's --import flag in the dev script.

Files Added

  • backend/src/tracing.ts — initializes the OpenTelemetry NodeSDK with a civickit-backend service name, the OTLP/HTTP trace exporter pointed at http://localhost:4318/v1/traces, and the default Node auto-instrumentations.

Files Modified

  • backend/docker-compose.yml — adds a jaeger service (jaegertracing/all-in-one:1.76.0) exposing 16686 for the UI and 4318 for the OTLP/HTTP receiver.
  • backend/package.json — adds the six @opentelemetry/* dependencies and changes the dev script to tsx watch --import ./src/tracing.ts src/server.ts.
  • backend/package-lock.json — lockfile updates for the new dependencies.

To Test:

cd backend

# install the new OpenTelemetry dependencies
npm install

# start Postgres + Jaeger
npm run db:up

# start the backend (tracing initializes on boot)
npm run dev

  • It prints tracing initialized in the console. Make a request from mobile application to see traces in the Jagger UI
  • Open the Jaeger UI at http://localhost:16686/, select the civickit-backend service, and hit Find Traces.

Expected: a trace for GET /api/issues/nearby with nested child spans for the PostGIS query, showing the duration breakdown per span.

jacksayshi and others added 30 commits July 10, 2026 18:36
…tures

The User model in prisma/schema.prisma gained emailVerified and image
fields (BetterAuth), but the two mock User fixtures in
auth.service.test.ts were never updated, breaking tsc --noEmit and
therefore npm run build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
migrate:dev pointed at ./src/prisma/schema.prisma and
./src/prisma.config.ts, neither of which exists; the real files are
./prisma/schema.prisma and ./prisma.config.ts. Also add a typecheck
script so CI can gate on tsc --noEmit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
npm test (vitest/esbuild) never type-checks, so a broken tsc/build
stayed green in CI. Add Typecheck and Build steps between Prisma
generation and the test run so CI actually catches type errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fails closed at import time if JWT_SECRET is missing or too short,
instead of silently falling back to the string 'undefined'.
Replaces String(process.env.JWT_SECRET) + dead undefined-guards in
auth.middleware.ts, login.service.ts, and auth.service.ts with the
validated JWT_SECRET import. Removes the auth-bypass risk where a
missing secret would previously sign/verify against the literal
string 'undefined'.
config/env.ts now throws at import time when JWT_SECRET is absent, so
every test file that transitively imports it needs the variable
seeded before the test module graph loads. Wires vitest setupFiles to
a new setup-env.ts (fake, non-production value only), removes the
now-dead runtime env mutation from login.service.test.ts's beforeEach,
and updates its by-value jwt.sign assertion to match.
Covers present/valid, unset, too-short, and the never-log-the-value
property directly against the exported helper.
The rbac branch added the Role enum and code reading user.role, but the
User model itself never got the column, so the generated Prisma client
had no role property. This caused the two CI typecheck errors in
authorize.middleware.ts and auth.repository.ts.

- Add role Role @default(REPORTER) to model User
- Add migration creating the Role enum and user.role column
- Add role to the User fixtures in auth.service.test.ts

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Update project status to reflect new timeline
- auth.repository.ts: findById now uses a Prisma select that omits
  passwordHash instead of fetching and reattaching it.
- /auth/user now requires the existing Bearer-token authMiddleware
  instead of reading a JWT from the query string; auth.service.ts
  gains getUserById(userId) (the middleware already verifies the
  token), replacing the token-parsing getUserByToken.
- Mobile AuthContext sends the token via an Authorization header
  instead of ?token=<jwt> in the URL, and gates the query on
  authToken being present so it no longer fires with token=null.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stop /auth/user from leaking password hashes and tokens in URLs
PR #166 added the Role model and put requirePermission("update:issue_status")
in front of PATCH /issues/:issueId/status, but POST /issues/:issueId/update
also writes the issue status via TimelineController.postUpdate and was left
behind authMiddleware alone. Any authenticated REPORTER could move any issue
to RESOLVED/CLOSED through it, which defeats the gate on the sibling route.

Gate the timeline route with the same permission, and add the tests that
should have shipped with the role work: the authorize middleware had none, so
neither the 403 path nor the fail-closed behaviour on a repository error was
covered.

Also drops the stale "add user restrictions here" TODO in IssueService, which
is now handled a layer up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bhuelsman and others added 12 commits July 21, 2026 11:15
fix(backend): close status-change bypass and cover the RBAC gate
The global error handler only recognized AppError instances, so plain
object-literal throws in issue.service.ts (e.g. { status: 404, message:
'Issue not found' }) fell through to a generic 500 "Internal Server
Error", hiding real status codes from clients. Also removes the double
res.status(error.statusCode) + next(error) pattern in login.controller.ts,
which threw when statusCode was undefined.

- error.middleware.ts now also honors a numeric status/statusCode on any
  thrown object, defaulting to 500 only for truly unknown errors.
- issue.service.ts's 4 object-literal throws are now AppError instances.
- login.controller.ts's catch block just forwards to next(error).
- error_handling.test.ts gains 4 cases covering AppError, {status}, and
  {statusCode} shapes, plus confirms unknown errors still stay opaque.
Make error middleware honor service error status codes
- Replace wildcard CORS origin with an ALLOWED_ORIGINS allowlist (env-configurable, falls back to localhost dev origin, still permits no-Origin requests for the mobile app).
- Add a stricter auth rate limiter and apply it to the BetterAuth mount, which previously ran unthrottled ahead of the general limiter registration.
- Remove dead `app.use('/api/issues/upvote', authMiddleware)` line, registered after the issue routes so it never executed; real upvote routes already carry authMiddleware.
Add a zod-backed validateBody middleware and createIssueSchema, and wire
them into POST /api/issues. Strips unknown keys (e.g. attacker-supplied
userId) and enforces types/bounds on title, coordinates, images, etc.
…ED_ORIGINS

Cover valid body, missing/short title, non-URL images, out-of-range
latitude, and unknown-key stripping (mass-assignment protection).
Document the new ALLOWED_ORIGINS env var in .env.example.
Fix by clearing `isLoading` immediately when the mount-time token read
returns null: with no token there is no user to fetch, and the correct
state is "not logged in, not loading." The existing flow for stored
tokens is unchanged.
mobile: exit auth loading state when no stored token exists
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants