v0.22.0: First-run setup wizard, local infra backup, hub verification

New controller features:
- Web-based setup wizard replaces docker-setup.sh interactive config
  - Dual listener: :8080 (Traefik) + :8081 (direct HTTP for LAN)
  - Drive scanner finds .felhom-infra-backup/ on all block devices
  - Hub recovery pull (GET /api/v1/recovery/{id}) with retrieval password
  - Fresh install: Hub config download or manual wizard
  - CSRF protection, state persistence, Hungarian UI
- Local infra backup written to all connected drives after each backup cycle
  - .felhom-infra-backup/backup.json + metadata.json with SHA256 checksum
- Hub verification: parse customer_blocked from report push response
  - Limited mode after 7 days without verification
- Recovery info page on Settings + recovery-info.txt file generation
- Pending events queue: DR events sent to Hub on next report push
- docker-setup.sh v6.0.0: removed interactive wizard, minimal controller.yaml only

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 12:33:17 +01:00
parent e217c3a445
commit 6eb75204b6
28 changed files with 2970 additions and 505 deletions
@@ -487,6 +487,46 @@ function pollUntilBack() {
{{end}}
</div>
<!-- Section: Recovery Info -->
{{if .RetrievalPassword}}
<div class="settings-card">
<h3>Veszhelyzeti informaciok</h3>
<p class="settings-card-desc">
Ezeket az adatokat mentse el biztos helyre. Ujratelepites eseten szukseg lesz rajuk a rendszer visszaallitasahoz.
</p>
<div class="settings-grid">
<div class="settings-row">
<span class="settings-label">Ugyfel azonosito</span>
<span class="settings-value mono">{{.CustomerID}}</span>
</div>
<div class="settings-row">
<span class="settings-label">Hub URL</span>
<span class="settings-value mono">{{.HubURL}}</span>
</div>
<div class="settings-row">
<span class="settings-label">Visszaallitasi jelszo</span>
<span class="settings-value">
<span id="retrieval-pw-hidden">••••••••••••••••
<button type="button" class="btn btn-xs btn-outline" onclick="document.getElementById('retrieval-pw-hidden').style.display='none';document.getElementById('retrieval-pw-visible').style.display='inline';">Megjelenit</button>
</span>
<span id="retrieval-pw-visible" style="display:none">
<code class="mono">{{.RetrievalPassword}}</code>
<button type="button" class="btn btn-xs btn-outline" onclick="document.getElementById('retrieval-pw-visible').style.display='none';document.getElementById('retrieval-pw-hidden').style.display='inline';">Elrejt</button>
</span>
</span>
</div>
<div class="settings-row">
<span class="settings-label">Tamogatas</span>
<span class="settings-value">
<a href="mailto:{{.SupportEmail}}" style="color: var(--accent-blue, #0088cc);">{{.SupportEmail}}</a>
&nbsp;|&nbsp;
<a href="{{.SupportURL}}" target="_blank" style="color: var(--accent-blue, #0088cc);">felhom.eu/kapcsolat</a>
</span>
</div>
</div>
</div>
{{end}}
<script>
function editStorageLabel(path, currentLabel) {
var wrap = document.getElementById('label-wrap-' + path);