import axios, { type AxiosError } from 'axios'; import { auth } from '$lib/stores/auth'; import { browser } from '$app/environment'; import { get } from 'svelte/store'; const API_BASE_URL_LOCAL = 'http://localhost:8000'; const API_BASE_URL_PROD = 'https://ohmj-api.c.nadal-fr.com'; const API_BASE_URL = import.meta.env.DEV ? API_BASE_URL_LOCAL : API_BASE_URL_PROD; const api = axios.create({ baseURL: API_BASE_URL, headers: { 'Content-Type': 'application/json' } }); export { api }; api.interceptors.request.use((config) => { const authState = get(auth); if (authState.token) { config.headers.Authorization = `Bearer ${authState.token}`; } return config; }); api.interceptors.response.use( (response) => response, (error: AxiosError) => { // Only logout on actual 401 from the server, not network/CORS errors if (error.response?.status === 401 && !error.message?.includes('Network Error')) { auth.logout(); if (browser) { window.location.href = '/'; } } return Promise.reject(error); } ); export interface Score { id: string; name: string; compositor: string; ressource?: string | null; instruments?: Instrument[]; } export interface Piece { id: number; name: string; } export interface Instrument { id: string; title: string; piece: string; parts: Part[]; } export interface Part { id: string; files: PdfFile[]; } export interface PdfFile { name: string; filename: string; path: string; part: string | null; key: string | null; clef: string | null; variant: string | null; } export const apiService = { async login(username: string, password: string): Promise<{ token: string; user: { username: string; role: string } }> { const response = await api.post('/login', { username, password }); return response.data; }, async getScores(): Promise { const response = await api.get('/scores'); return response.data.scores; }, async getScore(id: string): Promise { const response = await api.get(`/scores/${id}`); return response.data.score; }, async getInstruments(scoreId: string, pieceId?: number): Promise { const url = pieceId ? `/scores/${scoreId}/instruments?piece=${pieceId}` : `/scores/${scoreId}/instruments`; const response = await api.get(url); return response.data.instruments; }, async getPieces(scoreId: string): Promise { const response = await api.get(`/pieces/${scoreId}`); return response.data.pieces; }, async downloadPdf(path: string): Promise { const response = await api.get(`/download/${path}`, { responseType: 'blob' }); return response.data; }, getDownloadUrl(path: string): string { // Pass token in URL for direct browser access (PDF viewer, iframe, etc.) // Safe over HTTPS const authState = get(auth); const token = authState.token ? encodeURIComponent(authState.token) : ''; return `${API_BASE_URL}/download/${path}?token=${token}`; }, // New method to download with proper auth header async downloadFileWithAuth(path: string): Promise { const response = await api.get(`/download/${path}`, { responseType: 'blob' }); return response.data; }, async createScore(name: string, compositor: string, pieces: { number: number; name: string }[] = []): Promise<{ success: boolean; score?: any; error?: string }> { const response = await api.post('/admin/scores', { name, compositor, pieces }); return response.data; }, async updateScore(id: string, name: string, compositor: string, ressource: string = ''): Promise<{ success: boolean; error?: string }> { const response = await api.put(`/admin/scores/${id}`, { name, compositor, ressource }); return response.data; }, async deleteScore(id: string): Promise<{ success: boolean; error?: string }> { const response = await api.delete(`/admin/scores/${id}`); return response.data; }, async uploadPdf(scoreId: string, file: File, piece: string, instrument: string, version: string, key?: string, clef?: string, variant?: string, part?: string, watermark?: boolean, watermarkPosition?: 'left' | 'right'): Promise<{ success: boolean; path?: string; error?: string }> { const formData = new FormData(); formData.append('file', file); formData.append('piece', piece); formData.append('instrument', instrument); formData.append('version', version); if (key) formData.append('key', key); if (clef) formData.append('clef', clef); if (variant) formData.append('variant', variant); if (part) formData.append('part', part); if (watermark) formData.append('watermark', 'true'); if (watermarkPosition) formData.append('watermarkPosition', watermarkPosition); const response = await api.post(`/admin/scores/${scoreId}/upload`, formData, { headers: { 'Content-Type': undefined } }); return response.data; }, async getScoreFiles(scoreId: string): Promise<{ success: boolean; files: any[]; error?: string }> { const response = await api.get(`/admin/scores/${scoreId}/files`); return response.data; }, async deleteScoreFile(scoreId: string, filePath: string): Promise<{ success: boolean; error?: string }> { const response = await api.delete(`/admin/scores/${scoreId}/files`, { params: { path: filePath } }); return response.data; } };