// --------------------------------------------------------------------------- // Client-side API helpers for upload operations // --------------------------------------------------------------------------- import type { FolderEntry } from './client-types' import type { FileDiff } from './types' export interface CheckResult { exists: boolean diffs: FileDiff[] } export interface StageResult { stagingId: string folderName: string filesCount: number } // --------------------------------------------------------------------------- // Shared FormData builder // --------------------------------------------------------------------------- function buildUploadFormData( folder: FolderEntry, extra?: Record, ): FormData { const formData = new FormData() formData.append('folderName', folder.folderName) if (extra) { for (const [key, value] of Object.entries(extra)) { formData.append(key, value) } } formData.append('files', folder.modelFile) formData.append('fileTypes', 'model') formData.append('textureNames', '') for (const tex of folder.textures) { formData.append('files', tex.file) formData.append('fileTypes', 'texture') formData.append('textureNames', tex.name) } return formData } // --------------------------------------------------------------------------- // Check folder diffs against remote (GitHub) // --------------------------------------------------------------------------- /** * Check whether a folder already exists on the remote repo and compute diffs. * Throws on auth/network errors so callers can surface them to the user. */ export async function checkFolderDiffs( stagingId: string, secret: string, signal?: AbortSignal, ): Promise { const res = await fetch('/api/upload/check', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-upload-secret': secret.trim(), }, body: JSON.stringify({ stagingId }), signal, }) const data = await res.json() // Surface auth/server errors to the caller if (!res.ok) { throw new Error(data.error || `Erreur serveur (${res.status})`) } if (!data.success || !data.exists) { return { exists: false, diffs: [] } } return { exists: true, diffs: (data.diffs || []) as FileDiff[] } } export async function stageUpload( folder: FolderEntry, secret: string, signal?: AbortSignal, ): Promise { const formData = buildUploadFormData(folder) const res = await fetch('/api/upload/stage', { method: 'POST', headers: { 'x-upload-secret': secret.trim() }, body: formData, signal, }) const data = await res.json() if (!res.ok || !data.success) { throw new Error(data.error || `Erreur serveur (${res.status})`) } return { stagingId: data.stagingId, folderName: data.folderName, filesCount: data.filesCount, } } // --------------------------------------------------------------------------- // Upload original files to Nextcloud Drive // --------------------------------------------------------------------------- /** Upload original files to Nextcloud Drive. */ export async function uploadDrive( stagingId: string, secret: string, action: 'new' | 'replace', signal?: AbortSignal, ): Promise<{ success: boolean; error?: string }> { try { const res = await fetch('/api/upload/drive', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-upload-secret': secret.trim(), }, body: JSON.stringify({ stagingId, action }), signal, }) const data = await res.json() if (!data.success) return { success: false, error: data.error } return { success: true } } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') { return { success: false, error: 'Upload annule' } } return { success: false, error: 'Erreur reseau (Drive)' } } } // --------------------------------------------------------------------------- // Upload files to GitHub // --------------------------------------------------------------------------- /** Upload files to GitHub. */ export async function uploadGit( stagingId: string, secret: string, onProgress: (pct: number) => void, signal?: AbortSignal, ): Promise<{ success: boolean; filename?: string; error?: string }> { onProgress(10) try { const res = await fetch('/api/upload/git', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-upload-secret': secret.trim(), }, body: JSON.stringify({ stagingId }), signal, }) onProgress(80) const data = await res.json() if (!data.success) { return { success: false, error: data.error } } onProgress(100) return { success: true, filename: data.folderName } } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') { return { success: false, error: 'Upload annule' } } return { success: false, error: 'Erreur reseau' } } }