v0.3.0: structural refactor — go:embed templates, server split, domain rename
- Migrate all 7 HTML templates + CSS from Go string constants to individual go:embed files in internal/web/templates/ (templates.go: 2150→35 lines) - Split server.go into auth.go, handlers.go, funcmap.go (server.go: 540→120 lines) - Rename controller subdomain from dashboard.* to felhom.* in Traefik labels - Update documentation (CLAUDE.md, README.md, CONTEXT.md) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
{{define "layout_start"}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="hu">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Title}}Felhom.eu</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<img src="/static/felhom-logo.svg" alt="Felhom.eu" class="sidebar-logo">
|
||||
<span class="customer-name">{{.CustomerName}}</span>
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/" class="{{if eq .Page "dashboard"}}active{{end}}">Vezérlőpult</a></li>
|
||||
<li><a href="/stacks" class="{{if eq .Page "stacks"}}active{{end}}">Alkalmazások</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">v{{.Version}}</span>
|
||||
<a href="/logout" class="logout-link">Kijelentkezés ↗</a>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="content">
|
||||
{{end}}
|
||||
|
||||
{{define "layout_end"}}
|
||||
</main>
|
||||
<script>
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('a, button, .btn, input, select, textarea, .stack-actions, .stack-detail-actions')) return;
|
||||
var card = e.target.closest('[data-href]');
|
||||
if (card) window.location.href = card.dataset.href;
|
||||
});
|
||||
async function checkBeforeDeploy(e, name) {
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name);
|
||||
var data = await resp.json();
|
||||
if (data.ok && data.data && data.data.deployed) {
|
||||
e.preventDefault();
|
||||
alert('Ez az alkalmazás már telepítve van.');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
} catch(err) {}
|
||||
return true;
|
||||
}
|
||||
async function syncTemplates() {
|
||||
const btn = document.getElementById('sync-btn');
|
||||
const toast = document.getElementById('sync-toast');
|
||||
if (!btn) return;
|
||||
const origText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '↻ Frissítés...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/sync', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'}
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (toast) {
|
||||
toast.textContent = data.ok ? (data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
||||
setTimeout(function() { window.location.reload(); }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
if (toast) {
|
||||
toast.textContent = 'Hálózati hiba: ' + err.message;
|
||||
toast.className = 'sync-toast sync-toast-err';
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
}
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
async function stackAction(name, action) {
|
||||
const btn = event.currentTarget;
|
||||
const origText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Folyamatban...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'}
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!data.ok) {
|
||||
alert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
alert('Hálózati hiba: ' + err.message);
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
async function deleteOrphanStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'delete-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Betöltés...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
||||
var data = await resp.json();
|
||||
var hddInfo = '';
|
||||
var checkboxHTML = '';
|
||||
if (data.ok && data.data && data.data.has_hdd_data) {
|
||||
hddInfo = '<div class="modal-hdd-info"><strong>Felhasználói adatok a merevlemezen:</strong>';
|
||||
data.data.hdd_paths.forEach(function(p) {
|
||||
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'nem létezik') + ')</div>';
|
||||
});
|
||||
hddInfo += '</div>';
|
||||
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Felhasználói adatok törlése a merevlemezről</label>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Alkalmazás törlése: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-secondary);font-size:.9rem;margin-bottom:.75rem">Ez a művelet eltávolítja a konténereket, a köteteket és a konfigurációs fájlokat.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
|
||||
hddInfo + checkboxHTML +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeDeleteModal()">Mégsem</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Törlés</button>' +
|
||||
'</div>';
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Hiba</h3><p style="color:var(--text-secondary)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Bezárás</button></div>';
|
||||
}
|
||||
}
|
||||
function closeDeleteModal() {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmDelete(name) {
|
||||
var btn = document.getElementById('confirm-delete-btn');
|
||||
var checkbox = document.getElementById('delete-hdd-check');
|
||||
var removeHDD = checkbox ? checkbox.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Törlés folyamatban...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name, {
|
||||
method: 'DELETE',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo = '<p style="color:var(--text-secondary);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-secondary);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Sikeresen törölve!</h3>' +
|
||||
'<p style="color:var(--text-secondary)">Az alkalmazás (' + name + ') törölve lett.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
|
||||
} else {
|
||||
alert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Hálózati hiba: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user