diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c337bb..8cbf8bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Frontend now talks to the native `/api/v1/*` surface for every REST call (RTK Query base URL, raw `fetch()` for audio downloads and silent token refresh, the service-worker passthrough rules, the dev-server proxy, and the Swagger UI bootstrap). Tg-selection moves from `/api/auth/tg-selection` to `/api/v1/listener/tg-selection`; RadioReference CSV preview moves to `/api/v1/admin/radioreference/preview`; legacy-usage report is consumed at `/api/v1/admin/legacy-usage`. Legacy `/api/*` routes remain available for non-frontend clients with the existing deprecation headers. - API-key authentication on `/api/v1/*` upload routes accepts only `Authorization: Bearer `; the legacy `X-API-Key` header, `?key=` query parameter, and `key=` form field continue to work on legacy routes only. JWT-shaped Bearer tokens on v1 API-key routes are rejected with `invalid_credentials`. ## [1.2.1] — 2026-04-25 diff --git a/frontend/src/app/api.ts b/frontend/src/app/api.ts index bf08318..87f394b 100644 --- a/frontend/src/app/api.ts +++ b/frontend/src/app/api.ts @@ -12,7 +12,7 @@ import type { } from "@/types"; const rawBaseQuery = fetchBaseQuery({ - baseUrl: "/api", + baseUrl: "/api/v1", prepareHeaders: (headers, { getState }) => { const state = getState() as { auth: { token: string | null } }; const token = state.auth?.token; @@ -149,7 +149,7 @@ export const api = createApi({ providesTags: ["Bookmarks"], }), getLegacyUsage: builder.query({ - query: () => "/v1/admin/legacy-usage", + query: () => "/admin/legacy-usage", providesTags: ["LegacyUsage"], }), }), diff --git a/frontend/src/app/slices/admin/adminSlice.ts b/frontend/src/app/slices/admin/adminSlice.ts index 180b3be..2dc357f 100644 --- a/frontend/src/app/slices/admin/adminSlice.ts +++ b/frontend/src/app/slices/admin/adminSlice.ts @@ -75,7 +75,7 @@ const adminApi = api.injectEndpoints({ // ── RadioReference CSV preview (multipart file upload) ── rrPreviewCSV: builder.mutation({ query: (body) => ({ - url: "/admin/radioreference/preview/csv", + url: "/admin/radioreference/preview", method: "POST", body, }), diff --git a/frontend/src/app/slices/shared/authSlice.ts b/frontend/src/app/slices/shared/authSlice.ts index 69c9aad..88f4c4b 100644 --- a/frontend/src/app/slices/shared/authSlice.ts +++ b/frontend/src/app/slices/shared/authSlice.ts @@ -110,14 +110,14 @@ const authApi = api.injectEndpoints({ { disabledTGs: number[]; avoidList?: AvoidEntry[] }, void >({ - query: () => "/auth/tg-selection", + query: () => "/listener/tg-selection", }), updateTGSelection: builder.mutation< { ok: boolean }, { disabledTGs: number[]; avoidList: AvoidEntry[] } >({ query: (body) => ({ - url: "/auth/tg-selection", + url: "/listener/tg-selection", method: "PUT", body, }), diff --git a/frontend/src/components/admin/ToolsPanel.tsx b/frontend/src/components/admin/ToolsPanel.tsx index 7c27db8..0c57e39 100644 --- a/frontend/src/components/admin/ToolsPanel.tsx +++ b/frontend/src/components/admin/ToolsPanel.tsx @@ -25,7 +25,7 @@ import { selectToken } from "@/app/slices/shared/authSlice"; import { useAppSelector } from "@/app/store"; import RadioReferenceCard from "@/components/admin/RadioReferenceCard"; -const SWAGGER_URL = "/api/admin/docs/index.html"; +const SWAGGER_URL = "/api/v1/admin/docs/index.html"; export default function ToolsPanel() { const token = useAppSelector(selectToken); @@ -611,7 +611,7 @@ export default function ToolsPanel() {