SAwww / logs.html
SAyyeedd's picture
Upload 30 files
309ec79 verified
Raw
History Blame Contribute Delete
9.6 kB
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>سجل الأحداث — Sayed Bot</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{--bg:#0d1117;--surface:rgba(22,27,34,.72);--surface2:rgba(30,39,50,.75);--border:rgba(255,255,255,.06);--text:#e6edf3;--text2:#8b949e;--text3:#6e7681;--green:#3fb950;--green2:#56d364;--green-bg:rgba(63,185,80,.1);--blue:#58a6ff;--blue2:#79c0ff;--blue-bg:rgba(88,166,255,.1);--red:#f85149;--red2:#ff7b72;--red-bg:rgba(248,81,73,.1);--amber:#d29922;--amber-bg:rgba(210,153,34,.1);--radius:12px;--radius-sm:8px;--shadow:0 8px 32px rgba(0,0,0,.35);--font:system-ui,'Segoe UI',Roboto,sans-serif;--glass:rgba(22,27,34,.65);--glass-blur:20px}
html,body{min-height:100vh}
body{background:var(--bg);background-image:radial-gradient(ellipse 80% 50% at 20% 0%,rgba(63,185,80,.06) 0%,transparent 50%),radial-gradient(ellipse 60% 40% at 80% 100%,rgba(88,166,255,.04) 0%,transparent 50%);color:var(--text);font-family:var(--font);font-size:14px;direction:rtl;line-height:1.6}
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{transparent}::-webkit-scrollbar-thumb{background:rgba(63,185,80,.25);border-radius:10px}
::selection{background:rgba(63,185,80,.3);color:#fff}
.layout{display:flex;min-height:100vh}
.sidebar{width:220px;flex-shrink:0;position:fixed;right:0;top:0;bottom:0;background:var(--glass);backdrop-filter:blur(var(--glass-blur));border-left:1px solid rgba(255,255,255,.08);display:flex;flex-direction:column;z-index:100}
@media(max-width:900px){.sidebar{display:none}}
.sidebar-header{padding:20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px}
.sidebar-logo{width:40px;height:40px;border-radius:10px;background:linear-gradient(135deg,var(--green),var(--blue));display:flex;align-items:center;justify-content:center;font-size:20px}
.sidebar-brand{flex:1}.sidebar-title{font-size:14px;font-weight:800;color:var(--text)}.sidebar-ver{font-size:10px;color:var(--green);font-weight:700}
.sidebar-nav{flex:1;padding:12px}
.nav-section{font-size:10px;color:var(--text3);font-weight:700;text-transform:uppercase;letter-spacing:1px;padding:10px 12px 4px}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:var(--radius-sm);cursor:pointer;color:var(--text2);font-size:13px;font-weight:600;margin-bottom:2px;border:1px solid transparent;text-decoration:none;transition:all .2s}
.nav-item:hover{background:var(--green-bg);color:var(--green);border-color:rgba(63,185,80,.1)}
.nav-item.active{background:var(--green-bg);color:var(--green);border-color:rgba(63,185,80,.2)}
.nav-icon{font-size:15px;width:20px;text-align:center}
.main{flex:1;margin-right:220px;padding:20px 24px;min-width:0;max-width:1200px}
@media(max-width:900px){.main{margin-right:0;padding:16px}}
.main-header{margin-bottom:20px}
.main-title{font-size:20px;font-weight:900;display:flex;align-items:center;gap:8px}
.main-sub{color:var(--text2);font-size:13px;margin-top:4px}
.card{background:var(--surface);backdrop-filter:blur(var(--glass-blur));border:1px solid var(--border);border-radius:var(--radius);padding:24px;margin-bottom:16px}
.card-title{font-size:15px;font-weight:800;margin-bottom:16px;display:flex;align-items:center;gap:8px;padding-bottom:12px;border-bottom:1px solid var(--border)}
.log-container{background:rgba(0,0,0,.4);border-radius:var(--radius-sm);padding:12px;font-family:'Cascadia Code','Fira Code','Consolas',monospace;font-size:12px;line-height:1.7;max-height:600px;overflow-y:auto;direction:ltr;text-align:left}
.log-empty{color:var(--text3);text-align:center;padding:40px}
.log-entry{padding:2px 0;border-bottom:1px solid rgba(255,255,255,.03);white-space:pre-wrap;word-break:break-all}
.log-time{color:var(--text3);margin-right:6px}
.log-level{display:inline-block;width:48px;text-align:center;border-radius:3px;font-size:10px;font-weight:700;padding:1px 4px;margin:0 4px}
.log-level.LOG{color:var(--text2)}
.log-level.ERROR{color:var(--red2);background:var(--red-bg)}
.log-level.WARN{color:var(--amber);background:var(--amber-bg)}
.log-msg{color:var(--text)}
.controls{display:flex;gap:8px;margin-top:12px;flex-wrap:wrap}
.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:var(--radius-sm);font-size:12px;font-weight:700;cursor:pointer;border:1px solid rgba(255,255,255,.08);background:var(--glass);backdrop-filter:blur(10px);color:var(--text2);transition:all .25s;border:none}
.btn:hover{transform:translateY(-1px);box-shadow:0 4px 14px rgba(0,0,0,.25)}
.btn-green{background:linear-gradient(135deg,var(--green),#2ea043);color:#fff}
.btn-green:hover{box-shadow:0 4px 20px rgba(63,185,80,.35)}
.btn-secondary{background:var(--surface2);color:var(--text2);border:1px solid var(--border)}
.btn-secondary:hover{background:var(--surface3);color:var(--text)}
.auto-scroll{display:flex;align-items:center;gap:6px;color:var(--text2);font-size:12px;cursor:pointer}
.auto-scroll input{cursor:pointer}
</style>
</head>
<body>
<div class="sidebar">
<div class="sidebar-header">
<div class="sidebar-logo">🤖</div>
<div class="sidebar-brand">
<div class="sidebar-title">Sayed Bot</div>
<div class="sidebar-ver">v26 AI</div>
</div>
</div>
<nav class="sidebar-nav">
<div class="nav-section">لوحة التحكم</div>
<a class="nav-item" href="/dashboard"><span class="nav-icon">📊</span> الرئيسية</a>
<a class="nav-item" href="/setup"><span class="nav-icon">⚙️</span> إعداد السيرفر</a>
<a class="nav-item active" href="/logs"><span class="nav-icon">📋</span> السجل</a>
<a class="nav-item" href="/features"><span class="nav-icon">🔌</span> الميزات</a>
</nav>
</div>
<div class="main">
<div class="main-header">
<div>
<div class="main-title">📋 سجل الأحداث</div>
<div class="main-sub">جميع أحداث البوتات في الوقت الفعلي — يتم تحديثها تلقائياً</div>
</div>
</div>
<div class="card">
<div class="card-title">📝 السجل المباشر</div>
<div class="log-container" id="logContainer">
<div class="log-empty" id="logEmpty">⏳ جاري تحميل السجل...</div>
</div>
<div class="controls">
<button class="btn btn-secondary" onclick="clearLogs()">🗑️ مسح</button>
<button class="btn btn-secondary" onclick="loadLogs()">🔄 تحديث</button>
<label class="auto-scroll"><input type="checkbox" id="autoScroll" checked> تمرير تلقائي</label>
<span style="flex:1"></span>
<span id="logCount" style="color:var(--text3);font-size:12px">0</span>
</div>
</div>
</div>
<script>
let logBuffer = [];
let streamActive = false;
function escapeHtml(str) {
const d = document.createElement('div');
d.textContent = str;
return d.innerHTML;
}
function renderLogs(logs) {
const container = document.getElementById('logContainer');
const empty = document.getElementById('logEmpty');
if (empty) empty.remove();
for (const entry of logs) {
const time = entry.time.slice(11, 19);
const level = (entry.level || 'log').toUpperCase();
const msg = entry.message || '';
const div = document.createElement('div');
div.className = 'log-entry';
div.innerHTML = `<span class="log-time">[${time}]</span><span class="log-level ${level}">${level}</span><span class="log-msg">${escapeHtml(msg)}</span>`;
container.appendChild(div);
}
updateCount();
if (document.getElementById('autoScroll').checked) {
container.scrollTop = container.scrollHeight;
}
}
function updateCount() {
document.getElementById('logCount').textContent = document.getElementById('logContainer').children.length + ' سطر';
}
async function loadLogs() {
try {
const r = await fetch('/api/logs?count=200');
const data = await r.json();
if (data.logs) {
const container = document.getElementById('logContainer');
container.innerHTML = '';
logBuffer = data.logs;
renderLogs(data.logs);
}
} catch(_) {}
}
function connectStream() {
if (streamActive) return;
const evtSource = new EventSource('/api/logs/stream');
streamActive = true;
evtSource.onmessage = function(e) {
try {
const entry = JSON.parse(e.data);
if (entry.type === 'connected') return;
const container = document.getElementById('logContainer');
const empty = document.getElementById('logEmpty');
if (empty) empty.remove();
const time = entry.time.slice(11, 19);
const level = (entry.level || 'log').toUpperCase();
const msg = entry.message || '';
const div = document.createElement('div');
div.className = 'log-entry';
div.innerHTML = `<span class="log-time">[${time}]</span><span class="log-level ${level}">${level}</span><span class="log-msg">${escapeHtml(msg)}</span>`;
container.appendChild(div);
// Trim to last 500
while (container.children.length > 500) container.removeChild(container.firstChild);
updateCount();
if (document.getElementById('autoScroll').checked) {
container.scrollTop = container.scrollHeight;
}
} catch(_) {}
};
evtSource.onerror = function() {
streamActive = false;
evtSource.close();
setTimeout(connectStream, 3000);
};
}
function clearLogs() {
const container = document.getElementById('logContainer');
container.innerHTML = '';
updateCount();
}
loadLogs();
connectStream();
</script>
</body>
</html>