From e572bc910ff5cfeb354b3a5f9650bbcd98b24fdc Mon Sep 17 00:00:00 2001 From: Kunthawat Greethong Date: Mon, 31 Aug 2026 22:21:53 +0700 Subject: [PATCH] fix: boolean server_default must be 'false' not '0' for Postgres PostgreSQL rejects BOOLEAN DEFAULT 0 (DatatypeMismatch: column ... is of type boolean but default expression is of type integer). Migration 0008 (platform_settings.active) crashed the alembic upgrade run by the entrypoint, which in turn crash-looped the worker. Migration 0011 (password_reset_tokens. used) had the same latent bug and would have failed next. Change the Boolean server_default from text('0') to text('false') in both migrations and both ORM models so the DDL is valid on both PostgreSQL (production) and SQLite (local/tests). Verified: full 0001->0011 chain runs on fresh SQLite; alembic check reports no drift; backend suite 201 passed. --- backend/app/models/password_reset.py | 2 +- backend/app/models/settings.py | 2 +- backend/migrations/versions/0008_platform_settings.py | 2 +- backend/migrations/versions/0011_password_reset_tokens.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app/models/password_reset.py b/backend/app/models/password_reset.py index 0de1a70..019ce22 100644 --- a/backend/app/models/password_reset.py +++ b/backend/app/models/password_reset.py @@ -29,7 +29,7 @@ class PasswordResetToken(Base): token_hash: Mapped[str] = mapped_column(String(128), nullable=False) auth_version: Mapped[int] = mapped_column(nullable=False, default=0, server_default=text("0")) used: Mapped[bool] = mapped_column( - Boolean, nullable=False, default=False, server_default=text("0") + Boolean, nullable=False, default=False, server_default=text("false") ) expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) created_at: Mapped[datetime] = mapped_column( diff --git a/backend/app/models/settings.py b/backend/app/models/settings.py index fd8182b..1f3bc43 100644 --- a/backend/app/models/settings.py +++ b/backend/app/models/settings.py @@ -35,7 +35,7 @@ class PlatformSettings(Base): String(64), nullable=True ) active: Mapped[bool] = mapped_column( - Boolean, nullable=False, default=False, server_default=text("0") + Boolean, nullable=False, default=False, server_default=text("false") ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, default=_utc_now, server_default=func.now() diff --git a/backend/migrations/versions/0008_platform_settings.py b/backend/migrations/versions/0008_platform_settings.py index 046ee5f..8ece622 100644 --- a/backend/migrations/versions/0008_platform_settings.py +++ b/backend/migrations/versions/0008_platform_settings.py @@ -17,7 +17,7 @@ def upgrade() -> None: sa.Column("settings", sa.JSON(), nullable=True), sa.Column("secret_ref", sa.String(length=512), nullable=True), sa.Column("updated_by_user_id", sa.String(length=64), nullable=True), - sa.Column("active", sa.Boolean(), server_default=sa.text("0"), nullable=False), + sa.Column("active", sa.Boolean(), server_default=sa.text("false"), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.PrimaryKeyConstraint("id"), sa.UniqueConstraint("version", name="uq_platform_settings_version"), diff --git a/backend/migrations/versions/0011_password_reset_tokens.py b/backend/migrations/versions/0011_password_reset_tokens.py index edb30ce..a1754a8 100644 --- a/backend/migrations/versions/0011_password_reset_tokens.py +++ b/backend/migrations/versions/0011_password_reset_tokens.py @@ -16,7 +16,7 @@ def upgrade() -> None: sa.Column("user_id", sa.String(length=64), nullable=False), sa.Column("token_hash", sa.String(length=128), nullable=False), sa.Column("auth_version", sa.Integer(), server_default=sa.text("0"), nullable=False), - sa.Column("used", sa.Boolean(), server_default=sa.text("0"), nullable=False), + sa.Column("used", sa.Boolean(), server_default=sa.text("false"), nullable=False), sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),