Really-amin's picture
Upload 299 files
76dcb98 verified
raw
history blame
77.8 kB
// Crypto Intelligence Hub - Main JavaScript
// Global state
const AppState = {
currentTab: 'dashboard',
data: {},
charts: {}
};
// Initialize app
document.addEventListener('DOMContentLoaded', () => {
initTabs();
checkAPIStatus();
loadDashboard();
// Auto-refresh every 30 seconds
setInterval(() => {
if (AppState.currentTab === 'dashboard') {
loadDashboard();
}
}, 30000);
});
// Tab Navigation
function initTabs() {
const tabButtons = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
tabButtons.forEach(btn => {
btn.addEventListener('click', () => {
const tabId = btn.dataset.tab;
// Update buttons
tabButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
// Update content
tabContents.forEach(c => c.classList.remove('active'));
document.getElementById(`tab-${tabId}`).classList.add('active');
AppState.currentTab = tabId;
// Load tab data
loadTabData(tabId);
});
});
}
// Load tab-specific data - synchronized with HTML tabs
function loadTabData(tabId) {
switch(tabId) {
case 'dashboard':
case 'market':
loadMarketData();
break;
case 'monitor':
loadMonitorData();
break;
case 'advanced':
loadAdvancedData();
break;
case 'admin':
loadAdminData();
break;
case 'hf':
loadHFHealth();
loadModels();
break;
case 'pools':
loadPools();
break;
case 'logs':
loadLogs();
break;
case 'resources':
loadResources();
loadAPIRegistry();
break;
case 'reports':
loadReports();
break;
// Legacy tab names for backward compatibility
case 'models':
loadModels();
break;
case 'sentiment':
loadSentimentModels();
loadSentimentHistory();
break;
case 'news':
loadNews();
break;
case 'providers':
loadProviders();
break;
case 'diagnostics':
loadDiagnostics();
break;
case 'api-explorer':
loadAPIEndpoints();
break;
}
}
// Load available API endpoints
function loadAPIEndpoints() {
const endpointSelect = document.getElementById('api-endpoint');
if (!endpointSelect) return;
// Add more endpoints
const endpoints = [
{ value: '/api/health', text: 'GET /api/health - Health Check' },
{ value: '/api/status', text: 'GET /api/status - System Status' },
{ value: '/api/stats', text: 'GET /api/stats - Statistics' },
{ value: '/api/market', text: 'GET /api/market - Market Data' },
{ value: '/api/trending', text: 'GET /api/trending - Trending Coins' },
{ value: '/api/sentiment', text: 'GET /api/sentiment - Fear & Greed Index' },
{ value: '/api/news', text: 'GET /api/news - Latest News' },
{ value: '/api/news/latest', text: 'GET /api/news/latest - Latest News (Alt)' },
{ value: '/api/resources', text: 'GET /api/resources - Resources Summary' },
{ value: '/api/providers', text: 'GET /api/providers - List Providers' },
{ value: '/api/models/list', text: 'GET /api/models/list - List Models' },
{ value: '/api/models/status', text: 'GET /api/models/status - Models Status' },
{ value: '/api/models/data/stats', text: 'GET /api/models/data/stats - Models Statistics' },
{ value: '/api/logs/recent', text: 'GET /api/logs/recent - Recent Logs' },
{ value: '/api/logs/errors', text: 'GET /api/logs/errors - Error Logs' },
{ value: '/api/diagnostics/last', text: 'GET /api/diagnostics/last - Last Diagnostics' },
{ value: '/api/hf/models', text: 'GET /api/hf/models - HF Models' },
{ value: '/api/hf/health', text: 'GET /api/hf/health - HF Health' }
];
// Clear existing options except first one
endpointSelect.innerHTML = '<option value="">انتخاب Endpoint...</option>';
endpoints.forEach(ep => {
const option = document.createElement('option');
option.value = ep.value;
option.textContent = ep.text;
endpointSelect.appendChild(option);
});
}
// Check API Status
async function checkAPIStatus() {
try {
const response = await fetch('/health');
const data = await response.json();
const statusBadge = document.getElementById('api-status');
if (data.status === 'healthy') {
statusBadge.className = 'status-badge';
statusBadge.innerHTML = '<span class="status-dot"></span><span>✅ سیستم فعال</span>';
} else {
statusBadge.className = 'status-badge error';
statusBadge.innerHTML = '<span class="status-dot"></span><span>❌ خطا</span>';
}
} catch (error) {
const statusBadge = document.getElementById('api-status');
statusBadge.className = 'status-badge error';
statusBadge.innerHTML = '<span class="status-dot"></span><span>❌ اتصال برقرار نشد</span>';
}
}
// Load Dashboard
async function loadDashboard() {
try {
// Load resources
const resourcesRes = await fetch('/api/resources');
const resourcesData = await resourcesRes.json();
if (resourcesData.success && resourcesData.summary) {
document.getElementById('stat-total-resources').textContent = resourcesData.summary.total_resources || 0;
document.getElementById('stat-free-resources').textContent = resourcesData.summary.free_resources || 0;
document.getElementById('stat-models').textContent = resourcesData.summary.models_available || 0;
}
// Load system status
try {
const statusRes = await fetch('/api/status');
const statusData = await statusRes.json();
document.getElementById('stat-providers').textContent = statusData.total_apis || statusData.total_providers || 0;
// Display system status
const systemStatusDiv = document.getElementById('system-status');
const healthStatus = statusData.system_health || 'unknown';
const healthClass = healthStatus === 'healthy' ? 'alert-success' :
healthStatus === 'degraded' ? 'alert-warning' : 'alert-error';
systemStatusDiv.innerHTML = `
<div class="alert ${healthClass}">
<strong>وضعیت سیستم:</strong> ${healthStatus}<br>
<strong>APIهای آنلاین:</strong> ${statusData.online || 0}<br>
<strong>APIهای تخریب شده:</strong> ${statusData.degraded || 0}<br>
<strong>APIهای آفلاین:</strong> ${statusData.offline || 0}<br>
<strong>میانگین زمان پاسخ:</strong> ${statusData.avg_response_time_ms || 0}ms<br>
<strong>آخرین به‌روزرسانی:</strong> ${new Date(statusData.last_update || Date.now()).toLocaleString('fa-IR')}
</div>
`;
} catch (statusError) {
console.warn('Status endpoint not available:', statusError);
document.getElementById('stat-providers').textContent = '-';
}
// Load categories chart
if (resourcesData.success && resourcesData.summary.categories) {
createCategoriesChart(resourcesData.summary.categories);
}
} catch (error) {
console.error('Error loading dashboard:', error);
showError('خطا در بارگذاری داشبورد');
}
}
// Create Categories Chart
function createCategoriesChart(categories) {
const ctx = document.getElementById('categories-chart');
if (!ctx) return;
if (AppState.charts.categories) {
AppState.charts.categories.destroy();
}
AppState.charts.categories = new Chart(ctx, {
type: 'bar',
data: {
labels: Object.keys(categories),
datasets: [{
label: 'تعداد منابع',
data: Object.values(categories),
backgroundColor: 'rgba(102, 126, 234, 0.6)',
borderColor: 'rgba(102, 126, 234, 1)',
borderWidth: 2
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false }
},
scales: {
y: { beginAtZero: true }
}
}
});
}
// Load Market Data
async function loadMarketData() {
try {
const response = await fetch('/api/market');
const data = await response.json();
if (data.cryptocurrencies && data.cryptocurrencies.length > 0) {
const marketDiv = document.getElementById('market-data');
marketDiv.innerHTML = `
<div style="overflow-x: auto;">
<table>
<thead>
<tr>
<th>#</th>
<th>نام</th>
<th>قیمت (USD)</th>
<th>تغییر 24h</th>
<th>حجم 24h</th>
<th>مارکت کپ</th>
</tr>
</thead>
<tbody>
${data.cryptocurrencies.map(coin => `
<tr>
<td>${coin.rank || '-'}</td>
<td>
${coin.image ? `<img src="${coin.image}" style="width: 24px; height: 24px; margin-left: 8px; vertical-align: middle;" />` : ''}
<strong>${coin.symbol}</strong> ${coin.name}
</td>
<td>$${formatNumber(coin.price)}</td>
<td style="color: ${coin.change_24h >= 0 ? 'var(--success)' : 'var(--danger)'}; font-weight: 600;">
${coin.change_24h >= 0 ? '↑' : '↓'} ${Math.abs(coin.change_24h || 0).toFixed(2)}%
</td>
<td>$${formatNumber(coin.volume_24h)}</td>
<td>$${formatNumber(coin.market_cap)}</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
${data.total_market_cap ? `<div style="margin-top: 15px; padding: 15px; background: rgba(102, 126, 234, 0.1); border-radius: 10px;">
<strong>کل مارکت کپ:</strong> $${formatNumber(data.total_market_cap)} |
<strong>BTC Dominance:</strong> ${(data.btc_dominance || 0).toFixed(2)}%
</div>` : ''}
`;
} else {
document.getElementById('market-data').innerHTML = '<div class="alert alert-warning">داده‌ای یافت نشد</div>';
}
// Load trending
try {
const trendingRes = await fetch('/api/trending');
const trendingData = await trendingRes.json();
if (trendingData.trending && trendingData.trending.length > 0) {
const trendingDiv = document.getElementById('trending-coins');
trendingDiv.innerHTML = `
<div style="display: grid; gap: 10px;">
${trendingData.trending.map((coin, index) => `
<div style="padding: 15px; background: rgba(31, 41, 55, 0.6); border-radius: 10px; display: flex; justify-content: space-between; align-items: center; border-left: 4px solid var(--primary);">
<div style="display: flex; align-items: center; gap: 10px;">
<span style="font-size: 18px; font-weight: 800; color: var(--primary);">#${index + 1}</span>
<div>
<strong>${coin.symbol || coin.id}</strong> - ${coin.name || 'Unknown'}
${coin.market_cap_rank ? `<div style="font-size: 12px; color: var(--text-secondary);">رتبه مارکت کپ: ${coin.market_cap_rank}</div>` : ''}
</div>
</div>
<div style="font-size: 20px; font-weight: 700; color: var(--success);">${coin.score ? coin.score.toFixed(2) : 'N/A'}</div>
</div>
`).join('')}
</div>
`;
} else {
document.getElementById('trending-coins').innerHTML = '<div class="alert alert-warning">داده‌ای یافت نشد</div>';
}
} catch (trendingError) {
console.warn('Trending endpoint error:', trendingError);
document.getElementById('trending-coins').innerHTML = '<div class="alert alert-error">خطا در بارگذاری ارزهای ترند</div>';
}
// Load Fear & Greed
try {
const sentimentRes = await fetch('/api/sentiment');
const sentimentData = await sentimentRes.json();
if (sentimentData.fear_greed_index !== undefined) {
const fgDiv = document.getElementById('fear-greed');
const fgValue = sentimentData.fear_greed_index;
const fgLabel = sentimentData.fear_greed_label || 'Unknown';
// Determine color based on value
let fgColor = 'var(--warning)';
if (fgValue >= 75) fgColor = 'var(--success)';
else if (fgValue >= 50) fgColor = 'var(--info)';
else if (fgValue >= 25) fgColor = 'var(--warning)';
else fgColor = 'var(--danger)';
fgDiv.innerHTML = `
<div style="text-align: center; padding: 30px;">
<div style="font-size: 72px; font-weight: 800; margin-bottom: 10px; color: ${fgColor};">
${fgValue}
</div>
<div style="font-size: 24px; font-weight: 600; color: var(--text-primary); margin-bottom: 10px;">
${fgLabel}
</div>
<div style="font-size: 14px; color: var(--text-secondary);">
شاخص ترس و طمع بازار
</div>
${sentimentData.timestamp ? `<div style="font-size: 12px; color: var(--text-secondary); margin-top: 10px;">
آخرین به‌روزرسانی: ${new Date(sentimentData.timestamp).toLocaleString('fa-IR')}
</div>` : ''}
</div>
`;
} else {
document.getElementById('fear-greed').innerHTML = '<div class="alert alert-warning">داده‌ای یافت نشد</div>';
}
} catch (sentimentError) {
console.warn('Sentiment endpoint error:', sentimentError);
document.getElementById('fear-greed').innerHTML = '<div class="alert alert-error">خطا در بارگذاری شاخص ترس و طمع</div>';
}
} catch (error) {
console.error('Error loading market data:', error);
showError('خطا در بارگذاری داده‌های بازار');
}
}
// Format large numbers
function formatNumber(num) {
if (!num) return '0';
if (num >= 1e12) return (num / 1e12).toFixed(2) + 'T';
if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B';
if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
return num.toLocaleString('en-US', { maximumFractionDigits: 2 });
}
// Load Models
async function loadModels() {
try {
const response = await fetch('/api/models/list');
const data = await response.json();
const models = data.models || data || [];
if (models.length > 0) {
const modelsListDiv = document.getElementById('models-list');
modelsListDiv.innerHTML = `
<div style="display: grid; gap: 15px;">
${models.map(model => {
const status = model.status || 'unknown';
const isAvailable = status === 'available' || status === 'loaded';
const statusColor = isAvailable ? 'var(--success)' : 'var(--danger)';
const statusBg = isAvailable ? 'rgba(16, 185, 129, 0.2)' : 'rgba(239, 68, 68, 0.2)';
return `
<div style="padding: 20px; background: rgba(31, 41, 55, 0.6); border-radius: 12px; border-left: 4px solid ${statusColor};">
<div style="display: flex; justify-content: space-between; align-items: start; flex-wrap: wrap; gap: 10px;">
<div style="flex: 1;">
<h4 style="margin-bottom: 5px; color: var(--text-primary);">${model.model_id || model.name || 'Unknown'}</h4>
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 5px;">
${model.task || model.category || 'N/A'}
</div>
${model.category ? `<div style="font-size: 11px; color: var(--text-secondary);">دسته: ${model.category}</div>` : ''}
${model.requires_auth !== undefined ? `<div style="font-size: 11px; color: var(--text-secondary);">
${model.requires_auth ? '🔐 نیاز به احراز هویت' : '🔓 بدون احراز هویت'}
</div>` : ''}
</div>
<span style="background: ${statusBg}; color: ${statusColor}; padding: 5px 10px; border-radius: 5px; font-size: 12px; font-weight: 600;">
${isAvailable ? '✅ در دسترس' : '❌ غیرفعال'}
</span>
</div>
${model.key ? `<div style="margin-top: 10px; font-size: 11px; color: var(--text-secondary); font-family: monospace;">
Key: ${model.key}
</div>` : ''}
</div>
`;
}).join('')}
</div>
`;
} else {
document.getElementById('models-list').innerHTML = '<div class="alert alert-warning">هیچ مدلی یافت نشد</div>';
}
// Load models status
try {
const statusRes = await fetch('/api/models/status');
const statusData = await statusRes.json();
const statusDiv = document.getElementById('models-status');
const status = statusData.status || statusData.ok ? 'فعال' : 'غیرفعال';
const statusClass = statusData.status === 'ok' || statusData.ok ? 'alert-success' : 'alert-warning';
statusDiv.innerHTML = `
<div class="alert ${statusClass}">
<strong>وضعیت:</strong> ${status}<br>
<strong>مدل‌های بارگذاری شده:</strong> ${statusData.models_loaded || statusData.pipelines_loaded || 0}<br>
<strong>مدل‌های در دسترس:</strong> ${statusData.models_available || statusData.available_models?.length || 0}<br>
${statusData.mode ? `<strong>حالت:</strong> ${statusData.mode}<br>` : ''}
${statusData.transformers_available !== undefined ? `<strong>Transformers:</strong> ${statusData.transformers_available ? '✅' : '❌'}<br>` : ''}
</div>
`;
} catch (statusError) {
console.warn('Models status endpoint error:', statusError);
}
// Load models stats
try {
const statsRes = await fetch('/api/models/data/stats');
const statsData = await statsRes.json();
if (statsData.success && statsData.statistics) {
const statsDiv = document.getElementById('models-stats');
statsDiv.innerHTML = `
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
<div style="padding: 15px; background: rgba(102, 126, 234, 0.1); border-radius: 10px;">
<div style="font-size: 28px; font-weight: 800; color: var(--primary);">${statsData.statistics.total_analyses || 0}</div>
<div style="font-size: 14px; color: var(--text-secondary);">کل تحلیل‌ها</div>
</div>
<div style="padding: 15px; background: rgba(16, 185, 129, 0.1); border-radius: 10px;">
<div style="font-size: 28px; font-weight: 800; color: var(--success);">${statsData.statistics.unique_symbols || 0}</div>
<div style="font-size: 14px; color: var(--text-secondary);">نمادهای منحصر به فرد</div>
</div>
${statsData.statistics.most_used_model ? `
<div style="padding: 15px; background: rgba(245, 158, 11, 0.1); border-radius: 10px;">
<div style="font-size: 18px; font-weight: 800; color: var(--warning);">${statsData.statistics.most_used_model}</div>
<div style="font-size: 14px; color: var(--text-secondary);">پراستفاده‌ترین مدل</div>
</div>
` : ''}
</div>
`;
}
} catch (statsError) {
console.warn('Models stats endpoint error:', statsError);
}
} catch (error) {
console.error('Error loading models:', error);
showError('خطا در بارگذاری مدل‌ها');
}
}
// Initialize Models
async function initializeModels() {
try {
const response = await fetch('/api/models/initialize', { method: 'POST' });
const data = await response.json();
if (data.success) {
showSuccess('مدل‌ها با موفقیت بارگذاری شدند');
loadModels();
} else {
showError(data.error || 'خطا در بارگذاری مدل‌ها');
}
} catch (error) {
showError('خطا در بارگذاری مدل‌ها: ' + error.message);
}
}
// Load Sentiment Models
async function loadSentimentModels() {
try {
const response = await fetch('/api/models/list');
const data = await response.json();
const models = data.models || data || [];
const select = document.getElementById('sentiment-model');
select.innerHTML = '<option value="">انتخاب مدل...</option>';
models.filter(m => {
const status = m.status || 'unknown';
return status === 'available' || status === 'loaded' || !m.status;
}).forEach(model => {
const option = document.createElement('option');
const modelId = model.model_id || model.name || model.key || 'unknown';
const task = model.task || model.category || '';
option.value = model.key || modelId;
option.textContent = `${modelId}${task ? ` (${task})` : ''}`;
select.appendChild(option);
});
// If no models available, show message
if (select.options.length === 1) {
const option = document.createElement('option');
option.value = '';
option.textContent = 'هیچ مدلی در دسترس نیست';
option.disabled = true;
select.appendChild(option);
}
} catch (error) {
console.error('Error loading sentiment models:', error);
const select = document.getElementById('sentiment-model');
select.innerHTML = '<option value="">خطا در بارگذاری مدل‌ها</option>';
}
}
// Analyze Global Market Sentiment
async function analyzeGlobalSentiment() {
const resultDiv = document.getElementById('global-sentiment-result');
resultDiv.innerHTML = '<div class="loading"><div class="spinner"></div> در حال تحلیل احساسات بازار...</div>';
try {
// Use market text analysis with sample market-related text
const marketText = "Cryptocurrency market analysis: Bitcoin, Ethereum, and major altcoins showing mixed signals. Market sentiment analysis required.";
const response = await fetch('/api/sentiment/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: marketText, mode: 'crypto' })
});
const data = await response.json();
if (!data.available) {
resultDiv.innerHTML = `
<div class="alert alert-warning">
<strong>⚠️ مدل‌ها در دسترس نیستند:</strong> ${data.error || 'مدل‌های AI در حال حاضر در دسترس نیستند'}
</div>
`;
return;
}
const sentiment = data.sentiment || 'neutral';
const confidence = data.confidence || 0;
const sentimentEmoji = sentiment === 'bullish' ? '📈' : sentiment === 'bearish' ? '📉' : '➡️';
const sentimentColor = sentiment === 'bullish' ? 'var(--success)' : sentiment === 'bearish' ? 'var(--danger)' : 'var(--text-secondary)';
resultDiv.innerHTML = `
<div class="alert alert-success" style="border-left: 4px solid ${sentimentColor};">
<h4 style="margin-bottom: 15px;">احساسات کلی بازار</h4>
<div style="display: grid; gap: 10px;">
<div style="text-align: center; padding: 20px;">
<div style="font-size: 48px; margin-bottom: 10px;">${sentimentEmoji}</div>
<div style="font-size: 24px; font-weight: 700; color: ${sentimentColor}; margin-bottom: 5px;">
${sentiment === 'bullish' ? 'صعودی' : sentiment === 'bearish' ? 'نزولی' : 'خنثی'}
</div>
<div style="color: var(--text-secondary);">
اعتماد: ${(confidence * 100).toFixed(1)}%
</div>
</div>
<div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--border);">
<strong>جزئیات:</strong>
<div style="margin-top: 5px; font-size: 13px; color: var(--text-secondary);">
این تحلیل بر اساس مدل‌های AI انجام شده است.
</div>
</div>
</div>
</div>
`;
} catch (error) {
console.error('Global sentiment analysis error:', error);
resultDiv.innerHTML = `<div class="alert alert-error">خطا در تحلیل: ${error.message}</div>`;
showError('خطا در تحلیل احساسات بازار');
}
}
// Analyze Asset Sentiment
async function analyzeAssetSentiment() {
const symbol = document.getElementById('asset-symbol').value.trim().toUpperCase();
const text = document.getElementById('asset-sentiment-text').value.trim();
if (!symbol) {
showError('لطفاً نماد ارز را وارد کنید');
return;
}
const resultDiv = document.getElementById('asset-sentiment-result');
resultDiv.innerHTML = '<div class="loading"><div class="spinner"></div> در حال تحلیل...</div>';
try {
// Use provided text or default text with symbol
const analysisText = text || `${symbol} market analysis and sentiment`;
const response = await fetch('/api/sentiment/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: analysisText, mode: 'crypto', symbol: symbol })
});
const data = await response.json();
if (!data.available) {
resultDiv.innerHTML = `
<div class="alert alert-warning">
<strong>⚠️ مدل‌ها در دسترس نیستند:</strong> ${data.error || 'مدل‌های AI در حال حاضر در دسترس نیستند'}
</div>
`;
return;
}
const sentiment = data.sentiment || 'neutral';
const confidence = data.confidence || 0;
const sentimentEmoji = sentiment === 'bullish' ? '📈' : sentiment === 'bearish' ? '📉' : '➡️';
const sentimentColor = sentiment === 'bullish' ? 'var(--success)' : sentiment === 'bearish' ? 'var(--danger)' : 'var(--text-secondary)';
resultDiv.innerHTML = `
<div class="alert alert-success" style="border-left: 4px solid ${sentimentColor};">
<h4 style="margin-bottom: 15px;">نتیجه تحلیل احساسات ${symbol}</h4>
<div style="display: grid; gap: 10px;">
<div>
<strong>احساسات:</strong>
<span style="color: ${sentimentColor}; font-weight: 700; font-size: 18px;">
${sentimentEmoji} ${sentiment === 'bullish' ? 'صعودی' : sentiment === 'bearish' ? 'نزولی' : 'خنثی'}
</span>
</div>
<div>
<strong>اعتماد:</strong>
<span style="color: var(--primary); font-weight: 600;">
${(confidence * 100).toFixed(2)}%
</span>
</div>
${text ? `
<div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--border);">
<strong>متن تحلیل شده:</strong>
<div style="margin-top: 5px; padding: 10px; background: rgba(31, 41, 55, 0.6); border-radius: 5px; font-size: 13px; color: var(--text-secondary);">
"${text.substring(0, 200)}${text.length > 200 ? '...' : ''}"
</div>
</div>
` : ''}
</div>
</div>
`;
} catch (error) {
console.error('Asset sentiment analysis error:', error);
resultDiv.innerHTML = `<div class="alert alert-error">خطا در تحلیل: ${error.message}</div>`;
showError('خطا در تحلیل احساسات ارز');
}
}
// Analyze News Sentiment
async function analyzeNewsSentiment() {
const title = document.getElementById('news-title').value.trim();
const content = document.getElementById('news-content').value.trim();
if (!title && !content) {
showError('لطفاً عنوان یا محتوای خبر را وارد کنید');
return;
}
const resultDiv = document.getElementById('news-sentiment-result');
resultDiv.innerHTML = '<div class="loading"><div class="spinner"></div> در حال تحلیل...</div>';
try {
const response = await fetch('/api/news/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: title, content: content, description: content })
});
const data = await response.json();
if (!data.available) {
resultDiv.innerHTML = `
<div class="alert alert-warning">
<strong>⚠️ مدل‌ها در دسترس نیستند:</strong> ${data.news?.error || data.error || 'مدل‌های AI در حال حاضر در دسترس نیستند'}
</div>
`;
return;
}
const newsData = data.news || {};
const sentiment = newsData.sentiment || 'neutral';
const confidence = newsData.confidence || 0;
const sentimentEmoji = sentiment === 'bullish' || sentiment === 'positive' ? '📈' :
sentiment === 'bearish' || sentiment === 'negative' ? '📉' : '➡️';
const sentimentColor = sentiment === 'bullish' || sentiment === 'positive' ? 'var(--success)' :
sentiment === 'bearish' || sentiment === 'negative' ? 'var(--danger)' : 'var(--text-secondary)';
resultDiv.innerHTML = `
<div class="alert alert-success" style="border-left: 4px solid ${sentimentColor};">
<h4 style="margin-bottom: 15px;">نتیجه تحلیل خبر</h4>
<div style="display: grid; gap: 10px;">
<div>
<strong>عنوان:</strong>
<span style="color: var(--text-primary);">${title || 'بدون عنوان'}</span>
</div>
<div>
<strong>احساسات:</strong>
<span style="color: ${sentimentColor}; font-weight: 700; font-size: 18px;">
${sentimentEmoji} ${sentiment === 'bullish' || sentiment === 'positive' ? 'مثبت' :
sentiment === 'bearish' || sentiment === 'negative' ? 'منفی' : 'خنثی'}
</span>
</div>
<div>
<strong>اعتماد:</strong>
<span style="color: var(--primary); font-weight: 600;">
${(confidence * 100).toFixed(2)}%
</span>
</div>
</div>
</div>
`;
} catch (error) {
console.error('News sentiment analysis error:', error);
resultDiv.innerHTML = `<div class="alert alert-error">خطا در تحلیل: ${error.message}</div>`;
showError('خطا در تحلیل خبر');
}
}
// Analyze Sentiment (updated)
async function analyzeSentiment() {
const text = document.getElementById('sentiment-text').value;
const mode = document.getElementById('sentiment-mode').value;
const modelKey = document.getElementById('sentiment-model').value;
if (!text.trim()) {
showError('لطفاً متنی وارد کنید');
return;
}
const resultDiv = document.getElementById('sentiment-result');
resultDiv.innerHTML = '<div class="loading"><div class="spinner"></div> در حال تحلیل...</div>';
try {
let response;
// Use the sentiment/analyze endpoint with mode
response = await fetch('/api/sentiment/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: text, mode: mode })
});
const data = await response.json();
if (!data.available) {
resultDiv.innerHTML = `
<div class="alert alert-warning">
<strong>⚠️ مدل‌ها در دسترس نیستند:</strong> ${data.error || 'مدل‌های AI در حال حاضر در دسترس نیستند'}
</div>
`;
return;
}
const label = data.sentiment || 'neutral';
const confidence = data.confidence || 0;
const result = data.result || {};
// Determine sentiment emoji and color
const sentimentEmoji = label === 'bullish' || label === 'positive' ? '📈' :
label === 'bearish' || label === 'negative' ? '📉' : '➡️';
const sentimentColor = label === 'bullish' || label === 'positive' ? 'var(--success)' :
label === 'bearish' || label === 'negative' ? 'var(--danger)' : 'var(--text-secondary)';
resultDiv.innerHTML = `
<div class="alert alert-success" style="margin-top: 20px; border-left: 4px solid ${sentimentColor};">
<h4 style="margin-bottom: 15px;">نتیجه تحلیل احساسات</h4>
<div style="display: grid; gap: 10px;">
<div>
<strong>احساسات:</strong>
<span style="color: ${sentimentColor}; font-weight: 700; font-size: 18px;">
${sentimentEmoji} ${label === 'bullish' || label === 'positive' ? 'صعودی/مثبت' :
label === 'bearish' || label === 'negative' ? 'نزولی/منفی' : 'خنثی'}
</span>
</div>
<div>
<strong>اعتماد:</strong>
<span style="color: var(--primary); font-weight: 600;">
${(confidence * 100).toFixed(2)}%
</span>
</div>
<div>
<strong>نوع تحلیل:</strong>
<span style="color: var(--text-secondary);">${mode}</span>
</div>
<div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--border);">
<strong>متن تحلیل شده:</strong>
<div style="margin-top: 5px; padding: 10px; background: rgba(31, 41, 55, 0.6); border-radius: 5px; font-size: 13px; color: var(--text-secondary);">
"${text.substring(0, 200)}${text.length > 200 ? '...' : ''}"
</div>
</div>
</div>
</div>
`;
// Save to history (localStorage)
saveSentimentToHistory({
text: text.substring(0, 100),
label: label,
confidence: confidence,
model: mode,
timestamp: new Date().toISOString()
});
// Reload history
loadSentimentHistory();
} catch (error) {
console.error('Sentiment analysis error:', error);
resultDiv.innerHTML = `<div class="alert alert-error">خطا در تحلیل: ${error.message}</div>`;
showError('خطا در تحلیل احساسات');
}
}
// Save sentiment to history
function saveSentimentToHistory(analysis) {
try {
const history = JSON.parse(localStorage.getItem('sentiment_history') || '[]');
history.unshift(analysis);
// Keep only last 50
if (history.length > 50) history = history.slice(0, 50);
localStorage.setItem('sentiment_history', JSON.stringify(history));
} catch (e) {
console.warn('Could not save to history:', e);
}
}
// Load sentiment history
function loadSentimentHistory() {
try {
const history = JSON.parse(localStorage.getItem('sentiment_history') || '[]');
const historyDiv = document.getElementById('sentiment-history');
if (history.length === 0) {
historyDiv.innerHTML = '<div class="alert alert-warning">هیچ تاریخچه‌ای وجود ندارد</div>';
return;
}
historyDiv.innerHTML = `
<div style="display: grid; gap: 10px; max-height: 400px; overflow-y: auto;">
${history.slice(0, 20).map(item => {
const sentimentEmoji = item.label.toUpperCase().includes('POSITIVE') || item.label.toUpperCase().includes('BULLISH') ? '📈' :
item.label.toUpperCase().includes('NEGATIVE') || item.label.toUpperCase().includes('BEARISH') ? '📉' : '➡️';
return `
<div style="padding: 12px; background: rgba(31, 41, 55, 0.6); border-radius: 8px; border-left: 3px solid var(--primary);">
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 5px;">
<span style="font-weight: 600;">${sentimentEmoji} ${item.label}</span>
<span style="font-size: 11px; color: var(--text-secondary);">${new Date(item.timestamp).toLocaleString('fa-IR')}</span>
</div>
<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 5px;">${item.text}</div>
<div style="font-size: 11px; color: var(--text-secondary);">
اعتماد: ${(item.confidence * 100).toFixed(0)}% | مدل: ${item.model}
</div>
</div>
`;
}).join('')}
</div>
`;
} catch (e) {
console.warn('Could not load history:', e);
}
}
// Load News
async function loadNews() {
try {
// Try /api/news/latest first, fallback to /api/news
let response;
try {
response = await fetch('/api/news/latest');
} catch {
response = await fetch('/api/news?limit=20');
}
const data = await response.json();
const newsItems = data.news || data.data || [];
if (newsItems.length > 0) {
const newsDiv = document.getElementById('news-list');
newsDiv.innerHTML = `
<div style="display: grid; gap: 15px;">
${newsItems.map(item => {
const sentiment = item.sentiment_label || item.sentiment;
const sentimentColor = sentiment === 'positive' || sentiment === 'POSITIVE' ? 'var(--success)' :
sentiment === 'negative' || sentiment === 'NEGATIVE' ? 'var(--danger)' :
'var(--text-secondary)';
const sentimentEmoji = sentiment === 'positive' || sentiment === 'POSITIVE' ? '📈' :
sentiment === 'negative' || sentiment === 'NEGATIVE' ? '📉' : '➡️';
return `
<div style="padding: 20px; background: rgba(31, 41, 55, 0.6); border-radius: 12px; border-left: 4px solid ${sentimentColor};">
<h4 style="margin-bottom: 10px; color: var(--text-primary);">${item.title || 'بدون عنوان'}</h4>
${item.content || item.description ? `<p style="color: var(--text-secondary); margin-bottom: 10px; line-height: 1.6;">${(item.content || item.description).substring(0, 200)}...</p>` : ''}
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">
<div style="display: flex; gap: 15px; align-items: center;">
<span style="font-size: 12px; color: var(--text-secondary);">
${item.source || 'Unknown'}
</span>
${sentiment ? `<span style="font-size: 12px; color: ${sentimentColor};">
${sentimentEmoji} ${sentiment}
${item.sentiment_confidence ? ` (${(item.sentiment_confidence * 100).toFixed(0)}%)` : ''}
</span>` : ''}
${item.published_date || item.published_at ? `<span style="font-size: 12px; color: var(--text-secondary);">
${new Date(item.published_date || item.published_at).toLocaleDateString('fa-IR')}
</span>` : ''}
</div>
${item.url ? `<a href="${item.url}" target="_blank" rel="noopener noreferrer" style="color: var(--primary); text-decoration: none; font-weight: 600;">مطالعه بیشتر →</a>` : ''}
</div>
</div>
`;
}).join('')}
</div>
`;
} else {
document.getElementById('news-list').innerHTML = '<div class="alert alert-warning">هیچ خبری یافت نشد</div>';
}
} catch (error) {
console.error('Error loading news:', error);
showError('خطا در بارگذاری اخبار');
document.getElementById('news-list').innerHTML = '<div class="alert alert-error">خطا در بارگذاری اخبار</div>';
}
}
// Load Providers
async function loadProviders() {
try {
// Load providers and auto-discovery health summary in parallel
const [providersRes, healthRes] = await Promise.all([
fetch('/api/providers'),
fetch('/api/providers/health-summary').catch(() => null) // Optional
]);
const providersData = await providersRes.json();
const providers = providersData.providers || providersData || [];
// Update providers list
const providersDiv = document.getElementById('providers-list');
if (providersDiv) {
if (providers.length > 0) {
providersDiv.innerHTML = `
<div style="overflow-x: auto;">
<table>
<thead>
<tr>
<th>ID</th>
<th>نام</th>
<th>دسته</th>
<th>نوع</th>
<th>وضعیت</th>
<th>جزئیات</th>
</tr>
</thead>
<tbody>
${providers.map(provider => {
const status = provider.status || 'unknown';
const statusConfig = {
'VALID': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ معتبر' },
'validated': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ معتبر' },
'available': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ در دسترس' },
'online': { color: 'var(--success)', bg: 'rgba(16, 185, 129, 0.2)', text: '✅ آنلاین' },
'CONDITIONALLY_AVAILABLE': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ شرطی' },
'INVALID': { color: 'var(--danger)', bg: 'rgba(239, 68, 68, 0.2)', text: '❌ نامعتبر' },
'unvalidated': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ نامعتبر' },
'not_loaded': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ بارگذاری نشده' },
'offline': { color: 'var(--danger)', bg: 'rgba(239, 68, 68, 0.2)', text: '❌ آفلاین' },
'degraded': { color: 'var(--warning)', bg: 'rgba(245, 158, 11, 0.2)', text: '⚠️ تخریب شده' }
};
const statusInfo = statusConfig[status] || { color: 'var(--text-secondary)', bg: 'rgba(156, 163, 175, 0.2)', text: '❓ نامشخص' };
return `
<tr>
<td>${provider.provider_id || provider.id || '-'}</td>
<td><strong>${provider.name || 'Unknown'}</strong></td>
<td>${provider.category || '-'}</td>
<td>${provider.type || '-'}</td>
<td>
<span style="padding: 5px 10px; border-radius: 5px; background: ${statusInfo.bg}; color: ${statusInfo.color}; font-size: 12px;">
${statusInfo.text}
</span>
</td>
<td>
${provider.response_time_ms ? `<span style="font-size: 12px; color: var(--text-secondary);">${provider.response_time_ms}ms</span>` : ''}
${provider.endpoint ? `<a href="${provider.endpoint}" target="_blank" style="color: var(--primary); font-size: 12px;">🔗</a>` : ''}
${provider.error_reason ? `<span style="font-size: 11px; color: var(--danger);" title="${provider.error_reason}">⚠️</span>` : ''}
</td>
</tr>
`;
}).join('')}
</tbody>
</table>
</div>
<div style="margin-top: 15px; padding: 15px; background: rgba(102, 126, 234, 0.1); border-radius: 10px;">
<strong>کل Providerها:</strong> ${providersData.total || providers.length}
</div>
`;
} else {
providersDiv.innerHTML = '<div class="alert alert-warning">هیچ Providerی یافت نشد</div>';
}
}
// Update health summary if available
if (healthRes) {
try {
const healthData = await healthRes.json();
const healthSummaryDiv = document.getElementById('providers-health-summary');
if (healthSummaryDiv && healthData.ok && healthData.summary) {
const summary = healthData.summary;
healthSummaryDiv.innerHTML = `
<div class="card">
<h3>Provider Health Summary</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px;">
<div style="padding: 15px; background: rgba(16, 185, 129, 0.1); border-radius: 10px; border-left: 4px solid var(--success);">
<div style="font-size: 24px; font-weight: bold; color: var(--success);">${summary.total_active_providers || 0}</div>
<div style="font-size: 12px; color: var(--text-secondary);">Total Active</div>
</div>
<div style="padding: 15px; background: rgba(16, 185, 129, 0.1); border-radius: 10px; border-left: 4px solid var(--success);">
<div style="font-size: 24px; font-weight: bold; color: var(--success);">${summary.http_valid || 0}</div>
<div style="font-size: 12px; color: var(--text-secondary);">HTTP Valid</div>
</div>
<div style="padding: 15px; background: rgba(239, 68, 68, 0.1); border-radius: 10px; border-left: 4px solid var(--danger);">
<div style="font-size: 24px; font-weight: bold; color: var(--danger);">${summary.http_invalid || 0}</div>
<div style="font-size: 12px; color: var(--text-secondary);">HTTP Invalid</div>
</div>
<div style="padding: 15px; background: rgba(245, 158, 11, 0.1); border-radius: 10px; border-left: 4px solid var(--warning);">
<div style="font-size: 24px; font-weight: bold; color: var(--warning);">${summary.http_conditional || 0}</div>
<div style="font-size: 12px; color: var(--text-secondary);">Conditional</div>
</div>
</div>
</div>
`;
}
} catch (e) {
console.warn('Could not load health summary:', e);
}
}
} catch (error) {
console.error('Error loading providers:', error);
showError('خطا در بارگذاری Providerها');
const providersDiv = document.getElementById('providers-list');
if (providersDiv) {
providersDiv.innerHTML = '<div class="alert alert-error">خطا در بارگذاری Providerها</div>';
}
}
}
// Search Resources
async function searchResources() {
const query = document.getElementById('search-resources').value;
if (!query.trim()) {
showError('لطفاً عبارتی برای جستجو وارد کنید');
return;
}
const resultsDiv = document.getElementById('search-results');
resultsDiv.innerHTML = '<div class="loading"><div class="spinner"></div> در حال جستجو...</div>';
try {
const response = await fetch(`/api/resources/search?q=${encodeURIComponent(query)}`);
const data = await response.json();
if (data.success && data.resources && data.resources.length > 0) {
resultsDiv.innerHTML = `
<div style="margin-top: 15px;">
<div style="margin-bottom: 10px; color: var(--text-secondary);">
${data.count || data.resources.length} نتیجه یافت شد
</div>
<div style="display: grid; gap: 10px;">
${data.resources.map(resource => `
<div style="padding: 15px; background: rgba(31, 41, 55, 0.6); border-radius: 10px; border-left: 4px solid var(--primary);">
<div style="display: flex; justify-content: space-between; align-items: start; flex-wrap: wrap; gap: 10px;">
<div>
<strong style="font-size: 16px;">${resource.name || 'Unknown'}</strong>
<div style="font-size: 12px; color: var(--text-secondary); margin-top: 5px;">
دسته: ${resource.category || 'N/A'}
</div>
${resource.base_url ? `<div style="font-size: 11px; color: var(--text-secondary); margin-top: 3px; font-family: monospace;">
${resource.base_url}
</div>` : ''}
</div>
${resource.free !== undefined ? `
<span style="padding: 5px 10px; border-radius: 5px; background: ${resource.free ? 'rgba(16, 185, 129, 0.2)' : 'rgba(245, 158, 11, 0.2)'}; color: ${resource.free ? 'var(--success)' : 'var(--warning)'}; font-size: 12px;">
${resource.free ? '🆓 رایگان' : '💰 پولی'}
</span>
` : ''}
</div>
</div>
`).join('')}
</div>
</div>
`;
} else {
resultsDiv.innerHTML = '<div class="alert alert-warning" style="margin-top: 15px;">نتیجه‌ای یافت نشد</div>';
}
} catch (error) {
console.error('Search error:', error);
resultsDiv.innerHTML = '<div class="alert alert-error" style="margin-top: 15px;">خطا در جستجو</div>';
showError('خطا در جستجو');
}
}
// Load Diagnostics
async function loadDiagnostics() {
try {
// Load system status
try {
const statusRes = await fetch('/api/status');
const statusData = await statusRes.json();
const statusDiv = document.getElementById('diagnostics-status');
const health = statusData.system_health || 'unknown';
const healthClass = health === 'healthy' ? 'alert-success' :
health === 'degraded' ? 'alert-warning' : 'alert-error';
statusDiv.innerHTML = `
<div class="alert ${healthClass}">
<h4 style="margin-bottom: 10px;">وضعیت سیستم</h4>
<div style="display: grid; gap: 5px;">
<div><strong>وضعیت کلی:</strong> ${health}</div>
<div><strong>APIهای کل:</strong> ${statusData.total_apis || 0}</div>
<div><strong>آنلاین:</strong> ${statusData.online || 0}</div>
<div><strong>تخریب شده:</strong> ${statusData.degraded || 0}</div>
<div><strong>آفلاین:</strong> ${statusData.offline || 0}</div>
<div><strong>میانگین زمان پاسخ:</strong> ${statusData.avg_response_time_ms || 0}ms</div>
${statusData.last_update ? `<div><strong>آخرین به‌روزرسانی:</strong> ${new Date(statusData.last_update).toLocaleString('fa-IR')}</div>` : ''}
</div>
</div>
`;
} catch (statusError) {
document.getElementById('diagnostics-status').innerHTML = '<div class="alert alert-error">خطا در بارگذاری وضعیت سیستم</div>';
}
// Load error logs
try {
const errorsRes = await fetch('/api/logs/errors');
const errorsData = await errorsRes.json();
const errors = errorsData.errors || errorsData.error_logs || [];
const errorsDiv = document.getElementById('error-logs');
if (errors.length > 0) {
errorsDiv.innerHTML = `
<div style="display: grid; gap: 10px;">
${errors.slice(0, 10).map(error => `
<div style="padding: 15px; background: rgba(239, 68, 68, 0.1); border-left: 4px solid var(--danger); border-radius: 5px;">
<div style="font-weight: 600; color: var(--danger); margin-bottom: 5px;">
${error.message || error.error_message || error.type || 'خطا'}
</div>
${error.error_type ? `<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 3px;">نوع: ${error.error_type}</div>` : ''}
${error.provider ? `<div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 3px;">Provider: ${error.provider}</div>` : ''}
<div style="font-size: 11px; color: var(--text-secondary); margin-top: 5px;">
${error.timestamp ? new Date(error.timestamp).toLocaleString('fa-IR') : ''}
</div>
</div>
`).join('')}
</div>
${errors.length > 10 ? `<div style="margin-top: 10px; text-align: center; color: var(--text-secondary); font-size: 12px;">
نمایش ${Math.min(10, errors.length)} از ${errors.length} خطا
</div>` : ''}
`;
} else {
errorsDiv.innerHTML = '<div class="alert alert-success">هیچ خطایی یافت نشد ✅</div>';
}
} catch (errorsError) {
document.getElementById('error-logs').innerHTML = '<div class="alert alert-warning">خطا در بارگذاری گزارش خطاها</div>';
}
// Load recent logs
try {
const logsRes = await fetch('/api/logs/recent');
const logsData = await logsRes.json();
const logs = logsData.logs || logsData.recent || [];
const logsDiv = document.getElementById('recent-logs');
if (logs.length > 0) {
logsDiv.innerHTML = `
<div style="display: grid; gap: 10px; max-height: 400px; overflow-y: auto;">
${logs.slice(0, 20).map(log => {
const level = log.level || log.status || 'info';
const levelColor = level === 'ERROR' ? 'var(--danger)' :
level === 'WARNING' ? 'var(--warning)' :
'var(--text-secondary)';
return `
<div style="padding: 12px; background: rgba(31, 41, 55, 0.6); border-left: 3px solid ${levelColor}; border-radius: 5px;">
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 5px;">
<div style="font-size: 12px; font-weight: 600; color: ${levelColor};">
${level}
</div>
<div style="font-size: 11px; color: var(--text-secondary);">
${log.timestamp ? new Date(log.timestamp).toLocaleString('fa-IR') : ''}
</div>
</div>
<div style="font-size: 13px; color: var(--text-primary);">
${log.message || log.content || JSON.stringify(log)}
</div>
${log.provider ? `<div style="font-size: 11px; color: var(--text-secondary); margin-top: 3px;">Provider: ${log.provider}</div>` : ''}
</div>
`;
}).join('')}
</div>
`;
} else {
logsDiv.innerHTML = '<div class="alert alert-warning">هیچ لاگی یافت نشد</div>';
}
} catch (logsError) {
document.getElementById('recent-logs').innerHTML = '<div class="alert alert-warning">خطا در بارگذاری لاگ‌ها</div>';
}
} catch (error) {
console.error('Error loading diagnostics:', error);
showError('خطا در بارگذاری خطایابی');
}
}
// Run Diagnostics
async function runDiagnostics() {
try {
const response = await fetch('/api/diagnostics/run', { method: 'POST' });
const data = await response.json();
if (data.success) {
showSuccess('تشخیص با موفقیت اجرا شد');
setTimeout(loadDiagnostics, 1000);
} else {
showError(data.error || 'خطا در اجرای تشخیص');
}
} catch (error) {
showError('خطا در اجرای تشخیص: ' + error.message);
}
}
// Test API
async function testAPI() {
const endpoint = document.getElementById('api-endpoint').value;
const method = document.getElementById('api-method').value;
const bodyText = document.getElementById('api-body').value;
if (!endpoint) {
showError('لطفاً یک endpoint انتخاب کنید');
return;
}
const resultDiv = document.getElementById('api-result');
resultDiv.innerHTML = '<div class="loading"><div class="spinner"></div> در حال ارسال درخواست...</div>';
try {
const options = { method };
// Parse body if provided
let body = null;
if (method === 'POST' && bodyText) {
try {
body = JSON.parse(bodyText);
options.headers = { 'Content-Type': 'application/json' };
} catch (e) {
showError('JSON نامعتبر در body');
resultDiv.innerHTML = '<div class="alert alert-error">خطا در پارس JSON</div>';
return;
}
}
if (body) {
options.body = JSON.stringify(body);
}
const startTime = Date.now();
const response = await fetch(endpoint, options);
const responseTime = Date.now() - startTime;
let data;
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = { text: await response.text() };
}
const statusClass = response.ok ? 'alert-success' : 'alert-error';
const statusEmoji = response.ok ? '✅' : '❌';
resultDiv.innerHTML = `
<div style="margin-top: 20px;">
<div class="alert ${statusClass}" style="margin-bottom: 15px;">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;">
<div>
<strong>${statusEmoji} وضعیت:</strong> ${response.status} ${response.statusText}
</div>
<div style="font-size: 12px; color: var(--text-secondary);">
زمان پاسخ: ${responseTime}ms
</div>
</div>
</div>
<div style="padding: 15px; background: rgba(31, 41, 55, 0.6); border-radius: 10px;">
<h4 style="margin-bottom: 10px;">پاسخ:</h4>
<pre style="background: rgba(0, 0, 0, 0.3); padding: 15px; border-radius: 5px; overflow-x: auto; margin-top: 10px; font-size: 12px; max-height: 500px; overflow-y: auto;">${JSON.stringify(data, null, 2)}</pre>
</div>
<div style="margin-top: 10px; padding: 10px; background: rgba(102, 126, 234, 0.1); border-radius: 5px; font-size: 12px; color: var(--text-secondary);">
<strong>Endpoint:</strong> ${method} ${endpoint}
</div>
</div>
`;
} catch (error) {
resultDiv.innerHTML = `
<div class="alert alert-error" style="margin-top: 20px;">
<h4>خطا:</h4>
<p>${error.message}</p>
</div>
`;
showError('خطا در تست API: ' + error.message);
}
}
// Utility Functions
function showError(message) {
const alert = document.createElement('div');
alert.className = 'alert alert-error';
alert.textContent = message;
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
}
function showSuccess(message) {
const alert = document.createElement('div');
alert.className = 'alert alert-success';
alert.textContent = message;
document.body.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
}
// Additional tab loaders for HTML tabs
async function loadMonitorData() {
// Load API monitor data
try {
const response = await fetch('/api/status');
const data = await response.json();
const monitorContainer = document.getElementById('monitor-content');
if (monitorContainer) {
monitorContainer.innerHTML = `
<div class="card">
<h3>API Status</h3>
<pre>${JSON.stringify(data, null, 2)}</pre>
</div>
`;
}
} catch (error) {
console.error('Error loading monitor data:', error);
}
}
async function loadAdvancedData() {
// Load advanced/API explorer data
loadAPIEndpoints();
loadDiagnostics();
}
async function loadAdminData() {
// Load admin panel data
try {
const [providersRes, modelsRes] = await Promise.all([
fetch('/api/providers'),
fetch('/api/models/status')
]);
const providers = await providersRes.json();
const models = await modelsRes.json();
const adminContainer = document.getElementById('admin-content');
if (adminContainer) {
adminContainer.innerHTML = `
<div class="card">
<h3>System Status</h3>
<p>Providers: ${providers.total || 0}</p>
<p>Models: ${models.models_loaded || 0} loaded</p>
</div>
`;
}
} catch (error) {
console.error('Error loading admin data:', error);
}
}
async function loadHFHealth() {
// Load HF models health status
try {
const response = await fetch('/api/models/status');
const data = await response.json();
const hfContainer = document.getElementById('hf-status');
if (hfContainer) {
hfContainer.innerHTML = `
<div class="card">
<h3>HF Models Status</h3>
<p>Mode: ${data.hf_mode || 'unknown'}</p>
<p>Loaded: ${data.models_loaded || 0}</p>
<p>Failed: ${data.failed_count || 0}</p>
<p>Status: ${data.status || 'unknown'}</p>
</div>
`;
}
} catch (error) {
console.error('Error loading HF health:', error);
}
}
async function loadPools() {
// Load provider pools
try {
const response = await fetch('/api/pools');
const data = await response.json();
const poolsContainer = document.getElementById('pools-content');
if (poolsContainer) {
poolsContainer.innerHTML = `
<div class="card">
<h3>Provider Pools</h3>
<p>${data.message || 'No pools available'}</p>
<pre>${JSON.stringify(data, null, 2)}</pre>
</div>
`;
}
} catch (error) {
console.error('Error loading pools:', error);
}
}
async function loadLogs() {
// Load recent logs
try {
const response = await fetch('/api/logs/recent');
const data = await response.json();
const logsContainer = document.getElementById('logs-content');
if (logsContainer) {
const logsHtml = data.logs && data.logs.length > 0
? data.logs.map(log => `<div class="log-entry">${JSON.stringify(log)}</div>`).join('')
: '<p>No logs available</p>';
logsContainer.innerHTML = `<div class="card"><h3>Recent Logs</h3>${logsHtml}</div>`;
}
} catch (error) {
console.error('Error loading logs:', error);
}
}
async function loadReports() {
// Load reports/analytics
try {
const response = await fetch('/api/providers/health-summary');
const data = await response.json();
const reportsContainer = document.getElementById('reports-content');
if (reportsContainer) {
reportsContainer.innerHTML = `
<div class="card">
<h3>Provider Health Report</h3>
<pre>${JSON.stringify(data, null, 2)}</pre>
</div>
`;
}
} catch (error) {
console.error('Error loading reports:', error);
}
}
async function loadResources() {
// Load resources summary
try {
const response = await fetch('/api/resources');
const data = await response.json();
const resourcesContainer = document.getElementById('resources-summary');
if (resourcesContainer) {
const summary = data.summary || {};
resourcesContainer.innerHTML = `
<div class="card">
<h3>Resources Summary</h3>
<p>Total: ${summary.total_resources || 0}</p>
<p>Free: ${summary.free_resources || 0}</p>
<p>Models: ${summary.models_available || 0}</p>
</div>
`;
}
} catch (error) {
console.error('Error loading resources:', error);
}
}
async function loadAPIRegistry() {
// Load API registry from all_apis_merged_2025.json
try {
const response = await fetch('/api/resources/apis');
const data = await response.json();
if (!data.ok) {
console.warn('API registry not available:', data.error);
return;
}
const registryContainer = document.getElementById('api-registry-section');
if (registryContainer) {
const metadata = data.metadata || {};
const categories = data.categories || [];
registryContainer.innerHTML = `
<div class="card">
<h3>API Registry: ${metadata.name || 'Unknown'}</h3>
<p>Version: ${metadata.version || 'N/A'}</p>
<p>Description: ${metadata.description || 'N/A'}</p>
<h4>Categories:</h4>
<ul>
${categories.map(cat => `<li>${cat}</li>`).join('')}
</ul>
<p>Total Files: ${data.total_raw_files || 0}</p>
</div>
`;
}
// Also update metadata container if it exists
const metadataContainer = document.getElementById('api-registry-metadata');
if (metadataContainer) {
metadataContainer.innerHTML = `
<div class="card">
<h4>Metadata</h4>
<pre>${JSON.stringify(metadata, null, 2)}</pre>
</div>
`;
}
} catch (error) {
console.error('Error loading API registry:', error);
}
}