pushduck
Version:
The fastest way to add file uploads to any web application. Enterprise security, edge-ready. Works with 16+ frameworks and 5+ storage providers. No heavy AWS SDK required.
1 lines • 4.33 kB
JavaScript
"use client";let e=require(`react`);function t(e){return e<60?`${Math.round(e)}s`:e<3600?`${Math.round(e/60)}m`:`${Math.round(e/3600)}h`}function n(e){let t=[`B/s`,`KB/s`,`MB/s`,`GB/s`],n=e,r=0;for(;n>=1024&&r<t.length-1;)n/=1024,r++;return`${n.toFixed(1)} ${t[r]}`}async function r(e,t,n){return new Promise((r,i)=>{let a=new XMLHttpRequest,o=Date.now();a.upload.onprogress=e=>{if(e.lengthComputable&&n){let t=Math.round(e.loaded/e.total*100),r=(Date.now()-o)/1e3,i=e.loaded/r;n(t,i,(e.total-e.loaded)/i)}},a.onload=()=>{a.status>=200&&a.status<300?r():i(Error(`Upload failed with status: ${a.status}`))},a.onerror=()=>i(Error(`Upload failed`)),a.onabort=()=>i(Error(`Upload aborted`)),a.open(`PUT`,t),a.setRequestHeader(`Content-Type`,e.type),a.send(e)})}function i(t,n={}){let[i,a]=(0,e.useState)([]),[o,s]=(0,e.useState)(!1),[c,l]=(0,e.useState)([]),[u,d]=(0,e.useState)(0),[f,p]=(0,e.useState)(0),[m,h]=(0,e.useState)(0),g=(0,e.useRef)(new Map),_=(0,e.useCallback)(e=>{if(e.length===0){d(0),p(0),h(0);return}let t=e.filter(e=>e.status===`uploading`||e.status===`success`);if(t.length===0){d(0),p(0),h(0);return}let r=t.reduce((e,t)=>e+t.size,0),i=t.reduce((e,t)=>{let n=t.status===`success`?100:t.progress||0;return e+t.size*n/100},0),a=r>0?i/r*100:0,o=t.reduce((e,t)=>e+(t.uploadSpeed||0),0),s=r-i,c=o>0?s/o:0,l=Math.min(100,Math.max(0,a));d(l),p(o),h(c),n.onProgress?.(l)},[n.onProgress]),v=(0,e.useCallback)(()=>{g.current.forEach(e=>e.abort()),g.current.clear(),a([]),l([]),s(!1),d(0),p(0),h(0)},[]),y=(0,e.useCallback)((e,t,n,r)=>{a(i=>{let a=i.map(i=>i.id===e?{...i,progress:t,uploadSpeed:n,eta:r}:i);return _(a),a})},[_]),b=(0,e.useCallback)((e,t,n={})=>{a(r=>{let i=r.map(r=>r.id===e?{...r,status:t,...n}:r);return _(i),i})},[_]);return{files:i,uploadFiles:(0,e.useCallback)(async(e,i)=>{if(!e.length){s(!1);return}try{s(!0),l([]);let o=e.map(e=>({name:e.name,size:e.size,type:e.type})),c=e.map((e,t)=>({id:`${Date.now()}-${t}`,name:e.name,size:e.size,type:e.type,status:`pending`,progress:0,file:e}));a(c);let u=n.endpoint||`/api/s3-upload`,d=await fetch(`${u}?route=${String(t)}&action=presign`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({files:o,metadata:i})});if(!d.ok){let e=(await d.json()).error||`Server error: ${d.statusText}`;a(t=>t.map(t=>({...t,status:`error`,error:e}))),n.onError?.(Error(e));return}let f=await d.json();if(!f.success){let e=f.error||`Failed to get presigned URLs`;a(t=>t.map(t=>({...t,status:`error`,error:e}))),n.onError?.(Error(e));return}n.onStart&&await n.onStart(o),n.onProgress?.(0);let p=f.results.map(async(t,i)=>{let a=e[i],o=c[i];if(!t.success){let e=t.error||`Failed to get presigned URL`;return b(o.id,`error`,{error:e}),n.onError?.(Error(e)),null}try{return b(o.id,`uploading`,{uploadStartTime:Date.now()}),await r(a,t.presignedUrl,(e,t,n)=>y(o.id,e,t,n)),b(o.id,`success`,{progress:100,key:t.key}),{key:t.key,file:{name:a.name,size:a.size,type:a.type},metadata:t.metadata}}catch(e){let t=e instanceof Error?e.message:`Upload failed`;return b(o.id,`error`,{error:t}),n.onError?.(e instanceof Error?e:Error(t)),null}}),m=(await Promise.all(p)).filter(Boolean);if(m.length>0)try{let e=await fetch(`${u}?route=${String(t)}&action=complete`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({completions:m})});if(e.ok){let t=await e.json();t.success&&t.results&&t.results.forEach(e=>{if(e.success&&e.key){let t=m.find(t=>t?.key===e.key);if(t){let n=c.find(e=>e.name===t.file.name&&e.size===t.file.size);n&&b(n.id,`success`,{url:e.url,key:e.key,presignedUrl:e.presignedUrl,progress:100})}}})}}catch(e){console.warn(`Failed to notify server of upload completion:`,e)}n.onSuccess&&a(e=>{let t=e.filter(e=>e.status===`success`);return t.length>0&&n.onSuccess?.(t),e})}catch(e){let t=e instanceof Error?e.message:`Upload failed`;l(e=>[...e,t]),a(e=>e.map(e=>e.status===`success`||e.status===`error`?e:{...e,status:`error`,error:t})),n.onError?.(e instanceof Error?e:Error(t))}finally{s(!1),g.current.clear()}},[t,n,y,b]),reset:v,isUploading:o,errors:c,progress:u,uploadSpeed:f,eta:m}}Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return t}});