5 Commits

Author SHA1 Message Date
47d7c53fda feat(db): auto-guard against drizzle journal-skip regression
Twice now we've shipped a deploy that 500'd in production because
drizzle silently skipped freshly-generated migrations whose `when`
timestamps were older than a prior manually-bumped entry (0010/0011
in 1b7f553, then 0012/0013 in 2731888). Both times pnpm migrate
printed "Migrations applied." while the live DB schema lagged the
code's expectations.

Three layers of defence:

1. packages/db/src/journal-check.ts — pure helpers
   - assertJournalMonotonic(entries): walks idx-sorted entries and
     returns each one whose `when` <= the previous entry's `when`,
     plus a suggested `when` value to bump it to.
   - formatJournalViolations(result): renders an actionable
     multi-line message that points at the offending file path.

2. packages/db/src/migrate.ts — pre-flight
   Reads _journal.json BEFORE handing it to drizzle.migrate(). If
   the journal is non-monotonic, it prints the violations + bump
   instructions and exits with code 2. No more "Migrations applied."
   while silently skipping.

3. apps/web/src/test/drizzle-journal-monotonic.test.ts — CI guard
   Reads the committed _journal.json at test time. CI fails on the
   PR before the bad commit can ship. Imports the helper through a
   new "./journal-check" subpath export on @cmbot/db so the test
   doesn't rely on a deep path into the package.

Together: a bad commit fails CI; if it somehow got through, migrate
itself refuses to run; if migrate is bypassed, the previous deploy's
schema stays intact (drizzle wouldn't have skipped anything in any
case where the journal is monotonic).

Web suite 480 → 482 tests, all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 21:40:11 +08:00
838e129f37 chore: add bcryptjs to web + db packages
Pure-JS bcrypt for password hashing. Avoids the native-build pain
of node-bcrypt in our Alpine Docker images. Login is a rare event
so the perf gap is irrelevant for our scale.
2026-05-10 17:41:06 +08:00
e45bcb581a fix(web,build): consume packages/db + shared via dist; bind web to LAN
Two related fixes:

1. Phone (and any LAN client) couldn't reach the web container because
   the dev compose mapped 127.0.0.1:WEB_PORT instead of binding all
   interfaces. Drop the loopback prefix.

2. Turbopack and NodeNext disagree on extension handling: bot's tsc
   needs `.js` extensions in source imports; Turbopack's transpilePackages
   path can't resolve those `.js` requests back to `.ts` source. Switch
   to consuming the workspace packages via their compiled dist instead:
   - packages/db + packages/shared point `main`/`exports` at ./dist/*
   - drop transpilePackages from next.config.ts; web picks up the
     compiled `.js` files directly
   - dev compose command for web builds shared+db before running
     `next dev` so dist is fresh when Turbopack starts
   - put the `.js` extensions back in packages/db source so NodeNext
     compilers (bot's tsc, packages/db's own tsc) are happy
2026-05-10 00:18:56 +08:00
499bcf22ed fix(build): production tsc + Next.js workspace root resolution
Three small build-time fixes surfaced when the Docker images first ran
their full production build (previously only dev mode via tsx):

- packages/shared: exclude *.test.ts from tsc (vitest types not needed
  for shipped output), add @types/node dep so node:crypto resolves
- packages/db: add @types/node dep for the same reason
- apps/web: pin Next.js Turbopack root to the workspace root via
  next.config.ts so the bundler doesn't fail to detect the monorepo
  layout from inside the Docker image
2026-05-09 22:54:51 +08:00
fa4970a76c feat(db): add drizzle schema for all tables + initial migration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 15:19:36 +08:00