stepstead-web/assets/upload.html
jts dea01dd6a8 İlk commit: Stepstead web (statik panel + db)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 11:30:59 +03:00

131 lines
5.7 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex,nofollow">
<title>Stepstead — Asset Yükle</title>
<style>
:root{--bg:#0f1419;--panel:#1a2230;--line:#2c3a4f;--txt:#e6edf3;--muted:#8b99a8;--gold:#e8b923;--ok:#2ea043;--err:#e5534b}
*{box-sizing:border-box}
body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);color:var(--txt);line-height:1.5}
.wrap{max-width:640px;margin:0 auto;padding:24px 16px}
h1{font-size:20px;color:var(--gold);margin:0 0 4px}
.sub{color:var(--muted);font-size:13px;margin-bottom:20px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:18px;margin-bottom:16px}
input,button{font-size:14px;border-radius:6px}
input[type=email],input[type=password]{width:100%;background:#222d3d;color:var(--txt);border:1px solid var(--line);padding:9px 10px;margin-bottom:10px}
.btn{background:var(--gold);color:#1a1300;border:none;padding:9px 18px;font-weight:600;cursor:pointer}
.btn:disabled{opacity:.5;cursor:default}
.msg{font-size:13px;margin-top:10px}
.msg.ok{color:var(--ok)} .msg.err{color:var(--err)}
.drop{border:2px dashed var(--line);border-radius:10px;padding:36px 16px;text-align:center;color:var(--muted);cursor:pointer;transition:.15s}
.drop.over{border-color:var(--gold);color:var(--gold)}
table{width:100%;border-collapse:collapse;font-size:13px}
td,th{text-align:left;padding:7px 4px;border-top:1px solid var(--line)}
th{color:var(--muted);font-weight:500;font-size:12px}
a{color:var(--gold)}
.hide{display:none}
.size{color:var(--muted)}
img.thumb{max-height:40px;border-radius:4px;vertical-align:middle}
</style>
<script src="/nav.js?v=8" data-theme="dark" defer></script>
</head>
<body>
<div class="wrap">
<h1>📤 Asset Yükle</h1>
<div class="sub">Harita ve görselleri buradan yükle → <code>https://stepstead.com/assets/uploads/…</code></div>
<div class="card" id="loginCard">
<input type="email" id="email" placeholder="E-posta" autocomplete="username">
<input type="password" id="pass" placeholder="Şifre" autocomplete="current-password">
<button class="btn" id="loginBtn">Giriş</button>
<div class="msg" id="loginMsg"></div>
</div>
<div class="card hide" id="upCard">
<div class="drop" id="drop">Dosyayı buraya sürükle ya da tıkla<br><small>png / jpg / webp / svg / json / zip — max 30 MB</small></div>
<input type="file" id="file" hidden accept=".png,.jpg,.jpeg,.webp,.svg,.json,.zip">
<div class="msg" id="upMsg"></div>
</div>
<div class="card hide" id="listCard">
<table><thead><tr><th></th><th>Dosya</th><th>Boyut</th></tr></thead><tbody id="rows"></tbody></table>
</div>
</div>
<script>
const API="https://api.stepstead.com";
let TOKEN=localStorage.getItem("ss_upload_token")||"";
const $=id=>document.getElementById(id);
function fmt(n){return n>1048576?(n/1048576).toFixed(1)+" MB":Math.round(n/1024)+" KB";}
async function api(path,opts={}){
opts.headers=Object.assign({},opts.headers,{Authorization:"Bearer "+TOKEN});
const r=await fetch(API+path,opts);
let body={}; try{body=await r.json();}catch(e){}
return {ok:r.ok,status:r.status,body};
}
async function login(){
$("loginMsg").textContent="…";
const r=await fetch(API+"/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},
body:JSON.stringify({email:$("email").value.trim(),password:$("pass").value})});
const b=await r.json().catch(()=>({}));
if(r.ok&&b.access_token){
TOKEN=b.access_token; localStorage.setItem("ss_upload_token",TOKEN);
show();
}else{
$("loginMsg").className="msg err";
$("loginMsg").textContent=b.detail||"Giriş başarısız";
}
}
async function show(){
const r=await api("/admin/uploads");
if(!r.ok){
if(r.status===401||r.status===403){TOKEN="";localStorage.removeItem("ss_upload_token");}
$("loginCard").classList.remove("hide");
$("upCard").classList.add("hide"); $("listCard").classList.add("hide");
if(r.status===403){$("loginMsg").className="msg err";$("loginMsg").textContent="Bu hesabın yükleme yetkisi yok";}
return;
}
$("loginCard").classList.add("hide");
$("upCard").classList.remove("hide"); $("listCard").classList.remove("hide");
const tb=$("rows"); tb.innerHTML="";
for(const f of r.body){
const tr=document.createElement("tr");
const img=/\.(png|jpe?g|webp|svg)$/i.test(f.filename)?`<img class="thumb" src="${f.url}" loading="lazy">`:"📄";
tr.innerHTML=`<td>${img}</td><td><a href="${f.url}" target="_blank">${f.filename}</a></td><td class="size">${fmt(f.size)}</td>`;
tb.appendChild(tr);
}
}
async function upload(file){
if(!file)return;
$("upMsg").className="msg"; $("upMsg").textContent="⏳ Yükleniyor: "+file.name;
const fd=new FormData(); fd.append("file",file);
const r=await api("/admin/upload",{method:"POST",body:fd});
if(r.ok){
$("upMsg").className="msg ok";
$("upMsg").innerHTML=`✅ Yüklendi: <a href="${r.body.url}" target="_blank">${r.body.url}</a>`;
show();
}else{
$("upMsg").className="msg err";
$("upMsg").textContent="⚠ "+(r.body.detail||("Hata "+r.status));
}
}
$("loginBtn").onclick=login;
$("pass").addEventListener("keydown",e=>{if(e.key==="Enter")login();});
$("drop").onclick=()=>$("file").click();
$("file").onchange=e=>upload(e.target.files[0]);
$("drop").addEventListener("dragover",e=>{e.preventDefault();$("drop").classList.add("over");});
$("drop").addEventListener("dragleave",()=>$("drop").classList.remove("over"));
$("drop").addEventListener("drop",e=>{e.preventDefault();$("drop").classList.remove("over");upload(e.dataTransfer.files[0]);});
if(TOKEN)show();
</script>
</body>
</html>