Commit Graph

2 Commits

Author SHA1 Message Date
Paddy d960ac8e24 Fold the pre-deploy review findings into the calendar cutover
A fresh-context review before deploying found two things that would have
broken production, both in the runbook rather than the code.

The deploy order named only migration 003. Production has none of the three -
001 and 002 were only ever applied to the dev database - and the new API reads
the columns they add on every request, so following it literally would have
500'd every calendar call including the anonymous feed the public website
uses. Step 4 now carries a numbered checklist with a verification query.

APP_ORIGINS replaces the code's default list rather than adding to it, so
naming calendar.nachklang.art in DEFAULT_APP_ORIGINS is not enough if that
variable is set on the vhost - and its failure mode is the quiet one the
config already warns about, where everything works except sign-out. Added to
the same checklist.

Also from the review:

The two operands of the read guard on /json/next and /ical were swapped so the
password check short-circuits first. They are side-effect free, so the order
was free - but the old one put an admin-database query in front of the public
feed for any caller holding a .nachklang.art cookie, which is a dependency
that feed has never had. Two tests now assert the admin database is not
consulted at all.

/:calendar/json/next had no route-level test, despite being the endpoint the
public website actually calls and the property named as load-bearing. Covered
now, along with the rest of its credential matrix.

Migrations 001 and 002 gained IF NOT EXISTS. They are applied by hand with no
tracking table, so a partial re-run should be a no-op rather than an error
that aborts the rest of the paste. Verified by applying all three twice to a
throwaway container and diffing against the dev schema.

Swagger: two descriptions still claimed authentication was required where the
public calendar needs none, the calendar enum omitted `birthdays`, and a
`createdBy` request-body field was documented and read but never persisted -
misleading in a way that suggests a client can set authorship. Removed. The
CORS comment describing the calendar's query-parameter sessions is no longer
true and was rewritten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 22:41:49 +02:00
Paddy 61d3883479 Read calendar event creators from the admin module, and archive the old ones
Steps 1 and 3 of docs/calendar-auth-migration.md. The calendar is the last
module still authenticating against its own users/sessions tables; this is
the groundwork that lets step 4 swap it for the shared admin identity.

An event now records its creator twice: created_by_id, the legacy INT into
the calendar database's own users table, and created_by_user_id, the admin
module's VARCHAR(36) id. The two live in different databases, so there is no
foreign key and no join - a cross-schema reference would tie the schemas'
lifecycles together, and the name is instead resolved through one lookup per
result set against the admin database.

The creator is only ever rendered as a name; nothing authorises on it. That
is what makes the planned account backfill unnecessary - dropped by decision -
and what makes the read degrade rather than fail: an admin id that no longer
resolves falls back, and an unreachable admin database costs a name rather
than the response. The public calendar is read anonymously by nachklang.art
and has never depended on the admin database being up.

Since there is no backfill, step 5 dropping the legacy users table would have
erased the authorship of every pre-cutover event. Migration 002 brings that
part of step 5 forward and snapshots the names onto the events themselves, so
the data is safe well before the table holding it goes away.

The same SELECT and row mapper existed in four copies; collapsed to one of
each first, so the dual read is written once rather than four times.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 22:09:21 +02:00