Mint the download token when the button is pressed
A friend got "The requested resource wasn't found." downloading the adult Windows installer. It is not a permissions problem and not missing data: the row and the file are both there, and the same download returns 206 with a valid token. That message is exactly what a protected file answers when the token is missing or expired, verified against production. releases.file is protected, so the URL needs a file token, and a file token lives 180 seconds -- read off the JWT's own exp claim, not guessed. The download list minted three of them while building the rows and baked them into the hrefs, so three minutes after the page loaded every button was permanently dead, and pressing it again could not help because the stale token was in the markup. Nothing suggested reloading. The owner never saw it because he presses the button as soon as the tab renders. The setup flow I added this week makes it near-certain for anyone else: it walks somebody through choosing services, creating accounts, paying and fetching a TMDB key before pointing them at Stáhnout, which is many minutes after the download tab was first rendered. So the token is minted in the click handler now, and the link carries download=1 so PocketBase sends an attachment rather than leaving the browser to decide what to do with a .exe or a .zip. The app was already correct: UpdateService mints its token immediately before _dio.download, so auto-update on the family's devices was never affected. This was the web page only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
874eab21dc
commit
dd37b36f6e
1 changed files with 31 additions and 8 deletions
|
|
@ -1201,22 +1201,45 @@ async function loadDownloads(){
|
|||
li.style.cssText="padding:12px;border:1px solid var(--line);border-radius:10px;"+
|
||||
"margin-bottom:8px;background:var(--sunk);font-size:14px;list-style:none";
|
||||
if (m.ok && m.data && m.data.available){
|
||||
// `releases.file` is a protected file: the path alone 403s, it needs a
|
||||
// short-lived file token. Minted per link, exactly as the landing page does.
|
||||
var t=await api("POST","/api/files/token",null);
|
||||
var href=(t.ok && t.data && t.data.token)
|
||||
? m.data.downloadPath+"?token="+encodeURIComponent(t.data.token) : null;
|
||||
// **The token is minted when the button is pressed, not when this list is
|
||||
// built.** `releases.file` is protected, so the path needs a file token, and a
|
||||
// file token lives 180 seconds — measured, not assumed. Baking one into the
|
||||
// href at render time meant every download button died three minutes after
|
||||
// the page loaded, permanently, until someone thought to reload. PocketBase
|
||||
// answers an expired token with 404 "The requested resource wasn't found.",
|
||||
// which reads like the file is gone rather than like "press it again".
|
||||
//
|
||||
// That is not a hypothetical: a friend hit it on the Windows build. The setup
|
||||
// flow makes it near-certain, because it walks somebody through buying
|
||||
// subscriptions and fetching a TMDB key before it points them here, which is
|
||||
// many minutes, and the download tab may have been rendered at sign-in.
|
||||
li.innerHTML='<div class="rowbtn"><div><b>'+esc(p[1])+'</b><div class="meta">'+
|
||||
esc(m.data.version||"")+" · "+esc(m.data.variant||"")+'</div></div>'+
|
||||
(href ? '<a class="btn primary" style="width:auto;padding:9px 14px;margin:0;'+
|
||||
'text-decoration:none;display:inline-block" href="'+href+'">Stáhnout</a>'
|
||||
: '<span class="meta">odkaz se nepodařilo vytvořit</span>')+'</div>';
|
||||
'<button class="btn primary" style="width:auto;padding:9px 14px;margin:0" '+
|
||||
'data-dl="'+esc(m.data.downloadPath)+'">Stáhnout</button></div>';
|
||||
any=true;
|
||||
} else {
|
||||
li.innerHTML="<b>"+esc(p[1])+'</b><div class="meta">zatím nic ke stažení</div>';
|
||||
}
|
||||
box.appendChild(li);
|
||||
}
|
||||
// Wired after the rows exist, so each press mints its own token. `download=1`
|
||||
// makes PocketBase send the file as an attachment instead of leaving the browser
|
||||
// to decide what to do with a .zip or an .exe.
|
||||
Array.prototype.forEach.call(box.querySelectorAll("button[data-dl]"), function(b){
|
||||
b.onclick=async function(){
|
||||
busy(b,true,"Připravuji…");
|
||||
var t=await api("POST","/api/files/token",null);
|
||||
busy(b,false);
|
||||
if(!(t.ok && t.data && t.data.token)){
|
||||
setMsg($("dlMsg"),"Odkaz se nepodařilo vytvořit. Zkus to prosím znovu.","err");
|
||||
return;
|
||||
}
|
||||
setMsg($("dlMsg"),"");
|
||||
window.location.href = b.getAttribute("data-dl") +
|
||||
"?token=" + encodeURIComponent(t.data.token) + "&download=1";
|
||||
};
|
||||
});
|
||||
if (!any) setMsg($("dlMsg"),"Zatím není publikovaná žádná verze.","");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue