Files
ohmj2/partitions/src/lib/api.ts
2026-02-25 18:31:41 +01:00

177 lines
5.1 KiB
TypeScript

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<Score[]> {
const response = await api.get('/scores');
return response.data.scores;
},
async getScore(id: string): Promise<Score> {
const response = await api.get(`/scores/${id}`);
return response.data.score;
},
async getInstruments(scoreId: string, pieceId?: number): Promise<Instrument[]> {
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<Piece[]> {
const response = await api.get(`/pieces/${scoreId}`);
return response.data.pieces;
},
async downloadPdf(path: string): Promise<Blob> {
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<Blob> {
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;
}
};