26 Commits

Author SHA1 Message Date
0c281a494b
fix(check-ip): explicit error handling for UserCacheDir + geo.Close 2026-04-20 15:58:13 -06:00
b61ca0aa94
refactor(check-ip): collapse remaining server indirection
- drop format type / formatPretty / formatJSON / requestFormat /
  write / writeGeo — inline into one handler
- drop the inner check closure — inline into the handler
- one handler serves both GET / and GET /check
- fatal() replaced with log.Fatalf
- --serve is optional; without it, databases load and main returns
2026-04-20 15:57:48 -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
cdce7da04c
refactor(check-ip): simplify to 4 flags, push MkdirAll into libs
check-ip now takes only --serve, --geoip-conf, --blocklist-repo,
--cache-dir. Blocklist always comes from git; GeoIP mmdbs always go
through httpcache (when GeoIP.conf is available). Format negotiation
lives entirely server-side.

main.go is now straight-line wiring: parse flags, build the two
databases, run the server. All filesystem setup (MkdirAll for clone
target, for cache Path parents) is pushed into gitshallow and
httpcache so the cmd doesn't do filesystem bookkeeping.
2026-04-20 15:51:46 -06:00
3b5812ffcd
feat(dataset): add PollFiles fetcher for local-file sources
Stats the given paths and reports updated when any size/modtime
changes since the last call. First call always reports true so the
initial Load populates views.

check-ip uses it for --inbound/--outbound so edits to local lists
get picked up by Group.Tick without a restart.
2026-04-20 15:39:23 -06:00
7b798a739a
refactor(check-ip): split server into server.go, linearize main.go
main.go now reads top-to-bottom as setup + usage of the three
databases (blocklists group, whitelist cohort, geoip readers), then
dispatch to one-shot or serve. HTTP server code moved to server.go.

No behavior change.
2026-04-20 15:36:02 -06:00
786463cecd
refactor(dataset): Tick takes an onError callback, no more stderr
Libraries shouldn't decide where errors go. Tick now passes Load
errors to onError (nil to ignore); callers pick log/count/page.
check-ip supplies its own stderr writer.
2026-04-20 14:19:26 -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
a3d657ec61
fix(check-ip): create cache dir before httpcache writes into it
httpcache.Cacher.Fetch writes to <path>.tmp without MkdirAll; the
library expects the caller to own the directory. cacheDir now
MkdirAll's before returning.
2026-04-20 14:15:52 -06:00
82f0b53ba3
feat(check-ip): add --serve HTTP mode to exercise dataset.Tick
Long-running server exposes GET / (client IP) and GET /check?ip= for
ad-hoc lookups. signal.NotifyContext drives graceful shutdown; the
shared dataset.Group.Tick goroutine refreshes inbound/outbound views
in the background so the refresh path gets real exercise.

Factored the shared populate+report logic into a Checker struct so
oneshot and serve modes use the same code path.
2026-04-20 14:10:29 -06:00
11743c9a10
feat(sync/dataset): minimal group/view/fetcher for hot-swap refresh
Distilled from the previous net/dataset experiment and the inline
closure version in check-ip. Keeps what actually earned its keep:

  - Group ties one Fetcher to N views; a single Load drives all swaps,
    so shared sources (one git pull, one zip download) don't get
    re-fetched per view.
  - View[T].Value() is a lock-free atomic read; the atomic.Pointer is
    hidden so consumers never see in-flight reloads.
  - Tick runs Load on a ticker with stderr error logging.

Dropped from the v1 design: MultiSyncer (callers fan-out inline when
needed), Close (unused outside geoip), Name (callers wrap the logger),
standalone Dataset type (Group with one view covers it), Sync vs Init
asymmetry (Load handles first-call vs update internally).

check-ip rewires to use it — file/git/http modes all build a Group
with two views, uniform shape.
2026-04-20 13:33:05 -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
990b9e430c
refactor(check-ip): drop dataset pkg, inline atomic-swap + ticker
Uses atomic.Pointer[ipcohort.Cohort] directly and builds a per-source
refresh closure (files / git / http). One goroutine drives the ticker.
Exercises what the dataset pkg was abstracting so we can judge which
bits are worth a shared pkg.
2026-04-20 13:16:47 -06:00
9e9bd98540
refactor(check-ip): factor source selection, keep demo of all three backends
Extract the file/git/httpcache mode switch into newSource and the Group
wiring into newBlocklists. main becomes flag parsing + exit code logic
only; run owns ctx and the check. Helpers (loadCohort, cacheDir,
splitCSV, loadWhitelist) are small and single-purpose.

Still exercises dataset.Group + background refresh, gitshallow, and
httpcache as before.
2026-04-20 13:05:37 -06:00
bf4cba6fb5
feat: add bitwireGitURL const, show default URL in --git flag help 2026-04-20 12:55:08 -06:00
8c9924e559
refactor: check-ip uses CheckIPConfig struct + flag.FlagSet, adds -V/help 2026-04-20 12:54:13 -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
994d91b2bf
refactor: dataset.Add returns *Dataset, no View; main uses Group for all cases
Remove View[T] — Add now returns *Dataset[T] directly. Callers use Load()
on the returned Dataset; Init/Run belong to the owning Group.

main.go simplified: declare syncer + file paths per case, then one
g.Init() and one g.Run(). No manual loops over individual datasets.
Add gitshallow.Repo.FilePath helper.
2026-04-20 12:48:38 -06:00
03ea6934e9
refactor: HTTP datasets are independent, no Group; Group only for shared git repo 2026-04-20 12:41:07 -06:00
7b71dec445
feat: gitshallow.File for per-file path/open/sync; use in check-ip git case 2026-04-20 12:39:24 -06:00
6b420badbc
refactor: merge blacklist.go into main.go via dataset.MultiSyncer 2026-04-20 12:23:13 -06:00
3ac9683015
style: use blacklist/whitelist (industry standard) 2026-04-20 12:20:59 -06:00
e1108f3de7
fix: explicit path flags for blocklist; auto-discover GeoIP.conf
Blocklist:
- Add -inbound, -outbound, -whitelist flags for explicit file paths
- buildSources() replaces the old constructor trio; explicit flags always win
- -data-dir and -git still work as defaults for the bitwire-it layout

GeoIP:
- Auto-discover GeoIP.conf from ./GeoIP.conf then ~/.config/maxmind/GeoIP.conf
- If no conf found and no -city-db/-asn-db given: geoip disabled silently
- If no conf but paths given: use those files (Init fails if absent)
2026-04-20 12:18:33 -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
3e48e0a863
fix: check-ip fails on startup if data cannot be downloaded 2026-04-20 12:08:03 -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