chore/docs: move to GitHub as the single home; drop archived-spec references
Codeberg is being retired as the project's public site, so every reference now points at GitHub. That includes the Go module path (codeberg.org/mix/selfpost → github.com/mixeme/selfpost): leaving an import path on a host that is going away would break `go get` and `go install`, so this is not only a docs change. Touches go.mod, test/e2e/go.mod, all imports, Makefile MODULE, the -ldflags version stamp in build/Dockerfile and docs/development.md, the licence headers in the SVG/HTML assets, and README (no more primary/mirror pair). Comments no longer cite the archived specification. "spec 7.6.1", "spec 5.1" and friends pointed into docs/archive/specification-v1.0.md, which is marked as not a source of truth; each is now a reference to the live document that owns the subject — architecture.md (with section), product.md, security.md or the README. The review only asked for the 7.x refs (code-review.md § 4), but 4/5/6/ 8/9 had the same defect, so they went too. Comments only, no behaviour change. Also closes the remaining review items: architecture.md gained a Code layers section with the layer diagram (A2), and TestParseDelivery gained the exotic mail.log cases (§ 3). Fixes a bug that last test found: the delivery-line pattern matched status= greedily, taking the *last* occurrence on the line. Postfix appends the remote server's reply verbatim, so a rejection whose reply quoted "status=sent" was filed as a delivered message in the send log. It now takes the first status= after the recipient, which is the real field. R7 (CONTRIBUTING.md) moved to roadmap 2.x — one developer, no external PR flow, so the file would have no audience yet. R1 (compose image tag) and the git tag stay in roadmap § v1.x as the release-commit steps. gofmt/go vet clean on both modules; go test ./... green except the three known Windows-only failures (file perms, backslash paths, renaming an open file). Not exercised on the dev server — no Docker locally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+24
-20
@@ -6,23 +6,25 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Send-log status values (spec 7.3). "queued" is written by the journal-milter
|
||||
// when a message is accepted; the log-tailer advances it to one of the final
|
||||
// states as Postfix reports delivery per recipient.
|
||||
// Send-log status values (architecture.md § Persistence). "queued" is written
|
||||
// by the journal-milter when a message is accepted; the log-tailer advances it
|
||||
// to one of the final states as Postfix reports delivery per recipient.
|
||||
const (
|
||||
StatusQueued = "queued"
|
||||
StatusSent = "sent"
|
||||
StatusDeferred = "deferred"
|
||||
StatusBounced = "bounced"
|
||||
// StatusRejected marks a message the journal-milter refused with a 4xx under
|
||||
// a level-2 rate limit (spec 7.4). Such a row never gets a queue-id and is
|
||||
// excluded from the level-2 message count (it was never sent).
|
||||
// a level-2 rate limit (README § Rate limiting). Such a row never gets a
|
||||
// queue-id and is excluded from the level-2 message count (it was never
|
||||
// sent).
|
||||
StatusRejected = "rejected"
|
||||
)
|
||||
|
||||
// SendLogEntry is a single queued send-log row. The journal-milter creates one
|
||||
// per (queue-id, recipient) pair at end-of-message (spec 7.3.3); every field
|
||||
// except the status/timestamps comes from the accepted message.
|
||||
// per (queue-id, recipient) pair at end-of-message (architecture.md §
|
||||
// Persistence); every field except the status/timestamps comes from the
|
||||
// accepted message.
|
||||
type SendLogEntry struct {
|
||||
QueueID string
|
||||
Domain string
|
||||
@@ -34,8 +36,8 @@ type SendLogEntry struct {
|
||||
|
||||
// InsertQueued records an accepted message in the send log with status
|
||||
// "queued". It is called from the journal-milter hot path, so it returns any
|
||||
// error for the caller to log rather than deciding policy here; the milter must
|
||||
// stay fail-open regardless (spec 7.3).
|
||||
// error for the caller to log rather than deciding policy here; the milter
|
||||
// must stay fail-open regardless (architecture.md § Persistence).
|
||||
func (s *Store) InsertQueued(e SendLogEntry) error {
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
_, err := s.db.Exec(
|
||||
@@ -51,9 +53,10 @@ func (s *Store) InsertQueued(e SendLogEntry) error {
|
||||
}
|
||||
|
||||
// InsertRejected records a message the journal-milter refused under a level-2
|
||||
// rate limit (spec 7.4), so the rejection is visible in the send-log UI. Only
|
||||
// the fields known at MAIL FROM are set (domain, sender, app login); there is no
|
||||
// queue-id or recipient because the message was rejected before it was queued.
|
||||
// rate limit (README § Rate limiting), so the rejection is visible in the
|
||||
// send-log UI. Only the fields known at MAIL FROM are set (domain, sender, app
|
||||
// login); there is no queue-id or recipient because the message was rejected
|
||||
// before it was queued.
|
||||
func (s *Store) InsertRejected(e SendLogEntry) error {
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
_, err := s.db.Exec(
|
||||
@@ -88,9 +91,9 @@ func (s *Store) UpdateStatus(queueID, recipient, status string) (int64, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// SendLogRow is one row as returned to the monitoring UI (spec 7.2, 7.3.3): a
|
||||
// SendLogEntry plus the fields that only exist once a row has been written
|
||||
// (id, current status, timestamps).
|
||||
// SendLogRow is one row as returned to the monitoring UI (architecture.md §
|
||||
// Persistence): a SendLogEntry plus the fields that only exist once a row has
|
||||
// been written (id, current status, timestamps).
|
||||
type SendLogRow struct {
|
||||
ID int64
|
||||
QueueID string
|
||||
@@ -111,7 +114,7 @@ type SendLogFilter struct {
|
||||
}
|
||||
|
||||
// QuerySendLog returns send-log rows matching filter, newest first, for the
|
||||
// monitoring screen's server-side pagination (spec 7.2's send-log view).
|
||||
// monitoring screen's server-side pagination (product.md's send-log view).
|
||||
func (s *Store) QuerySendLog(filter SendLogFilter, limit, offset int) ([]SendLogRow, error) {
|
||||
where, args := sendLogWhere(filter)
|
||||
args = append(args, limit, offset)
|
||||
@@ -171,10 +174,11 @@ func sendLogWhere(f SendLogFilter) (string, []any) {
|
||||
return " WHERE " + strings.Join(clauses, " AND "), args
|
||||
}
|
||||
|
||||
// DeleteSendLogBefore removes send-log rows created before cutoff, implementing
|
||||
// the configurable retention window (spec 7.3, SEND_LOG_RETENTION_DAYS). It
|
||||
// returns the number of rows pruned. created_at is stored as RFC3339 UTC, so a
|
||||
// lexical comparison against the same format is chronologically correct.
|
||||
// DeleteSendLogBefore removes send-log rows created before cutoff,
|
||||
// implementing the configurable retention window (architecture.md §
|
||||
// Persistence, SEND_LOG_RETENTION_DAYS). It returns the number of rows pruned.
|
||||
// created_at is stored as RFC3339 UTC, so a lexical comparison against the
|
||||
// same format is chronologically correct.
|
||||
func (s *Store) DeleteSendLogBefore(cutoff time.Time) (int64, error) {
|
||||
res, err := s.db.Exec(
|
||||
`DELETE FROM send_log WHERE created_at < ?`,
|
||||
|
||||
Reference in New Issue
Block a user