Update
This commit is contained in:
@@ -29,23 +29,32 @@ depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
'system_settings',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('key', sa.String(100), nullable=False),
|
||||
sa.Column('value', sa.String(500), nullable=False),
|
||||
sa.Column('description', sa.String(256), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
)
|
||||
op.create_index('ix_system_settings_key', 'system_settings', ['key'], unique=True)
|
||||
|
||||
# Seed the default: registration is open on fresh installs.
|
||||
op.execute(
|
||||
"INSERT INTO system_settings (key, value, description, updated_at) "
|
||||
"VALUES ('registration_enabled', 'true', "
|
||||
"'Allow new users to self-register via /auth/register', NOW())"
|
||||
)
|
||||
# Guard: db.create_all() on first startup already creates this table.
|
||||
# Only create it if it doesn't exist so the migration is idempotent.
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
if 'system_settings' not in inspector.get_table_names():
|
||||
op.create_table(
|
||||
'system_settings',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('key', sa.String(100), nullable=False),
|
||||
sa.Column('value', sa.String(500), nullable=False),
|
||||
sa.Column('description', sa.String(256), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
)
|
||||
op.create_index('ix_system_settings_key', 'system_settings', ['key'], unique=True)
|
||||
op.execute(
|
||||
"INSERT INTO system_settings (key, value, description, updated_at) "
|
||||
"VALUES ('registration_enabled', 'true', "
|
||||
"'Allow new users to self-register via /auth/register', NOW())"
|
||||
)
|
||||
# If the table already exists (created by db.create_all), ensure the
|
||||
# index exists — create_all does not create named Alembic indexes.
|
||||
else:
|
||||
existing_indexes = [idx['name'] for idx in inspector.get_indexes('system_settings')]
|
||||
if 'ix_system_settings_key' not in existing_indexes:
|
||||
op.create_index('ix_system_settings_key', 'system_settings', ['key'], unique=True)
|
||||
|
||||
|
||||
def downgrade():
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
"""Render existing plain-text comment bodies to sanitized HTML.
|
||||
|
||||
Revision ID: 003_render_comments
|
||||
Revises: 002_add_system_settings
|
||||
Create Date: 2026-03-31
|
||||
|
||||
Rationale
|
||||
---------
|
||||
Feature #8 (Markdown rendering) stores rendered HTML in Comment.body at
|
||||
write time. Comments created before this feature are stored as plain text
|
||||
and must be migrated so all bodies are consistently sanitized HTML.
|
||||
|
||||
Also widens alembic_version.version_num from VARCHAR(32) to VARCHAR(64)
|
||||
to accommodate longer revision ID strings.
|
||||
|
||||
Apply
|
||||
-----
|
||||
flask db upgrade
|
||||
|
||||
Rollback
|
||||
--------
|
||||
flask db downgrade
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
revision = '003_render_comments'
|
||||
down_revision = '002_add_system_settings'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
|
||||
# ── 1. Widen alembic_version.version_num to VARCHAR(64) ──────────────────
|
||||
# Flask-Migrate creates this column as VARCHAR(32). Long revision IDs
|
||||
# (>32 chars) cause DataError when Alembic tries to record the new head.
|
||||
# This widen is idempotent — safe to run even if already widened.
|
||||
op.execute(
|
||||
"ALTER TABLE alembic_version "
|
||||
"MODIFY COLUMN version_num VARCHAR(64) NOT NULL"
|
||||
)
|
||||
|
||||
# ── 2. Add body_plain_backup column (idempotent) ──────────────────────────
|
||||
existing_cols = [c['name'] for c in inspector.get_columns('comments')]
|
||||
if 'body_plain_backup' not in existing_cols:
|
||||
op.add_column(
|
||||
'comments',
|
||||
sa.Column('body_plain_backup', sa.Text(), nullable=True),
|
||||
)
|
||||
|
||||
# ── 3. Render plain-text comment bodies to sanitized HTML ─────────────────
|
||||
session = Session(bind=bind)
|
||||
|
||||
from app.services.validation_service import render_comment_body
|
||||
|
||||
rows = session.execute(sa.text('SELECT id, body FROM comments')).fetchall()
|
||||
updated = 0
|
||||
|
||||
for row in rows:
|
||||
comment_id, body = row[0], row[1]
|
||||
if not body:
|
||||
continue
|
||||
# Skip bodies already rendered as HTML (start with an opening tag).
|
||||
if body.lstrip().startswith('<'):
|
||||
continue
|
||||
rendered = render_comment_body(body)
|
||||
session.execute(
|
||||
sa.text(
|
||||
'UPDATE comments '
|
||||
'SET body_plain_backup = :plain, body = :rendered '
|
||||
'WHERE id = :id'
|
||||
),
|
||||
{'plain': body, 'rendered': rendered, 'id': comment_id},
|
||||
)
|
||||
updated += 1
|
||||
|
||||
session.commit()
|
||||
print(f'[MIGRATION 003] Rendered {updated} plain-text comment bodies to HTML.')
|
||||
|
||||
|
||||
def downgrade():
|
||||
bind = op.get_bind()
|
||||
session = Session(bind=bind)
|
||||
|
||||
session.execute(sa.text(
|
||||
'UPDATE comments '
|
||||
'SET body = body_plain_backup '
|
||||
'WHERE body_plain_backup IS NOT NULL'
|
||||
))
|
||||
session.commit()
|
||||
|
||||
op.drop_column('comments', 'body_plain_backup')
|
||||
print('[MIGRATION 003] Downgrade complete — plain-text bodies restored.')
|
||||
Reference in New Issue
Block a user