19 Commits

Author SHA1 Message Date
f75d5c489a
refactor(httpcache): use http.Header instead of AuthHeader/AuthValue
Cacher.Header is a stdlib http.Header that's merged into every request.
Authorization is stripped on redirect unconditionally (presigned S3/R2
targets, etc). Callers build the header with the usual http.Header
literal; BasicAuth/Bearer still produce the Authorization value.
2026-04-20 16:55:15 -06:00
4753888402
refactor(geoip): ParseConf takes a string, not a file path
The old ParseConf opened the file itself, which the name did not
convey. Now it parses the config text directly, matching
encoding/json.Unmarshal-style conventions: callers read the file (or
source the string however they like) and pass it in. Also introduce
errors.ErrMissingCredentials for the credential-missing case so callers
can branch on it.
2026-04-20 16:53:17 -06:00
56a150826e
refactor: geoip opens tar.gz in place, no Transform, no intermediate mmdb
- httpcache.Cacher loses Transform (always atomic copy to Path); adds
  BasicAuth and Bearer helpers for Authorization header values.
- geoip.Open now reads <dir>/GeoLite2-City.tar.gz and GeoLite2-ASN.tar.gz
  directly: extracts the .mmdb entry in memory and opens via
  geoip2.FromBytes. No .mmdb files written to disk.
- geoip.Downloader/New/NewCacher/Fetch/ExtractMMDB removed — geoip is
  purely read/lookup; fetching is each caller's concern.
- cmd/check-ip/main.go is a single main() again: blocklists via
  gitshallow+dataset, geoip via two httpcache.Cachers (if GeoIP.conf
  present) + geoip.Open. No geo refresh loop, no dataset.Group for geo.
- cmd/geoip-update and the integration test construct httpcache.Cachers
  directly against geoip.DownloadBase + edition IDs, writing .tar.gz.
2026-04-20 16:27:32 -06:00
cb39f30d91
refactor(geoip,check-ip): inline literal mmdb filenames
Use 'GeoLite2-City.mmdb' / 'GeoLite2-ASN.mmdb' directly instead of
composing from the edition constants. Reads plainly — the actual
filename is right there.
2026-04-20 16:13:30 -06:00
359b740cec
refactor(geoip): Open takes dir, derives canonical edition paths
Filenames are deterministic (<dir>/GeoLite2-City.mmdb,
<dir>/GeoLite2-ASN.mmdb) — callers no longer pass both paths. cmd/check-ip
drops its cityPath/asnPath locals and just hands the maxmind dir to
geoip.Open and the fetcher builder.
2026-04-20 16:12:46 -06:00
9b92136f91
refactor(geoip,check-ip): lift download/refresh out of geoip into cmd
geoip.Open now just opens files; download/refresh/polling logic lives at
the cmd layer using dataset.Group with a combined httpcache.Cacher
fetcher (or PollFiles when no GeoIP.conf is available). Removes
geoip.OpenDatabases — the library is no longer concerned with refresh.
2026-04-20 16:10:51 -06:00
a84116f806
refactor: strip all optional/nil-guard plumbing from check-ip + geoip
- drop Checker struct, loadCohort helper, and contains() nil-wrapper
- inline check logic into server as a closure
- geoip.Databases: no nil-receiver guards, no nil-field branches, no
  "disabled" mode. City + ASN are both required; caller hands explicit
  paths and OpenDatabases returns a fully-initialized value or an err
- main.go is now straight-line wiring with no helper functions
2026-04-20 15:55:55 -06:00
912e1179d4
feat(check-ip): --format pretty|json, move rendering out of geoip
geoip.Databases now exposes a structured Lookup(ip) Info. Rendering
moved up to the cmd — the library no longer writes to io.Writer.

check-ip adds a Result struct and --format flag (pretty/json). Serve
mode dispatches on ?format=json or Accept: application/json. Pretty
is the default for both one-shot and HTTP.
2026-04-20 14:18:39 -06:00
5985ea5e2d
refactor(geoip): drop dataset dep, become barebones load/open/get
Databases is now just two *geoip2.Reader fields with Open/Close/PrintInfo.
OpenDatabases still auto-discovers conf and downloads stale .mmdb files
via httpcache before opening, but it no longer runs background goroutines
or holds atomic pointers. Long-running callers that want refresh can wire
httpcache.Cacher to atomic.Pointer themselves.

check-ip drops geo.Init/geo.Run — OpenDatabases does the fetch+open work
itself, and a one-shot CLI doesn't need background refresh.
2026-04-20 13:20:34 -06:00
f5f992ae94
refactor: move geoip setup into geoip.OpenDatabases, remove cmd/check-ip/geo.go
OpenDatabases(confPath, cityPath, asnPath) handles conf discovery, cache
dir setup, and Databases construction. DefaultConfPaths lists the standard
GeoIP.conf locations. cmd/check-ip/geo.go deleted; main calls one function.
2026-04-20 12:51:50 -06:00
ddd0986e20
refactor: push complexity into packages; main.go is orchestration only
- geoip.Databases: wraps city+ASN datasets with nil-safe Init/Run/PrintInfo
- geoip.(*Downloader).NewDatabases: builds Databases from downloader
- cmd/check-ip/geo.go: setupGeo() handles conf parsing, dir creation, DB path resolution
- cmd/check-ip/blacklist.go: isBlocked() + cohortSize() moved here
- cmd/check-ip/main.go: flags, source selection, init, check, print — nothing else
2026-04-20 12:15:14 -06:00
34a54c2d66
refactor: multi-module workspace + dataset owns Syncer interface
- Each package gets its own go.mod: net/{dataset,httpcache,gitshallow,ipcohort,geoip,formmailer}
- go.work with replace directives for cross-module workspace resolution
- dataset.Syncer/NopSyncer moved here from httpcache; callers duck-type it
- dataset.View[T] returned by Add to prevent Init/Sync/Run misuse on group members
- cmd/check-ip moved from net/ipcohort/cmd/check-ip to top-level cmd/check-ip
- Add net/ipcohort/cmd/ipcohort-contains for standalone cohort membership testing
2026-04-20 11:22:01 -06:00
bd62122ac8
feat: default cache dirs; test both inbound files
- geoip.DefaultCacheDir() → ~/.cache/maxmind (os.UserCacheDir based)
- check-ip defaults data dir to ~/.cache/bitwire-it; -data-dir flag overrides;
  positional data-dir arg removed (IP is now the only required arg)
- geoip conf: DatabaseDirectory defaults to geoip.DefaultCacheDir() when blank
- httpcache integration tests now cover both inbound files (single_ips + networks)
2026-04-20 10:11:49 -06:00
297fba10f5
feat: persist ETag/Last-Modified to sidecar file; add integration tests
httpcache: write <path>.meta JSON sidecar after each successful download;
load it on first Fetch so conditional GETs work after process restarts.

Tests verify: download, sidecar written, same-cacher 304, fresh-cacher 304
(the last being the key case — no in-memory state, sidecar drives ETag).
MaxMind integration test reads GeoIP.conf, downloads City+ASN, verifies
fresh-cacher conditional GET skips re-download via sidecar ETag.
2026-04-20 10:04:56 -06:00
3feb248ce1
refactor: replace Username/Password with AuthHeader/AuthValue in httpcache
Generic header pair works for any auth scheme — Bearer, X-API-Key, Basic, etc.
Auth is forwarded on redirects; the MaxMind-specific stripping is removed.
geoip.go encodes Basic auth credentials directly into AuthValue.
2026-04-20 09:58:08 -06:00
2abdc1c229
feat: geoip.ParseConf, geoip-update uses it, check-ip auto-downloads+hot-swaps GeoIP
geoip.ParseConf() extracted from geoip-update into the geoip package so
both cmds can read GeoIP.conf without duplication.

check-ip gains -geoip-conf flag: reads AccountID+LicenseKey, resolves
mmdb paths into data-dir, builds httpcache.Cachers with geoip.NewCacher.
Background runLoop now refreshes both blocklists and GeoIP DBs on each
tick, hot-swapping geoip2.Reader via atomic.Pointer.Swap + old.Close().
2026-04-20 00:38:54 -06:00
52f422ec93
feat: httpcache auth+rate-limit, geoip via httpcache, rename cmd to check-ip
httpcache.Cacher gains:
  - Username/Password: Basic Auth, stripped before following redirects
  - MaxAge: skip HTTP if local file mtime is within this duration
  - MinInterval: skip HTTP if last Fetch attempt was within this duration
  - Transform: post-process response body (e.g. extract .mmdb from tar.gz)

geoip.Downloader now builds an httpcache.Cacher via NewCacher(), removing
its own HTTP client. ExtractMMDB is now exported for use as a Transform.

check-ip-blacklist renamed to check-ip; adds -city-db / -asn-db flags
for GeoLite2 lookup (country, city, subdivision, ASN) printed after each
blocklist result.
2026-04-20 00:31:49 -06:00
e29c294a75
docs: add MaxMind DB binary format spec to net/geoip 2026-04-20 00:23:49 -06:00
da33660c7c
feat: add net/geoip for MaxMind GeoLite2 database downloads
Downloader checks file mtime before fetching (30/day rate limit).
Extracts .mmdb atomically from tar.gz, preserving MaxMind's release
date as mtime so freshness checks survive restarts. Strips auth header
on redirects (302 → Cloudflare R2 presigned URL). Default: 3-day
threshold, 5-minute timeout.

Also ignores GeoIP.conf and *.mmdb in .gitignore.
2026-04-20 00:21:31 -06:00