Squeamish about New()'s empty-domain-string sentinel for "dev mode, skip
OIDC discovery" - split into New (always makes a real OIDC discovery
call, all params required) and NewDev (no ctx/domain/credentials at all,
since none are used). main.go now branches on cfg.DevAuthEnabled to pick
the right constructor instead of main.go/config.go coordinating on when
it's safe to pass empty strings.
Also finishes out the dev-auth flow this enables: config.Load reads a
DEV_AUTH_ENABLED-aware env file and only requires Auth0 vars when dev
auth is off; a PORT config var replaces the hardcoded :8082; and the nav
UI (layout/index templates, ui router) points login/logout links at
/api/auth/dev-login and a new /api/auth/dev-logout route when dev auth
is enabled, so the whole login/logout loop works locally without a real
Auth0 app.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Dispatching an event to the listener previously happened from an
un-awaited goroutine, with only a log line on failure - once
processed_at was set, a dropped or failed notification was permanently
and silently lost, with no way to tell it had happened.
Replaces the processed/processed_successfully booleans with a
notified_at/processed_at pair (migration 000031): the dispatcher sets
notified_at and hands the event to MockEventListener.Notify, which now
also receives an ack callback the listener calls whenever it's truly
done, synchronously or arbitrarily later. Anything still "notified" but
unacked past notifyRetryAfter (a tunable field, not a stored
per-row timestamp) gets notified again on the dispatcher's normal
poll/reactive loop - no new retry mechanism needed. ack is idempotent,
since a late ack from an earlier attempt and one from a retry can both
eventually fire for the same event.
Dispatch is deferred until after the transaction that recorded
notified_at has actually committed, so ack's independent write can't
race a still-open transaction it implicitly depends on being visible.
The real SSE listener (server/sse/db_event_publisher.go) acks inline,
since its work is synchronous.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEDaCB7C2NEBgyvqEtZuxY