Files
auth-proxy/watcher.go
db123-test b02b720dc6 init: added the base files by ai and debugged by hand
it's great but currently it has a basic error which is go nil pointer dereference

WIP
2026-05-03 16:17:04 +03:30

110 lines
2.6 KiB
Go

// watcher.go — permanent whitelist file watcher
//
// The watcher polls the permanent whitelist file every cfg.WatchInterval
// seconds and reloads the in-memory store if the file has changed.
//
// Why poll instead of inotify?
//
// - Polling is simpler and works inside Docker containers where inotify
// may not be available (no host filesystem access).
// - The polling interval (30s default) is fast enough for admin
// whitelisting operations.
// - If you want inotify, add it later.
//
// We only reload when the file's mtime changes, so frequent touch-
// operations don't cause unnecessary reloads.
//
// The watcher uses a mutex to protect the in-memory store. This is
// necessary because the watcher runs in a separate goroutine and the
// auth handler reads from the same store.
package main
import (
"bufio"
"log"
"os"
"time"
)
// newPermanentWhitelistWatcher creates a watcher that reloads the
// permanent whitelist file from disk.
//
// The watcher loads the file once immediately on creation, then polls
// every interval.
func newPermanentWhitelistWatcher(path string, interval time.Duration, store *PermanentWhitelist) *watcher {
w := &watcher{
path: path,
interval: interval,
store: store,
}
w.reload()
return w
}
// watcher polls a file and reloads the whitelist when it changes.
type watcher struct {
path string
interval time.Duration
store *PermanentWhitelist
lastMod time.Time
}
// start begins the polling loop.
func (w *watcher) start() {
go func() {
ticker := time.NewTicker(w.interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
w.reload()
}
}
}()
}
// reload reads the whitelist file and updates the in-memory store.
//
// We only reload if the file has changed (mtime). This prevents
// unnecessary reloads when the file is touched frequently.
func (w *watcher) reload() {
info, err := os.Stat(w.path)
if err != nil {
// File doesn't exist yet — skip.
return
}
// Only reload if the file has changed.
if info.ModTime().Equal(w.lastMod) {
return
}
w.lastMod = info.ModTime()
// Read and parse the file.
entries := make(map[string]struct{})
f, err := os.Open(w.path)
if err != nil {
log.Printf("failed to open whitelist file: %v", err)
return
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if line == "" || line[0] == '#' {
continue
}
entries[line] = struct{}{}
}
// Update the store.
w.store.mu.Lock()
w.store.entries = entries
w.store.mu.Unlock()
log.Printf("loaded %d entries from %s", len(entries), w.path)
}