214 lines
9.8 KiB
JavaScript
214 lines
9.8 KiB
JavaScript
/* Stepstead ortak header + footer.
|
||
Kullanım: <script src="/nav.js?v=7" data-theme="light|dark" defer></script>
|
||
Panel linkleri role/izin bazlı görünür: GET /auth/me → permissions[].
|
||
İzinler: upload, lore, map_view, map_edit, users ("*" = admin). */
|
||
(function(){
|
||
"use strict";
|
||
const API="https://api.stepstead.com";
|
||
const PUBLIC_LINKS=[
|
||
{href:"/",label:"🏠 Ana Sayfa"},
|
||
{href:"/db/",label:"📖 Craft Veritabanı"},
|
||
{href:"/items/",label:"🎒 Eşyalar"},
|
||
];
|
||
const PANEL_LINKS=[
|
||
{href:"/assets/",label:"🎨 Asset Takip",perms:["upload"]},
|
||
{href:"/assets/upload.html",label:"📤 Dosya Yükle",perms:["upload"]},
|
||
{href:"/assets/lore.html",label:"📜 Lore",perms:["lore"]},
|
||
{href:"/assets/quests.html",label:"🗡 Görevler",perms:["lore"]},
|
||
{href:"/assets/npcs.html",label:"🧙 NPC'ler",perms:["npc"]},
|
||
{href:"/assets/chars.html",label:"⚔ Karakterler",perms:["chars"]},
|
||
{href:"/assets/map-editor.html",label:"🗺 Harita Editörü",perms:["map_view","map_edit"]},
|
||
{href:"/assets/users.html",label:"👥 Kullanıcılar",perms:["users"]},
|
||
];
|
||
const script=document.currentScript;
|
||
const theme=(script&&script.dataset.theme)==="light"?"light":"dark";
|
||
|
||
const css=`
|
||
.ss-nav{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;line-height:1.4;position:relative}
|
||
.ss-nav .ss-in{max-width:1040px;margin:0 auto;padding:0 16px;display:flex;align-items:center;gap:4px;flex-wrap:wrap;min-height:46px}
|
||
.ss-nav .ss-brand{font-weight:700;font-size:15px;text-decoration:none;margin-right:10px;letter-spacing:.2px}
|
||
.ss-nav a.ss-lnk{text-decoration:none;font-size:13px;padding:5px 10px;border-radius:6px;white-space:nowrap}
|
||
.ss-nav .ss-adm{display:none;align-items:center;gap:4px;flex-wrap:wrap}
|
||
.ss-nav .ss-adm.on{display:flex}
|
||
.ss-nav .ss-admtag{font-size:10px;text-transform:uppercase;letter-spacing:.08em;opacity:.6;margin:0 2px 0 8px}
|
||
.ss-nav .ss-authbtn{margin-left:auto;background:none;border:1px solid transparent;font-size:13px;padding:5px 10px;border-radius:6px;cursor:pointer;white-space:nowrap}
|
||
.ss-nav .ss-loginbox{display:none;position:absolute;right:16px;top:100%;margin-top:6px;z-index:50;padding:12px;border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,.35);min-width:230px}
|
||
.ss-nav .ss-loginbox.on{display:block}
|
||
.ss-nav .ss-loginbox input{display:block;width:100%;box-sizing:border-box;margin-bottom:8px;padding:7px 9px;border-radius:6px;font-size:13px}
|
||
.ss-nav .ss-loginbox button{width:100%;padding:7px;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer}
|
||
.ss-nav .ss-loginmsg{font-size:12px;margin-top:6px;min-height:14px}
|
||
.ss-foot{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:12px;margin-top:48px}
|
||
.ss-foot .ss-in{max-width:1040px;margin:0 auto;padding:16px;display:flex;gap:14px;flex-wrap:wrap;align-items:center}
|
||
.ss-foot a{text-decoration:none}
|
||
.ss-foot .ss-sp{margin-left:auto}
|
||
/* dark */
|
||
.ss-dark.ss-nav{background:#0b1016;border-bottom:1px solid #2c3a4f}
|
||
.ss-dark.ss-nav .ss-brand{color:#e8b923}
|
||
.ss-dark.ss-nav a.ss-lnk{color:#8b99a8}
|
||
.ss-dark.ss-nav a.ss-lnk:hover{color:#e6edf3;background:#1a2230}
|
||
.ss-dark.ss-nav a.ss-lnk.on{color:#e8b923;background:#1a2230}
|
||
.ss-dark.ss-nav .ss-admtag{color:#8b99a8}
|
||
.ss-dark.ss-nav .ss-authbtn{color:#8b99a8;border-color:#2c3a4f;background:#0b1016}
|
||
.ss-dark.ss-nav .ss-authbtn:hover{color:#e8b923;border-color:#e8b923}
|
||
.ss-dark.ss-nav .ss-loginbox{background:#1a2230;border:1px solid #2c3a4f}
|
||
.ss-dark.ss-nav .ss-loginbox input{background:#222d3d;color:#e6edf3;border:1px solid #2c3a4f}
|
||
.ss-dark.ss-nav .ss-loginbox button{background:#e8b923;color:#1a1300}
|
||
.ss-dark.ss-nav .ss-loginmsg{color:#e5534b}
|
||
.ss-dark.ss-foot{background:#0b1016;border-top:1px solid #2c3a4f;color:#8b99a8}
|
||
.ss-dark.ss-foot a{color:#8b99a8}
|
||
.ss-dark.ss-foot a:hover{color:#e8b923}
|
||
/* light */
|
||
.ss-light.ss-nav{background:#2f4a35;border-bottom:1px solid #253b2a}
|
||
.ss-light.ss-nav .ss-brand{color:#fff}
|
||
.ss-light.ss-nav a.ss-lnk{color:rgba(255,255,255,.75)}
|
||
.ss-light.ss-nav a.ss-lnk:hover{color:#fff;background:rgba(255,255,255,.12)}
|
||
.ss-light.ss-nav a.ss-lnk.on{color:#fff;background:rgba(255,255,255,.18)}
|
||
.ss-light.ss-nav .ss-admtag{color:rgba(255,255,255,.6)}
|
||
.ss-light.ss-nav .ss-authbtn{color:rgba(255,255,255,.75);border-color:rgba(255,255,255,.3);background:transparent}
|
||
.ss-light.ss-nav .ss-authbtn:hover{color:#fff;border-color:#fff}
|
||
.ss-light.ss-nav .ss-loginbox{background:#fff;border:1px solid #e4e0d6}
|
||
.ss-light.ss-nav .ss-loginbox input{background:#f5f3ee;color:#2d3a2e;border:1px solid #e4e0d6}
|
||
.ss-light.ss-nav .ss-loginbox button{background:#3a5a40;color:#fff}
|
||
.ss-light.ss-nav .ss-loginmsg{color:#c0392b}
|
||
.ss-light.ss-foot{background:transparent;border-top:1px solid #e4e0d6;color:#77806f}
|
||
.ss-light.ss-foot a{color:#77806f}
|
||
.ss-light.ss-foot a:hover{color:#3a5a40}
|
||
`;
|
||
|
||
let PERMS=[];
|
||
function tok(){return localStorage.getItem("ss_upload_token")||"";}
|
||
function hasPerm(p){return PERMS.includes("*")||PERMS.includes(p);}
|
||
function anyPerm(list){return list.some(hasPerm);}
|
||
|
||
function norm(p){return p.replace(/index\.html$/,"");}
|
||
function link(l){
|
||
const a=document.createElement("a");
|
||
a.className="ss-lnk"+(norm(location.pathname)===norm(l.href)?" on":"");
|
||
a.href=l.href;a.textContent=l.label;
|
||
return a;
|
||
}
|
||
|
||
function applyPerms(role){
|
||
const adm=document.getElementById("ssAdmLinks");
|
||
adm.innerHTML="";
|
||
// Panel yetkisi olan (bir rolü/izni bulunan) herkes TÜM sekmeleri görür.
|
||
// İşlem yetkisi sayfa/backend tarafında izne göre ayrıca denetlenir.
|
||
const isPanel=PERMS.length>0;
|
||
if(isPanel){
|
||
const tag=document.createElement("span");
|
||
tag.className="ss-admtag";tag.textContent=role||"panel";
|
||
adm.appendChild(tag);
|
||
for(const l of PANEL_LINKS)adm.appendChild(link(l));
|
||
adm.classList.add("on");
|
||
}else adm.classList.remove("on");
|
||
document.getElementById("ssAuthBtn").textContent=PERMS.length?"🚪 Çıkış":"🔑 Giriş";
|
||
if(PERMS.length)sessionStorage.setItem("ss_nav_perms",JSON.stringify({p:PERMS,r:role||""}));
|
||
else sessionStorage.removeItem("ss_nav_perms");
|
||
}
|
||
|
||
function build(){
|
||
const st=document.createElement("style");st.textContent=css;document.head.appendChild(st);
|
||
|
||
const nav=document.createElement("nav");
|
||
nav.className="ss-nav ss-"+theme;
|
||
const inner=document.createElement("div");inner.className="ss-in";
|
||
const brand=document.createElement("a");
|
||
brand.className="ss-brand";brand.href="/";brand.textContent="👣 Stepstead";
|
||
inner.appendChild(brand);
|
||
for(const l of PUBLIC_LINKS)inner.appendChild(link(l));
|
||
const adm=document.createElement("span");adm.className="ss-adm";adm.id="ssAdmLinks";
|
||
inner.appendChild(adm);
|
||
|
||
const authBtn=document.createElement("button");
|
||
authBtn.className="ss-authbtn";authBtn.id="ssAuthBtn";authBtn.textContent="🔑 Giriş";
|
||
authBtn.onclick=()=>{
|
||
if(PERMS.length){
|
||
localStorage.removeItem("ss_upload_token");
|
||
PERMS=[];applyPerms(null);
|
||
return;
|
||
}
|
||
document.getElementById("ssLoginBox").classList.toggle("on");
|
||
document.getElementById("ssLgEmail").focus();
|
||
};
|
||
inner.appendChild(authBtn);
|
||
|
||
const box=document.createElement("div");
|
||
box.className="ss-loginbox";box.id="ssLoginBox";
|
||
box.innerHTML=`
|
||
<input type="email" id="ssLgEmail" placeholder="E-posta" autocomplete="username">
|
||
<input type="password" id="ssLgPass" placeholder="Şifre" autocomplete="current-password">
|
||
<button id="ssLgBtn">Giriş yap</button>
|
||
<div class="ss-loginmsg" id="ssLgMsg"></div>`;
|
||
nav.appendChild(inner);
|
||
nav.appendChild(box);
|
||
document.body.prepend(nav);
|
||
|
||
document.getElementById("ssLgBtn").onclick=doLogin;
|
||
document.getElementById("ssLgPass").addEventListener("keydown",e=>{if(e.key==="Enter")doLogin();});
|
||
document.addEventListener("click",e=>{
|
||
if(!box.contains(e.target)&&e.target!==authBtn)box.classList.remove("on");
|
||
});
|
||
|
||
const foot=document.createElement("footer");
|
||
foot.className="ss-foot ss-"+theme;
|
||
foot.innerHTML=`<div class="ss-in">
|
||
<span>© ${new Date().getFullYear()} Stepstead — her adımın değer kazanır</span>
|
||
<span class="ss-sp"><a href="/">Ana Sayfa</a></span>
|
||
<span><a href="/db/">Craft Veritabanı</a></span>
|
||
</div>`;
|
||
document.body.appendChild(foot);
|
||
}
|
||
|
||
async function doLogin(){
|
||
const msg=document.getElementById("ssLgMsg");
|
||
msg.textContent="…";
|
||
try{
|
||
const r=await fetch(API+"/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},
|
||
body:JSON.stringify({email:document.getElementById("ssLgEmail").value.trim(),
|
||
password:document.getElementById("ssLgPass").value})});
|
||
const b=await r.json().catch(()=>({}));
|
||
if(!r.ok||!b.access_token){msg.textContent=b.detail||"Giriş başarısız";return;}
|
||
localStorage.setItem("ss_upload_token",b.access_token);
|
||
msg.textContent="";
|
||
await refresh();
|
||
if(PERMS.length){
|
||
document.getElementById("ssLoginBox").classList.remove("on");
|
||
document.getElementById("ssLgPass").value="";
|
||
}else msg.textContent="Giriş tamam — ama bu hesabın panel yetkisi yok";
|
||
}catch(e){msg.textContent="Bağlantı hatası";}
|
||
}
|
||
|
||
async function refresh(){
|
||
if(!tok()){PERMS=[];applyPerms(null);return;}
|
||
try{
|
||
const r=await fetch(API+"/auth/me",{headers:{Authorization:"Bearer "+tok()}});
|
||
if(r.ok){
|
||
const u=await r.json();
|
||
PERMS=u.permissions||[];
|
||
applyPerms(u.role);
|
||
}else{
|
||
if(r.status===401)localStorage.removeItem("ss_upload_token");
|
||
PERMS=[];applyPerms(null);
|
||
}
|
||
}catch(e){
|
||
// geçici ağ hatası: cache'e güven
|
||
try{
|
||
const c=JSON.parse(sessionStorage.getItem("ss_nav_perms")||"null");
|
||
if(c){PERMS=c.p;applyPerms(c.r);return;}
|
||
}catch(_){}
|
||
PERMS=[];applyPerms(null);
|
||
}
|
||
}
|
||
window.ssNavRefresh=refresh;
|
||
|
||
function init(){
|
||
build();
|
||
try{
|
||
const c=JSON.parse(sessionStorage.getItem("ss_nav_perms")||"null");
|
||
if(c&&tok()){PERMS=c.p;applyPerms(c.r);}
|
||
}catch(_){}
|
||
refresh();
|
||
}
|
||
if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",init);
|
||
else init();
|
||
})();
|