import type { components } from "./__generated__/openapi.types" import { fetchClient } from "." import { ACCESS_TOKEN_REGEXP, API_URL } from "@shared/lib/constants" type FileInfoResponse = components["schemas"]["FileInfoResponse"] type ProgressCallback = (percent: number) => void /** * Upload a file to the storage API. * Handles FormData construction and Content-Type header override * required for multipart uploads via openapi-fetch. * * @param file - File object to upload * @param folder - Target folder in storage (default: "") * @returns FileInfoResponse with file_path and file_url */ export async function uploadFile( file: File, folder = "", ): Promise { const formData = new FormData() formData.append("file", file) formData.append("folder", folder) const { data, error } = await fetchClient.POST("/api/files/upload/", { body: formData as unknown as { file: string; folder: string }, bodySerializer: () => formData as unknown as string, headers: { "Content-Type": null }, }) if (error || !data) { throw new Error("File upload failed") } return data } /** * Upload a file with real-time progress tracking. * Uses XMLHttpRequest for access to upload progress events. * * @param file - File object to upload * @param folder - Target folder in storage (default: "") * @param onProgress - Callback receiving upload percentage (0–100) * @returns FileInfoResponse with file_path and file_url */ export function uploadFileWithProgress( file: File, folder = "", onProgress?: ProgressCallback, ): Promise { return new Promise((resolve, reject) => { const formData = new FormData() formData.append("file", file) formData.append("folder", folder) const xhr = new XMLHttpRequest() xhr.open("POST", `${API_URL}/api/files/upload/`) const token = document.cookie.replace(ACCESS_TOKEN_REGEXP, "$1") if (token.length) { xhr.setRequestHeader("Authorization", `Bearer ${token}`) } xhr.upload.onprogress = (e) => { if (e.lengthComputable && onProgress) { onProgress(Math.round((e.loaded / e.total) * 100)) } } xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { try { resolve(JSON.parse(xhr.responseText) as FileInfoResponse) } catch { reject(new Error("Не удалось разобрать ответ сервера")) } } else { reject(new Error("Ошибка загрузки файла")) } } xhr.onerror = () => reject(new Error("Ошибка сети при загрузке")) xhr.send(formData) }) }