/* ============================================================
ANIMEHUNT CMS - COMPLETE ANALYTICS SYSTEM
============================================================ */
const API_BASE =
"https://animehunt-backend.animehunt715.workers.dev/api"
const ADMIN_ANALYTICS =
API_BASE + "/admin/analytics"
const TRACK_API =
API_BASE + "/analytics/track"
const TOKEN_KEY = "AUTH_TOKEN"
const rangeSelect = document.getElementById("range")
/* ============================================================
AUTH CHECK
============================================================ */
function getToken(){
return localStorage.getItem(TOKEN_KEY)
}
if(!getToken()){
location.href="login.html"
}
/* ============================================================
SAFE FETCH
============================================================ */
async function api(url,options={}){
try{
const res = await fetch(url,{
...options,
headers:{
"Content-Type":"application/json",
"Authorization":"Bearer " + getToken(),
...(options.headers||{})
}
})
if(res.status===401){
location.href="login.html"
return
}
if(!res.ok){
throw new Error("API Error")
}
return await res.json()
}catch(err){
console.error("API Error:",err)
return null
}
}
/* ============================================================
LIST RENDER
============================================================ */
function fillList(el,list,keyName,valName){
if(!el) return
el.innerHTML=""
if(!list || list.length===0){
el.innerHTML =
`
No data available
`return
}
const maxVal = Math.max(...list.map(i=>i[valName]||0))
list.forEach(item=>{
const key = item[keyName] || "Unknown"
const val = item[valName] || 0
const percent =
maxVal>0 ? (val/maxVal)*100 : 0
const li = document.createElement("li")
li.style.cssText="position:relative; padding:10px; margin-bottom:4px; border-bottom:1px solid #1a2035; display:flex; justify-content:space-between; overflow:hidden;"
li.innerHTML=`
${key}
${val}
`
el.appendChild(li)
})
}
/* ============================================================
LOAD ANALYTICS
============================================================ */
async function loadAnalytics(){
const range = rangeSelect?.value || "7"
document.querySelectorAll(".stat-card p")
.forEach(el=>el.style.opacity="0.4")
const data = await api(
"${ADMIN_ANALYTICS}?range=${range}"
)
if(!data){
return
}
/* STATS */
setText("visitors",data.stats.visitors)
setText("views",data.stats.pageViews)
setText("animeViews",data.stats.animeViews)
setText("episodeViews",data.stats.episodeViews)
setText("downloads",data.stats.downloads)
setText("searches",data.stats.searches)
/* LISTS */
fillList(
document.getElementById("topAnime"),
data.topAnime,
"slug",
"v"
)
fillList(
document.getElementById("topEpisodes"),
data.topEpisodes,
"id",
"v"
)
fillList(
document.getElementById("topSearches"),
data.topSearches,
"q",
"c"
)
fillList(
document.getElementById("topCategories"),
data.topCategories,
"cat",
"v"
)
fillList(
document.getElementById("topBanners"),
data.topBanners,
"ban",
"c"
)
fillList(
document.getElementById("topServers"),
data.topServers,
"srv",
"v"
)
document.querySelectorAll(".stat-card p")
.forEach(el=>el.style.opacity="1")
}
/* ============================================================
SET TEXT SAFE
============================================================ */
function setText(id,val){
const el = document.getElementById(id)
if(!el) return
el.textContent =
Number(val||0).toLocaleString()
}
/* ============================================================
TRACKING FUNCTIONS
============================================================ */
function trackEvent(type,data={}){
fetch(TRACK_API,{
method:"POST",
headers:{
"Content-Type":"application/json"
},
body:JSON.stringify({
type,
...data
})
})
}
/* PAGE VIEW */
function trackPage(){
trackEvent("page",{
ref:location.pathname
})
}
/* ANIME VIEW */
function trackAnime(slug){
trackEvent("anime",{
ref:slug
})
}
/* EPISODE VIEW */
function trackEpisode(id){
trackEvent("episode",{
ref:id
})
}
/* DOWNLOAD */
function trackDownload(id){
trackEvent("download",{
ref:id
})
}
/* SEARCH */
function trackSearch(query){
trackEvent("search",{
value:query
})
}
/* BANNER */
function trackBanner(id){
trackEvent("banner",{
ref:id
})
}
/* SERVER */
function trackServer(server){
trackEvent("server",{
ref:server
})
}
/* CATEGORY */
function trackCategory(cat){
trackEvent("category",{
ref:cat
})
}
/* ============================================================
INIT
============================================================ */
document.addEventListener("DOMContentLoaded",()=>{
trackPage()
loadAnalytics()
})
if(rangeSelect){
rangeSelect.addEventListener(
"change",
loadAnalytics
)
}
/* AUTO REFRESH */
setInterval(loadAnalytics,120000)