From 5051ffd40f205ea222cd6f18e821d3c0b5800428 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Thu, 6 Aug 2026 01:28:37 +0200 Subject: [PATCH] fix: RLS seeding, FQDN 422, stale migration hash, mail_accounts.password_salt - Use migration engine (crm_migration, BYPASSRLS) for default data seeding in app/main.py instead of crm_api role which is RLS-enforced - Skip domains PATCH for dockercompose apps in deploy_api() to avoid 422 - Regenerate migration_hashes.txt for 0085_restore_tenant_rls.py - Add migration 0110: password_salt column to mail_accounts --- alembic/migration_hashes.txt | 2 +- ...0110_add_password_salt_to_mail_accounts.py | 39 +++++++++++++++++++ app/main.py | 6 ++- scripts/deploy.py | 20 ++++++++-- 4 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 alembic/versions/0110_add_password_salt_to_mail_accounts.py diff --git a/alembic/migration_hashes.txt b/alembic/migration_hashes.txt index 43256b3..1eacf55 100644 --- a/alembic/migration_hashes.txt +++ b/alembic/migration_hashes.txt @@ -83,7 +83,7 @@ ba5b221f7ce0271a1b531eb441d2f0afe7b3d53bd44e602b8e839a3806059bfb 0081_disable_r 1705c1788ea57085c2ffe99d985e077ffa2e2e45482a5b6af162a76dcbeda34c 0082_add_sensitivity_to_custom_field_definitions.py f8409a0e4952703b5a1a1ba064f8622071f12c657ad4e8ff1a09c2020d768762 0083_add_missing_deleted_at_columns.py d2bdad015bdf16f6c911f58a08103b1814f0f6d987b4ecd290732ee7a185a843 0084_rls_fail_closed_reactivate.py -9d398d6997302ab5bc045bd655fdfba08fd617b087b86dd2a02356254244570e 0085_restore_tenant_rls.py +b66e11bbcb52d7cfde518cde523a4d8808ddb4a62cc3b8c39aec3c58b95abe19 0085_restore_tenant_rls.py b184eab067c0dfaa66712bd74471b4c65715e90a07521b17577ed15bac707259 0086_fix_global_tables_force_rls.py f0f33e314b52a849f1bad06cfa9ffb5da07890764bc8d22dcd43237293ed90db 0087_add_timestamps_to_password_reset_tokens.py 38e3f4454e079faed2e6fc78cec632d6f78189c46750a7668a9c9c1a845f2bd4 0088_auth_rls_policies.py diff --git a/alembic/versions/0110_add_password_salt_to_mail_accounts.py b/alembic/versions/0110_add_password_salt_to_mail_accounts.py new file mode 100644 index 0000000..b0ec270 --- /dev/null +++ b/alembic/versions/0110_add_password_salt_to_mail_accounts.py @@ -0,0 +1,39 @@ +"""Add password_salt column to mail_accounts. + +⚠️ KI / AGENT HINWEIS — BITTE VOR ÄNDERUNGEN LESEN ⚠️ + +Das ORM-Model MailAccount (app/plugins/builtins/mail/models.py) referenziert +die Spalte `password_salt` (String(64), nullable=False, default=""). +In der Produktions-DB fehlt diese Spalte, was zu SQLAlchemy-Fehlern führt +beim Lesen oder Schreiben von MailAccount-Datensätzen. + +Diese Migration fügt die Spalte mit ADD COLUMN IF NOT EXISTS hinzu, sodass +bestehende Datensätze den Default-Wert "" (leerer String) erhalten. + +Revision ID: 0110 +Revises: 0109 +""" + +from __future__ import annotations + +from alembic import op +import sqlalchemy as sa + +revision = "0110" +down_revision = "0109" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.execute( + "ALTER TABLE mail_accounts " + "ADD COLUMN IF NOT EXISTS password_salt VARCHAR(64) NOT NULL DEFAULT ''" + ) + + +def downgrade() -> None: + op.execute( + "ALTER TABLE mail_accounts " + "DROP COLUMN IF EXISTS password_salt" + ) diff --git a/app/main.py b/app/main.py index cf9d045..e855f14 100644 --- a/app/main.py +++ b/app/main.py @@ -287,9 +287,13 @@ async def lifespan(app: FastAPI): logger.info("Field definitions registered for %d active plugins", len(active_plugin_names)) # Seed default data (EUR currency, 19%/7% tax rates) for all tenants + # ⚠️ Use migration engine (crm_migration, BYPASSRLS) — RLS on currencies/taxes + # blocks inserts from crm_api role without tenant context. from app.core.seeds import seed_default_data + from app.core.db import get_migration_session_factory - async with async_session() as db: + mig_session_factory = get_migration_session_factory() + async with mig_session_factory() as db: try: await seed_default_data(db) await db.commit() diff --git a/scripts/deploy.py b/scripts/deploy.py index 595b48e..6cfc3af 100644 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -307,13 +307,25 @@ def ssh_run(cmd: str, timeout: int = 60) -> tuple[int, str]: def deploy_api(client: CoolifyClient, app_uuid: str, skip_build: bool = False) -> StepResult: """Deploy or restart the application via Coolify API. Uses /api/v1/deploy which works for both dockerfile and dockercompose build packs.""" - # Set FQDN via PATCH + # Set FQDN via PATCH — only for non-dockercompose apps. + # dockercompose apps use docker_compose_domains, not domains. + # PATCHing domains on a dockercompose app returns 422. if APP_DOMAIN: - print(f" Setting FQDN to {APP_DOMAIN}...") try: - client.update_application(app_uuid, domains=APP_DOMAIN) + app_info = client.get_application(app_uuid) + build_pack = app_info.get("build_pack", "") except Exception as e: - print(f" Warning: could not set FQDN: {e}") + print(f" Warning: could not fetch app info to check build_pack: {e}") + build_pack = "" + + if build_pack == "dockercompose": + print(f" Skipping FQDN PATCH (dockercompose app — domain set via docker_compose_domains)") + else: + print(f" Setting FQDN to {APP_DOMAIN}...") + try: + client.update_application(app_uuid, domains=APP_DOMAIN) + except Exception as e: + print(f" Warning: could not set FQDN: {e}") print(" Triggering Coolify deploy via /api/v1/deploy...") try: