Files
benvin 92213090fe
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
Expand ASN groups from the iplocate ip-to-asn database
Replace the (unconfirmed) iplocate API expander with a database-backed one that
reads the iplocate ip-to-asn CSV (network,asn,...) proxied through the artifactapi
github remote. It downloads and indexes the whole DB once (ASN -> CIDRs), serves
every asn address group from the in-memory index, and rebuilds on a 24h TTL;
refresh failures keep the last-good index (fail-safe). No API key needed.

- Add IPLocateDB expander (zip + CSV parsing, ASN normalization).
- Wire it in main (TOMSWALLAPI_IPLOCATE_DB_URL overrides the default artifactapi
  URL); remove the dead API client.
- Unit tests: CSV indexing (incl. quoted org fields), zip extraction, missing
  columns, and ASN normalization.
2026-07-21 22:36:19 +10:00

75 lines
2.2 KiB
Go

// Package config loads tomswallapi runtime configuration from the environment.
package config
import (
"fmt"
"net/url"
"os"
)
// Config holds all runtime configuration, sourced from environment variables.
type Config struct {
ListenAddr string
DBHost string
DBPort string
DBUser string
DBPassword string
DBName string
DBSSLMode string
// WriteToken guards all mutating API endpoints (used by the Terraform provider).
WriteToken string
// AgentToken guards the per-device config endpoint (used by tomswall agents).
AgentToken string
// IPLocateAPIKey is used to expand ASN address groups into prefixes via the
// iplocate API (legacy path). The DB expander below is preferred.
IPLocateAPIKey string
// IPLocateDBURL points at the iplocate ip-to-asn CSV database (proxied via
// artifactapi). Empty uses the built-in default.
IPLocateDBURL string
}
// Load reads configuration from the environment, applying defaults.
func Load() (*Config, error) {
c := &Config{
ListenAddr: env("TOMSWALLAPI_LISTEN_ADDR", ":8000"),
DBHost: env("TOMSWALLAPI_DB_HOST", "localhost"),
DBPort: env("TOMSWALLAPI_DB_PORT", "5432"),
DBUser: env("TOMSWALLAPI_DB_USER", "tomswallapi"),
DBPassword: os.Getenv("TOMSWALLAPI_DB_PASSWORD"),
DBName: env("TOMSWALLAPI_DB_NAME", "tomswallapi"),
DBSSLMode: env("TOMSWALLAPI_DB_SSLMODE", "disable"),
WriteToken: os.Getenv("TOMSWALLAPI_WRITE_TOKEN"),
AgentToken: os.Getenv("TOMSWALLAPI_AGENT_TOKEN"),
IPLocateAPIKey: os.Getenv("TOMSWALLAPI_IPLOCATE_API_KEY"),
IPLocateDBURL: os.Getenv("TOMSWALLAPI_IPLOCATE_DB_URL"),
}
if c.DBName == "" {
return nil, fmt.Errorf("TOMSWALLAPI_DB_NAME must not be empty")
}
return c, nil
}
// DatabaseDSN builds a libpq-style connection string.
func (c *Config) DatabaseDSN() string {
u := url.URL{
Scheme: "postgres",
User: url.UserPassword(c.DBUser, c.DBPassword),
Host: fmt.Sprintf("%s:%s", c.DBHost, c.DBPort),
Path: c.DBName,
}
q := u.Query()
q.Set("sslmode", c.DBSSLMode)
u.RawQuery = q.Encode()
return u.String()
}
func env(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}