feat(api): add native /api/v1/* REST surface#38
Merged
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
{"error":{"code","message","details"}}with stable string codes (validation_failed,unauthorized,forbidden,not_found,conflict,unprocessable,rate_limited,internal). 5xx envelopes auto-injectdetails.requestId.V1Marker()+V1ErrorEnvelope()middleware on the/api/v1group: 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.POST /api/v1/callswith the canonical multipart field names (systemId,talkgroupId,startedAt,frequencyHz,durationMs,unitId).startedAtmust be RFC 3339 — unix timestamps are rejected withvalidation_failed. CompanionPOST /api/v1/calls/testreturns 204.Authorization: Bearer <api-key>is the only accepted transport on v1 upload routes. Legacy routes keep acceptingX-API-Keyheader,?key=query, andkey=form field. JWT-shaped Bearer tokens on v1 API-key routes are rejected withinvalid_credentialsso clients surface the right error./api/v1/*, includingGET/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/csvsuffix.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. unixstartedAtrejected).go vet ./... && go build ./... && go test ./...clean.Compatibility
No legacy route, header, query param, or field name changes. Trunk Recorder's
rdioscanner_uploaderplugin and the existing frontend continue to use the legacy paths and remain unaffected.