<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CloudSearch 管理后台</title>
<style>
:root {
  --bg: #0a0e17; --bg-card: rgba(16,22,36,0.8); --bg-card-hover: rgba(22,30,48,0.9);
  --border: rgba(255,255,255,0.06); --border-active: rgba(99,102,241,0.3);
  --text: #e2e8f0; --text-secondary: #94a3b8; --text-muted: #64748b;
  --primary: #6366f1; --primary-glow: rgba(99,102,241,0.2);
  --success: #22c55e; --success-glow: rgba(34,197,94,0.2);
  --warning: #f59e0b; --danger: #ef4444;
  --radius: 16px; --radius-sm: 10px;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
  font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display','Segoe UI',system-ui,sans-serif;
  background:var(--bg);color:var(--text);min-height:100vh;line-height:1.5;-webkit-font-smoothing:antialiased
}
body::before{
  content:'';position:fixed;inset:0;
  background:radial-gradient(ellipse 80% 50% at 20% 0%,rgba(99,102,241,0.08) 0%,transparent 60%),
             radial-gradient(ellipse 60% 40% at 80% 100%,rgba(34,197,94,0.06) 0%,transparent 60%);
  pointer-events:none;z-index:0
}
.header{
  position:sticky;top:0;z-index:100;
  background:rgba(10,14,23,0.75);backdrop-filter:blur(20px) saturate(180%);
  -webkit-backdrop-filter:blur(20px) saturate(180%);border-bottom:1px solid var(--border);padding:0 24px
}
.header-inner{max-width:1100px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;height:64px}
.logo{display:flex;align-items:center;gap:12px}
.logo-icon{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,var(--primary),#8b5cf6);display:flex;align-items:center;justify-content:center;font-size:18px;box-shadow:0 0 20px var(--primary-glow)}
.logo-text h1{font-size:17px;font-weight:700;letter-spacing:-0.3px}
.logo-text span{font-size:11px;color:var(--text-muted);font-weight:500}
.header-actions{display:flex;gap:8px;align-items:center}
.btn{padding:8px 16px;border-radius:8px;font-size:13px;font-weight:500;cursor:pointer;border:1px solid var(--border);background:rgba(255,255,255,0.04);color:var(--text-secondary);transition:all 0.2s;display:flex;align-items:center;gap:6px;font-family:inherit}
.btn:hover{background:rgba(255,255,255,0.08);color:var(--text)}
.btn-primary{background:var(--primary);border-color:var(--primary);color:white}
.btn-primary:hover{background:#4f46e5;box-shadow:0 0 20px var(--primary-glow)}
.stats-strip{max-width:1100px;margin:24px auto 0;padding:0 24px;display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
.stat-mini{background:var(--bg-card);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--border);border-radius:var(--radius-sm);padding:16px;text-align:center;transition:all 0.2s}
.stat-mini:hover{border-color:var(--border-active);background:var(--bg-card-hover);transform:translateY(-2px)}
.stat-mini .stat-num{font-size:28px;font-weight:800;letter-spacing:-1px;background:linear-gradient(135deg,var(--primary),#8b5cf6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.stat-mini .stat-label{font-size:12px;color:var(--text-muted);margin-top:2px;font-weight:500}
.main{max-width:1100px;margin:20px auto 40px;padding:0 24px;position:relative;z-index:1}
.section-title{font-size:11px;text-transform:uppercase;letter-spacing:2px;color:var(--text-muted);font-weight:700;margin:28px 0 14px;display:flex;align-items:center;gap:10px}
.section-title::after{content:'';flex:1;height:1px;background:var(--border)}
.feature-grid{display:grid;gap:8px}
.feature-card{background:var(--bg-card);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--border);border-radius:var(--radius-sm);padding:16px 20px;display:flex;align-items:center;justify-content:space-between;gap:16px;transition:all 0.25s;position:relative;overflow:hidden}
.feature-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;border-radius:3px 0 0 3px;transition:all 0.3s}
.feature-card.on::before{background:var(--success)}
.feature-card.off::before{background:var(--border)}
.feature-card:hover{background:var(--bg-card-hover);border-color:var(--border-active);transform:translateX(2px)}
.feature-card.loading{opacity:0.5;pointer-events:none}
.feature-info h3{font-size:14px;font-weight:600;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.feature-info .code{font-size:11px;color:var(--text-muted);font-family:monospace;background:rgba(255,255,255,0.03);padding:2px 8px;border-radius:5px;margin-top:4px;display:inline-block}
.feature-info .updated{font-size:11px;color:var(--text-muted);margin-left:8px}
.badge{font-size:10px;padding:3px 8px;border-radius:999px;font-weight:600}
.badge-synced{background:rgba(34,197,94,0.12);color:var(--success)}
.badge-mismatch{background:rgba(239,68,68,0.12);color:var(--danger)}
.toggle-wrap{flex-shrink:0}
.toggle{width:48px;height:26px;border-radius:26px;border:none;cursor:pointer;position:relative;transition:all 0.3s cubic-bezier(0.4,0,0.2,1);background:rgba(255,255,255,0.1)}
.toggle.on{background:var(--success);box-shadow:0 0 16px var(--success-glow)}
.toggle::after{content:'';position:absolute;top:3px;left:3px;width:20px;height:20px;border-radius:50%;background:white;transition:transform 0.3s cubic-bezier(0.4,0,0.2,1);box-shadow:0 2px 4px rgba(0,0,0,0.2)}
.toggle.on::after{transform:translateX(22px)}
.toast-container{position:fixed;bottom:24px;right:24px;z-index:999;display:flex;flex-direction:column;gap:8px}
.toast{background:var(--bg-card);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px 20px;font-size:13px;font-weight:500;display:flex;align-items:center;gap:8px;opacity:0;transform:translateX(40px);transition:all 0.3s cubic-bezier(0.4,0,0.2,1);min-width:240px;box-shadow:0 4px 24px rgba(0,0,0,0.3)}
.toast.show{opacity:1;transform:translateX(0)}
.toast.success{border-color:var(--success)}
.toast.error{border-color:var(--danger)}
.toast.info{border-color:var(--primary)}
.toast-icon{font-size:18px;flex-shrink:0}
.spinner{width:24px;height:24px;border:2px solid var(--border);border-top-color:var(--primary);border-radius:50%;animation:spin 0.6s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}
.saving{animation:pulse 0.8s ease-in-out infinite}
.loader{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:80px 0;gap:16px;color:var(--text-muted)}
@media(max-width:768px){.stats-strip{grid-template-columns:repeat(2,1fr)}.feature-card{flex-direction:column;align-items:flex-start}.feature-card .toggle-wrap{align-self:flex-end;margin-top:-32px}.header-inner{height:56px}}
</style>
</head>
<body>
<header class="header"><div class="header-inner"><div class="logo"><div class="logo-icon">⚙</div><div class="logo-text"><h1>CloudSearch 管理后台</h1><span>功能开关 &amp; 系统配置</span></div></div><div class="header-actions"><button class="btn" onclick="refreshAll()">🔄 刷新</button><button class="btn btn-primary" onclick="saveAll()" id="btn-save-all">💾 全部保存</button></div></div></header>
<div class="stats-strip" id="stats"></div>
<main class="main" id="main"><div class="loader"><div class="spinner"></div><span>加载功能开关...</span></div></main>
<div class="toast-container" id="toast-container"></div>
<script>
var FEATURES={{ features | tojson }};
var currentFlags={},statsData=null;
function refreshAll(){Promise.all([loadFlags(),loadStats()]).then(render)}
function loadFlags(){fetch('/api/flags').then(function(r){return r.json()}).then(function(d){currentFlags=d}).catch(function(){showToast('无法连接','error')})}
function loadStats(){var t=sessionStorage.getItem('admin_token');if(!t)return;fetch('http://127.0.0.1:9527/api/admin/stats',{headers:{'Authorization':'Bearer '+t}}).then(function(r){return r.ok?r.json():null}).then(function(d){statsData=d}).catch(function(){})}
function render(){renderStats();renderFeatures()}
function renderStats(){var e=document.getElementById('stats');if(!statsData){e.innerHTML='';return}e.innerHTML='<div class="stat-mini"><div class="stat-num">'+(statsData.todaySearches||0)+'</div><div class="stat-label">今日搜索</div></div><div class="stat-mini"><div class="stat-num">'+(statsData.todaySaves||0)+'</div><div class="stat-label">今日转存</div></div><div class="stat-mini"><div class="stat-num">'+(statsData.totalSearches||0)+'</div><div class="stat-label">总搜索</div></div><div class="stat-mini"><div class="stat-num">'+(statsData.totalSaves||0)+'</div><div class="stat-label">总转存</div></div>'}
function renderFeatures(){var entries=Object.entries(FEATURES).map(function(e){e[1]=Object.assign({},e[1],{flag:currentFlags[e[0]]});return e});var groups={};entries.forEach(function(e){var g=e[1].group;(groups[g]=groups[g]||[]).push(e)});var order=['核心','增强','转存'],icons={'核心':'🔵','增强':'🟣','转存':'🟢'},html='';order.forEach(function(g){if(!groups[g])return;html+='<div class="section-title">'+(icons[g]||'📌')+' '+g+'功能</div><div class="feature-grid">';groups[g].forEach(function(p){var key=p[0],item=p[1],flag=item.flag||{},on=flag.value,synced=flag.synced!==false,updated=flag.updated_at?flag.updated_at.replace('T',' ').slice(0,16):'';html+='<div class="feature-card '+(on?'on':'off')+'" id="card-'+key+'"><div class="feature-info"><h3>'+item.name+(synced?'':' <span class="badge badge-mismatch">⚠ 未同步</span>')+(synced&&on?' <span class="badge badge-synced">● 已启用</span>':'')+'</h3><span class="code">'+key+'</span>'+(updated?'<span class="updated">'+updated+'</span>':'')+'</div><div class="toggle-wrap"><button class="toggle '+(on?'on':'off')+'" onclick="toggleFlag(\''+key+'\','+(!on)+')"><span style="display:none">开关</span></button></div></div>'});html+='</div>'});document.getElementById('main').innerHTML=html}
function toggleFlag(key,value){var card=document.getElementById('card-'+key);if(!card)return;card.classList.add('loading');fetch('/api/flags/'+key,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({value:value})}).then(function(r){return r.json()}).then(function(d){if(d.ok){showToast(FEATURES[key].name+' → '+(value?'✅ 已开启':'❌ 已关闭'),'success');loadFlags().then(render)}else showToast(d.error||'失败','error')}).catch(function(e){showToast('网络错误: '+e.message,'error');card.classList.remove('loading')}).finally(function(){card.classList.remove('loading')})}
function saveAll(){var btn=document.getElementById('btn-save-all');btn.classList.add('saving');btn.innerHTML='⏳ 保存中...';fetch('/api/flags').then(function(r){return r.json()}).then(function(d){showToast('已同步 '+Object.keys(d).length+' 个开关 ✓','success')}).catch(function(){showToast('同步失败','error')}).finally(function(){setTimeout(function(){btn.classList.remove('saving');btn.innerHTML='💾 全部保存'},600)})}
function showToast(msg,type){var c=document.getElementById('toast-container'),icons={success:'✓',error:'✕',info:'ℹ'},el=document.createElement('div');el.className='toast '+(type||'info');el.innerHTML='<span class="toast-icon">'+(icons[type]||'')+'</span>'+msg;c.appendChild(el);requestAnimationFrame(function(){el.classList.add('show')});setTimeout(function(){el.classList.remove('show');setTimeout(function(){el.remove()},300)},2800)}
(function init(){try{fetch('http://127.0.0.1:9527/api/admin/login',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({username:'admin',password:'0nL5kLhMIJ1121PYmQb25A'})}).then(function(r){return r.ok?r.json():null}).then(function(d){if(d&&d.token)sessionStorage.setItem('admin_token',d.token)}).catch(function(){})}catch(e){}refreshAll()})();
</script>
</body>
</html>
