Add SQLite persistence and write-through gRPC mutations
Database (internal/db) stores listeners, routes, and firewall rules with WAL mode, foreign keys, and idempotent migrations. First run seeds from TOML config; subsequent runs load from DB as source of truth. gRPC admin API now writes to the database before updating in-memory state (write-through cache pattern). Adds snapshot command for VACUUM INTO backups. Refactors firewall.New to accept raw rule slices instead of config struct for flexibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
|
||||
"git.wntrmute.dev/kyle/mc-proxy/internal/config"
|
||||
)
|
||||
|
||||
type geoIPRecord struct {
|
||||
@@ -27,15 +26,15 @@ type Firewall struct {
|
||||
mu sync.RWMutex // protects all mutable state
|
||||
}
|
||||
|
||||
// New creates a Firewall from the given configuration.
|
||||
func New(cfg config.Firewall) (*Firewall, error) {
|
||||
// New creates a Firewall from raw rule lists and an optional GeoIP database path.
|
||||
func New(geoIPPath string, ips, cidrs, countries []string) (*Firewall, error) {
|
||||
f := &Firewall{
|
||||
blockedIPs: make(map[netip.Addr]struct{}),
|
||||
blockedCountries: make(map[string]struct{}),
|
||||
geoDBPath: cfg.GeoIPDB,
|
||||
geoDBPath: geoIPPath,
|
||||
}
|
||||
|
||||
for _, ip := range cfg.BlockedIPs {
|
||||
for _, ip := range ips {
|
||||
addr, err := netip.ParseAddr(ip)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid blocked IP %q: %w", ip, err)
|
||||
@@ -43,7 +42,7 @@ func New(cfg config.Firewall) (*Firewall, error) {
|
||||
f.blockedIPs[addr] = struct{}{}
|
||||
}
|
||||
|
||||
for _, cidr := range cfg.BlockedCIDRs {
|
||||
for _, cidr := range cidrs {
|
||||
prefix, err := netip.ParsePrefix(cidr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid blocked CIDR %q: %w", cidr, err)
|
||||
@@ -51,12 +50,12 @@ func New(cfg config.Firewall) (*Firewall, error) {
|
||||
f.blockedCIDRs = append(f.blockedCIDRs, prefix)
|
||||
}
|
||||
|
||||
for _, code := range cfg.BlockedCountries {
|
||||
for _, code := range countries {
|
||||
f.blockedCountries[strings.ToUpper(code)] = struct{}{}
|
||||
}
|
||||
|
||||
if len(f.blockedCountries) > 0 {
|
||||
if err := f.loadGeoDB(cfg.GeoIPDB); err != nil {
|
||||
if len(f.blockedCountries) > 0 && geoIPPath != "" {
|
||||
if err := f.loadGeoDB(geoIPPath); err != nil {
|
||||
return nil, fmt.Errorf("loading GeoIP database: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user