99 lines
3.0 KiB
Python
99 lines
3.0 KiB
Python
"""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.')
|