From eaa71780d471832c3afd627b4a03a24ecfd62e5e Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Fri, 24 Jul 2026 10:34:38 +0200 Subject: [PATCH] fix: add missing deleted_at columns to all plugin tables, fix system-settings response schema, fix transaction rollback in permissions --- app/core/permissions.py | 14 ++++++++--- .../calendar/migrations/0001_initial.sql | 24 +++++++++++++++---- .../migrations/0002_add_deleted_at.sql | 17 +++++++++++++ .../entity_links/migrations/0001_initial.sql | 1 + .../migrations/0002_add_deleted_at.sql | 3 +++ .../mail/migrations/0010_add_deleted_at.sql | 18 ++++++++++++++ .../mcp_client/migrations/0001_initial.sql | 1 + .../migrations/0002_add_deleted_at.sql | 3 +++ .../builtins/tags/migrations/0001_initial.sql | 2 ++ .../tags/migrations/0002_add_deleted_at.sql | 4 ++++ .../migrations/0001_initial.sql | 5 +++- .../migrations/0003_add_deleted_at.sql | 6 +++++ app/routes/system_settings.py | 2 +- app/schemas/system_settings.py | 12 +++++----- 14 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 app/plugins/builtins/calendar/migrations/0002_add_deleted_at.sql create mode 100644 app/plugins/builtins/entity_links/migrations/0002_add_deleted_at.sql create mode 100644 app/plugins/builtins/mail/migrations/0010_add_deleted_at.sql create mode 100644 app/plugins/builtins/mcp_client/migrations/0002_add_deleted_at.sql create mode 100644 app/plugins/builtins/tags/migrations/0002_add_deleted_at.sql create mode 100644 app/plugins/builtins/unified_search/migrations/0003_add_deleted_at.sql diff --git a/app/core/permissions.py b/app/core/permissions.py index 88ce6b1..2fc90b0 100644 --- a/app/core/permissions.py +++ b/app/core/permissions.py @@ -105,9 +105,17 @@ async def resolve_permissions( } """ # Check system admin first - user_q = select(User.is_system_admin).where(User.id == user_id) - user_result = await db.execute(user_q) - is_system_admin = user_result.scalar() or False + # If a previous query in this session failed, the transaction may be aborted. + # Rollback to recover before executing our query. + try: + user_q = select(User.is_system_admin).where(User.id == user_id) + user_result = await db.execute(user_q) + is_system_admin = user_result.scalar() or False + except Exception: + await db.rollback() + user_q = select(User.is_system_admin).where(User.id == user_id) + user_result = await db.execute(user_q) + is_system_admin = user_result.scalar() or False if is_system_admin: return { diff --git a/app/plugins/builtins/calendar/migrations/0001_initial.sql b/app/plugins/builtins/calendar/migrations/0001_initial.sql index 5d55c02..eeed794 100644 --- a/app/plugins/builtins/calendar/migrations/0001_initial.sql +++ b/app/plugins/builtins/calendar/migrations/0001_initial.sql @@ -46,7 +46,10 @@ CREATE TABLE IF NOT EXISTS calendar_entry_links ( tenant_id UUID NOT NULL, entry_id UUID REFERENCES calendar_entries(id) ON DELETE CASCADE, entity_type VARCHAR(50) NOT NULL, - entity_id UUID NOT NULL + entity_id UUID NOT NULL, + deleted_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_entry_links_entry ON calendar_entry_links(entry_id); @@ -56,7 +59,10 @@ CREATE TABLE IF NOT EXISTS calendar_shares ( calendar_id UUID REFERENCES calendars(id) ON DELETE CASCADE, user_id UUID, group_id UUID, - permission VARCHAR(10) NOT NULL + permission VARCHAR(10) NOT NULL, + deleted_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_calendar_shares_cal ON calendar_shares(calendar_id); @@ -74,7 +80,9 @@ CREATE TABLE IF NOT EXISTS subtasks ( entry_id UUID REFERENCES calendar_entries(id) ON DELETE CASCADE, title VARCHAR(500) NOT NULL, completed BOOLEAN DEFAULT false, - created_at TIMESTAMPTZ DEFAULT NOW() + deleted_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_subtasks_entry ON subtasks(entry_id); @@ -82,7 +90,10 @@ CREATE TABLE IF NOT EXISTS resources ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, name VARCHAR(200) NOT NULL, - type VARCHAR(50) NOT NULL + type VARCHAR(50) NOT NULL, + deleted_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_resources_tenant ON resources(tenant_id); @@ -92,7 +103,10 @@ CREATE TABLE IF NOT EXISTS resource_bookings ( resource_id UUID REFERENCES resources(id) ON DELETE CASCADE, entry_id UUID REFERENCES calendar_entries(id) ON DELETE CASCADE, start_at TIMESTAMPTZ NOT NULL, - end_at TIMESTAMPTZ NOT NULL + end_at TIMESTAMPTZ NOT NULL, + deleted_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_bookings_resource ON resource_bookings(resource_id); CREATE INDEX IF NOT EXISTS idx_bookings_entry ON resource_bookings(entry_id); diff --git a/app/plugins/builtins/calendar/migrations/0002_add_deleted_at.sql b/app/plugins/builtins/calendar/migrations/0002_add_deleted_at.sql new file mode 100644 index 0000000..010550a --- /dev/null +++ b/app/plugins/builtins/calendar/migrations/0002_add_deleted_at.sql @@ -0,0 +1,17 @@ +-- Fix: CalendarEntryLink, CalendarShare, Subtask, Resource, ResourceBooking inherit TenantMixin -> SoftDeleteMixin (deleted_at) +-- Migration 0001 only added deleted_at to calendars and calendar_entries +ALTER TABLE calendar_entry_links ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE calendar_shares ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE subtasks ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE resources ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE resource_bookings ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +-- Also add missing created_at/updated_at for tables that lacked them +ALTER TABLE calendar_entry_links ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE calendar_entry_links ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE calendar_shares ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE calendar_shares ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE subtasks ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE resources ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE resources ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE resource_bookings ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ DEFAULT NOW(); +ALTER TABLE resource_bookings ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW(); diff --git a/app/plugins/builtins/entity_links/migrations/0001_initial.sql b/app/plugins/builtins/entity_links/migrations/0001_initial.sql index 71b337c..698ecb1 100644 --- a/app/plugins/builtins/entity_links/migrations/0001_initial.sql +++ b/app/plugins/builtins/entity_links/migrations/0001_initial.sql @@ -6,6 +6,7 @@ CREATE TABLE IF NOT EXISTS entity_links ( entity_type VARCHAR(20) NOT NULL, entity_id UUID NOT NULL, created_by UUID, + deleted_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_entity_links_file_entity UNIQUE (tenant_id, file_id, entity_type, entity_id) diff --git a/app/plugins/builtins/entity_links/migrations/0002_add_deleted_at.sql b/app/plugins/builtins/entity_links/migrations/0002_add_deleted_at.sql new file mode 100644 index 0000000..dcc4cec --- /dev/null +++ b/app/plugins/builtins/entity_links/migrations/0002_add_deleted_at.sql @@ -0,0 +1,3 @@ +-- Fix: EntityLink inherits TenantMixin -> SoftDeleteMixin (deleted_at) +-- Migration 0001 did not include this column +ALTER TABLE entity_links ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; diff --git a/app/plugins/builtins/mail/migrations/0010_add_deleted_at.sql b/app/plugins/builtins/mail/migrations/0010_add_deleted_at.sql new file mode 100644 index 0000000..e9fd34b --- /dev/null +++ b/app/plugins/builtins/mail/migrations/0010_add_deleted_at.sql @@ -0,0 +1,18 @@ +-- Fix: All mail models inherit TenantMixin -> SoftDeleteMixin (deleted_at) +-- Migration 0001 did not include deleted_at on most tables; 0008 only fixed mail_sync_queue +-- This migration adds deleted_at to all remaining mail tables +ALTER TABLE mail_accounts ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_folders ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mails ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_attachments ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_labels ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_label_assignments ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_rules ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_templates ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_signatures ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE vacation_sent_log ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_seen_by ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_account_delegates ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE mail_account_send_permissions ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE pgp_keys ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE contact_pgp_keys ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; diff --git a/app/plugins/builtins/mcp_client/migrations/0001_initial.sql b/app/plugins/builtins/mcp_client/migrations/0001_initial.sql index a4df65c..2df223d 100644 --- a/app/plugins/builtins/mcp_client/migrations/0001_initial.sql +++ b/app/plugins/builtins/mcp_client/migrations/0001_initial.sql @@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS mcp_server_configs ( description TEXT, last_connected_at TIMESTAMPTZ, created_by UUID, + deleted_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); diff --git a/app/plugins/builtins/mcp_client/migrations/0002_add_deleted_at.sql b/app/plugins/builtins/mcp_client/migrations/0002_add_deleted_at.sql new file mode 100644 index 0000000..55013b8 --- /dev/null +++ b/app/plugins/builtins/mcp_client/migrations/0002_add_deleted_at.sql @@ -0,0 +1,3 @@ +-- Fix: McpServerConfig inherits TenantMixin -> SoftDeleteMixin (deleted_at) +-- Migration 0001 did not include this column, causing queries to crash +ALTER TABLE mcp_server_configs ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; diff --git a/app/plugins/builtins/tags/migrations/0001_initial.sql b/app/plugins/builtins/tags/migrations/0001_initial.sql index f3d01f7..0b6873a 100644 --- a/app/plugins/builtins/tags/migrations/0001_initial.sql +++ b/app/plugins/builtins/tags/migrations/0001_initial.sql @@ -4,6 +4,7 @@ CREATE TABLE IF NOT EXISTS tags ( tenant_id UUID NOT NULL, name VARCHAR(100) NOT NULL, color VARCHAR(7) NOT NULL DEFAULT '#6B7280', + deleted_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_tags_tenant_name UNIQUE (tenant_id, name) @@ -16,6 +17,7 @@ CREATE TABLE IF NOT EXISTS tag_assignments ( tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE, entity_type VARCHAR(20) NOT NULL, entity_id UUID NOT NULL, + deleted_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CONSTRAINT uq_tag_assignments_entity UNIQUE (tenant_id, tag_id, entity_type, entity_id) diff --git a/app/plugins/builtins/tags/migrations/0002_add_deleted_at.sql b/app/plugins/builtins/tags/migrations/0002_add_deleted_at.sql new file mode 100644 index 0000000..72384d1 --- /dev/null +++ b/app/plugins/builtins/tags/migrations/0002_add_deleted_at.sql @@ -0,0 +1,4 @@ +-- Fix: Tag and TagAssignment inherit TenantMixin -> SoftDeleteMixin (deleted_at) +-- Migration 0001 did not include this column, causing queries to crash +ALTER TABLE tags ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE tag_assignments ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; diff --git a/app/plugins/builtins/unified_search/migrations/0001_initial.sql b/app/plugins/builtins/unified_search/migrations/0001_initial.sql index 4f19573..7e89f5e 100644 --- a/app/plugins/builtins/unified_search/migrations/0001_initial.sql +++ b/app/plugins/builtins/unified_search/migrations/0001_initial.sql @@ -11,6 +11,7 @@ CREATE TABLE IF NOT EXISTS unified_search_providers ( plugin_name VARCHAR(80) NOT NULL, is_active BOOLEAN NOT NULL DEFAULT TRUE, config JSONB NOT NULL DEFAULT '{}'::jsonb, + deleted_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), UNIQUE(tenant_id, entity_type) @@ -27,7 +28,9 @@ CREATE TABLE IF NOT EXISTS unified_search_index_log ( action VARCHAR(20) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'pending', error_message TEXT, - created_at TIMESTAMPTZ NOT NULL DEFAULT now() + deleted_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS ix_usil_tenant ON unified_search_index_log(tenant_id); diff --git a/app/plugins/builtins/unified_search/migrations/0003_add_deleted_at.sql b/app/plugins/builtins/unified_search/migrations/0003_add_deleted_at.sql new file mode 100644 index 0000000..65bab79 --- /dev/null +++ b/app/plugins/builtins/unified_search/migrations/0003_add_deleted_at.sql @@ -0,0 +1,6 @@ +-- Fix: SearchProviderRegistry and SearchIndexLog inherit TenantMixin -> SoftDeleteMixin (deleted_at) +-- Migration 0001 did not include this column +ALTER TABLE unified_search_providers ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +ALTER TABLE unified_search_index_log ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ; +-- SearchIndexLog also needs updated_at (TimestampMixin) +ALTER TABLE unified_search_index_log ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now(); diff --git a/app/routes/system_settings.py b/app/routes/system_settings.py index 9420e8e..e2192b7 100644 --- a/app/routes/system_settings.py +++ b/app/routes/system_settings.py @@ -24,7 +24,7 @@ async def get_system_settings( tenant_id = uuid.UUID(current_user["tenant_id"]) result = await system_settings_service.get_system_settings(db, tenant_id) if result is None: - return {} + return SystemSettingsResponse() return result diff --git a/app/schemas/system_settings.py b/app/schemas/system_settings.py index 1527c7b..f0e9b2b 100644 --- a/app/schemas/system_settings.py +++ b/app/schemas/system_settings.py @@ -36,13 +36,13 @@ class SystemSettingsUpsert(BaseModel): class SystemSettingsResponse(BaseModel): - id: str - company_name: str + id: str | None = None + company_name: str = "" company_legal_form: str | None = None - company_street: str - company_city: str - company_zip: str - company_country: str + company_street: str = "" + company_city: str = "" + company_zip: str = "" + company_country: str = "" tax_number: str | None = None vat_id: str | None = None iban: str | None = None