Collapse migrations, require email at registration

No deployed environments yet, so consolidate the entire migration history
into a single Initial migration and tighten the schema accordingly.

- User.Email is now non-nullable (required); the partial unique index
  used to tolerate legacy null emails is gone in favor of a plain
  unique index.
- Both register paths require an email up front. Invite-path
  registrations must match the invited address (server ignores any
  mismatched client value); family-code registrations bind whatever the
  user supplies but stay unconfirmed (EmailConfirmedAt = null) since
  the family code does not prove email ownership.
- Validation order in /register reworked: invite/family-code resolution
  runs before duplicate-name/email checks so a consumed token surfaces
  a clean "invitation invalid" error instead of getting masked by the
  duplicate-email response.
- All 14 prior migrations replaced with a single Initial migration.
- Test fixtures, builders, and unit tests updated to supply emails.
- Login page register form now collects an email field; invite-bound
  registrations show the invited address as a read-only input.

Local dev DBs need to be recreated (drop the yeschef-pgdata volume or
the yeschef Postgres database). No production data exists yet.
This commit is contained in:
Josh Rogers
2026-05-08 22:58:27 -05:00
parent af085cfb90
commit 09bec105f6
27 changed files with 608 additions and 3574 deletions
+1 -3
View File
@@ -39,9 +39,7 @@ public class YesChefDb(DbContextOptions<YesChefDb> options) : DbContext(options)
e.HasIndex(u => u.Name).IsUnique();
e.Property(u => u.Name).HasMaxLength(100);
e.Property(u => u.Email).HasMaxLength(254);
// Partial unique index: only enforced where Email is set so legacy
// rows (and any future user without an email) don't collide on null.
e.HasIndex(u => u.Email).IsUnique().HasFilter("\"Email\" IS NOT NULL");
e.HasIndex(u => u.Email).IsUnique();
});
modelBuilder.Entity<PasswordResetToken>(e =>