import { api } from '../index'; import { toUser, type RawUser } from '../mappers'; import type { User, ServicesStatus, SourceInfo } from '../types'; interface ReindexJob { source: string; jobId: string; } /** * Admin user management. The backend models authorization as `is_superuser` / * `is_active` (no `role`/`email`); `toUser` maps superuser→role for the UI and * the mutations translate role back to `is_superuser` on the way out. */ export const adminApi = api.injectEndpoints({ endpoints: (build) => ({ getUsers: build.query({ query: () => '/admin/users', transformResponse: (raw: RawUser[]) => raw.map(toUser), providesTags: ['User'], }), getUser: build.query({ query: (id) => `/admin/users/${id}`, transformResponse: (raw: RawUser) => toUser(raw), providesTags: (_res, _err, id) => [{ type: 'User', id }], }), createUser: build.mutation< User, { username: string; password: string; role: 'admin' | 'user' } >({ query: ({ username, password, role }) => ({ url: '/admin/users', method: 'POST', body: { username, password, is_superuser: role === 'admin' }, }), transformResponse: (raw: RawUser) => toUser(raw), invalidatesTags: ['User'], }), updateUser: build.mutation< User, { id: string; role?: 'admin' | 'user'; isActive?: boolean } >({ query: ({ id, role, isActive }) => ({ url: `/admin/users/${id}`, method: 'PATCH', body: { is_superuser: role === undefined ? undefined : role === 'admin', is_active: isActive, }, }), transformResponse: (raw: RawUser) => toUser(raw), invalidatesTags: (_res, _err, { id }) => ['User', { type: 'User', id }], }), // Soft delete on the backend: deactivates the account and revokes its // sessions (see admin.py::deactivate_user), so it also refreshes the row. deleteUser: build.mutation({ query: (id) => ({ url: `/admin/users/${id}`, method: 'DELETE' }), invalidatesTags: (_res, _err, id) => ['User', { type: 'User', id }], }), resetPassword: build.mutation({ query: ({ id, newPassword }) => ({ url: `/admin/users/${id}/reset-password`, method: 'POST', body: { new_password: newPassword }, }), }), // -- instance (§A9) ------------------------------------------------------- getServices: build.query({ query: () => '/admin/services', }), getSources: build.query({ query: () => '/admin/sources', }), reindex: build.mutation({ query: () => ({ url: '/admin/reindex', method: 'POST' }), transformResponse: (raw: { jobs: { source: string; job_id: string }[] }) => raw.jobs.map((j) => ({ source: j.source, jobId: j.job_id })), // A fresh scan changes the catalogue once the worker runs. invalidatesTags: ['Track', 'Album', 'Artist', 'Storage'], }), }), overrideExisting: false, }); export const { useGetUsersQuery, useGetUserQuery, useCreateUserMutation, useUpdateUserMutation, useDeleteUserMutation, useResetPasswordMutation, useGetServicesQuery, useGetSourcesQuery, useReindexMutation, } = adminApi;