Files
2026-05-21 21:37:00 +01:00

101 lines
5.3 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Speed Test</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:monospace;padding:40px;background:white;color:black}
h1{margin-bottom:20px}
.box{border:1px solid black;padding:20px;max-width:500px}
button{border:1px solid black;background:white;padding:8px 16px;cursor:pointer;font-family:monospace;margin:2px}
button:hover{background:black;color:white}
#status{margin-top:10px}
#progress-wrap{display:none;margin-top:10px}
#bar{width:100%;height:20px;border:1px solid black}
#fill{height:100%;background:black;width:0%}
#text{margin-top:4px}
.nav{margin-top:40px;font-size:12px}
a{color:black}
.section{margin-bottom:10px}
</style>
</head>
<body>
<h1>Speed Test</h1>
<div class="box">
<div class="section">
<b>Download:</b><br>
<button onclick="testDown(10)">10 MB</button>
<button onclick="testDown(50)">50 MB</button>
<button onclick="testDown(100)">100 MB</button>
<button onclick="testDown(250)">250 MB</button>
</div>
<div class="section" id="uploadSection" style="display:none">
<b>Upload:</b><br>
<button onclick="testUp(10)">10 MB</button>
<button onclick="testUp(50)">50 MB</button>
<button onclick="testUp(100)">100 MB</button>
<button onclick="testUp(250)">250 MB</button>
</div>
<div id="status"></div>
<div id="progress-wrap">
<div id="bar"><div id="fill"></div></div>
<div id="text"></div>
</div>
</div>
<div class="nav"><a href="/">Home</a> | <a href="/speedtest">Speed Test</a> | <a id="navAuth" href="/login">Login</a> | <a href="/terms">Terms</a> | <a href="/privacy">Privacy</a></div>
<script>
function getToken(){return localStorage.getItem('upload_token')||''}
function authHeaders(){return{'Authorization':'Bearer '+getToken()}}
function formatBytes(b){if(b<1024)return b+' B';if(b<1048576)return(b/1024).toFixed(1)+' KB';if(b<1073741824)return(b/1048576).toFixed(1)+' MB';return(b/1073741824).toFixed(2)+' GB'}
function formatTime(s){if(s<60)return Math.ceil(s)+'s';if(s<3600)return Math.floor(s/60)+'m '+Math.ceil(s%60)+'s';return Math.floor(s/3600)+'h '+Math.floor((s%3600)/60)+'m'}
const nav=document.getElementById('navAuth');
if(getToken()){
nav.href='/dashboard';
nav.textContent='Dashboard';
document.getElementById('uploadSection').style.display='block';
}
async function testDown(mb){
const status=document.getElementById('status');
const wrap=document.getElementById('progress-wrap');
const fill=document.getElementById('fill');
const text=document.getElementById('text');
const total=mb*1024*1024;
status.textContent='Getting download URL...';wrap.style.display='block';fill.style.width='0%';text.textContent='';
const resp=await fetch('/api/speedtest/download-url/'+mb);
const data=await resp.json();
status.textContent='Downloading '+mb+' MB...';
const xhr=new XMLHttpRequest();
xhr.open('GET',data.download_url);xhr.responseType='blob';
const start=Date.now();
xhr.onprogress=function(e){if(!e.lengthComputable)return;const pct=(e.loaded/e.total)*100;fill.style.width=pct+'%';const elapsed=(Date.now()-start)/1000;const speed=e.loaded/elapsed;text.textContent=pct.toFixed(1)+'% | '+formatBytes(e.loaded)+' / '+formatBytes(e.total)+' | '+formatBytes(speed)+'/s'};
xhr.onload=function(){if(xhr.status>=200&&xhr.status<300){const elapsed=(Date.now()-start)/1000;const speed=total/elapsed;fill.style.width='100%';status.textContent='Download: '+formatBytes(speed)+'/s ('+mb+' MB in '+formatTime(elapsed)+')';text.textContent='100%'}else{status.textContent='Download failed: '+xhr.status+' (upload test data first)'}};
xhr.onerror=function(){status.textContent='Download failed (upload test data for this size first)'};
xhr.send();
}
async function testUp(mb){
const status=document.getElementById('status');
const wrap=document.getElementById('progress-wrap');
const fill=document.getElementById('fill');
const text=document.getElementById('text');
const total=mb*1024*1024;
if(!getToken()){status.textContent='No token. Go to Login page first.';return}
status.textContent='Getting upload URL...';wrap.style.display='block';fill.style.width='0%';text.textContent='';
const resp=await fetch('/api/speedtest/upload-url/'+mb,{headers:authHeaders()});
const data=await resp.json();
if(!resp.ok){status.textContent='Error: '+(data.error||'unauthorized');return}
status.textContent='Uploading '+mb+' MB...';
const blob=new Blob([new ArrayBuffer(total)]);
const xhr=new XMLHttpRequest();
xhr.open('PUT',data.upload_url);
const start=Date.now();
xhr.upload.onprogress=function(e){if(!e.lengthComputable)return;const pct=(e.loaded/e.total)*100;fill.style.width=pct+'%';const elapsed=(Date.now()-start)/1000;const speed=e.loaded/elapsed;text.textContent=pct.toFixed(1)+'% | '+formatBytes(e.loaded)+' / '+formatBytes(e.total)+' | '+formatBytes(speed)+'/s | ETA '+formatTime((e.total-e.loaded)/speed)};
xhr.onload=function(){if(xhr.status>=200&&xhr.status<300){const elapsed=(Date.now()-start)/1000;const speed=total/elapsed;fill.style.width='100%';status.textContent='Upload: '+formatBytes(speed)+'/s ('+mb+' MB in '+formatTime(elapsed)+')';text.textContent='100%'}else{status.textContent='Upload failed: '+xhr.status}};
xhr.onerror=function(){status.textContent='Upload failed: network error'};
xhr.send(blob);
}
</script>
</body>
</html>