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:
@@ -22,6 +22,12 @@ type PushStatus struct {
|
||||
Consecutive int // consecutive failures
|
||||
}
|
||||
|
||||
// PushResponse is the parsed response from the Hub after a report push.
|
||||
type PushResponse struct {
|
||||
Status string `json:"status"`
|
||||
CustomerBlocked bool `json:"customer_blocked"`
|
||||
}
|
||||
|
||||
// Pusher sends reports to the central hub.
|
||||
type Pusher struct {
|
||||
hubURL string
|
||||
@@ -32,6 +38,10 @@ type Pusher struct {
|
||||
|
||||
statusMu sync.RWMutex
|
||||
status PushStatus
|
||||
|
||||
// OnPushResponse is called after each successful report push with the parsed response.
|
||||
// Set by main.go to update hub verification state.
|
||||
OnPushResponse func(resp *PushResponse)
|
||||
}
|
||||
|
||||
// NewPusher creates a new report pusher from hub configuration.
|
||||
@@ -85,7 +95,9 @@ func (p *Pusher) Push(report *Report) error {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
|
||||
// Read response body to parse customer_blocked field
|
||||
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||
@@ -95,6 +107,14 @@ func (p *Pusher) Push(report *Report) error {
|
||||
p.status.LastError = ""
|
||||
p.status.Consecutive = 0
|
||||
p.statusMu.Unlock()
|
||||
|
||||
// Parse response for customer_blocked field
|
||||
if p.OnPushResponse != nil && len(respBody) > 0 {
|
||||
var pr PushResponse
|
||||
if json.Unmarshal(respBody, &pr) == nil {
|
||||
p.OnPushResponse(&pr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
lastErr = fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
|
||||
Reference in New Issue
Block a user