fix: bound document downloads and cancel stale sessions
This commit is contained in:
@@ -16,7 +16,7 @@ const KATEGORILER = ['Yasal Belgeler', 'Kalite Belgeleri', 'Mali Belgeler', 'Res
|
||||
*/
|
||||
export const DocumentVault = () => {
|
||||
const { authUser } = useApp();
|
||||
return <DocumentVaultSession key={authUser?.email || 'signed-out'} signedIn={Boolean(authUser)} />;
|
||||
return <DocumentVaultSession key={`${authUser?.email || 'signed-out'}:${getToken()}`} signedIn={Boolean(authUser)} />;
|
||||
};
|
||||
|
||||
const DocumentVaultSession = ({signedIn}) => {
|
||||
@@ -36,6 +36,15 @@ const DocumentVaultSession = ({signedIn}) => {
|
||||
const [islemeBilgi, setIslemeBilgi] = useOwnedState(null);
|
||||
const [kategori, setKategori] = useOwnedState(KATEGORILER[0]);
|
||||
const dosyaRef = useRef(null);
|
||||
const downloads = useRef(new Set());
|
||||
const [downloading,setDownloading] = useOwnedState([]);
|
||||
const indir = async file => {
|
||||
if (!current() || downloads.current.has(file.id)) return;
|
||||
downloads.current.add(file.id);setDownloading([...downloads.current]);setHata(null);
|
||||
try { await downloadDocument(file.id,file.ad,{signal:readLifetime.current.signal}); }
|
||||
catch(error) { if(!readLifetime.current.signal.aborted)setHata(error.message); }
|
||||
finally { downloads.current.delete(file.id);setDownloading([...downloads.current]); }
|
||||
};
|
||||
|
||||
const yukle = () => {
|
||||
if (!signedIn || !current()) return;
|
||||
@@ -320,9 +329,10 @@ const DocumentVaultSession = ({signedIn}) => {
|
||||
Kalemleri yeniden çıkar
|
||||
</button>
|
||||
)}
|
||||
<button onClick={() => downloadDocument(d.id, d.ad).catch((e) => setHata(e.message))}
|
||||
className="text-slate-400 hover:text-teal-600 p-1" title="İndir">
|
||||
<Download className="w-4 h-4" />
|
||||
<button onClick={() => indir(d)} disabled={downloading.includes(d.id) || d.erisilebilir===false}
|
||||
aria-label={`${d.ad} indir`} aria-busy={downloading.includes(d.id)}
|
||||
className="text-slate-400 hover:text-teal-600 p-1 disabled:opacity-40" title="İndir">
|
||||
{downloading.includes(d.id) ? <span role="status" className="text-xs">İndiriliyor…</span> : <Download className="w-4 h-4" />}
|
||||
</button>
|
||||
<button onClick={() => sil(d.id)} disabled={busy} className="text-slate-400 hover:text-red-500 p-1" title="Sil">
|
||||
<Trash2 className="w-4 h-4" />
|
||||
|
||||
+40
-17
@@ -1,3 +1,4 @@
|
||||
import {awaitWithAbort} from './abortable.js';
|
||||
import { apiKeyRequest } from './apiKeys.js';
|
||||
import { sessionGuard } from './sessionGuard.js';
|
||||
/**
|
||||
@@ -46,25 +47,47 @@ export async function uploadDocument(file, kategori, ikn, ek = {}) {
|
||||
}
|
||||
|
||||
/** Auth başlığı gerektiği için <a href> yerine blob ile indirilir. */
|
||||
export async function downloadDocument(id, ad) {
|
||||
export async function downloadDocument(id, ad, {signal,timeoutMs=120000}={}) {
|
||||
const current = sessionGuard(getToken);
|
||||
const res = await fetch(`/api/me/documents/download?id=${encodeURIComponent(id)}`, {
|
||||
headers: { Authorization: `Bearer ${getToken()}` },
|
||||
});
|
||||
if (!res.ok) {
|
||||
const d = await res.json().catch(() => ({}));
|
||||
throw new Error(d.error || `İndirilemedi (HTTP ${res.status})`);
|
||||
if (!current()) throw new Error('Belgeyi indirmek için hesabınıza giriş yapın.');
|
||||
const controller = new AbortController();
|
||||
const cancel = () => controller.abort(signal.reason);
|
||||
if (signal?.aborted) cancel(); else signal?.addEventListener('abort',cancel,{once:true});
|
||||
let timedOut=false;
|
||||
const timer=setTimeout(()=>{timedOut=true;controller.abort();},timeoutMs);
|
||||
const assertCurrent=()=>{if(!current())throw new Error('Oturum değişti; belgeyi yeniden açın.');};
|
||||
try {
|
||||
controller.signal.throwIfAborted();
|
||||
const res = await awaitWithAbort(fetch(`/api/me/documents/download?id=${encodeURIComponent(id)}`, {
|
||||
signal:controller.signal,headers:{Authorization:`Bearer ${getToken()}`},
|
||||
}),controller.signal);
|
||||
assertCurrent();
|
||||
if (!res.ok) {
|
||||
const data=await awaitWithAbort(res.json().catch(()=>({})),controller.signal);
|
||||
assertCurrent();
|
||||
throw new Error(data?.error || `İndirilemedi (HTTP ${res.status})`);
|
||||
}
|
||||
const blob=await awaitWithAbort(res.blob(),controller.signal);
|
||||
assertCurrent();
|
||||
controller.signal.throwIfAborted();
|
||||
const url=URL.createObjectURL(blob);
|
||||
let anchor;
|
||||
try {
|
||||
anchor=document.createElement('a');
|
||||
anchor.href=url;anchor.download=ad || 'belge';
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
} finally {
|
||||
anchor?.remove();
|
||||
setTimeout(()=>URL.revokeObjectURL(url),5000);
|
||||
}
|
||||
} catch(error) {
|
||||
assertCurrent();
|
||||
if(timedOut)throw new Error('Belge zamanında indirilemedi. Bağlantınızı kontrol edip yeniden deneyin.');
|
||||
throw error;
|
||||
} finally {
|
||||
clearTimeout(timer);signal?.removeEventListener('abort',cancel);
|
||||
}
|
||||
const blob = await res.blob();
|
||||
if (!current()) throw new Error('Oturum değişti; belgeyi yeniden açın.');
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = ad || 'belge';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
setTimeout(() => URL.revokeObjectURL(url), 5000);
|
||||
}
|
||||
|
||||
export function formatBayt(b) {
|
||||
|
||||
Reference in New Issue
Block a user