video-accessibility-old/frontend/src/lib/api.ts
2025-10-10 09:19:39 -05:00

283 lines
No EOL
8.9 KiB
TypeScript

import axios from 'axios';
import type { AxiosInstance } from 'axios';
import type {
LoginRequest,
LoginResponse,
RefreshResponse,
MicrosoftLoginResponse,
Job,
JobCreateRequest,
JobListResponse,
JobDownloadsResponse,
VttContentResponse,
VttUpdateRequest,
AssetValidationResponse,
BulkDeleteRequest,
BulkDeleteResponse,
JobDeleteResponse,
User,
UserListResponse,
CreateUserRequest,
UpdateUserRequest,
ResetPasswordResponse,
AdminStatsResponse,
} from '../types/api';
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000';
class ApiClient {
private client: AxiosInstance;
private accessToken: string | null = null;
constructor() {
this.client = axios.create({
baseURL: `${API_BASE_URL}/api/v1`,
withCredentials: true,
timeout: 30000,
});
this.setupInterceptors();
}
private setupInterceptors() {
// Request interceptor to add auth token
this.client.interceptors.request.use(
(config) => {
if (this.accessToken) {
config.headers.Authorization = `Bearer ${this.accessToken}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor to handle token refresh
this.client.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
// Don't try to refresh if this is already the refresh endpoint
if (error.response?.status === 401 &&
!originalRequest._retry &&
originalRequest.url !== '/auth/refresh') {
originalRequest._retry = true;
try {
const refreshResponse = await this.client.post('/auth/refresh');
this.accessToken = refreshResponse.data.access_token;
// Retry original request with new token
originalRequest.headers.Authorization = `Bearer ${this.accessToken}`;
return this.client(originalRequest);
} catch (refreshError) {
// Refresh failed, clear token and update auth state
this.accessToken = null;
// Clear auth state in the store
const { useAuthStore } = await import('./auth');
useAuthStore.getState().logout();
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
}
setAccessToken(token: string) {
this.accessToken = token;
}
clearAccessToken() {
this.accessToken = null;
}
getAccessToken(): string | null {
return this.accessToken;
}
// Auth endpoints
async login(credentials: LoginRequest): Promise<LoginResponse> {
const response = await this.client.post('/auth/login', credentials);
this.setAccessToken(response.data.access_token);
return response.data;
}
async loginWithMicrosoft(idToken: string): Promise<MicrosoftLoginResponse> {
const response = await this.client.post('/auth/microsoft', { id_token: idToken });
this.setAccessToken(response.data.access_token);
return response.data;
}
async refresh(): Promise<RefreshResponse> {
const response = await this.client.post('/auth/refresh');
this.setAccessToken(response.data.access_token);
return response.data;
}
async logout(): Promise<void> {
await this.client.post('/auth/logout');
this.clearAccessToken();
}
// Job endpoints
async getJobs(filters?: { status?: string; mine?: boolean; page?: number; size?: number }): Promise<JobListResponse> {
const params = new URLSearchParams();
if (filters?.status) params.append('status', filters.status);
if (filters?.mine) params.append('mine', 'true');
if (filters?.page) params.append('page', filters.page.toString());
if (filters?.size) params.append('size', filters.size.toString());
const response = await this.client.get(`/jobs?${params.toString()}`);
return response.data;
}
async getJob(id: string): Promise<Job> {
const response = await this.client.get(`/jobs/${id}`);
return response.data;
}
async createJob(data: JobCreateRequest, file: File, onUploadProgress?: (progressEvent: { loaded: number; total: number }) => void): Promise<Job> {
const formData = new FormData();
formData.append('title', data.title);
formData.append('language', data.language);
formData.append('requested_outputs', JSON.stringify(data.requested_outputs));
formData.append('file', file);
const response = await this.client.post('/jobs', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: onUploadProgress ? (progressEvent) => {
if (progressEvent.total) {
onUploadProgress({
loaded: progressEvent.loaded,
total: progressEvent.total
});
}
} : undefined,
});
return response.data;
}
async updateJob(id: string, data: Partial<Job>): Promise<Job> {
const response = await this.client.patch(`/jobs/${id}`, data);
return response.data;
}
async approveEnglish(id: string, notes?: string): Promise<Job> {
const response = await this.client.post(`/jobs/${id}/actions/approve_english`, { notes });
return response.data;
}
async rejectJob(id: string, notes: string): Promise<Job> {
const response = await this.client.post(`/jobs/${id}/actions/reject`, { notes });
return response.data;
}
async completeJob(id: string, notes?: string): Promise<Job> {
const response = await this.client.post(`/jobs/${id}/actions/complete`, { notes });
return response.data;
}
async rejectFinalReview(id: string, notes: string): Promise<Job> {
const response = await this.client.post(`/jobs/${id}/actions/reject_final`, { notes });
return response.data;
}
async getJobDownloads(id: string): Promise<JobDownloadsResponse> {
const response = await this.client.get(`/jobs/${id}/downloads`);
return response.data;
}
async getJobVttContent(id: string, language: string = 'en'): Promise<VttContentResponse> {
const response = await this.client.get(`/jobs/${id}/vtt?language=${language}`);
return response.data;
}
async updateJobVttContent(id: string, data: VttUpdateRequest): Promise<Job> {
const response = await this.client.patch(`/jobs/${id}/vtt`, data);
return response.data;
}
async adjustVttTiming(id: string, data: {
offset_seconds: number;
language?: string;
adjust_captions?: boolean;
adjust_audio_description?: boolean;
}): Promise<Job> {
const response = await this.client.post(`/jobs/${id}/vtt/adjust-timing`, data);
return response.data;
}
async validateJobAssets(id: string): Promise<AssetValidationResponse> {
const response = await this.client.get(`/jobs/${id}/validate`);
return response.data;
}
async deleteJob(id: string): Promise<JobDeleteResponse> {
const response = await this.client.delete(`/jobs/${id}`);
return response.data;
}
async bulkDeleteJobs(data: BulkDeleteRequest): Promise<BulkDeleteResponse> {
const response = await this.client.delete('/jobs/bulk', { data });
return response.data;
}
async reprocessJob(id: string): Promise<{ message: string }> {
const response = await this.client.post(`/admin/maintenance/reprocess-job/${id}`);
return response.data;
}
// User Management endpoints
async listUsers(filters?: {
page?: number;
size?: number;
role?: string;
active_only?: boolean;
}): Promise<UserListResponse> {
const params = new URLSearchParams();
if (filters?.page) params.append('page', filters.page.toString());
if (filters?.size) params.append('size', filters.size.toString());
if (filters?.role) params.append('role', filters.role);
if (filters?.active_only !== undefined) params.append('active_only', filters.active_only.toString());
const response = await this.client.get(`/admin/users?${params.toString()}`);
return response.data;
}
async getUser(userId: string): Promise<User> {
const response = await this.client.get(`/admin/users/${userId}`);
return response.data;
}
async createUser(data: CreateUserRequest): Promise<User> {
const response = await this.client.post('/admin/users', data);
return response.data;
}
async updateUser(userId: string, data: UpdateUserRequest): Promise<User> {
const response = await this.client.patch(`/admin/users/${userId}`, data);
return response.data;
}
async deactivateUser(userId: string): Promise<{ message: string }> {
const response = await this.client.delete(`/admin/users/${userId}`);
return response.data;
}
async resetUserPassword(userId: string): Promise<ResetPasswordResponse> {
const response = await this.client.post(`/admin/users/${userId}/password/reset`);
return response.data;
}
async getAdminStats(): Promise<AdminStatsResponse> {
const response = await this.client.get('/admin/stats');
return response.data;
}
}
export const apiClient = new ApiClient();
export const api = apiClient;