- Implemented Docker operations including image building, container management, and resource stats. - Added Gitea API client for repository management and webhook handling. - Introduced monitoring service to collect and store container metrics in InfluxDB. - Created a queue system using BullMQ for managing deployment jobs with real-time log streaming. - Developed Telegram notification service for deployment status updates. - Added Traefik label generation for dynamic reverse proxy configuration. - Implemented WebSocket endpoints for log streaming and terminal access to containers. - Created an updater sidecar for self-updating the AutoDeployer container.
169 lines
8.6 KiB
JavaScript
169 lines
8.6 KiB
JavaScript
import { settingsApi, auth, system as systemApi, services as servicesApi } from '../api.js';
|
|
import { icons } from '../icons.js';
|
|
|
|
export function renderSettings(container) {
|
|
let queueStatus = null;
|
|
let version = null;
|
|
let interval;
|
|
|
|
container.innerHTML = `
|
|
<div class="page-header"><div><h2>Settings</h2><p>Configurazione globale e test connessioni</p></div></div>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px" id="settings-grid">
|
|
<!-- Gitea -->
|
|
<div class="card" id="s-gitea">
|
|
<h3 class="card-title mb-4">${icons.gitBranch(16)} Connessione Gitea</h3>
|
|
<p class="text-sm text-muted mb-4">Testa la connessione al server Gitea configurato</p>
|
|
<button class="btn btn-secondary" id="test-gitea">Test Connessione</button>
|
|
<div id="gitea-result" class="mt-4 text-sm hidden"></div>
|
|
</div>
|
|
<!-- Telegram -->
|
|
<div class="card" id="s-telegram">
|
|
<h3 class="card-title mb-4">${icons.bell(16)} Notifiche Telegram</h3>
|
|
<p class="text-sm text-muted mb-4">Invia un messaggio di test al bot Telegram configurato</p>
|
|
<button class="btn btn-secondary" id="test-telegram">Test Telegram</button>
|
|
<div id="telegram-result" class="mt-4 text-sm hidden"></div>
|
|
</div>
|
|
<!-- Queue -->
|
|
<div class="card" id="s-queue">
|
|
<h3 class="card-title mb-4">${icons.database(16)} Build Queue</h3>
|
|
<div id="queue-content"><p class="text-muted text-sm">Caricamento...</p></div>
|
|
</div>
|
|
<!-- Password -->
|
|
<div class="card">
|
|
<h3 class="card-title mb-4">${icons.key(16)} Cambia Password</h3>
|
|
<form id="pw-form">
|
|
<div class="form-group"><label class="form-label">Password Attuale</label><input type="password" class="form-input" id="pw-current" required></div>
|
|
<div class="form-group"><label class="form-label">Nuova Password</label><input type="password" class="form-input" id="pw-new" required minlength="12"><div class="form-hint">Minimo 12 caratteri</div></div>
|
|
<div class="form-group"><label class="form-label">Conferma</label><input type="password" class="form-input" id="pw-confirm" required></div>
|
|
<div id="pw-msg" class="text-sm mb-2 hidden"></div>
|
|
<button type="submit" class="btn btn-primary">Aggiorna Password</button>
|
|
</form>
|
|
</div>
|
|
<!-- Self-Update -->
|
|
<div class="card">
|
|
<h3 class="card-title mb-4">${icons.refreshCw(16)} Self-Update</h3>
|
|
<div id="version-info" class="text-sm text-muted mb-4"></div>
|
|
<p class="text-sm text-muted mb-4">Aggiorna AutoDeployer all'ultima versione. Il servizio si riavvierà automaticamente.</p>
|
|
<button class="btn btn-primary" id="self-update">Aggiorna AutoDeployer</button>
|
|
<div id="update-result" class="mt-4 text-sm hidden"></div>
|
|
</div>
|
|
<!-- Cleanup -->
|
|
<div class="card">
|
|
<h3 class="card-title mb-4">${icons.trash2(16)} Pulizia Container Orfani</h3>
|
|
<p class="text-sm text-muted mb-4">Rimuovi container temporanei rimasti da deploy falliti.</p>
|
|
<button class="btn btn-secondary" id="cleanup-btn">Scansiona e Pulisci</button>
|
|
<div id="cleanup-result" class="mt-4 text-sm hidden"></div>
|
|
</div>
|
|
<!-- Prune -->
|
|
<div class="card">
|
|
<h3 class="card-title mb-4">${icons.hardDrive(16)} Pulizia Immagini Vecchie</h3>
|
|
<p class="text-sm text-muted mb-4">Rimuovi immagini Docker obsolete, mantenendo le 2 più recenti per servizio.</p>
|
|
<button class="btn btn-secondary" id="prune-btn">Pulisci Immagini</button>
|
|
<div id="prune-result" class="mt-4 text-sm hidden"></div>
|
|
</div>
|
|
</div>`;
|
|
|
|
// Gitea test
|
|
document.getElementById('test-gitea').onclick = async () => {
|
|
const btn = document.getElementById('test-gitea');
|
|
btn.textContent = '⏳ Testing...'; btn.disabled = true;
|
|
const res = document.getElementById('gitea-result');
|
|
try {
|
|
const r = await settingsApi.testGitea();
|
|
res.innerHTML = r.ok ? `${icons.checkCircle(16)} Connesso come <strong>${r.user}</strong>` : `${icons.xCircle(16)} ${r.error}`;
|
|
} catch (err) { res.innerHTML = `${icons.xCircle(16)} ${err.message}`; }
|
|
res.classList.remove('hidden'); btn.textContent = 'Test Connessione'; btn.disabled = false;
|
|
};
|
|
|
|
// Telegram test
|
|
document.getElementById('test-telegram').onclick = async () => {
|
|
const btn = document.getElementById('test-telegram');
|
|
btn.textContent = '⏳ Testing...'; btn.disabled = true;
|
|
const res = document.getElementById('telegram-result');
|
|
try {
|
|
const r = await settingsApi.testTelegram();
|
|
res.innerHTML = r.ok ? `${icons.checkCircle(16)} Messaggio inviato` : `${icons.xCircle(16)} ${r.error}`;
|
|
} catch (err) { res.innerHTML = `${icons.xCircle(16)} ${err.message}`; }
|
|
res.classList.remove('hidden'); btn.textContent = 'Test Telegram'; btn.disabled = false;
|
|
};
|
|
|
|
// Queue status
|
|
async function loadQueue() {
|
|
try {
|
|
queueStatus = await settingsApi.queueStatus();
|
|
document.getElementById('queue-content').innerHTML = `
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
|
|
<div><div class="metric-label">In Attesa</div><div class="metric-value" style="font-size:1.5rem">${queueStatus.waiting}</div></div>
|
|
<div><div class="metric-label">Attive</div><div class="metric-value" style="font-size:1.5rem">${queueStatus.active}</div></div>
|
|
<div><div class="metric-label">Completate</div><div class="text-sm font-bold" style="color:var(--status-running)">${queueStatus.completed}</div></div>
|
|
<div><div class="metric-label">Fallite</div><div class="text-sm font-bold" style="color:var(--status-error)">${queueStatus.failed}</div></div>
|
|
</div>`;
|
|
} catch {}
|
|
}
|
|
|
|
// Version
|
|
async function loadVersion() {
|
|
try {
|
|
version = await systemApi.version();
|
|
document.getElementById('version-info').innerHTML = `Versione: <strong class="mono">${version.commit}</strong> (${version.branch})<br><span class="text-xs">${version.date}</span>`;
|
|
} catch {}
|
|
}
|
|
|
|
// Password
|
|
document.getElementById('pw-form').onsubmit = async (e) => {
|
|
e.preventDefault();
|
|
const msg = document.getElementById('pw-msg');
|
|
msg.classList.add('hidden');
|
|
const newPw = document.getElementById('pw-new').value;
|
|
const confirm = document.getElementById('pw-confirm').value;
|
|
if (newPw !== confirm) { msg.textContent = 'Le password non coincidono'; msg.classList.remove('hidden'); return; }
|
|
try {
|
|
await auth.changePassword(document.getElementById('pw-current').value, newPw);
|
|
msg.textContent = '✅ Password aggiornata'; msg.classList.remove('hidden');
|
|
document.getElementById('pw-form').reset();
|
|
} catch (err) { msg.textContent = '❌ ' + err.message; msg.classList.remove('hidden'); }
|
|
};
|
|
|
|
// Self-update
|
|
document.getElementById('self-update').onclick = async () => {
|
|
const btn = document.getElementById('self-update');
|
|
btn.textContent = 'Aggiornamento...'; btn.disabled = true;
|
|
const res = document.getElementById('update-result');
|
|
try {
|
|
await systemApi.selfUpdate();
|
|
res.innerHTML = `${icons.checkCircle(14)} Update avviato. AutoDeployer si riavvierà a breve.`;
|
|
} catch (err) { res.innerHTML = `${icons.xCircle(14)} ${err.message}`; }
|
|
res.classList.remove('hidden'); btn.textContent = 'Aggiorna AutoDeployer'; btn.disabled = false;
|
|
};
|
|
|
|
// Cleanup
|
|
document.getElementById('cleanup-btn').onclick = async () => {
|
|
const btn = document.getElementById('cleanup-btn');
|
|
btn.textContent = 'Scansione...'; btn.disabled = true;
|
|
const res = document.getElementById('cleanup-result');
|
|
try {
|
|
const r = await servicesApi.cleanup();
|
|
res.textContent = `${r.orphans_found} orfani trovati, ${r.results?.filter(x => x.status === 'removed').length || 0} rimossi`;
|
|
} catch (err) { res.innerHTML = `<span style="color:var(--status-error)">${err.message}</span>`; }
|
|
res.classList.remove('hidden'); btn.textContent = 'Scansiona e Pulisci'; btn.disabled = false;
|
|
};
|
|
|
|
// Prune
|
|
document.getElementById('prune-btn').onclick = async () => {
|
|
const btn = document.getElementById('prune-btn');
|
|
btn.textContent = 'Pulizia...'; btn.disabled = true;
|
|
const res = document.getElementById('prune-result');
|
|
try {
|
|
const r = await servicesApi.pruneImages();
|
|
res.textContent = `${r.results?.filter(x => x.status === 'removed').length || 0} immagini rimosse`;
|
|
} catch (err) { res.innerHTML = `<span style="color:var(--status-error)">${err.message}</span>`; }
|
|
res.classList.remove('hidden'); btn.textContent = 'Pulisci Immagini'; btn.disabled = false;
|
|
};
|
|
|
|
loadQueue();
|
|
loadVersion();
|
|
interval = setInterval(loadQueue, 10000);
|
|
|
|
return () => clearInterval(interval);
|
|
}
|