Init repo

This commit is contained in:
2026-02-26 11:51:36 -05:00
commit 5400d82acd
50 changed files with 17748 additions and 0 deletions

View File

@@ -0,0 +1,650 @@
// Providers Page Module
class ProvidersPage {
constructor() {
this.providers = [];
this.init();
}
async init() {
// Load data
await this.loadProviderStats();
await this.loadProvidersList();
await this.loadModelsList();
await this.loadConnectionTests();
// Setup event listeners
this.setupEventListeners();
}
async loadProviderStats() {
const container = document.getElementById('provider-stats');
if (!container) return;
container.innerHTML = `
<div class="stat-card">
<div class="stat-icon primary">
<i class="fas fa-server"></i>
</div>
<div class="stat-content">
<div class="stat-value">4</div>
<div class="stat-label">Total Providers</div>
<div class="stat-change">
<i class="fas fa-check-circle"></i>
3 active
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon success">
<i class="fas fa-plug"></i>
</div>
<div class="stat-content">
<div class="stat-value">3</div>
<div class="stat-label">Connected</div>
<div class="stat-change positive">
<i class="fas fa-arrow-up"></i>
All systems operational
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon warning">
<i class="fas fa-exclamation-triangle"></i>
</div>
<div class="stat-content">
<div class="stat-value">1</div>
<div class="stat-label">Issues</div>
<div class="stat-change">
<i class="fas fa-info-circle"></i>
DeepSeek: 85% health
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-icon danger">
<i class="fas fa-times-circle"></i>
</div>
<div class="stat-content">
<div class="stat-value">1</div>
<div class="stat-label">Offline</div>
<div class="stat-change">
<i class="fas fa-redo"></i>
Grok: Connection failed
</div>
</div>
</div>
`;
}
async loadProvidersList() {
const container = document.getElementById('providers-list');
if (!container) return;
this.providers = [
{ name: 'OpenAI', enabled: true, status: 'online', apiKey: 'sk-*****123', models: ['gpt-4', 'gpt-3.5-turbo'], lastUsed: '2024-01-15 14:32:15' },
{ name: 'Gemini', enabled: true, status: 'online', apiKey: 'AIza*****456', models: ['gemini-pro', 'gemini-pro-vision'], lastUsed: '2024-01-15 14:30:45' },
{ name: 'DeepSeek', enabled: true, status: 'warning', apiKey: 'sk-*****789', models: ['deepseek-chat', 'deepseek-coder'], lastUsed: '2024-01-15 14:28:12' },
{ name: 'Grok', enabled: false, status: 'offline', apiKey: 'gk-*****012', models: ['grok-beta'], lastUsed: '2024-01-12 10:15:22' }
];
container.innerHTML = this.providers.map(provider => {
const statusClass = provider.status === 'online' ? 'success' :
provider.status === 'warning' ? 'warning' : 'danger';
const statusIcon = provider.status === 'online' ? 'check-circle' :
provider.status === 'warning' ? 'exclamation-triangle' : 'times-circle';
return `
<div class="provider-card">
<div class="provider-header">
<div class="provider-info">
<h4 class="provider-name">${provider.name}</h4>
<span class="status-badge ${statusClass}">
<i class="fas fa-${statusIcon}"></i>
${provider.status}
</span>
</div>
<div class="provider-actions">
<label class="toggle-switch">
<input type="checkbox" ${provider.enabled ? 'checked' : ''} data-provider="${provider.name}">
<span class="toggle-slider"></span>
</label>
<button class="btn-action" title="Configure" data-action="configure" data-provider="${provider.name}">
<i class="fas fa-cog"></i>
</button>
<button class="btn-action" title="Test Connection" data-action="test" data-provider="${provider.name}">
<i class="fas fa-play"></i>
</button>
</div>
</div>
<div class="provider-details">
<div class="detail-item">
<span class="detail-label">API Key:</span>
<code class="detail-value">${provider.apiKey}</code>
<button class="btn-copy" data-text="${provider.apiKey}" title="Copy">
<i class="fas fa-copy"></i>
</button>
</div>
<div class="detail-item">
<span class="detail-label">Models:</span>
<span class="detail-value">${provider.models.join(', ')}</span>
</div>
<div class="detail-item">
<span class="detail-label">Last Used:</span>
<span class="detail-value">${provider.lastUsed}</span>
</div>
</div>
</div>
`;
}).join('');
// Add CSS for provider cards
this.addProviderStyles();
}
addProviderStyles() {
const style = document.createElement('style');
style.textContent = `
.provider-card {
background-color: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1rem;
margin-bottom: 1rem;
}
.provider-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.provider-info {
display: flex;
align-items: center;
gap: 0.5rem;
}
.provider-name {
font-size: 1rem;
font-weight: 600;
color: var(--text-primary);
margin: 0;
}
.provider-actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--text-light);
transition: .4s;
border-radius: 24px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: var(--success);
}
input:checked + .toggle-slider:before {
transform: translateX(26px);
}
.provider-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 0.75rem;
font-size: 0.875rem;
}
.detail-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.detail-label {
color: var(--text-secondary);
font-weight: 500;
min-width: 70px;
}
.detail-value {
color: var(--text-primary);
flex: 1;
}
.btn-copy {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
font-size: 0.75rem;
padding: 0.25rem;
transition: color 0.2s ease;
}
.btn-copy:hover {
color: var(--primary);
}
`;
document.head.appendChild(style);
}
async loadModelsList() {
const container = document.getElementById('models-list');
if (!container) return;
const models = [
{ provider: 'OpenAI', name: 'gpt-4', enabled: true, context: 8192, maxTokens: 4096 },
{ provider: 'OpenAI', name: 'gpt-3.5-turbo', enabled: true, context: 16384, maxTokens: 4096 },
{ provider: 'Gemini', name: 'gemini-pro', enabled: true, context: 32768, maxTokens: 8192 },
{ provider: 'Gemini', name: 'gemini-pro-vision', enabled: true, context: 32768, maxTokens: 4096 },
{ provider: 'DeepSeek', name: 'deepseek-chat', enabled: true, context: 16384, maxTokens: 4096 },
{ provider: 'DeepSeek', name: 'deepseek-coder', enabled: true, context: 16384, maxTokens: 4096 },
{ provider: 'Grok', name: 'grok-beta', enabled: false, context: 8192, maxTokens: 2048 }
];
container.innerHTML = models.map(model => `
<div class="model-item">
<div class="model-header">
<span class="model-name">${model.name}</span>
<span class="model-provider">${model.provider}</span>
</div>
<div class="model-details">
<span class="model-detail">
<i class="fas fa-microchip"></i>
Context: ${model.context.toLocaleString()} tokens
</span>
<span class="model-detail">
<i class="fas fa-ruler"></i>
Max: ${model.maxTokens.toLocaleString()} tokens
</span>
<span class="model-status ${model.enabled ? 'enabled' : 'disabled'}">
<i class="fas fa-${model.enabled ? 'check' : 'times'}"></i>
${model.enabled ? 'Enabled' : 'Disabled'}
</span>
</div>
</div>
`).join('');
// Add CSS for model items
this.addModelStyles();
}
addModelStyles() {
const style = document.createElement('style');
style.textContent = `
.model-item {
background-color: var(--bg-secondary);
border-radius: var(--border-radius-sm);
padding: 0.75rem;
margin-bottom: 0.5rem;
}
.model-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.model-name {
font-weight: 600;
color: var(--text-primary);
}
.model-provider {
font-size: 0.75rem;
color: var(--text-secondary);
background-color: var(--bg-primary);
padding: 0.25rem 0.5rem;
border-radius: 12px;
}
.model-details {
display: flex;
flex-wrap: wrap;
gap: 1rem;
font-size: 0.75rem;
}
.model-detail {
display: flex;
align-items: center;
gap: 0.25rem;
color: var(--text-secondary);
}
.model-detail i {
font-size: 0.625rem;
}
.model-status {
font-size: 0.75rem;
padding: 0.125rem 0.5rem;
border-radius: 12px;
}
.model-status.enabled {
background-color: rgba(16, 185, 129, 0.1);
color: var(--success);
}
.model-status.disabled {
background-color: rgba(239, 68, 68, 0.1);
color: var(--danger);
}
`;
document.head.appendChild(style);
}
async loadConnectionTests() {
const container = document.getElementById('connection-tests');
if (!container) return;
const tests = [
{ provider: 'OpenAI', status: 'success', latency: 245, timestamp: '2024-01-15 14:35:00' },
{ provider: 'Gemini', status: 'success', latency: 189, timestamp: '2024-01-15 14:34:30' },
{ provider: 'DeepSeek', status: 'warning', latency: 520, timestamp: '2024-01-15 14:34:00' },
{ provider: 'Grok', status: 'error', latency: null, timestamp: '2024-01-15 14:33:30' }
];
container.innerHTML = tests.map(test => {
const statusClass = test.status === 'success' ? 'success' :
test.status === 'warning' ? 'warning' : 'danger';
const statusIcon = test.status === 'success' ? 'check-circle' :
test.status === 'warning' ? 'exclamation-triangle' : 'times-circle';
return `
<div class="test-result">
<div class="test-provider">${test.provider}</div>
<div class="test-status">
<span class="status-badge ${statusClass}">
<i class="fas fa-${statusIcon}"></i>
${test.status}
</span>
</div>
<div class="test-latency">${test.latency ? `${test.latency}ms` : 'N/A'}</div>
<div class="test-time">${test.timestamp}</div>
</div>
`;
}).join('');
// Add CSS for test results
this.addTestStyles();
}
addTestStyles() {
const style = document.createElement('style');
style.textContent = `
.test-result {
display: grid;
grid-template-columns: 1fr 1fr 1fr 2fr;
gap: 1rem;
align-items: center;
padding: 0.75rem;
border-bottom: 1px solid var(--border-color);
}
.test-result:last-child {
border-bottom: none;
}
.test-provider {
font-weight: 500;
color: var(--text-primary);
}
.test-latency {
color: var(--text-secondary);
font-family: monospace;
}
.test-time {
color: var(--text-light);
font-size: 0.75rem;
}
`;
document.head.appendChild(style);
}
setupEventListeners() {
// Test all providers button
const testAllBtn = document.getElementById('test-all-providers');
if (testAllBtn) {
testAllBtn.addEventListener('click', () => {
this.testAllProviders();
});
}
// Toggle switches
document.addEventListener('change', (e) => {
if (e.target.matches('.toggle-switch input')) {
const provider = e.target.dataset.provider;
const enabled = e.target.checked;
this.toggleProvider(provider, enabled);
}
});
// Action buttons
document.addEventListener('click', (e) => {
if (e.target.closest('.btn-action')) {
const button = e.target.closest('.btn-action');
const action = button.dataset.action;
const provider = button.dataset.provider;
switch (action) {
case 'configure':
this.configureProvider(provider);
break;
case 'test':
this.testProvider(provider);
break;
}
}
// Copy buttons
if (e.target.closest('.btn-copy')) {
const button = e.target.closest('.btn-copy');
const text = button.dataset.text;
this.copyToClipboard(text);
if (window.authManager) {
window.authManager.showToast('Copied to clipboard', 'success');
}
}
});
}
toggleProvider(providerName, enabled) {
const provider = this.providers.find(p => p.name === providerName);
if (!provider) return;
// In a real app, this would update the provider via API
provider.enabled = enabled;
provider.status = enabled ? 'online' : 'offline';
if (window.authManager) {
window.authManager.showToast(
`${providerName} ${enabled ? 'enabled' : 'disabled'}`,
enabled ? 'success' : 'warning'
);
}
// Refresh providers list
this.loadProvidersList();
}
configureProvider(providerName) {
const provider = this.providers.find(p => p.name === providerName);
if (!provider) return;
// Show configuration modal
const modal = document.createElement('div');
modal.className = 'modal active';
modal.innerHTML = `
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">Configure ${providerName}</h3>
<button class="modal-close">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<form id="configure-provider-form">
<div class="form-control">
<label for="api-key">API Key</label>
<input type="password" id="api-key" value="${provider.apiKey}" placeholder="Enter API key" required>
</div>
<div class="form-control">
<label for="base-url">Base URL (Optional)</label>
<input type="text" id="base-url" placeholder="https://api.openai.com/v1">
</div>
<div class="form-control">
<label for="timeout">Timeout (seconds)</label>
<input type="number" id="timeout" value="30" min="1" max="300">
</div>
<div class="form-control">
<label for="retry-count">Retry Count</label>
<input type="number" id="retry-count" value="3" min="0" max="10">
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn btn-secondary close-modal">Cancel</button>
<button class="btn btn-primary save-config">Save Configuration</button>
</div>
</div>
`;
document.body.appendChild(modal);
// Setup event listeners
const closeBtn = modal.querySelector('.modal-close');
const closeModalBtn = modal.querySelector('.close-modal');
const saveBtn = modal.querySelector('.save-config');
const closeModal = () => {
modal.classList.remove('active');
setTimeout(() => modal.remove(), 300);
};
closeBtn.addEventListener('click', closeModal);
closeModalBtn.addEventListener('click', closeModal);
saveBtn.addEventListener('click', () => {
// In a real app, this would save provider configuration
if (window.authManager) {
window.authManager.showToast(`${providerName} configuration saved`, 'success');
}
closeModal();
});
// Close on background click
modal.addEventListener('click', (e) => {
if (e.target === modal) {
closeModal();
}
});
}
testProvider(providerName) {
const provider = this.providers.find(p => p.name === providerName);
if (!provider) return;
// Show testing in progress
if (window.authManager) {
window.authManager.showToast(`Testing ${providerName} connection...`, 'info');
}
// Simulate API test
setTimeout(() => {
// In a real app, this would test the provider connection via API
const success = Math.random() > 0.3; // 70% success rate for demo
if (window.authManager) {
window.authManager.showToast(
`${providerName} connection ${success ? 'successful' : 'failed'}`,
success ? 'success' : 'error'
);
}
// Refresh connection tests
this.loadConnectionTests();
}, 1500);
}
testAllProviders() {
if (window.authManager) {
window.authManager.showToast('Testing all providers...', 'info');
}
// Test each provider sequentially
this.providers.forEach((provider, index) => {
setTimeout(() => {
this.testProvider(provider.name);
}, index * 2000); // Stagger tests
});
}
copyToClipboard(text) {
navigator.clipboard.writeText(text).catch(err => {
console.error('Failed to copy:', err);
});
}
refresh() {
this.loadProviderStats();
this.loadProvidersList();
this.loadModelsList();
this.loadConnectionTests();
}
}
// Initialize providers page when needed
window.initProviders = async () => {
window.providersPage = new ProvidersPage();
};
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = ProvidersPage;
}