/** * RPRO3: API-Client für den RennmausPro-III-Backup-Import. * POST /import/rpro3/analyze -> multipart 'backup' (+ optional 'images') -> Auswertung (kein Schreiben) * POST /import/rpro3/execute -> derselbe Upload -> idempotenter Import * * Upload via FormData (nicht api.post — das erzwingt application/json); der Browser setzt * den multipart-Boundary-Header selbst. */ import { API_BASE_URL, ApiError } from './client' import { de } from '../strings/de' export interface Rpro3Counts { ownAnimals: number externalRaw: number externalAfterDedup: number litters: number contacts: number genotypes: number duplicatesMerged: number mergeClusters: number } export interface Rpro3AmbiguousVariant { count: number dob: string farbe: string origin: string isOwn: boolean } export interface Rpro3AmbiguousName { name: string variants: Rpro3AmbiguousVariant[] bareCount: number } export interface Rpro3MergeSample { name: string recordCount: number dob: string farbe: string origin: string } export interface Rpro3AnalyzeResult { counts: Rpro3Counts newVsCurrentNew: number newVsCurrentExisting: number topMerges: Rpro3MergeSample[] ambiguousNames: Rpro3AmbiguousName[] photosProvided: boolean photoFilesAvailable: number } export interface Rpro3ExecuteResult { gerbilsImported: number littersImported: number contactsImported: number healthRecordsImported: number weightRecordsImported: number photosImported: number message: string } async function postUpload(path: string, backup: File, images: File | null): Promise { const form = new FormData() form.append('backup', backup) if (images) form.append('images', images) let response: Response try { response = await fetch(`${API_BASE_URL}${path}`, { method: 'POST', body: form }) } catch { throw new ApiError(de.api.errors.network, null, path) } if (!response.ok) { // 400 trägt eine deutsche Klartext-Fehlermeldung (z. B. „keine _rpro3.db gefunden"). let body: unknown let text: string | null = null try { text = await response.clone().text() body = text ? JSON.parse(text) : undefined } catch { body = undefined } const message = response.status === 400 && text ? text.replace(/^"|"$/g, '') : response.status >= 500 ? de.api.errors.server : de.api.errors.unknown throw new ApiError(message, response.status, path, body) } return (await response.json()) as T } export function analyzeRpro3(backup: File, images: File | null): Promise { return postUpload('/import/rpro3/analyze', backup, images) } export function executeRpro3(backup: File, images: File | null): Promise { return postUpload('/import/rpro3/execute', backup, images) }