import { useCallback, useRef, useMemo } from 'react' import type { UploadFile } from '../types' import { request } from '@/http/request' import { createCustomOSSUploader } from '@bty/uploader' import { getSTSToken, getOssSignatureUrl } from '@apis/oss' // Simple helper to mimic MIME type detection export const ACCEPT_FILE_TYPE_LIST = [ '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.txt', '.json', '.csv', '.md', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.svg', '.ico', '.html', '.py', '.jsonld', '.xml', '.zip', '.mp3', '.mp4', '.mov', '.m4a', '.pdb', '.mermaid', ] export function getMimeByAcceptList(filename: string): string | undefined { const ext = `.${(filename.split('.').pop() || '').toLowerCase()}` const map: Record = { '.pdf': 'application/pdf', '.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', '.xls': 'application/vnd.ms-excel', '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', '.ppt': 'application/vnd.ms-powerpoint', '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation', '.txt': 'text/plain', '.json': 'application/json', '.csv': 'text/csv', '.md': 'text/markdown', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.bmp': 'image/bmp', '.webp': 'image/webp', '.svg': 'image/svg+xml', '.ico': 'image/x-icon', '.html': 'text/html', '.jsonld': 'application/ld+json', '.pdb': 'application/vnd.microsoft.portable-executable', '.mermaid': 'text/mermaid', } return map[ext] || undefined } export interface UseFileUploaderProps { onUploadStart?: (file: UploadFile) => void onUploadEnd?: (file: UploadFile) => void onUploadError?: (error: Error, file?: UploadFile) => void onFileUpdate?: (file: UploadFile) => void } export function useFileUploader({ onUploadStart, onUploadEnd, onUploadError, onFileUpdate, }: UseFileUploaderProps = {}) { const uploadUUId = useRef('') // Initialize OSS Uploader with STS token provider from our local package const ossUploader = useMemo(() => { return createCustomOSSUploader(getSTSToken) }, []) const uploadFile = useCallback(async (file: File) => { // 1. Validation const isValidSize = file.size <= 100 * 1024 * 1024 // 100MB if (!isValidSize) { console.warn('File size exceeds 100MB limit') return } // 2. Init file object and State const uid = crypto.randomUUID() const mimeType = getMimeByAcceptList(file.name) || file.type || 'application/octet-stream' const tempFile: UploadFile = { uid, name: file.name, type: mimeType, byte_size: file.size, uploadStatus: 'pending', progress: 0, url: URL.createObjectURL(file), } onUploadStart?.(tempFile) try { // 3. Construct File Path (Business Logic) const timestamp = new Date().valueOf() const uuid = crypto.randomUUID() uploadUUId.current = uuid const filePath = `super_agent/user_upload_file/${uuid}/_${timestamp}_${file.name}` // 4. Upload to OSS using the shared package await ossUploader.multipartUpload({ filePath, file, options: { headers: { 'Content-Type': mimeType, 'Content-Disposition': 'inline', }, progress: (progress: number) => { // OSS SDK returns progress as 0-1 onFileUpdate?.({ ...tempFile, progress: Math.floor(progress * 100), uploadStatus: 'uploading' }) } }, }) // 5. Get Signature URL (Optional / if private) const signatureUrl = await getOssSignatureUrl(filePath) // 6. Create File Upload Record (Backend Sync) const lastDotIndex = file.name.lastIndexOf('.') const splitName = lastDotIndex !== -1 ? [file.name.substring(0, lastDotIndex), file.name.substring(lastDotIndex + 1)] : [file.name] const safeName = `${splitName[0]}-${Math.random().toString(36).substring(2, 5)}${splitName.length > 1 ? `.${splitName[1]}` : ''}` const res = await request.post<{ file_upload_record_id: string }>('/file/record', { file_url: filePath, file_type: file.type || 'application/octet-stream', file_name: safeName, file_byte_size: file.size || 0, conversation_id: uuid, }) // 7. Finalize const finalFile: UploadFile = { ...tempFile, name: safeName, url: signatureUrl, upload_file_id: res.file_upload_record_id, progress: 100, uploadStatus: 'success', } onFileUpdate?.(finalFile) onUploadEnd?.(finalFile) } catch (error) { console.error('Upload failed:', error) const errorFile: UploadFile = { ...tempFile, uploadStatus: 'error' } onFileUpdate?.(errorFile) onUploadError?.(error as Error, errorFile) } }, [ossUploader, onUploadStart, onUploadEnd, onUploadError, onFileUpdate]) return { uploadFile, accept: ACCEPT_FILE_TYPE_LIST.join(','), } }