Really-amin's picture
Upload 143 files
5cd2b89 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel - Crypto API Monitor</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(-45deg, #667eea, #764ba2, #f093fb, #4facfe);
background-size: 400% 400%;
animation: gradientShift 15s ease infinite;
padding: 20px;
color: #1a1a1a;
min-height: 100vh;
}
@keyframes gradientShift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
.container {
max-width: 1200px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 24px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
h1 {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 36px;
margin-bottom: 10px;
}
.nav-tabs {
display: flex;
gap: 10px;
margin: 30px 0;
border-bottom: 3px solid #e9ecef;
padding-bottom: 0;
}
.tab {
padding: 12px 24px;
background: #f8f9fa;
border: none;
border-radius: 12px 12px 0 0;
cursor: pointer;
font-weight: 600;
transition: all 0.3s;
}
.tab.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.tab-content {
display: none;
animation: fadeIn 0.3s;
}
.tab-content.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.section {
background: #f8f9fa;
padding: 24px;
border-radius: 16px;
margin: 20px 0;
border: 2px solid #dee2e6;
}
.section h3 {
color: #667eea;
margin-bottom: 16px;
font-size: 20px;
}
.form-group {
margin: 16px 0;
}
label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #495057;
}
input, select, textarea {
width: 100%;
padding: 12px;
border: 2px solid #dee2e6;
border-radius: 8px;
font-family: inherit;
font-size: 14px;
transition: all 0.3s;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
}
.btn {
padding: 12px 24px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
margin: 5px;
transition: all 0.3s;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #6c757d;
}
.btn-danger {
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
}
.btn-success {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
}
.api-list {
list-style: none;
}
.api-item {
background: white;
padding: 16px;
margin: 12px 0;
border-radius: 12px;
border: 2px solid #dee2e6;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s;
}
.api-item:hover {
border-color: #667eea;
transform: translateX(5px);
}
.api-info {
flex: 1;
}
.api-name {
font-weight: 700;
font-size: 16px;
color: #667eea;
}
.api-url {
font-size: 12px;
color: #6c757d;
font-family: monospace;
margin: 4px 0;
}
.api-category {
display: inline-block;
padding: 4px 10px;
background: #e9ecef;
border-radius: 8px;
font-size: 11px;
font-weight: 600;
margin-top: 4px;
}
.status-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
}
.status-online { background: #10b981; box-shadow: 0 0 10px #10b981; }
.status-offline { background: #ef4444; box-shadow: 0 0 10px #ef4444; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.stat-box {
background: white;
padding: 20px;
border-radius: 12px;
border: 2px solid #dee2e6;
text-align: center;
}
.stat-value {
font-size: 32px;
font-weight: 700;
color: #667eea;
margin: 10px 0;
}
.stat-label {
font-size: 14px;
color: #6c757d;
font-weight: 600;
}
.alert {
padding: 16px;
border-radius: 12px;
margin: 16px 0;
border-left: 4px solid;
}
.alert-success {
background: #d1fae5;
border-color: #10b981;
color: #065f46;
}
.alert-error {
background: #fee2e2;
border-color: #ef4444;
color: #991b1b;
}
.alert-info {
background: #dbeafe;
border-color: #3b82f6;
color: #1e40af;
}
pre {
background: #1e293b;
color: #e2e8f0;
padding: 16px;
border-radius: 8px;
overflow-x: auto;
font-size: 12px;
}
</style>
</head>
<body>
<div class="container">
<h1>βš™οΈ Admin Panel</h1>
<p style="color: #6c757d; margin-bottom: 20px;">Configure and manage your crypto API monitoring system</p>
<div style="margin: 20px 0;">
<button class="btn" onclick="window.location.href='/'">🏠 Dashboard</button>
<button class="btn" onclick="window.location.href='/hf_console.html'">πŸ€— HF Console</button>
</div>
<div class="nav-tabs">
<button class="tab active" onclick="switchTab('apis')">πŸ“‘ API Sources</button>
<button class="tab" onclick="switchTab('settings')">βš™οΈ Settings</button>
<button class="tab" onclick="switchTab('stats')">πŸ“Š Statistics</button>
</div>
<!-- Tab 1: API Sources -->
<div class="tab-content active" id="tab-apis">
<div class="section">
<h3>βž• Add New API Source</h3>
<div class="form-group">
<label>API Name</label>
<input type="text" id="newApiName" placeholder="e.g., CoinGecko">
</div>
<div class="form-group">
<label>API URL</label>
<input type="text" id="newApiUrl" placeholder="https://api.example.com/endpoint">
</div>
<div class="form-group">
<label>Category</label>
<select id="newApiCategory">
<option value="market_data">Market Data</option>
<option value="blockchain_explorers">Blockchain Explorers</option>
<option value="news">News & Social</option>
<option value="sentiment">Sentiment</option>
<option value="defi">DeFi</option>
<option value="nft">NFT</option>
</select>
</div>
<div class="form-group">
<label>Test Field (optional - JSON field to verify)</label>
<input type="text" id="newApiTestField" placeholder="e.g., data or status">
</div>
<button class="btn btn-success" onclick="addNewAPI()">βž• Add API Source</button>
</div>
<div class="section">
<h3>πŸ“‹ Current API Sources</h3>
<div id="apisList">Loading...</div>
</div>
</div>
<!-- Tab 2: Settings -->
<div class="tab-content" id="tab-settings">
<div class="section">
<h3>πŸ”„ Refresh Settings</h3>
<div class="form-group">
<label>API Check Interval (seconds)</label>
<input type="number" id="checkInterval" value="30" min="10" max="300">
<small style="color: #6c757d;">How often to check API status (10-300 seconds)</small>
</div>
<div class="form-group">
<label>Dashboard Auto-Refresh (seconds)</label>
<input type="number" id="dashboardRefresh" value="30" min="5" max="300">
<small style="color: #6c757d;">How often dashboard updates (5-300 seconds)</small>
</div>
<button class="btn btn-success" onclick="saveSettings()">πŸ’Ύ Save Settings</button>
</div>
<div class="section">
<h3>πŸ€— HuggingFace Settings</h3>
<div class="form-group">
<label>HuggingFace Token (optional)</label>
<input type="password" id="hfToken" placeholder="hf_...">
<small style="color: #6c757d;">For higher rate limits</small>
</div>
<div class="form-group">
<label>Enable Sentiment Analysis</label>
<select id="enableSentiment">
<option value="true">Enabled</option>
<option value="false">Disabled</option>
</select>
</div>
<div class="form-group">
<label>Sentiment Model</label>
<select id="sentimentModel">
<option value="ElKulako/cryptobert">ElKulako/cryptobert</option>
<option value="kk08/CryptoBERT">kk08/CryptoBERT</option>
</select>
</div>
<button class="btn btn-success" onclick="saveHFSettings()">πŸ’Ύ Save HF Settings</button>
</div>
<div class="section">
<h3>πŸ”§ System Configuration</h3>
<div class="form-group">
<label>Request Timeout (seconds)</label>
<input type="number" id="requestTimeout" value="5" min="1" max="30">
</div>
<div class="form-group">
<label>Max Concurrent Requests</label>
<input type="number" id="maxConcurrent" value="10" min="1" max="50">
</div>
<button class="btn btn-success" onclick="saveSystemSettings()">πŸ’Ύ Save System Settings</button>
</div>
</div>
<!-- Tab 3: Statistics -->
<div class="tab-content" id="tab-stats">
<div class="grid">
<div class="stat-box">
<div class="stat-label">Total API Sources</div>
<div class="stat-value" id="statTotal">0</div>
</div>
<div class="stat-box">
<div class="stat-label">Currently Online</div>
<div class="stat-value" style="color: #10b981;" id="statOnline">0</div>
</div>
<div class="stat-box">
<div class="stat-label">Currently Offline</div>
<div class="stat-value" style="color: #ef4444;" id="statOffline">0</div>
</div>
</div>
<div class="section">
<h3>πŸ“Š System Information</h3>
<pre id="systemInfo">Loading...</pre>
</div>
<div class="section">
<h3>πŸ” Current Configuration</h3>
<pre id="currentConfig">Loading...</pre>
</div>
</div>
</div>
<script>
let currentAPIs = [];
let settings = {
checkInterval: 30,
dashboardRefresh: 30,
requestTimeout: 5,
maxConcurrent: 10,
hfToken: '',
enableSentiment: true,
sentimentModel: 'ElKulako/cryptobert'
};
function switchTab(tabName) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
event.target.classList.add('active');
document.getElementById('tab-' + tabName).classList.add('active');
if (tabName === 'stats') {
loadStats();
}
}
async function loadAPIs() {
try {
const res = await fetch('/api/providers');
const providers = await res.json();
currentAPIs = providers;
const list = document.getElementById('apisList');
list.innerHTML = '<ul class="api-list">' + providers.map(api => `
<li class="api-item">
<div class="api-info">
<div class="api-name">
<span class="status-indicator status-${api.status}"></span>
${api.name}
</div>
<div class="api-url">${api.category}</div>
<span class="api-category">${api.status.toUpperCase()}</span>
<span class="api-category">${api.response_time_ms}ms</span>
</div>
<div>
<button class="btn btn-secondary" onclick="testAPI('${api.name}')">πŸ§ͺ Test</button>
</div>
</li>
`).join('') + '</ul>';
} catch (error) {
document.getElementById('apisList').innerHTML =
'<div class="alert alert-error">Error loading APIs: ' + error.message + '</div>';
}
}
async function addNewAPI() {
const name = document.getElementById('newApiName').value;
const url = document.getElementById('newApiUrl').value;
const category = document.getElementById('newApiCategory').value;
const testField = document.getElementById('newApiTestField').value;
if (!name || !url) {
alert('Please fill in API name and URL');
return;
}
const newAPI = {
name: name,
url: url,
category: category,
test_field: testField || null
};
// Save to localStorage for now
let customAPIs = JSON.parse(localStorage.getItem('customAPIs') || '[]');
customAPIs.push(newAPI);
localStorage.setItem('customAPIs', JSON.stringify(customAPIs));
document.getElementById('newApiName').value = '';
document.getElementById('newApiUrl').value = '';
document.getElementById('newApiTestField').value = '';
alert('βœ… API added! Note: Restart server to activate. Custom APIs are saved in browser storage.');
loadAPIs();
}
async function testAPI(name) {
alert('Testing ' + name + '...\n\nThis will check if the API is responding.');
await loadAPIs();
}
function saveSettings() {
settings.checkInterval = parseInt(document.getElementById('checkInterval').value);
settings.dashboardRefresh = parseInt(document.getElementById('dashboardRefresh').value);
localStorage.setItem('monitorSettings', JSON.stringify(settings));
alert('βœ… Settings saved!\n\nNote: Some settings require server restart to take effect.');
}
function saveHFSettings() {
settings.hfToken = document.getElementById('hfToken').value;
settings.enableSentiment = document.getElementById('enableSentiment').value === 'true';
settings.sentimentModel = document.getElementById('sentimentModel').value;
localStorage.setItem('monitorSettings', JSON.stringify(settings));
alert('βœ… HuggingFace settings saved!\n\nRestart server to apply changes.');
}
function saveSystemSettings() {
settings.requestTimeout = parseInt(document.getElementById('requestTimeout').value);
settings.maxConcurrent = parseInt(document.getElementById('maxConcurrent').value);
localStorage.setItem('monitorSettings', JSON.stringify(settings));
alert('βœ… System settings saved!\n\nRestart server to apply changes.');
}
async function loadStats() {
try {
const res = await fetch('/api/status');
const data = await res.json();
document.getElementById('statTotal').textContent = data.total_providers;
document.getElementById('statOnline').textContent = data.online;
document.getElementById('statOffline').textContent = data.offline;
document.getElementById('systemInfo').textContent = JSON.stringify({
total_providers: data.total_providers,
online: data.online,
offline: data.offline,
degraded: data.degraded,
avg_response_time_ms: data.avg_response_time_ms,
system_health: data.system_health,
last_check: data.timestamp
}, null, 2);
document.getElementById('currentConfig').textContent = JSON.stringify(settings, null, 2);
} catch (error) {
document.getElementById('systemInfo').textContent = 'Error: ' + error.message;
}
}
// Load settings from localStorage
function loadSettings() {
const saved = localStorage.getItem('monitorSettings');
if (saved) {
settings = JSON.parse(saved);
document.getElementById('checkInterval').value = settings.checkInterval;
document.getElementById('dashboardRefresh').value = settings.dashboardRefresh;
document.getElementById('requestTimeout').value = settings.requestTimeout;
document.getElementById('maxConcurrent').value = settings.maxConcurrent;
document.getElementById('hfToken').value = settings.hfToken;
document.getElementById('enableSentiment').value = settings.enableSentiment.toString();
document.getElementById('sentimentModel').value = settings.sentimentModel;
}
}
// Initialize
loadAPIs();
loadSettings();
</script>
</body>
</html>