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
This commit is contained in:
75
main.go
Normal file
75
main.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// main.go — auth-gate entry point
|
||||
//
|
||||
// This file wires together configuration loading, the HTTP server, and the
|
||||
// background processes (permanent-whitelist file watcher, temporary-whitelist
|
||||
// cleanup).
|
||||
//
|
||||
// Why this structure?
|
||||
//
|
||||
// - Main keeps the wiring thin. Each component (config, auth, cleanup, watcher)
|
||||
// owns its own logic so we can test and reason about them independently.
|
||||
// - We don't embed the HTTP server logic inside main; instead main calls
|
||||
// createAuthHandler(config) and server.Serve(). This makes the handler
|
||||
// functionally pure (aside from config reads) and easier to mock.
|
||||
// - The background goroutines are started explicitly so we can stop them on
|
||||
// SIGTERM without leaking resources.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := loadConfig()
|
||||
|
||||
// Load the permanent whitelist from disk once at startup.
|
||||
// We reload it periodically via watcher (see watcher.go).
|
||||
loadPermanentWhitelist(cfg.PermanentWhitelistFile)
|
||||
|
||||
// Start the background watcher that reloads the permanent whitelist file.
|
||||
// It only reloads when the file actually changes (mtime comparison), so
|
||||
// frequent touch-operations don't cause unnecessary restarts.
|
||||
w := newPermanentWhitelistWatcher(cfg.PermanentWhitelistFile, cfg.WatchInterval, cfg.PermanentWhitelist)
|
||||
w.start()
|
||||
|
||||
// Start the cleanup goroutine that expires temporary whitelisted IPs
|
||||
// whose TTL has elapsed. It runs every cfg.CleanupInterval.
|
||||
go cleanupLoop(cfg.TemporaryWhitelist, cfg.CleanupInterval)
|
||||
|
||||
// Build the HTTP server.
|
||||
addr := ":" + cfg.Port
|
||||
server := newAuthServer(cfg, addr)
|
||||
|
||||
// Start serving in a goroutine so we can do graceful shutdown.
|
||||
go func() {
|
||||
log.Printf("auth-gate listening on %s", addr)
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Graceful shutdown on SIGTERM / SIGINT.
|
||||
//
|
||||
// We wait for a signal, then call server.Shutdown(ctx) which:
|
||||
// 1. Stops accepting new connections
|
||||
// 2. Closes idle connections
|
||||
// 3. Waits for in-flight requests to complete (up to 30s)
|
||||
//
|
||||
// This is important for Docker deployments: without it the container
|
||||
// would be killed mid-request and clients would see connection errors.
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
|
||||
<-quit
|
||||
|
||||
log.Println("shutting down...")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
server.Shutdown(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user