From 05f5b49e261ef2c1feaed3327f76fcb4ca1b5f98 Mon Sep 17 00:00:00 2001 From: Kyle Pope Date: Tue, 17 Mar 2026 03:53:42 +0800 Subject: [PATCH] =?UTF-8?q?Fix=20500=20on=20POST=20/api/projects/:id/membe?= =?UTF-8?q?rs=20=E2=80=94=20add=20project=5Finvite=20types=20to=20notifica?= =?UTF-8?q?tion=20CHECK=20constraint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The invite_members handler called create_notification with type="project_invite", which is not in the ck_notifications_type CHECK constraint. The db.flush() inside the handler flushed both the ProjectMember and Notification INSERTs atomically, causing a CheckViolationError → 500. Added "project_invite", "project_invite_accepted", "project_invite_rejected" to the model tuple and migration 060 drops/recreates the constraint to include them. Co-Authored-By: Claude Sonnet 4.6 --- .../060_expand_notification_types_project.py | 35 +++++++++++++++++++ backend/app/models/notification.py | 1 + 2 files changed, 36 insertions(+) create mode 100644 backend/alembic/versions/060_expand_notification_types_project.py diff --git a/backend/alembic/versions/060_expand_notification_types_project.py b/backend/alembic/versions/060_expand_notification_types_project.py new file mode 100644 index 0000000..723295d --- /dev/null +++ b/backend/alembic/versions/060_expand_notification_types_project.py @@ -0,0 +1,35 @@ +"""Expand notification type CHECK for project invite types + +Revision ID: 060 +Revises: 059 +""" +from alembic import op + +revision = "060" +down_revision = "059" +branch_labels = None +depends_on = None + +_OLD_TYPES = ( + "connection_request", "connection_accepted", "connection_rejected", + "calendar_invite", "calendar_invite_accepted", "calendar_invite_rejected", + "event_invite", "event_invite_response", + "info", "warning", "reminder", "system", +) +_NEW_TYPES = _OLD_TYPES + ( + "project_invite", "project_invite_accepted", "project_invite_rejected", +) + + +def _check_sql(types: tuple) -> str: + return f"type IN ({', '.join(repr(t) for t in types)})" + + +def upgrade() -> None: + op.drop_constraint("ck_notifications_type", "notifications", type_="check") + op.create_check_constraint("ck_notifications_type", "notifications", _check_sql(_NEW_TYPES)) + + +def downgrade() -> None: + op.drop_constraint("ck_notifications_type", "notifications", type_="check") + op.create_check_constraint("ck_notifications_type", "notifications", _check_sql(_OLD_TYPES)) diff --git a/backend/app/models/notification.py b/backend/app/models/notification.py index e0d6f47..676be3e 100644 --- a/backend/app/models/notification.py +++ b/backend/app/models/notification.py @@ -9,6 +9,7 @@ _NOTIFICATION_TYPES = ( "connection_request", "connection_accepted", "connection_rejected", "calendar_invite", "calendar_invite_accepted", "calendar_invite_rejected", "event_invite", "event_invite_response", + "project_invite", "project_invite_accepted", "project_invite_rejected", "info", "warning", "reminder", "system", )