6 Commits

Author SHA1 Message Date
b23610fdf1
refactor(formmailer): production-readiness + dataset.View compatibility
- Blacklist is now a CohortSource interface (Value() *ipcohort.Cohort).
  *dataset.View[ipcohort.Cohort] satisfies it directly; callers with
  an atomic.Pointer can wrap. Drops the atomic/sync import from the
  public API.
- SMTP send now uses net.Dialer.DialContext with a bounded SMTPTimeout
  (default 15s) and conn deadline, so a slow/hung relay no longer holds
  the request goroutine for WriteTimeout. Opportunistic STARTTLS added.
- MX lookup uses net.DefaultResolver.LookupMX with a bounded MXTimeout
  (default 3s), cancellable via r.Context().
- clientIP uses net.SplitHostPort (was LastIndex(":"), broken for IPv6).
- Per-IP limiter map now has a 10-minute TTL with opportunistic sweep
  every 1024 requests — previously grew unbounded.
- Sentinel errors switched to errors.New; fmt.Errorf was unused.
2026-04-20 19:32:40 -06:00
01a9185c03
refactor: delete net/dataset package
check-ip and geoip no longer use it; formmailer now takes
*atomic.Pointer[ipcohort.Cohort] for Blacklist so callers own the
refresh + swap lifecycle directly. gitshallow doc comments that
referenced dataset.Syncer are trimmed.

The concepts the package tried to share (atomic-swap, group sync,
ticker-driven refresh) may come back under sync/dataset once we have
more than one in-tree caller that wants them.
2026-04-20 13:22:08 -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
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
225faec549
fix: FormFields defaults to GravityForms-compatible input_N names 2026-04-20 11:02:46 -06:00
d57c810c2e
feat: add net/formmailer with updated paradigms
Rewrite from feat-formmailer WIP:
- Blacklist is *dataset.View[ipcohort.Cohort] — caller wires dataset group
- http.Handler via ServeHTTP — drop-in for any mux
- SuccessBody/ErrorBody []byte — caller loads files; no file I/O per request
- Rate limiter per-instance (sync.Once init), not global
- Fields configurable (default standard names, not GravityForms input_N)
- AllowedCountries []string for geo-blocking via iploc (nil = allow all)
- ContainsAddr used directly (pre-parsed netip.Addr, no re-parse)
- No Init()/Run() — caller drives dataset lifecycle
- Fix getErrorBotty typo; expose support email only to legitimate errors
2026-04-20 11:01:15 -06:00