+ + +
+
+

+ + Market Overview +

+
+
- - `; - listElement.appendChild(item); - } - } - - async function exportJSON() { - try { - const response = await fetch('/api/v2/export/json', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ include_history: true }) - }); - const data = await response.json(); - showToast('✅ JSON export created!', 'success'); - addLog(`Exported to JSON: ${data.filepath}`); - } catch (error) { - showToast('❌ Export failed', 'error'); - console.error(error); - } - } - - async function exportCSV() { - try { - const response = await fetch('/api/v2/export/csv', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ flatten: true }) - }); - const data = await response.json(); - showToast('✅ CSV export created!', 'success'); - addLog(`Exported to CSV: ${data.filepath}`); - } catch (error) { - showToast('❌ Export failed', 'error'); - console.error(error); - } - } - - async function createBackup() { - try { - const response = await fetch('/api/v2/backup', { method: 'POST' }); - const data = await response.json(); - showToast('✅ Backup created!', 'success'); - addLog(`Backup created: ${data.backup_file}`); - } catch (error) { - showToast('❌ Backup failed', 'error'); - console.error(error); - } - } - - async function clearCache() { - if (!confirm('Clear all cached data?')) return; - try { - await fetch('/api/v2/cleanup/cache', { method: 'POST' }); - showToast('✅ Cache cleared!', 'success'); - addLog('Cache cleared'); - } catch (error) { - showToast('❌ Failed to clear cache', 'error'); - console.error(error); - } - } - - async function forceUpdateAll() { - try { - const response = await fetch('/api/v2/config/apis'); - const data = await response.json(); - for (const apiId of Object.keys(data.apis)) { - await forceUpdate(apiId); - await new Promise(resolve => setTimeout(resolve, 100)); - } - showToast('✅ All APIs updated!', 'success'); - } catch (error) { - showToast('❌ Update failed', 'error'); - console.error(error); - } - } - - async function forceUpdate(apiId) { - try { - await fetch(`/api/v2/schedule/tasks/${apiId}/force-update`, { method: 'POST' }); - addLog(`Forced update: ${apiId}`); - } catch (error) { - console.error(error); - } - } - - function addLog(text) { - const logContainer = document.getElementById('activityLog'); - const time = new Date().toLocaleTimeString(); - const entry = document.createElement('div'); - entry.style.cssText = 'padding: 10px; border-left: 3px solid var(--accent-blue); margin-bottom: 8px;'; - entry.innerHTML = `${time} ${text}`; - logContainer.insertBefore(entry, logContainer.firstChild); - while (logContainer.children.length > 50) { - logContainer.removeChild(logContainer.lastChild); - } - } - - // Admin Functions - async function loadAdminData() { - try { - const [status, providers] = await Promise.all([ - fetch('/api/status').then(r => r.json()), - fetch('/api/providers').then(r => r.json()) - ]); - - document.getElementById('statTotal').textContent = status.total_providers || 0; - document.getElementById('statOnline').textContent = status.online || 0; - document.getElementById('statOffline').textContent = status.offline || 0; - - // Handle providers as both array and object - let providersArray = []; - if (Array.isArray(providers)) { - providersArray = providers; - } else if (providers && typeof providers === 'object') { - // Convert object to array - providersArray = Object.values(providers); - } +
- const list = document.getElementById('apisList'); - if (providersArray.length === 0) { - list.innerHTML = '
هیچ APIای یافت نشد
'; - } else { - list.innerHTML = providersArray.map(api => ` -
-
-
${api.name}
-
${api.category}
-
- ${api.status.toUpperCase()} +
+
+
- `).join(''); - } catch (error) { - console.error('Error loading admin data:', error); - } - } - - async function addNewAPI() { - const name = document.getElementById('newApiName').value; - const url = document.getElementById('newApiUrl').value; - const category = document.getElementById('newApiCategory').value; - - if (!name || !url) { - showToast('Please fill in API name and URL', 'info'); - return; - } - - showToast('✅ API added! Note: Restart server to activate.', 'success'); - document.getElementById('newApiName').value = ''; - document.getElementById('newApiUrl').value = ''; - loadAdminData(); - } - - function saveSettings() { - const interval = document.getElementById('checkInterval').value; - const refresh = document.getElementById('dashboardRefresh').value; - localStorage.setItem('monitorSettings', JSON.stringify({ interval, refresh })); - showToast('✅ Settings saved!', 'success'); - } - - // HuggingFace Functions - async function loadHFHealth() { - try { - const data = await fetch('/api/hf/health').then(r => r.json()); - document.getElementById('healthOutput').textContent = JSON.stringify(data, null, 2); - } catch (err) { - document.getElementById('healthOutput').textContent = `Error: ${err.message}`; - } - } - - async function loadModels() { - try { - document.getElementById('modelsList').innerHTML = '

Loading...

'; - // Mock data for now - document.getElementById('modelsList').innerHTML = '

Models registry endpoint not implemented

'; - } catch (err) { - document.getElementById('modelsList').innerHTML = `

Error: ${err.message}

`; - } - } - - async function loadDatasets() { - try { - document.getElementById('datasetsList').innerHTML = '

Loading...

'; - // Mock data for now - document.getElementById('datasetsList').innerHTML = '

Datasets registry endpoint not implemented

'; - } catch (err) { - document.getElementById('datasetsList').innerHTML = `

Error: ${err.message}

`; - } - } - - async function doSearch() { - const q = document.getElementById('searchQuery').value; - document.getElementById('searchResults').innerHTML = `

Searching for "${q}"...

`; - // Mock search - document.getElementById('searchResults').innerHTML = '

Search endpoint not implemented

'; - } - - async function doSearchDatasets() { - const q = document.getElementById('searchQuery').value; - document.getElementById('searchResults').innerHTML = `

Searching datasets for "${q}"...

`; - // Mock search - document.getElementById('searchResults').innerHTML = '

Search endpoint not implemented

'; - } - - async function doSentiment() { - const texts = document.getElementById('sentimentTexts').value.split('\n').filter(t => t.trim()); - if (texts.length === 0) { - showToast('Please enter at least one text sample', 'info'); - return; - } - try { - document.getElementById('voteDisplay').innerHTML = '⏳ Analyzing...'; - document.getElementById('sentimentOutput').textContent = 'Running sentiment analysis...'; - - const data = await fetch('/api/hf/run-sentiment', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ texts }) - }).then(r => r.json()); - - const vote = data.vote || 0; - let voteClass = 'vote-neutral'; - let voteEmoji = '😐'; - if (vote > 0.2) { voteClass = 'vote-positive'; voteEmoji = '📈'; } - else if (vote < -0.2) { voteClass = 'vote-negative'; voteEmoji = '📉'; } - - document.getElementById('voteDisplay').innerHTML = `${voteEmoji} ${vote.toFixed(3)}`; - document.getElementById('sentimentOutput').textContent = JSON.stringify(data, null, 2); - } catch (err) { - document.getElementById('voteDisplay').innerHTML = 'Error'; - document.getElementById('sentimentOutput').textContent = `Error: ${err.message}`; - } - } - - // Pools Functions - let currentPoolId = null; - let allProvidersList = []; - - async function loadPools() { - try { - const [poolsRes, historyRes] = await Promise.all([ - fetch('/api/pools').then(r => r.json()), - fetch('/api/pools/history?limit=20').then(r => r.json()) - ]); - - const pools = poolsRes.pools || []; - const container = document.getElementById('poolsContainer'); - - if (pools.length === 0) { - container.innerHTML = ` -
-
🔄
-
No pools configured
-
Create your first pool to get started with API source rotation
- -
- `; - } else { - container.innerHTML = pools.map(pool => createPoolCard(pool)).join(''); - } +
+
- // Load rotation history - const history = historyRes.history || []; - const historyContainer = document.getElementById('rotationHistory'); - if (history.length === 0) { - historyContainer.innerHTML = '

No rotation history yet

'; - } else { - historyContainer.innerHTML = history.map(h => ` -
-
-
-
${h.pool_name}
-
Rotated to: ${h.provider_name}
-
-
-
${new Date(h.timestamp).toLocaleString()}
- ${h.reason} -
-
-
- `).join(''); - } - } catch (error) { - console.error('Error loading pools:', error); - document.getElementById('poolsContainer').innerHTML = ` -
-
❌ Error loading pools: ${error.message}
+ + - ${currentProvider} - -
-
-
Strategy
-
${pool.rotation_strategy.replace('_', ' ')}
-
-
-
Rotations
-
${pool.total_rotations || 0}
-
-
-
Members
-
${pool.members ? pool.members.length : 0}
-
-
-
Status
- ${pool.enabled ? 'Enabled' : 'Disabled'} -
+ + - async function deletePool(poolId, poolName) { - if (!confirm(`Are you sure you want to delete pool "${poolName}"?`)) { - return; - } + + - async function backupResources() { - try { - const response = await fetch('/api/resources/backup', { method: 'POST' }); - const data = await response.json(); - showToast(`✅ Backup created: ${data.filepath}`, 'success'); - } catch (error) { - showToast('❌ Backup failed', 'error'); - console.error(error); - } - } - - function showImportModal() { - document.getElementById('importModal').classList.add('active'); - } - - function closeImportModal() { - document.getElementById('importModal').classList.remove('active'); - const form = document.getElementById('importForm'); - if (form) form.reset(); - } - - // Import form handler - const importForm = document.getElementById('importForm'); - if (importForm) { - importForm.addEventListener('submit', async (e) => { - e.preventDefault(); - const filePath = document.getElementById('importFilePath').value; - const merge = document.getElementById('importMode').value === 'true'; - - try { - const response = await fetch(`/api/resources/import/json?file_path=${encodeURIComponent(filePath)}&merge=${merge}`, { - method: 'POST' - }); - - if (response.ok) { - const data = await response.json(); - showToast(`✅ Resources imported successfully (${data.merged ? 'merged' : 'replaced'})`, 'success'); - closeImportModal(); - loadResources(); - } else { - const error = await response.json(); - showToast(`❌ Import failed: ${error.detail}`, 'error'); - } - } catch (error) { - showToast('❌ Error importing resources', 'error'); - console.error(error); - } - }); - } - - // ===== Reports Functions ===== - - async function loadReports() { - await Promise.all([ - loadDiscoveryReport(), - loadModelsReport(), - loadLastDiagnostics() - ]); - } - - // Load System Alerts - async function loadSystemAlerts() { - try { - const response = await fetch('/api/diagnostics/last'); - const report = await response.json(); - - if (report.message || !report.issues || report.issues.length === 0) { - document.getElementById('systemAlertsSection').style.display = 'none'; - return; - } - - displaySystemAlerts(report.issues); - document.getElementById('systemAlertsSection').style.display = 'block'; - } catch (error) { - console.error('Error loading system alerts:', error); - } - } - - function displaySystemAlerts(issues) { - const container = document.getElementById('systemAlertsContainer'); - if (!container) return; - - const severityConfig = { - 'critical': { - icon: 'icon-error', - color: 'var(--accent-red)', - bg: 'rgba(239, 68, 68, 0.1)', - border: 'rgba(239, 68, 68, 0.3)' - }, - 'warning': { - icon: 'icon-warning', - color: 'var(--accent-yellow)', - bg: 'rgba(245, 158, 11, 0.1)', - border: 'rgba(245, 158, 11, 0.3)' - }, - 'info': { - icon: 'icon-info', - color: 'var(--accent-blue)', - bg: 'rgba(59, 130, 246, 0.1)', - border: 'rgba(59, 130, 246, 0.3)' - } - }; - - const solutions = { - 'HF_API_TOKEN': { - title: 'تنظیم متغیر محیطی HF_API_TOKEN', - steps: [ - '1. یک توکن از HuggingFace دریافت کنید:', - ' - به https://huggingface.co/settings/tokens بروید', - ' - یک توکن جدید ایجاد کنید', - '2. توکن را به متغیر محیطی اضافه کنید:', - ' Windows: set HF_API_TOKEN=your_token_here', - ' Linux/Mac: export HF_API_TOKEN=your_token_here', - ' یا در فایل .env: HF_API_TOKEN=your_token_here' - ] - }, - 'resources.json': { - title: 'ایجاد فایل resources.json', - steps: [ - 'این فایل به صورت خودکار ساخته می‌شود.', - 'اگر نیاز به تنظیمات دستی دارید، می‌توانید آن را ایجاد کنید:', - '{', - ' "resources": []', - '}' - ] - }, - 'config.json': { - title: 'ایجاد فایل config.json', - steps: [ - 'این فایل به صورت خودکار ساخته می‌شود.', - 'اگر نیاز به تنظیمات دستی دارید، می‌توانید آن را ایجاد کنید.' - ] - }, - 'HuggingFace API': { - title: 'رفع مشکل اتصال به HuggingFace', - steps: [ - '1. بررسی اتصال اینترنت', - '2. بررسی فایروال و پروکسی', - '3. بررسی DNS settings', - '4. اگر از VPN استفاده می‌کنید، آن را غیرفعال کنید', - '5. بررسی کنید که https://api.huggingface.co قابل دسترسی باشد' - ] - }, - 'Auto-Discovery': { - title: 'فعال‌سازی Auto-Discovery Service', - steps: [ - 'برای فعال‌سازی سرویس Auto-Discovery:', - '1. متغیر محیطی را تنظیم کنید:', - ' export ENABLE_AUTO_DISCOVERY=true', - '2. یا در فایل .env اضافه کنید:', - ' ENABLE_AUTO_DISCOVERY=true', - '3. سرور را restart کنید' - ] - } - }; - - let html = ''; - issues.forEach((issue, index) => { - const config = severityConfig[issue.severity] || severityConfig['info']; - const solutionKey = issue.title.includes('HF_API_TOKEN') ? 'HF_API_TOKEN' : - issue.title.includes('resources.json') ? 'resources.json' : - issue.title.includes('config.json') ? 'config.json' : - issue.title.includes('HuggingFace') ? 'HuggingFace API' : - issue.title.includes('Auto-Discovery') ? 'Auto-Discovery' : null; - - const solution = solutionKey ? solutions[solutionKey] : null; - - html += ` -
-
-
- -
-
-
-
-

- ${issue.title} -

-
- ${issue.description} -
-
- - ${issue.category} - -
- - ${solution ? ` -
-
- - راه‌حل: -
-
- ${solution.steps.map(step => `
${step}
`).join('')} -
-
- ` : ''} - -
- ${new Date(issue.timestamp).toLocaleString('fa-IR')} -
-
-
+ + - if (report.fixed_issues && report.fixed_issues.length > 0) { - html += ` -
-

✅ مشکلات تعمیر شده

- ${report.fixed_issues.map(issue => ` -
- ${issue.title}
- ${issue.description} -
- `).join('')} + + + + + - if (report.message) { - return; // هیچ گزارشی موجود نیست - } - - displayDiagnosticsReport(report); - - // Load system alerts - if (report.issues && report.issues.length > 0) { - displaySystemAlerts(report.issues); - document.getElementById('systemAlertsSection').style.display = 'block'; - } - } catch (error) { - console.error('Error loading last diagnostics:', error); - } - } - - async function loadDiscoveryReport() { - const reportDiv = document.getElementById('discoveryReport'); - reportDiv.innerHTML = '
'; - - try { - const response = await fetch('/api/reports/discovery'); - const report = await response.json(); - - const lastRun = report.last_run; - const status = report.service_status; - - let html = ` -
-
-
- ${report.enabled ? '✅' : '❌'} -
-
وضعیت سرویس
-
-
-
${report.model || 'N/A'}
-
مدل استفاده شده
-
-
-
${Math.floor(report.interval_seconds / 3600)}h
-
فاصله اجرا
-
- ${report.next_run_estimate ? ` -
-
-
اجرای بعدی
-
- ${new Date(report.next_run_estimate).toLocaleString('fa-IR')} -
-
- ` : ''} + + - async function loadModelsReport() { - const reportDiv = document.getElementById('modelsReport'); - reportDiv.innerHTML = '
'; + + - if (report.error) { - reportDiv.innerHTML = ` -
- ⚠️ ${report.error} -
- `; - return; - } +