Skip to content

feat(api): add native /api/v1/* REST surface#38

Merged
revtex merged 1 commit intodevfrom
feat/native-api-n1-v1-rest
Apr 26, 2026
Merged

feat(api): add native /api/v1/* REST surface#38
revtex merged 1 commit intodevfrom
feat/native-api-n1-v1-rest

Conversation

@revtex
Copy link
Copy Markdown
Owner

@revtex revtex commented Apr 26, 2026

Introduces the native /api/v1/* REST surface alongside the existing legacy routes. Legacy endpoints, field names, and auth transports are unchanged — this is purely additive.

Highlights

  • Structured error envelope on all v1 responses: {"error":{"code","message","details"}} with stable string codes (validation_failed, unauthorized, forbidden, not_found, conflict, unprocessable, rate_limited, internal). 5xx envelopes auto-inject details.requestId.
  • V1Marker() + V1ErrorEnvelope() middleware on the /api/v1 group: marker sets a gin-context flag (apiVersion=v1) so downstream code can branch without URL-prefix matching; envelope rewriter buffers responses and converts legacy {"error":"<string>"} shapes into the native shape, leaving already-native bodies and 2xx responses untouched.
  • Native call upload at POST /api/v1/calls with the canonical multipart field names (systemId, talkgroupId, startedAt, frequencyHz, durationMs, unitId). startedAt must be RFC 3339 — unix timestamps are rejected with validation_failed. Companion POST /api/v1/calls/test returns 204.
  • Bearer-only API-key auth on v1: Authorization: Bearer <api-key> is the only accepted transport on v1 upload routes. Legacy routes keep accepting X-API-Key header, ?key= query, and key= form field. JWT-shaped Bearer tokens on v1 API-key routes are rejected with invalid_credentials so clients surface the right error.
  • Listener / admin endpoints mirrored under /api/v1/*, including GET/PUT /api/v1/listener/tg-selection (renamed from /api/auth/tg-selection), share/bookmark/calls list/audio/transcript routes, and /api/v1/admin/{import/*, radioreference/preview, transcriptions/status, docs/session}. RadioReference preview path drops the /csv suffix.

Tests

  • backend/internal/handler/shared/errors_test.go — table tests for every status the envelope helper produces.
  • backend/internal/handler/calls/v1_test.go — v1 upload happy path + validation failures (e.g. unix startedAt rejected).
  • go vet ./... && go build ./... && go test ./... clean.

Compatibility

No legacy route, header, query param, or field name changes. Trunk Recorder's rdioscanner_uploader plugin and the existing frontend continue to use the legacy paths and remain unaffected.

Introduces the native API surface alongside the existing legacy routes,
without breaking any current consumer.

Routing & middleware:
- New /api/v1 route group with V1Marker() (sets apiVersion=v1 in the
  gin context) and V1ErrorEnvelope() (rewrites legacy {"error":"..."}
  responses into the native {"error":{"code","message","details"}}
  envelope; already-native and 2xx responses pass through unchanged).
- shared.WriteAPIError + APIError/APIErrorResponse types with stable
  string codes (validation_failed, unauthorized, forbidden, not_found,
  conflict, unprocessable, rate_limited, internal). 5xx envelopes
  inject details.requestId from the request-id middleware.

Auth:
- APIKeyAuth on v1 paths accepts ONLY Authorization: Bearer <api-key>;
  legacy paths keep accepting X-API-Key, ?key=, and form key=.
- JWT-shaped Bearer values on v1 API-key routes are rejected with the
  invalid_credentials envelope so clients surface the right error.

Endpoints:
- POST /api/v1/calls — native upload with field names systemId,
  talkgroupId, startedAt (RFC 3339 only — unix timestamps rejected),
  frequencyHz, durationMs, unitId. POST /api/v1/calls/test returns 204.
- Listener: GET/PUT /api/v1/listener/tg-selection (renamed from
  /api/auth/tg-selection), plus calls list/audio/transcript, share,
  bookmarks, and unauth health/setup/auth endpoints.
- Admin: /api/v1/admin/{import/*, radioreference/preview (no /csv
  suffix), transcriptions/status, docs/session}, all JWT+admin gated.

Tests: shared/errors_test.go and calls/v1_test.go cover the envelope
shape and v1-specific upload validation (including unix-startedAt
rejection).
@revtex revtex merged commit a132399 into dev Apr 26, 2026
7 checks passed
@revtex revtex deleted the feat/native-api-n1-v1-rest branch April 26, 2026 23:12
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.

1 participant