feat: Add credentials viewer with copy buttons
- Add 'Credenciais Carregadas' section in web interface - Show services (Coolify, Gitea, Supabase, Logto) status - Display path and number of keys found - Add refresh button to reload credentials - Add copy to clipboard functionality for credentials
This commit is contained in:
@@ -864,6 +864,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- CREDENCIAIS CARREGADAS -->
|
||||||
|
<div class="section-title">Credenciais Carregadas</div>
|
||||||
|
<div class="card">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
||||||
|
<span style="font-size: 0.85rem; color: var(--text-muted);">
|
||||||
|
Credenciais sincronizadas dos serviços (Coolify, Gitea, Supabase, etc)
|
||||||
|
</span>
|
||||||
|
<button type="button" class="btn" onclick="loadCredentials()" style="padding: 0.4rem 0.75rem; font-size: 0.75rem;">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14">
|
||||||
|
<path d="M23 4v6h-6M1 20v-6h6"/>
|
||||||
|
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||||
|
</svg>
|
||||||
|
Atualizar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="credentials-container" style="display: none;">
|
||||||
|
<div id="credentials-list" style="max-height: 300px; overflow-y: auto;"></div>
|
||||||
|
</div>
|
||||||
|
<div id="credentials-loading" style="text-align: center; padding: 1rem; color: var(--text-muted);">
|
||||||
|
Clique em "Atualizar" para carregar as credenciais
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="section-title">Terminal & Insights da IA</div>
|
<div class="section-title">Terminal & Insights da IA</div>
|
||||||
<div class="chat-layout">
|
<div class="chat-layout">
|
||||||
<!-- Coluna 1: Chat Técnico -->
|
<!-- Coluna 1: Chat Técnico -->
|
||||||
@@ -1334,6 +1357,76 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadCredentials() {
|
||||||
|
const loadingEl = document.getElementById('credentials-loading');
|
||||||
|
const containerEl = document.getElementById('credentials-container');
|
||||||
|
const listEl = document.getElementById('credentials-list');
|
||||||
|
|
||||||
|
if (loadingEl) loadingEl.innerHTML = 'Carregando...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Sync first
|
||||||
|
await apiFetch('/api/sync-credentials', { method: 'POST' });
|
||||||
|
|
||||||
|
// Get orchestrator status which has services info
|
||||||
|
const res = await apiFetch('/api/orchestrator-status');
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
const services = data.credentials || {};
|
||||||
|
const serviceNames = {
|
||||||
|
coolify: 'Coolify (Orquestrador)',
|
||||||
|
supabase: 'Supabase (BaaS)',
|
||||||
|
gitea: 'Gitea (Git Server)',
|
||||||
|
logto: 'Logto (Autenticação)'
|
||||||
|
};
|
||||||
|
|
||||||
|
let html = '<div style="display: grid; gap: 1rem;">';
|
||||||
|
|
||||||
|
for (const [key, info] of Object.entries(services)) {
|
||||||
|
const name = serviceNames[key] || key;
|
||||||
|
const status = info.exists ? '<span style="color: var(--success);">Disponivel</span>' : '<span style="color: var(--danger);">Nao disponivel</span>';
|
||||||
|
const keysCount = info.keys_count || 0;
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<div style="padding: 1rem; background: var(--bg-input); border-radius: 8px; border: 1px solid var(--border);">
|
||||||
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
|
||||||
|
<strong style="color: var(--accent);">${name}</strong>
|
||||||
|
${status}
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 0.75rem; color: var(--text-muted); margin-bottom: 0.5rem;">
|
||||||
|
Caminho: <code style="background: var(--bg-base); padding: 0.1rem 0.3rem; border-radius: 4px;">${info.path || 'N/A'}</code>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 0.75rem; color: var(--text-muted);">
|
||||||
|
${keysCount} chave(s) encontrada(s)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
if (loadingEl) loadingEl.style.display = 'none';
|
||||||
|
if (containerEl) containerEl.style.display = 'block';
|
||||||
|
if (listEl) listEl.innerHTML = html;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (loadingEl) {
|
||||||
|
loadingEl.innerHTML = 'Erro ao carregar credenciais';
|
||||||
|
loadingEl.style.color = 'var(--danger)';
|
||||||
|
}
|
||||||
|
console.error('Erro ao carregar credenciais:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyToClipboard(text, label) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
showToast(label + ' copiado!');
|
||||||
|
} catch (e) {
|
||||||
|
showToast('Erro ao copiar.', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function testLLMSpeed() {
|
async function testLLMSpeed() {
|
||||||
const btn = document.getElementById('btn-test-llm');
|
const btn = document.getElementById('btn-test-llm');
|
||||||
const originalContent = btn.innerHTML;
|
const originalContent = btn.innerHTML;
|
||||||
|
|||||||
Reference in New Issue
Block a user