import { useState, useEffect } from 'react'; import { getDownloadInfo } from '../../api/download'; import { tryRefresh } from '../../api/client'; import './AdminCommon.css'; const BASE = import.meta.env.VITE_API_BASE_URL || ''; function sendXhr(url, token, file, { onProgress, onDone, onError }) { const xhr = new XMLHttpRequest(); xhr.upload.onprogress = (event) => { if (event.lengthComputable) { onProgress(Math.round((event.loaded / event.total) * 100)); } }; xhr.onload = () => onDone(xhr); xhr.onerror = () => onError(); xhr.open('POST', url); xhr.setRequestHeader('Authorization', `Bearer ${token}`); xhr.setRequestHeader('Content-Type', 'application/octet-stream'); xhr.send(file); } function UploadForm({ title, hint, accept, endpoint, onSuccess }) { const [file, setFile] = useState(null); const [uploading, setUploading] = useState(false); const [progress, setProgress] = useState(0); const [error, setError] = useState(''); const handleFileChange = (e) => { setFile(e.target.files[0] || null); setError(''); setProgress(0); }; const handleUpload = (e) => { e.preventDefault(); if (!file) return; const token = localStorage.getItem('token'); const url = `${BASE}${endpoint}?filename=${encodeURIComponent(file.name)}`; setUploading(true); setError(''); const handleDone = (xhr) => { // 401 시 토큰 갱신 후 재시도 if (xhr.status === 401) { tryRefresh() .then((newToken) => { sendXhr(url, newToken, file, { onProgress: (p) => setProgress(p), onDone: (retryXhr) => { setUploading(false); if (retryXhr.status === 401) { window.dispatchEvent(new Event('auth:unauthorized')); return; } parseXhrResponse(retryXhr); }, onError: handleError, }); }) .catch(() => { setUploading(false); window.dispatchEvent(new Event('auth:unauthorized')); }); return; } setUploading(false); parseXhrResponse(xhr); }; const parseXhrResponse = (xhr) => { try { const body = JSON.parse(xhr.responseText || '{}'); if (xhr.status >= 200 && xhr.status < 300) { onSuccess(body); setFile(null); setProgress(0); } else { setError(body.error || '업로드에 실패했습니다.'); setProgress(0); } } catch { if (xhr.status >= 200 && xhr.status < 300) { setError('응답을 처리할 수 없습니다.'); } else { setError('업로드에 실패했습니다.'); } setProgress(0); } }; const handleError = () => { setUploading(false); setError('네트워크 오류가 발생했습니다.'); setProgress(0); }; sendXhr(url, token, file, { onProgress: (p) => setProgress(p), onDone: handleDone, onError: handleError, }); }; return (
); } export default function DownloadAdmin() { const [info, setInfo] = useState(null); useEffect(() => { getDownloadInfo().then(setInfo).catch((err) => { console.error('다운로드 정보 로드 실패:', err); }); }, []); return (