Architecture: - Extract reconcileNetworking into internal/reconcile package with 7 consumer-side interfaces - Add locked modifyState helper to fix state.yaml read-modify-write race condition - Extract CrowdSec and VPN handler groups with interfaces documenting dependency surface - Replace raw map[string]any YAML manipulation with typed AddDHCPStaticLease/RemoveDHCPStaticLease - Extract Cloudflare API functions into cfClient struct, eliminating repeated auth boilerplate Complexity reduction: - haproxy.GenerateWithOpts: 50 → 6 (extracted 8 focused helpers) - reconcile.Reconcile: 42 → 11 (extracted buildRoutes, buildDNSEntries, writeHAProxyConfig) - AutheliaUpdateConfig: 25 → 10 (extracted enableAuthelia/disableAuthelia) Test coverage improvements: - reconcile: 4.5% → 56.8% (stub-based tests for route building, DNS entries, orchestration) - dnsfilter: 10.7% → 45.6% (Manager.Compile, AddList, ToggleList, custom entries) - config: 67.3% → 89.8% (DHCP static lease mutation tests)
163 lines
4.0 KiB
Go
163 lines
4.0 KiB
Go
package v1
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/wild-cloud/wild-central/internal/domains"
|
|
"github.com/wild-cloud/wild-central/internal/sse"
|
|
)
|
|
|
|
// EnsureCentralDomain registers (or updates) Central's own domain as a domain
|
|
// so it participates in DNS, HAProxy routing, and TLS cert tracking like any
|
|
// other domain. Called on startup and when global config changes.
|
|
func (api *API) EnsureCentralDomain() {
|
|
centralDomain := api.getCentralDomain()
|
|
port := api.getRunningPort()
|
|
backend := fmt.Sprintf("127.0.0.1:%d", port)
|
|
|
|
// Check if the current registration already matches — skip if so.
|
|
if centralDomain != "" {
|
|
if existing, _ := api.domains.Get(centralDomain); existing != nil &&
|
|
existing.Source == "central" &&
|
|
existing.Backend.Address == backend {
|
|
return
|
|
}
|
|
}
|
|
|
|
// Clean up stale Central registrations (e.g. domain changed).
|
|
doms, _ := api.domains.List()
|
|
for _, dom := range doms {
|
|
if dom.Source == "central" && dom.DomainName != centralDomain {
|
|
_ = api.domains.Deregister(dom.DomainName)
|
|
}
|
|
}
|
|
|
|
if centralDomain == "" {
|
|
return
|
|
}
|
|
|
|
_ = api.domains.Register(domains.Domain{
|
|
DomainName: centralDomain,
|
|
Source: "central",
|
|
Backend: domains.Backend{
|
|
Address: backend,
|
|
Type: domains.BackendHTTP,
|
|
},
|
|
TLS: domains.TLSTerminate,
|
|
})
|
|
}
|
|
|
|
// Reconcile runs networking reconciliation immediately. Called on startup
|
|
// and automatically whenever a domain is registered/updated/deregistered.
|
|
func (api *API) Reconcile() {
|
|
api.reconciler.Reconcile()
|
|
}
|
|
|
|
// broadcastDnsmasqEvent broadcasts SSE events for dnsmasq status changes
|
|
func (api *API) broadcastDnsmasqEvent(eventType string, message string) {
|
|
if api.sseManager == nil {
|
|
return
|
|
}
|
|
|
|
// Get current dnsmasq status
|
|
status, err := api.dnsmasq.GetStatus()
|
|
if err != nil {
|
|
status = nil
|
|
}
|
|
|
|
event := &sse.Event{
|
|
ID: fmt.Sprintf("dnsmasq-%d", time.Now().UnixNano()),
|
|
Type: eventType,
|
|
InstanceName: "global",
|
|
Timestamp: time.Now(),
|
|
Data: map[string]any{
|
|
"message": message,
|
|
"status": status,
|
|
},
|
|
}
|
|
|
|
api.sseManager.Broadcast(event)
|
|
}
|
|
|
|
// broadcastHaproxyEvent broadcasts SSE events for HAProxy status changes
|
|
func (api *API) broadcastHaproxyEvent(eventType string, message string) {
|
|
if api.sseManager == nil {
|
|
return
|
|
}
|
|
|
|
event := &sse.Event{
|
|
ID: fmt.Sprintf("haproxy-%d", time.Now().UnixNano()),
|
|
Type: eventType,
|
|
InstanceName: "global",
|
|
Timestamp: time.Now(),
|
|
Data: map[string]any{
|
|
"message": message,
|
|
},
|
|
}
|
|
|
|
api.sseManager.Broadcast(event)
|
|
}
|
|
|
|
// broadcastAutheliaEvent broadcasts SSE events for Authelia status changes
|
|
func (api *API) broadcastAutheliaEvent(eventType string, message string) {
|
|
if api.sseManager == nil {
|
|
return
|
|
}
|
|
|
|
event := &sse.Event{
|
|
ID: fmt.Sprintf("authelia-%d", time.Now().UnixNano()),
|
|
Type: eventType,
|
|
InstanceName: "global",
|
|
Timestamp: time.Now(),
|
|
Data: map[string]any{
|
|
"message": message,
|
|
},
|
|
}
|
|
|
|
api.sseManager.Broadcast(event)
|
|
}
|
|
|
|
// broadcastCentralStatusEvent broadcasts SSE events for central status changes
|
|
func (api *API) broadcastCentralStatusEvent(startTime time.Time) {
|
|
if api.sseManager == nil {
|
|
return
|
|
}
|
|
|
|
uptime := time.Since(startTime)
|
|
|
|
event := &sse.Event{
|
|
ID: fmt.Sprintf("central-%d", time.Now().UnixNano()),
|
|
Type: "central:status",
|
|
InstanceName: "global",
|
|
Timestamp: time.Now(),
|
|
Data: map[string]any{
|
|
"status": "running",
|
|
"version": api.version,
|
|
"uptime": uptime.String(),
|
|
"uptimeSeconds": int(uptime.Seconds()),
|
|
},
|
|
}
|
|
|
|
api.sseManager.Broadcast(event)
|
|
}
|
|
|
|
// broadcastDNSFilterEvent broadcasts SSE events for DNS filter changes
|
|
func (api *API) broadcastDNSFilterEvent(eventType string, message string) {
|
|
if api.sseManager == nil {
|
|
return
|
|
}
|
|
|
|
event := &sse.Event{
|
|
ID: fmt.Sprintf("dns-filter-%d", time.Now().UnixNano()),
|
|
Type: eventType,
|
|
InstanceName: "global",
|
|
Timestamp: time.Now(),
|
|
Data: map[string]any{
|
|
"message": message,
|
|
},
|
|
}
|
|
|
|
api.sseManager.Broadcast(event)
|
|
}
|