Files
ServerKit/backend/migrations/versions/088_soft_delete_managed_databases.py
Juan DenisandClaude Fable 5 f237d4f3b4 feat(databases): ManagedDatabase adopts SoftDelete into the Recycle Bin (plan 77 B5)
First of the regret-ranked adoptions: a plain delete now tombstones the
row (migration 088; no unique constraints, so no partial-index rewrite).
The database content and its managed BackupPolicy survive the bin window
— restore brings everything back intact; PURGE removes the policy but
NEVER drops database content; the explicit ?drop=true path stays
immediately destructive. Active read paths (list/get/adopt/manifest/env
references/app detail) switch to query_active(); the backup executor
deliberately stays tombstone-blind so bin-parked data keeps backing up.
Server (98 query sites, fleet enrollment + HMAC secrets) is the next
ranked adoption — deliberately its own pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 05:43:53 -04:00

38 lines
1.3 KiB
Python

"""SoftDelete adoption for managed databases (plan 77 B5).
Adds the tombstone pair to managed_databases (migration 083 pattern). No
unique constraints exist on the table, so no partial-unique-index rewrite is
needed.
Revision ID: 088_soft_delete_managed_databases
Revises: 087_run_log_entries
"""
import sqlalchemy as sa
from alembic import op
revision = '088_soft_delete_managed_databases'
down_revision = '087_run_log_entries'
branch_labels = None
depends_on = None
def _has_column(table, column):
bind = op.get_bind()
return column in [c['name'] for c in sa.inspect(bind).get_columns(table)]
def upgrade():
if not _has_column('managed_databases', 'deleted_at'):
op.add_column('managed_databases', sa.Column('deleted_at', sa.DateTime(), nullable=True))
op.create_index('ix_managed_databases_deleted_at', 'managed_databases', ['deleted_at'])
if not _has_column('managed_databases', 'deleted_by_id'):
op.add_column('managed_databases', sa.Column('deleted_by_id', sa.Integer(), nullable=True))
def downgrade():
if _has_column('managed_databases', 'deleted_at'):
op.drop_index('ix_managed_databases_deleted_at', table_name='managed_databases')
op.drop_column('managed_databases', 'deleted_at')
if _has_column('managed_databases', 'deleted_by_id'):
op.drop_column('managed_databases', 'deleted_by_id')