<?php
declare(strict_types=1);
require __DIR__ . '/lib.php';
start_admin_session();
$cfg = config();

function h(mixed $v): string { return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
function go(string $message = ''): never {
    if ($message !== '') $_SESSION['flash'] = $message;
    header('Location: /admin'); exit;
}
function audit(string $account, string $action, string $detail = ''): void {
    db()->prepare('INSERT INTO audit_logs(account,action,detail,ip,created_at) VALUES(?,?,?,?,?)')
        ->execute([$account, $action, $detail, client_ip(), gmdate('c')]);
}

if (isset($_GET['logout'])) { session_destroy(); header('Location: /admin'); exit; }
$loginError = '';
if (empty($_SESSION['admin_ok']) && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $userOk = hash_equals((string)$cfg['admin_user'], (string)($_POST['username'] ?? ''));
    $passOk = password_verify((string)($_POST['password'] ?? ''), (string)$cfg['admin_password_hash']);
    if ($userOk && $passOk) {
        session_regenerate_id(true); $_SESSION['admin_ok'] = true; go();
    }
    usleep(500000); $loginError = '账号或密码不正确';
}
if (empty($_SESSION['admin_ok'])) {
?><!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>登录 · HS License</title>
<style>
*{box-sizing:border-box}body{margin:0;min-height:100vh;display:grid;place-items:center;font:14px/1.5 Inter,"PingFang SC","Microsoft YaHei",sans-serif;color:#f3eee4;background:#050505;overflow:hidden}.orb{position:fixed;border-radius:50%;filter:blur(90px);opacity:.2}.o1{width:520px;height:520px;background:#a97b25;left:-180px;top:-220px}.o2{width:420px;height:420px;background:#674b17;right:-140px;bottom:-190px}.login{position:relative;width:min(420px,calc(100vw - 32px));padding:38px;background:rgba(14,13,11,.9);border:1px solid #493b20;border-radius:24px;box-shadow:0 30px 100px #000b;backdrop-filter:blur(22px)}.brand{display:flex;align-items:center;gap:13px;margin-bottom:34px}.logo{width:44px;height:44px;display:grid;place-items:center;border-radius:13px;color:#171109;background:linear-gradient(135deg,#f2d891,#a87927);box-shadow:0 10px 28px #c3983b35;font-weight:800;font-size:18px}.brand b{font-size:18px}.brand small{display:block;color:#8e816a;margin-top:2px}.login h1{font-size:28px;margin:0 0 8px}.sub{color:#8f846f;margin:0 0 28px}.field{margin:15px 0}.field label{display:block;color:#b8aa90;font-size:12px;margin:0 0 7px}.field input{width:100%;height:48px;border:1px solid #3d3424;border-radius:11px;background:#080807;color:#fff;padding:0 14px;outline:none;transition:.2s}.field input:focus{border-color:#d3ae57;box-shadow:0 0 0 3px #c59a3b1c}.submit{width:100%;height:49px;margin-top:10px;border:0;border-radius:11px;color:#1a1308;font-weight:800;background:linear-gradient(135deg,#f0d58d,#b5852f);cursor:pointer;box-shadow:0 12px 28px #af842d32}.error{padding:10px 12px;border-radius:9px;background:#ff53651c;color:#ff8994;margin-bottom:15px}.secure{text-align:center;color:#716754;font-size:12px;margin-top:22px}
</style></head><body><div class="orb o1"></div><div class="orb o2"></div><form class="login" method="post"><div class="brand"><div class="logo">HS</div><div><b>HS License</b><small>EA 授权管理平台</small></div></div><h1>欢迎回来</h1><p class="sub">登录以管理交易账户授权与设备状态</p><?php if($loginError):?><div class="error"><?=h($loginError)?></div><?php endif;?><div class="field"><label>管理员账号</label><input name="username" autocomplete="username" placeholder="请输入账号" required autofocus></div><div class="field"><label>登录密码</label><input name="password" type="password" autocomplete="current-password" placeholder="请输入密码" required></div><button class="submit">安全登录</button><div class="secure">HTTPS 加密连接 · 登录会话受保护</div></form></body></html><?php exit; }

if (isset($_GET['export']) && $_GET['export'] === 'csv') {
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="ea-licenses-'.gmdate('Ymd-His').'.csv"');
    echo "\xEF\xBB\xBF";
    $out=fopen('php://output','w');
    fputcsv($out,['MT5账户','状态','到期日期','备注','最后在线','来源IP','终端公司','更新时间']);
    foreach(db()->query('SELECT * FROM licenses ORDER BY updated_at DESC') as $r)
        fputcsv($out,[$r['account'],$r['status'],$r['expires_at'],$r['note'],$r['last_seen_at'],$r['last_ip'],$r['terminal_company'],$r['updated_at']]);
    fclose($out); exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    verify_csrf();
    $action = (string)($_POST['action'] ?? 'save');
    if ($action === 'delete') {
        $account = trim((string)($_POST['account'] ?? ''));
        if (!valid_account($account)) exit('Invalid account');
        db()->prepare('DELETE FROM licenses WHERE account=?')->execute([$account]);
        audit($account, 'delete', '删除授权账户'); go('账户 '.$account.' 已删除');
    }
    if ($action === 'bulk_status') {
        $accounts = array_values(array_filter((array)($_POST['accounts'] ?? []), fn($a)=>valid_account((string)$a)));
        $status = (string)($_POST['bulk_status'] ?? '');
        if (!$accounts || !in_array($status,['active','close_only','disabled'],true)) go('请选择账户和目标状态');
        $stmt = db()->prepare('UPDATE licenses SET status=?,updated_at=? WHERE account=?');
        foreach ($accounts as $a) { $stmt->execute([$status,gmdate('c'),$a]); audit((string)$a,'bulk_status',$status); }
        go('已批量更新 '.count($accounts).' 个账户');
    }
    $account = trim((string)($_POST['account'] ?? ''));
    $status = (string)($_POST['status'] ?? 'active');
    $expires = trim((string)($_POST['expires_at'] ?? ''));
    $note = mb_substr(trim((string)($_POST['note'] ?? '')),0,500);
    if (!valid_account($account)) exit('Invalid account');
    if (!in_array($status,['active','close_only','disabled'],true)) exit('Invalid status');
    if ($expires !== '' && !preg_match('/^\d{4}-\d{2}-\d{2}$/',$expires)) exit('Invalid date');
    $exists = db()->prepare('SELECT 1 FROM licenses WHERE account=?'); $exists->execute([$account]);
    db()->prepare('INSERT INTO licenses(account,status,expires_at,note,updated_at) VALUES(?,?,?,?,?) ON CONFLICT(account) DO UPDATE SET status=excluded.status,expires_at=excluded.expires_at,note=excluded.note,updated_at=excluded.updated_at')
        ->execute([$account,$status,$expires?:null,$note,gmdate('c')]);
    audit($account,$exists->fetchColumn()?'update':'create',$status.' / '.($expires?:'永久'));
    go('账户 '.$account.' 已保存');
}

$pdo = db();
$allRows = $pdo->query('SELECT * FROM licenses ORDER BY updated_at DESC')->fetchAll();
$logs = $pdo->query('SELECT * FROM audit_logs ORDER BY id DESC LIMIT 30')->fetchAll();
$now = time(); $stats=['total'=>count($allRows),'active'=>0,'close_only'=>0,'disabled'=>0,'online'=>0,'warning'=>0,'expired'=>0,'permanent'=>0];
foreach($allRows as $r){
    if($r['status']==='active')$stats['active']++;
    if($r['status']==='close_only')$stats['close_only']++;
    if($r['status']==='disabled')$stats['disabled']++;
    if(!empty($r['last_seen_at']) && strtotime($r['last_seen_at']) >= $now-600)$stats['online']++;
    if(!empty($r['expires_at']) && strtotime($r['expires_at'].' 23:59:59 UTC') <= $now+7*86400)$stats['warning']++;
    if(!empty($r['expires_at']) && strtotime($r['expires_at'].' 23:59:59 UTC') < $now)$stats['expired']++;
    if(empty($r['expires_at']))$stats['permanent']++;
}
$riskRows=array_values(array_filter($allRows,fn($r)=>$r['status']!=='active'||(!empty($r['expires_at'])&&strtotime($r['expires_at'].' 23:59:59 UTC')<=$now+7*86400)));
$flash=(string)($_SESSION['flash']??''); unset($_SESSION['flash']);
$csrf=csrf_token();
$labels=['active'=>'正常使用','close_only'=>'仅管理仓位','disabled'=>'已停用'];
$logLabels=['create'=>'新增账户','update'=>'更新账户','delete'=>'删除账户','bulk_status'=>'批量调整'];
?><!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>HS License · 授权控制台</title>
<style>
:root{--bg:#080c15;--side:#0c111d;--card:#111827;--card2:#151e30;--line:#222d42;--text:#e9eef8;--muted:#8490a8;--primary:#6c6ff7;--green:#22c98e;--orange:#f3a83b;--red:#f26070}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.5 Inter,"PingFang SC","Microsoft YaHei",sans-serif}button,input,select,textarea{font:inherit}.app{min-height:100vh;display:grid;grid-template-columns:238px 1fr}.sidebar{position:fixed;inset:0 auto 0 0;width:238px;padding:25px 18px;background:var(--side);border-right:1px solid #ffffff0a}.brand{display:flex;align-items:center;gap:12px;padding:0 9px 28px}.logo{width:39px;height:39px;display:grid;place-items:center;border-radius:12px;background:linear-gradient(135deg,#7477ff,#4945d4);font-weight:800;box-shadow:0 10px 25px #5250db44}.brand b{font-size:16px}.brand small{display:block;color:#68748d}.nav-label{font-size:10px;color:#59657b;letter-spacing:1.5px;margin:15px 12px 8px}.nav a{display:flex;gap:12px;align-items:center;color:#8895ad;text-decoration:none;padding:11px 13px;border-radius:10px;margin:3px 0}.nav a.active{color:#fff;background:#6b6ef719}.nav a.active:before{content:"";width:3px;height:20px;background:#7477ff;border-radius:5px;margin-left:-13px}.dot{width:8px;height:8px;border-radius:50%;background:#4d596e}.nav .active .dot{background:#777aff;box-shadow:0 0 12px #777aff}.server{position:absolute;left:18px;right:18px;bottom:25px;padding:13px;background:#121a29;border:1px solid #202b40;border-radius:12px}.server span{display:flex;align-items:center;gap:8px}.pulse{width:8px;height:8px;border-radius:50%;background:var(--green);box-shadow:0 0 0 5px #22c98e16}.server small{display:block;color:#68758d;margin-top:5px}.main{grid-column:2;padding:0 32px 40px;min-width:0}.topbar{height:82px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #ffffff0b}.topbar h1{font-size:20px;margin:0}.topbar p{margin:3px 0 0;color:var(--muted);font-size:12px}.top-actions{display:flex;align-items:center;gap:12px}.logout{color:#9aa6bc;text-decoration:none;padding:8px 12px;border:1px solid var(--line);border-radius:9px}.btn{border:0;border-radius:10px;padding:10px 15px;color:#fff;cursor:pointer;font-weight:650}.btn-primary{background:linear-gradient(135deg,#7477ff,#5752de);box-shadow:0 8px 22px #5a55df32}.btn-ghost{background:#182237;color:#b6c0d2}.btn-danger{background:#f260701b;color:#ff8490}.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin:24px 0}.stat{position:relative;padding:20px;background:linear-gradient(145deg,#121a2a,#0f1624);border:1px solid #202a3e;border-radius:15px;overflow:hidden}.stat:after{content:"";position:absolute;width:90px;height:90px;border-radius:50%;right:-35px;top:-35px;background:var(--c);opacity:.08}.stat .label{color:#8490a6;font-size:12px}.stat strong{display:block;font-size:28px;margin:7px 0 4px}.stat small{color:#65718a}.stat i{position:absolute;right:18px;bottom:18px;width:9px;height:9px;border-radius:50%;background:var(--c);box-shadow:0 0 13px var(--c)}.panel{background:var(--card);border:1px solid #202a3d;border-radius:16px;margin:18px 0;box-shadow:0 12px 40px #0002}.panel-head{display:flex;align-items:center;justify-content:space-between;padding:18px 20px;border-bottom:1px solid #202a3d}.panel-head h2{font-size:15px;margin:0}.panel-head small{color:var(--muted)}.tools{display:flex;gap:10px;align-items:center}.search{position:relative}.search input{width:240px;height:38px;padding:0 13px 0 34px;background:#0c1320;color:#dfe5f0;border:1px solid #263148;border-radius:9px;outline:none}.search:before{content:"⌕";position:absolute;left:12px;top:7px;color:#748098;font-size:18px}.filter{height:38px;background:#0c1320;color:#bbc5d7;border:1px solid #263148;border-radius:9px;padding:0 10px}.table-wrap{overflow:auto}table{width:100%;border-collapse:collapse;min-width:960px}th{padding:12px 16px;color:#66738a;font-size:11px;text-transform:uppercase;letter-spacing:.6px;text-align:left;background:#0e1624}td{padding:14px 16px;border-top:1px solid #1c2638;color:#cbd3e1}tbody tr{transition:.15s}tbody tr:hover{background:#151f30}.account{font-weight:700;color:#f2f5fb}.company{font-size:11px;color:#66738a;margin-top:2px;max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.badge{display:inline-flex;align-items:center;gap:6px;padding:5px 9px;border-radius:20px;font-size:11px;font-weight:650}.badge:before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}.active{background:#1bc88b16;color:#35d99e}.close_only{background:#f1a73b17;color:#f5b552}.disabled{background:#ef607019;color:#ff7886}.online{color:#39d59e}.offline{color:#637087}.expiry-warn{color:#ffb454}.note{max-width:180px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#8f9bb0}.actions{display:flex;gap:6px}.icon-btn{padding:7px 9px;border:1px solid #28344a;border-radius:8px;background:#151e2e;color:#aab5c8;cursor:pointer}.icon-btn:hover{border-color:#6065e8;color:#fff}.bulk{display:none;align-items:center;gap:10px;padding:12px 20px;background:#121c2d;border-top:1px solid var(--line)}.bulk.show{display:flex}.bulk span{color:#8e9ab0;margin-right:auto}.empty{text-align:center!important;padding:48px!important;color:#68758d}.lower{display:grid;grid-template-columns:1.3fr .7fr;gap:18px}.log{display:flex;gap:12px;padding:13px 20px;border-top:1px solid #1d2739}.log-mark{width:30px;height:30px;display:grid;place-items:center;border-radius:9px;background:#6b6ef716;color:#8d90ff}.log-main{flex:1}.log-main b{font-size:12px}.log-main p{margin:2px 0;color:#7d899f;font-size:11px}.log time{font-size:10px;color:#59667c}.guide{padding:20px}.guide-item{display:flex;gap:12px;margin:15px 0}.guide-num{flex:0 0 27px;height:27px;display:grid;place-items:center;border-radius:8px;background:#6b6ef71c;color:#9799ff;font-weight:700}.guide b{font-size:12px}.guide p{margin:2px 0;color:#738097;font-size:11px}.modal-back{display:none;position:fixed;inset:0;background:#02050bb8;backdrop-filter:blur(6px);z-index:20;place-items:center}.modal-back.show{display:grid}.modal{width:min(500px,calc(100vw - 28px));background:#111a29;border:1px solid #29344a;border-radius:18px;box-shadow:0 30px 100px #000b}.modal-head{display:flex;justify-content:space-between;padding:20px 22px;border-bottom:1px solid var(--line)}.modal-head h3{margin:0;font-size:16px}.close{background:none;border:0;color:#7f8ba2;font-size:22px;cursor:pointer}.modal-body{padding:20px 22px}.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}.field{margin-bottom:14px}.field label{display:block;color:#8c98ae;font-size:11px;margin-bottom:6px}.field input,.field select,.field textarea{width:100%;background:#0b1220;color:#e5eaf3;border:1px solid #29354a;border-radius:9px;padding:10px;outline:none}.field textarea{height:82px;resize:vertical}.modal-foot{display:flex;justify-content:flex-end;gap:10px;padding:0 22px 21px}.toast{position:fixed;right:24px;top:22px;z-index:30;background:#152338;border:1px solid #2d3e59;color:#dce5f2;padding:11px 16px;border-radius:10px;box-shadow:0 12px 40px #0007;animation:hide .4s 3.2s forwards}@keyframes hide{to{opacity:0;transform:translateY(-8px);visibility:hidden}}@media(max-width:1050px){.stats{grid-template-columns:1fr 1fr}.lower{grid-template-columns:1fr}.sidebar{width:76px}.app{grid-template-columns:76px 1fr}.brand div:not(.logo),.nav span,.nav-label,.server{display:none}.nav a{justify-content:center}.main{grid-column:2;padding:0 18px 35px}}@media(max-width:650px){.app{display:block}.sidebar{display:none}.main{padding:0 12px 30px}.topbar{height:70px}.topbar p{display:none}.stats{gap:10px;margin:15px 0}.stat{padding:15px}.stat strong{font-size:22px}.panel-head{align-items:flex-start;gap:12px;flex-direction:column}.tools{width:100%}.search{flex:1}.search input{width:100%}.top-actions .logout{display:none}.grid2{grid-template-columns:1fr}}
</style><style>
/* Black Gold Edition */
:root{--bg:#050505;--side:#090909;--card:#0e0e0f;--card2:#15130f;--line:#2c281f;--text:#f4f0e7;--muted:#948b79;--primary:#d6b66d;--green:#55c995;--orange:#d9a94f;--red:#db6871}body{background:radial-gradient(circle at 70% -20%,#33270d55,transparent 35%),#050505}.sidebar{background:linear-gradient(180deg,#0c0b09,#070707);border-right-color:#332d20}.logo{color:#171109;background:linear-gradient(135deg,#f5dda0,#a87b28);box-shadow:0 10px 28px #c49a3b2b}.brand small,.topbar p{color:#8d8067}.nav a.active{background:linear-gradient(90deg,#d2ad5823,#d2ad5808);color:#f2ddb0}.nav a.active:before,.nav .active .dot{background:#d8b763;box-shadow:0 0 14px #d8b763}.server{background:#100f0c;border-color:#39301e}.topbar{border-color:#2c261a}.btn-primary{color:#171109;background:linear-gradient(135deg,#efd78f,#b88931);box-shadow:0 10px 28px #bf963a2b}.logout,.icon-btn,.filter,.search input{border-color:#3a3222;background:#0a0a0a}.panel,.stat{background:linear-gradient(145deg,#11110f,#0b0b0b);border-color:#2f291e}.panel{box-shadow:0 18px 60px #0008}.panel-head,td,.bulk{border-color:#2b271e}th{background:#0a0907;color:#897d67}.stat:before{content:"";position:absolute;left:0;top:0;right:0;height:1px;background:linear-gradient(90deg,transparent,#d8b65e77,transparent)}tbody tr:hover{background:#17140d}.badge.active{background:#c8aa5420;color:#e3c46f}.badge.close_only{background:#b97c251c;color:#d8a34d}.badge.disabled{background:#9e39391c;color:#df7070}.modal{background:#11100d;border-color:#403723}.modal-head{border-color:#332c1e}.field input,.field select,.field textarea{background:#090909;border-color:#3a3222}.guide-num,.log-mark{background:#d1ad5320;color:#e5c570}.toast{background:#17130b;border-color:#5a4722}.insights{display:grid;grid-template-columns:1.05fr 1.15fr .9fr;gap:16px;margin:0 0 18px}.insight{min-height:176px;padding:20px;background:linear-gradient(145deg,#11100d,#0b0b0a);border:1px solid #30291d;border-radius:15px}.insight h3{margin:0;font-size:14px}.insight .hint{color:#807663;font-size:11px;margin-top:3px}.dist{display:flex;align-items:center;gap:22px;margin-top:18px}.donut{width:88px;height:88px;border-radius:50%;display:grid;place-items:center;background:conic-gradient(#d7b45f 0 var(--a),#9a6a28 var(--a) var(--b),#49332d var(--b));position:relative}.donut:after{content:"";position:absolute;inset:13px;border-radius:50%;background:#0e0d0a}.donut b{z-index:1;font-size:20px}.legend{display:grid;gap:7px;font-size:11px;color:#9a907d}.legend span:before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--lc);margin-right:7px}.risk-list{margin-top:13px}.risk{display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-top:1px solid #282319}.risk:first-child{border:0}.risk b{font-size:12px}.risk small{display:block;color:#766d5d}.risk em{font-style:normal;font-size:10px;color:#d7ae55}.api-box{margin-top:15px;padding:11px;border:1px solid #332c20;border-radius:9px;background:#080807;color:#a8997c;font:11px ui-monospace,monospace;word-break:break-all}.api-status{display:flex;align-items:center;gap:8px;color:#57c891;margin-top:15px}.copy{width:100%;margin-top:13px}.quick-days{display:flex;gap:7px;flex-wrap:wrap;margin-top:8px}.quick-days button{border:1px solid #3c3321;background:#14120d;color:#cdb576;padding:6px 9px;border-radius:7px;cursor:pointer}.quick-days button:hover{border-color:#d1ad55}.gold-link{color:#d9b961;text-decoration:none;border:1px solid #413720;padding:9px 12px;border-radius:9px}@media(max-width:1150px){.insights{grid-template-columns:1fr 1fr}.insight:last-child{grid-column:1/-1}}@media(max-width:650px){.insights{grid-template-columns:1fr}.insight:last-child{grid-column:auto}.gold-link{display:none}}
</style></head><body>
<?php if($flash):?><div class="toast">✓ <?=h($flash)?></div><?php endif;?>
<div class="app"><aside class="sidebar"><div class="brand"><div class="logo">HS</div><div><b>HS License</b><small>Control Center</small></div></div><div class="nav-label">工作台</div><nav class="nav"><a class="active" href="#accounts"><i class="dot"></i><span>授权账户</span></a><a href="#activity"><i class="dot"></i><span>操作记录</span></a><a href="/api/license" target="_blank"><i class="dot"></i><span>API 状态</span></a></nav><div class="server"><span><i class="pulse"></i><b>服务运行正常</b></span><small>API · HTTPS · HMAC</small></div></aside>
<main class="main"><header class="topbar"><div><h1>授权控制台</h1><p>管理 MT5 交易账户访问权限和在线状态</p></div><div class="top-actions"><a class="gold-link" href="?export=csv">导出 CSV</a><button class="btn btn-primary" onclick="openModal()">＋ 新增授权</button><a class="logout" href="?logout=1">退出</a></div></header>
<section class="stats"><div class="stat" style="--c:#d7b45f"><div class="label">授权账户总数</div><strong><?=$stats['total']?></strong><small>全部登记账户</small><i></i></div><div class="stat" style="--c:#e7cb82"><div class="label">正常授权</div><strong><?=$stats['active']?></strong><small>允许完整交易</small><i></i></div><div class="stat" style="--c:#55c995"><div class="label">当前在线</div><strong><?=$stats['online']?></strong><small>10 分钟内有心跳</small><i></i></div><div class="stat" style="--c:#b47a2f"><div class="label">即将到期</div><strong><?=$stats['warning']?></strong><small>7 天内到期或已过期</small><i></i></div></section>
<?php $p1=$stats['total']?round($stats['active']/$stats['total']*100):0;$p2=$stats['total']?round(($stats['active']+$stats['close_only'])/$stats['total']*100):0; ?>
<section class="insights"><div class="insight"><h3>授权状态分布</h3><div class="hint">所有账户实时占比</div><div class="dist"><div class="donut" style="--a:<?=$p1?>%;--b:<?=$p2?>%"><b><?=$stats['total']?></b></div><div class="legend"><span style="--lc:#d7b45f">正常使用 <?=$stats['active']?></span><span style="--lc:#9a6a28">仅管理 <?=$stats['close_only']?></span><span style="--lc:#49332d">已停用 <?=$stats['disabled']?></span><span style="--lc:#746c5d">永久授权 <?=$stats['permanent']?></span></div></div></div><div class="insight"><h3>风险与到期提醒</h3><div class="hint">需要优先处理的授权账户</div><div class="risk-list"><?php foreach(array_slice($riskRows,0,4) as $r):?><div class="risk"><div><b><?=h($r['account'])?></b><small><?=h($r['note']?:$labels[$r['status']])?></small></div><em><?=!empty($r['expires_at'])?h($r['expires_at']):h($labels[$r['status']])?></em></div><?php endforeach;?><?php if(!$riskRows):?><div class="risk"><div><b>当前无风险项</b><small>所有账户状态正常</small></div><em>安全</em></div><?php endif;?></div></div><div class="insight"><h3>授权 API</h3><div class="hint">EA 客户端心跳接口</div><div class="api-status"><i class="pulse"></i> HTTPS 服务正常</div><div class="api-box" id="apiUrl">https://dc.hongshenglh.com/api/license</div><button class="btn btn-ghost copy" type="button" onclick="copyApi(this)">复制接口地址</button></div></section>
<section class="panel" id="accounts"><div class="panel-head"><div><h2>授权账户</h2><small>实时控制 EA 的使用权限</small></div><div class="tools"><div class="search"><input id="search" placeholder="搜索账户、备注或IP"></div><select class="filter" id="statusFilter"><option value="">全部状态</option><option value="active">正常使用</option><option value="close_only">仅管理</option><option value="disabled">已停用</option></select></div></div>
<form method="post" id="bulkForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="bulk_status"><div class="table-wrap"><table><thead><tr><th><input type="checkbox" id="checkAll"></th><th>交易账户</th><th>授权状态</th><th>连接状态</th><th>授权期限</th><th>最近心跳</th><th>来源 IP</th><th>备注</th><th>操作</th></tr></thead><tbody id="accountRows">
<?php foreach($allRows as $r): $seen=!empty($r['last_seen_at'])?strtotime($r['last_seen_at']):0;$isOnline=$seen>=$now-600;$exp=(string)($r['expires_at']??'');$warn=$exp!==''&&strtotime($exp.' 23:59:59 UTC')<=$now+7*86400; ?><tr data-status="<?=h($r['status'])?>" data-search="<?=h(strtolower($r['account'].' '.$r['note'].' '.$r['last_ip'].' '.$r['terminal_company']))?>"><td><input class="row-check" type="checkbox" name="accounts[]" value="<?=h($r['account'])?>"></td><td><div class="account"><?=h($r['account'])?></div><div class="company"><?=h($r['terminal_company']?:'尚无终端信息')?></div></td><td><span class="badge <?=h($r['status'])?>"><?=h($labels[$r['status']]??$r['status'])?></span></td><td><span class="<?=$isOnline?'online':'offline'?>">● <?=$isOnline?'在线':'离线'?></span></td><td class="<?=$warn?'expiry-warn':''?>"><?=$exp?h($exp):'永久有效'?></td><td><?=$seen?h(gmdate('m-d H:i',$seen)).' UTC':'从未连接'?></td><td><?=h($r['last_ip']?:'—')?></td><td><div class="note" title="<?=h($r['note'])?>"><?=h($r['note']?:'—')?></div></td><td><div class="actions"><button type="button" class="icon-btn" onclick='editAccount(<?=json_encode($r,JSON_UNESCAPED_UNICODE|JSON_HEX_APOS|JSON_HEX_QUOT)?>)'>编辑</button><button type="button" class="icon-btn" onclick="removeAccount('<?=h($r['account'])?>')">删除</button></div></td></tr><?php endforeach;?>
<?php if(!$allRows):?><tr><td colspan="9" class="empty">暂无授权账户，点击右上角“新增授权”开始配置</td></tr><?php endif;?></tbody></table></div><div class="bulk" id="bulkBar"><span>已选择 <b id="selectedCount">0</b> 个账户</span><select class="filter" name="bulk_status" required><option value="">批量设置状态</option><option value="active">恢复正常</option><option value="close_only">设为仅管理</option><option value="disabled">全部停用</option></select><button class="btn btn-primary">应用</button></div></form></section>
<section class="lower" id="activity"><div class="panel"><div class="panel-head"><div><h2>最近操作</h2><small>后台权限变更审计记录</small></div></div><?php foreach($logs as $l):?><div class="log"><div class="log-mark">↗</div><div class="log-main"><b><?=h($logLabels[$l['action']]??$l['action'])?> · <?=h($l['account'])?></b><p><?=h($l['detail'])?>　IP <?=h($l['ip'])?></p></div><time><?=h(gmdate('m-d H:i',strtotime($l['created_at'])))?></time></div><?php endforeach;?><?php if(!$logs):?><div class="empty">暂无操作记录</div><?php endif;?></div><div class="panel"><div class="panel-head"><div><h2>状态说明</h2><small>远程控制策略</small></div></div><div class="guide"><div class="guide-item"><div class="guide-num">A</div><div><b>正常使用</b><p>允许 EA 开仓并管理现有仓位</p></div></div><div class="guide-item"><div class="guide-num">C</div><div><b>仅管理仓位</b><p>禁止新开仓，止盈止损和平仓继续运行</p></div></div><div class="guide-item"><div class="guide-num">D</div><div><b>停用</b><p>禁止开仓；空仓后 EA 自动卸载</p></div></div></div></div></section></main></div>
<div class="modal-back" id="modal"><form class="modal" method="post"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="save"><div class="modal-head"><h3 id="modalTitle">新增授权账户</h3><button type="button" class="close" onclick="closeModal()">×</button></div><div class="modal-body"><div class="grid2"><div class="field"><label>MT5 交易账户</label><input id="mAccount" name="account" inputmode="numeric" pattern="[0-9]{1,20}" placeholder="例如 12345678" required></div><div class="field"><label>授权状态</label><select id="mStatus" name="status"><option value="active">正常使用</option><option value="close_only">仅管理仓位</option><option value="disabled">停用</option></select></div></div><div class="field"><label>到期日期（留空为永久）</label><input id="mExpires" name="expires_at" type="date"><div class="quick-days"><button type="button" onclick="setDays(30)">＋30天</button><button type="button" onclick="setDays(90)">＋90天</button><button type="button" onclick="setDays(180)">＋180天</button><button type="button" onclick="setDays(365)">＋1年</button><button type="button" onclick="setDays(0)">永久</button></div></div><div class="field"><label>账户备注</label><textarea id="mNote" name="note" placeholder="客户名称、联系方式或授权说明"></textarea></div></div><div class="modal-foot"><button type="button" class="btn btn-ghost" onclick="closeModal()">取消</button><button class="btn btn-primary">保存授权</button></div></form></div>
<form method="post" id="deleteForm"><input type="hidden" name="csrf" value="<?=h($csrf)?>"><input type="hidden" name="action" value="delete"><input type="hidden" name="account" id="deleteAccount"></form>
<script>
const modal=document.getElementById('modal'),search=document.getElementById('search'),filter=document.getElementById('statusFilter'),rows=[...document.querySelectorAll('#accountRows tr[data-status]')];
function openModal(){document.getElementById('modalTitle').textContent='新增授权账户';['mAccount','mExpires','mNote'].forEach(id=>document.getElementById(id).value='');document.getElementById('mAccount').readOnly=false;document.getElementById('mStatus').value='active';modal.classList.add('show')}
function editAccount(r){document.getElementById('modalTitle').textContent='编辑账户 '+r.account;document.getElementById('mAccount').value=r.account;document.getElementById('mAccount').readOnly=true;document.getElementById('mStatus').value=r.status;document.getElementById('mExpires').value=r.expires_at||'';document.getElementById('mNote').value=r.note||'';modal.classList.add('show')}
function closeModal(){modal.classList.remove('show')} modal.addEventListener('click',e=>{if(e.target===modal)closeModal()});document.addEventListener('keydown',e=>{if(e.key==='Escape')closeModal()});
function applyFilter(){const q=search.value.trim().toLowerCase(),s=filter.value;rows.forEach(r=>r.style.display=(!q||r.dataset.search.includes(q))&&(!s||r.dataset.status===s)?'':'none')};search.addEventListener('input',applyFilter);filter.addEventListener('change',applyFilter);
const checks=[...document.querySelectorAll('.row-check')],all=document.getElementById('checkAll'),bar=document.getElementById('bulkBar'),count=document.getElementById('selectedCount');function sync(){const n=checks.filter(c=>c.checked).length;count.textContent=n;bar.classList.toggle('show',n>0);all.checked=n===checks.length&&n>0;all.indeterminate=n>0&&n<checks.length}checks.forEach(c=>c.addEventListener('change',sync));all.addEventListener('change',()=>{checks.forEach(c=>{if(c.closest('tr').style.display!=='none')c.checked=all.checked});sync()});
function removeAccount(a){if(confirm('确定删除账户 '+a+' 的授权记录吗？\n该账户下一次验证将被停用。')){document.getElementById('deleteAccount').value=a;document.getElementById('deleteForm').submit()}}
function setDays(n){const e=document.getElementById('mExpires');if(!n){e.value='';return}const d=new Date();d.setDate(d.getDate()+n);e.value=d.toISOString().slice(0,10)}
async function copyApi(btn){await navigator.clipboard.writeText(document.getElementById('apiUrl').textContent);const old=btn.textContent;btn.textContent='已复制';setTimeout(()=>btn.textContent=old,1400)}
</script></body></html>
