feat: Extract Wild Central as standalone Go service
Extract the Central networking functionality from wild-cloud/api into a standalone service. Wild Central manages DNS (dnsmasq), gateway (HAProxy), firewall (nftables), VPN (WireGuard), TLS (certbot), security (CrowdSec), and DDNS — all the network appliance concerns. All Cloud-specific code (instances, clusters, nodes, apps, backups, operations, kubectl/talosctl tooling) has been removed. The API struct and route registration contain only Central endpoints. Tests updated to match the new API signature. Builds and all tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
138
internal/logging/console.go
Normal file
138
internal/logging/console.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ANSI color codes
|
||||
const (
|
||||
dim = "\033[2m"
|
||||
red = "\033[31m"
|
||||
yellow = "\033[33m"
|
||||
cyan = "\033[36m"
|
||||
reset = "\033[0m"
|
||||
)
|
||||
|
||||
// ConsoleHandler formats log output for human readability on terminals.
|
||||
// It produces compact, color-coded lines:
|
||||
//
|
||||
// 20:15:54 INF daemon started addr=:5055
|
||||
// 20:15:54 ERR backup failed component=backup error="connection refused"
|
||||
type ConsoleHandler struct {
|
||||
w io.Writer
|
||||
level slog.Leveler
|
||||
attrs []slog.Attr
|
||||
mu *sync.Mutex
|
||||
}
|
||||
|
||||
// NewConsoleHandler creates a handler that writes human-friendly colored logs.
|
||||
func NewConsoleHandler(w io.Writer, opts *slog.HandlerOptions) *ConsoleHandler {
|
||||
level := slog.LevelInfo
|
||||
if opts != nil && opts.Level != nil {
|
||||
level = opts.Level.Level()
|
||||
}
|
||||
return &ConsoleHandler{
|
||||
w: w,
|
||||
level: level,
|
||||
mu: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ConsoleHandler) Enabled(_ context.Context, level slog.Level) bool {
|
||||
return level >= h.level.Level()
|
||||
}
|
||||
|
||||
func (h *ConsoleHandler) Handle(_ context.Context, r slog.Record) error {
|
||||
// Time
|
||||
buf := []byte(dim + r.Time.Format("15:04:05") + reset + " ")
|
||||
|
||||
// Level badge
|
||||
switch {
|
||||
case r.Level >= slog.LevelError:
|
||||
buf = append(buf, red+"ERR"+reset+" "...)
|
||||
case r.Level >= slog.LevelWarn:
|
||||
buf = append(buf, yellow+"WRN"+reset+" "...)
|
||||
default:
|
||||
buf = append(buf, cyan+"INF"+reset+" "...)
|
||||
}
|
||||
|
||||
// Message
|
||||
buf = append(buf, r.Message...)
|
||||
|
||||
// Pre-set attrs (from slog.With)
|
||||
for _, a := range h.attrs {
|
||||
buf = appendAttr(buf, a)
|
||||
}
|
||||
|
||||
// Inline attrs
|
||||
r.Attrs(func(a slog.Attr) bool {
|
||||
buf = appendAttr(buf, a)
|
||||
return true
|
||||
})
|
||||
|
||||
buf = append(buf, '\n')
|
||||
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
_, err := h.w.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *ConsoleHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
return &ConsoleHandler{
|
||||
w: h.w,
|
||||
level: h.level,
|
||||
attrs: append(slices.Clone(h.attrs), attrs...),
|
||||
mu: h.mu,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ConsoleHandler) WithGroup(name string) slog.Handler {
|
||||
// Groups are rare in this codebase; treat as a prefixed attr set
|
||||
return &ConsoleHandler{
|
||||
w: h.w,
|
||||
level: h.level,
|
||||
attrs: append(slices.Clone(h.attrs), slog.String("group", name)),
|
||||
mu: h.mu,
|
||||
}
|
||||
}
|
||||
|
||||
func appendAttr(buf []byte, a slog.Attr) []byte {
|
||||
if a.Equal(slog.Attr{}) {
|
||||
return buf
|
||||
}
|
||||
v := a.Value.Resolve()
|
||||
buf = append(buf, ' ')
|
||||
buf = append(buf, dim...)
|
||||
buf = append(buf, a.Key...)
|
||||
buf = append(buf, '=')
|
||||
buf = append(buf, reset...)
|
||||
|
||||
s := v.String()
|
||||
if needsQuote(s) {
|
||||
buf = append(buf, fmt.Sprintf("%q", s)...)
|
||||
} else {
|
||||
buf = append(buf, s...)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func needsQuote(s string) bool {
|
||||
if s == "" {
|
||||
return true
|
||||
}
|
||||
for _, c := range s {
|
||||
if c <= ' ' || c == '"' || c == '\\' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Verify interface compliance at compile time.
|
||||
var _ slog.Handler = (*ConsoleHandler)(nil)
|
||||
Reference in New Issue
Block a user