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>
Prep PR for the admin auth module (docs/plan-admin-auth.md step 1).
better-auth 1.7 ships ESM only, so the API moves off CommonJS:
- "type": "module", module nodenext, target ES2024, .js suffixes on all
relative imports, require('mariadb'|'cors') replaced by imports, and
export= packages (winston, app-root-path, bcrypt) consumed via default
imports. The logger now uses appRoot.path explicitly.
- TypeScript 5.9, @types/node 26, tslint removed. Node 26 pinned via
engines and .nvmrc (Plesk runs 26).
- Jest 28 + ts-jest replaced by vitest 5. Eight test files depend on
hoisted module mocks with static imports and resetModules + require,
which Jest's ESM mode does not support; vitest keeps them nearly
verbatim. Coverage via @vitest/coverage-v8 (lcov), Sonar generic report
via vitest-sonar-reporter, so sonar-project.properties is unchanged.
vitest.config.ts sets FEEDBACK_IP_SALT so the suite passes without a
local .env.
- dotenv 8 -> 16 and axios 0.24 -> 1.x: their old typings are not
resolvable under nodenext.
- autoCommit: false dropped from the pool configs; it is not a mariadb
connector option and was silently ignored.
tsc clean, 96/96 tests green, compiled app boots and serves /, /docs and
CORS under Node ESM.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>