This reverts 2529b51. Not a retreat — a reordering, on the operator's
call, and the better sequence.
The squash's acceptance test (run 4971) found ~130 places where the ORM
models do not describe the deployed schema (#3275), including a
unique=True the database never had and two UNIQUE indexes that exist
only in migrations. Collapsing now would have baked all of that into the
one file a public installer starts from.
So: fix the drift first as ordinary migrations on the intact chain, let
the operator deploy so their database moves to the corrected head, and
only then collapse. The baseline is then generated from reconciled
models and reproduces a schema worth reproducing.
Nothing is lost by reverting. The baseline was never deployed, and
regenerating it after the fixes is strictly better than patching this
copy — it will come out of autogenerate correct rather than needing the
same hand-finishing twice.
91 lines
3.3 KiB
Python
91 lines
3.3 KiB
Python
"""external_link table — off-platform file-host links found in post bodies
|
|
|
|
Creators host the real files on mega.nz / Google Drive / MediaFire / Dropbox /
|
|
Pixeldrain and link them in the post text. This table records each such link
|
|
(so nothing is silently dropped), and doubles as the dedup + dead-letter ledger
|
|
the download worker (a later slice) walks. `url` keeps the FULL link including
|
|
the `#fragment` — mega.nz's decryption key lives there; truncating it makes the
|
|
file undownloadable.
|
|
|
|
CHECK whitelists for host + status include the full enum up front (incl. the
|
|
download-worker statuses) so the worker slice needs no constraint migration.
|
|
|
|
Revision ID: 0049
|
|
Revises: 0048
|
|
Create Date: 2026-06-14
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision: str = "0049"
|
|
down_revision: Union[str, None] = "0048"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"external_link",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column(
|
|
"post_id", sa.Integer(),
|
|
sa.ForeignKey("post.id", ondelete="CASCADE"), nullable=False,
|
|
),
|
|
sa.Column(
|
|
"artist_id", sa.Integer(),
|
|
sa.ForeignKey("artist.id", ondelete="SET NULL"), nullable=True,
|
|
),
|
|
sa.Column("host", sa.String(length=16), nullable=False),
|
|
sa.Column("url", sa.Text(), nullable=False),
|
|
sa.Column("label", sa.Text(), nullable=True),
|
|
sa.Column(
|
|
"status", sa.String(length=16), nullable=False,
|
|
server_default="pending",
|
|
),
|
|
sa.Column("attempts", sa.Integer(), nullable=False, server_default="0"),
|
|
sa.Column("last_error", sa.Text(), nullable=True),
|
|
sa.Column(
|
|
"attachment_id", sa.Integer(),
|
|
sa.ForeignKey("post_attachment.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"created_at", sa.DateTime(timezone=True), nullable=False,
|
|
server_default=sa.func.now(),
|
|
),
|
|
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("duration_seconds", sa.Float(), nullable=True),
|
|
sa.CheckConstraint(
|
|
"host IN ('mega','gdrive','mediafire','dropbox','pixeldrain')",
|
|
name="ck_external_link_host",
|
|
),
|
|
sa.CheckConstraint(
|
|
"status IN ('pending','downloading','downloaded','failed',"
|
|
"'skipped','dead')",
|
|
name="ck_external_link_status",
|
|
),
|
|
)
|
|
op.create_index(
|
|
"ix_external_link_post_id", "external_link", ["post_id"],
|
|
)
|
|
op.create_index(
|
|
"ix_external_link_artist_id", "external_link", ["artist_id"],
|
|
)
|
|
op.create_index(
|
|
"ix_external_link_status", "external_link", ["status"],
|
|
)
|
|
op.create_index(
|
|
"uq_external_link_post_url", "external_link", ["post_id", "url"],
|
|
unique=True,
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("uq_external_link_post_url", table_name="external_link")
|
|
op.drop_index("ix_external_link_status", table_name="external_link")
|
|
op.drop_index("ix_external_link_artist_id", table_name="external_link")
|
|
op.drop_index("ix_external_link_post_id", table_name="external_link")
|
|
op.drop_table("external_link")
|