91 lines
2.4 KiB
TypeScript
91 lines
2.4 KiB
TypeScript
const API_URL = import.meta.env.VITE_API_URL || '/api/v1';
|
|
const SESSION_KEY = 'compor_hub_session';
|
|
|
|
export type ApiRole = 'student' | 'instructor' | 'admin';
|
|
|
|
export interface ApiUser {
|
|
id: string;
|
|
email: string;
|
|
name: string;
|
|
role: ApiRole;
|
|
}
|
|
|
|
export interface ManagedUser {
|
|
id: string;
|
|
email: string;
|
|
name: string;
|
|
role: ApiRole;
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
}
|
|
|
|
interface Session {
|
|
token: string;
|
|
user: ApiUser;
|
|
}
|
|
|
|
export class ApiError extends Error {
|
|
constructor(message: string, public status: number) {
|
|
super(message);
|
|
}
|
|
}
|
|
|
|
export function getSession(): Session | null {
|
|
const value = localStorage.getItem(SESSION_KEY);
|
|
if (!value) return null;
|
|
try {
|
|
return JSON.parse(value) as Session;
|
|
} catch {
|
|
localStorage.removeItem(SESSION_KEY);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function saveSession(session: Session) {
|
|
localStorage.setItem(SESSION_KEY, JSON.stringify(session));
|
|
}
|
|
|
|
export function clearSession() {
|
|
localStorage.removeItem(SESSION_KEY);
|
|
}
|
|
|
|
export async function apiRequest<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
const session = getSession();
|
|
const headers = new Headers(options.headers);
|
|
if (options.body) headers.set('Content-Type', 'application/json');
|
|
if (session) headers.set('Authorization', `Bearer ${session.token}`);
|
|
|
|
const response = await fetch(`${API_URL}${path}`, { ...options, headers });
|
|
if (response.status === 204) return undefined as T;
|
|
|
|
const body = await response.json().catch(() => ({}));
|
|
if (!response.ok) {
|
|
throw new ApiError(body.error || 'The request could not be completed', response.status);
|
|
}
|
|
return body as T;
|
|
}
|
|
|
|
export const authApi = {
|
|
async login(email: string, password: string) {
|
|
return apiRequest<Session>('/auth/login', { method: 'POST', body: JSON.stringify({ email, password }) });
|
|
},
|
|
async register(name: string, email: string, password: string) {
|
|
return apiRequest<Session>('/auth/register', { method: 'POST', body: JSON.stringify({ name, email, password }) });
|
|
},
|
|
async me() {
|
|
return apiRequest<{ user: ApiUser }>('/auth/me');
|
|
},
|
|
};
|
|
|
|
export const adminApi = {
|
|
async listUsers() {
|
|
return apiRequest<{ data: ManagedUser[] }>('/admin/users');
|
|
},
|
|
async updateUser(userId: string, update: Partial<Pick<ManagedUser, 'role' | 'isActive'>>) {
|
|
return apiRequest<{ data: ManagedUser }>(`/admin/users/${userId}`, {
|
|
method: 'PATCH',
|
|
body: JSON.stringify(update),
|
|
});
|
|
},
|
|
};
|