This commit is contained in:
cash
2026-03-29 23:50:49 -05:00
commit eb5e194331
56 changed files with 4010 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
"""merge user-branch fixes
Revision ID: 03568bb37289
Revises: user_counters_defaults, user_counters_defaults_old
Create Date: 2025-05-01 16:01:28.514674
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '03568bb37289'
down_revision: Union[str, None] = ('user_counters_defaults', 'user_counters_defaults_old')
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
pass
def downgrade() -> None:
"""Downgrade schema."""
pass

View File

@@ -0,0 +1,67 @@
"""tier text not int
Revision ID: 05d6342e2105
Revises: 20250506abcd
Create Date: 2025-05-06 16:42:38.378374
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy import text
# revision identifiers, used by Alembic.
revision: str = '05d6342e2105'
down_revision: Union[str, None] = '20250506abcd'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# 1) make sure every numeric tier is inside the allowed range
# (optional safety; no type change yet)
op.execute("""
UPDATE users
SET tier = 0
WHERE pg_typeof(tier)::text = 'integer'
AND tier NOT IN (0,1,2,3);
""")
# 2) ALTER COLUMN to TEXT first numeric values become '0','1',...
op.alter_column(
"users", "tier",
existing_type=sa.Integer(),
type_=sa.Text(),
postgresql_using="tier::text",
nullable=False,
server_default=sa.text("'Online'")
)
# 3) now map the stringified numbers to names
op.execute("""
UPDATE users SET tier =
CASE tier
WHEN '0' THEN 'Online'
WHEN '1' THEN 'Rank 1'
WHEN '2' THEN 'Rank 2'
WHEN '3' THEN 'Rank 3'
ELSE tier
END;
""")
def downgrade() -> None:
# revert to integer, mapping back Online→0 etc. if you really need it
op.alter_column(
"users", "tier",
existing_type=sa.Text(),
type_=sa.Integer(),
postgresql_using="""
CASE tier
WHEN 'Rank 1' THEN 1
WHEN 'Rank 2' THEN 2
WHEN 'Rank 3' THEN 3
ELSE 0
END::integer
""",
server_default=sa.text("0"),
nullable=False,
)

View File

@@ -0,0 +1,37 @@
"""add bonus_active_until to users
Revision ID: 175f03f1c9f7
Revises: ff38ddad43af
Create Date: 2025-05-01 16:58:15.855501
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '175f03f1c9f7'
down_revision: Union[str, None] = 'ff38ddad43af'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade():
if not _has_column("users", "bonus_active_until"):
op.add_column(
"users",
sa.Column("bonus_active_until", sa.DateTime(), nullable=True),
)
def downgrade():
op.drop_column("users", "bonus_active_until")
# helper
from sqlalchemy.engine.reflection import Inspector
def _has_column(table, column):
bind = op.get_bind()
insp = Inspector.from_engine(bind)
return column in [c["name"] for c in insp.get_columns(table)]

View File

@@ -0,0 +1,55 @@
"""Drop XP / tier related columns (+data) if they exist
Revision ID: 20250521_remove_xp_system
Revises: 05d6342e2105
Create Date: 2025-05-21
"""
from alembic import op
import sqlalchemy as sa
revision = "20250521_remove_xp_system"
down_revision = "05d6342e2105"
branch_labels = None
depends_on = None
# added "data" here ↓↓↓
XP_COLUMNS = (
"videos_downloaded",
"mb_usage",
"level",
"xp",
"tier",
"admin",
"vip_badge",
"bonus_active_until",
"score",
"data", # ← drop the NOT-NULL JSON/profile blob
)
def upgrade() -> None:
conn = op.get_bind()
existing = {c["name"] for c in sa.inspect(conn).get_columns("users")}
with op.batch_alter_table("users") as batch:
for col in XP_COLUMNS:
if col in existing:
batch.drop_column(col)
def downgrade() -> None:
conn = op.get_bind()
existing = {c["name"] for c in sa.inspect(conn).get_columns("users")}
with op.batch_alter_table("users") as batch:
# Only re-add if missing; "data" comes back as JSON/Text and NULL-able
add = lambda name, *args, **kw: (
batch.add_column(sa.Column(name, *args, **kw))
if name not in existing else None
)
add("videos_downloaded", sa.Integer(), server_default="0", nullable=False)
add("mb_usage", sa.Float(), server_default="0", nullable=False)
add("level", sa.Integer(), server_default="1", nullable=False)
add("xp", sa.Integer(), server_default="0", nullable=False)
add("tier", sa.Text(), server_default="Online")
add("admin", sa.Boolean(), server_default="false", nullable=False)
add("vip_badge", sa.Text())
add("bonus_active_until", sa.DateTime(timezone=True))
add("score", sa.Float(), server_default="0")
add("data", sa.JSON(), server_default="{}") # <<<

View File

@@ -0,0 +1,36 @@
"""default zeros for new user counters"""
from alembic import op
import sqlalchemy as sa
revision = "user_counters_defaults"
down_revision = "add_ok_to_dl_stats"
branch_labels = None
depends_on = None
def _add(column: str, coltype, default_sql: str):
# Add column nullable=True with default, then make NOT NULL & drop default
op.add_column(
"users",
sa.Column(column, coltype, nullable=True, server_default=sa.text(default_sql)),
)
op.alter_column("users", column, server_default=None, nullable=False)
def upgrade():
_add("videos_downloaded", sa.Integer(), "0")
_add("mb_usage", sa.Float(), "0")
_add("level", sa.Integer(), "1")
_add("xp", sa.Integer(), "0")
_add("tier", sa.Integer(), "0")
_add("ban_status", sa.Boolean(), "false")
_add("soft_banned", sa.Boolean(), "false")
def downgrade():
for col in (
"soft_banned", "ban_status", "tier",
"xp", "level", "mb_usage", "videos_downloaded",
):
op.drop_column("users", col)

View File

@@ -0,0 +1,64 @@
"""drop url column, make ip the primary key on users
Revision ID: 55327cbf08df
Revises: 70e118917866
Create Date: 2025-05-01 07:26:50.279482
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.engine.reflection import Inspector
# revision identifiers, used by Alembic.
revision: str = '55327cbf08df'
down_revision: Union[str, None] = '70e118917866'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def _has_column(conn, table, column):
return column in [
c["name"] for c in Inspector.from_engine(conn).get_columns(table)
]
def upgrade():
conn = op.get_bind()
insp = Inspector.from_engine(conn)
# 1) Drop any UNIQUE constraints on url
for uc in insp.get_unique_constraints("users"):
if "url" in uc["column_names"]:
op.drop_constraint(uc["name"], "users", type_="unique")
# 2) Drop whatever PRIMARY KEY exists today
pk = insp.get_pk_constraint("users")["name"]
if pk:
op.drop_constraint(pk, "users", type_="primary")
# 3) Remove the old url column
if "url" in {c["name"] for c in insp.get_columns("users")}:
op.drop_column("users", "url")
# 4) Create a new PK on ip
op.create_primary_key("users_pkey", "users", ["ip"])
def downgrade():
conn = op.get_bind()
insp = Inspector.from_engine(conn)
# 1) Drop the ip PK
pk = insp.get_pk_constraint("users")["name"]
if pk:
op.drop_constraint(pk, "users", type_="primary")
# 2) Re-create url column
op.add_column("users", sa.Column("url", sa.Text(), nullable=False))
# 3) Restore the PK on url
op.create_primary_key("users_pkey", "users", ["url"])

View File

@@ -0,0 +1,22 @@
"""add ok column to dl_stats"""
from alembic import op
import sqlalchemy as sa
revision = "add_ok_to_dl_stats"
down_revision = "55327cbf08df" # or latest hash
branch_labels = None
depends_on = None
def upgrade():
op.add_column(
"dl_stats",
sa.Column("ok", sa.Boolean(), nullable=False, server_default=sa.text("false"))
)
# drop default if you like
op.alter_column("dl_stats", "ok", server_default=None)
def downgrade():
op.drop_column("dl_stats", "ok")

View File

@@ -0,0 +1,43 @@
"""add default to users.videos_downloaded
Revision ID: 20250506abcd
Revises: previous_revision_id
Create Date: 2025-05-06 22:10:00.000000
"""
from typing import Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "20250506abcd"
down_revision = 'abe00f7f8f72'
branch_labels = None
depends_on = None
def upgrade():
# 1) backfill existing NULLs
op.execute(
"UPDATE users SET videos_downloaded = 0 "
"WHERE videos_downloaded IS NULL;"
)
# 2) give the column a serverside default
op.alter_column(
"users",
"videos_downloaded",
existing_type=sa.Integer(),
server_default=sa.text("0"),
nullable=False,
)
def downgrade():
op.alter_column(
"users",
"videos_downloaded",
existing_type=sa.Integer(),
server_default=None,
nullable=True,
)

View File

@@ -0,0 +1,27 @@
"""add ok column to dl_stats
Revision ID: 7064708f684e
Revises: 86141e89fea3
Create Date: 2025-05-01 16:29:31.009976
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '7064708f684e'
down_revision: Union[str, None] = '86141e89fea3'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade():
# if “success” exists, rename it; otherwise just add ok
op.add_column("dl_stats", sa.Column("ok", sa.Boolean(), server_default="false", nullable=False))
def downgrade():
op.drop_column("dl_stats", "ok")

View File

@@ -0,0 +1,48 @@
"""make ip the primary key on users, drop obsolete url
Revision ID: 70e118917866
Revises: e03269ce4058
Create Date: 2025-05-01 07:03:06.119500
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.engine import reflection
# revision identifiers, used by Alembic.
revision: str = '70e118917866'
down_revision: Union[str, None] = 'e03269ce4058'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def _column_exists(conn, table, column):
insp = reflection.Inspector.from_engine(conn)
return column in [c["name"] for c in insp.get_columns(table)]
def upgrade():
conn = op.get_bind()
# 1) add ip column if the old table never had it
if not _column_exists(conn, "users", "ip"):
op.add_column("users", sa.Column("ip", sa.Text(), nullable=False))
# 2) drop the old PK that was on url (or any other column mix)
op.drop_constraint("users_pkey", "users", type_="primary")
# 3) create new PK on ip
op.create_primary_key("users_pkey", "users", ["ip"])
# 4) drop obsolete url column if its still there
if _column_exists(conn, "users", "url"):
op.drop_column("users", "url")
def downgrade():
# reverse: recreate url, restore old PK
op.add_column("users", sa.Column("url", sa.Text(), nullable=False))
op.drop_constraint("users_pkey", "users", type_="primary")
op.create_primary_key("users_pkey", "users", ["url"])
op.drop_column("users", "ip")

View File

@@ -0,0 +1,28 @@
"""merge heads
Revision ID: 86141e89fea3
Revises: 03568bb37289, ffae4495003d
Create Date: 2025-05-01 16:08:14.791524
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '86141e89fea3'
down_revision: Union[str, None] = ('03568bb37289', 'ffae4495003d')
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
pass
def downgrade() -> None:
"""Downgrade schema."""
pass

View File

@@ -0,0 +1,40 @@
"""add banned and updated_at columns to proxies
Revision ID: 957c893a8a67
Revises:
Create Date: 2025-05-01 06:45:41.546150
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '957c893a8a67'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ── Add the new 'banned' column with default FALSE ──────────
op.add_column(
'proxies',
sa.Column('banned', sa.Boolean(), nullable=False, server_default=sa.text('FALSE'))
)
# ── (Optional) Add an 'updated_at' timestamp column ─────────
op.add_column(
'proxies',
sa.Column('updated_at', sa.DateTime(), nullable=True)
)
# ── (Optional) Remove server_default if you dont need it going forward ─
op.alter_column('proxies', 'banned', server_default=None)
def downgrade() -> None:
# ── Drop in reverse order ───────────────────────────────
op.drop_column('proxies', 'updated_at')
op.drop_column('proxies', 'banned')

View File

@@ -0,0 +1,51 @@
"""proxy leasing columns
Revision ID: abe00f7f8f72
Revises: 175f03f1c9f7
Create Date: 20250505 18:12:44
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy import inspect
# revision identifiers, used by Alembic.
revision = "abe00f7f8f72"
down_revision = "175f03f1c9f7"
branch_labels = None
depends_on = None
def _has_column(bind, table: str, column: str) -> bool:
insp = inspect(bind)
return column in {c["name"] for c in insp.get_columns(table)}
def upgrade() -> None:
bind = op.get_bind()
# add only the columns that are missing
add_in_use = not _has_column(bind, "proxies", "in_use")
add_last_fail = not _has_column(bind, "proxies", "last_fail")
if add_in_use or add_last_fail:
with op.batch_alter_table("proxies") as batch:
if add_in_use:
batch.add_column(sa.Column("in_use", sa.Integer(), server_default="0"))
if add_last_fail:
batch.add_column(sa.Column("last_fail", sa.DateTime()))
def downgrade() -> None:
# downgrade assumes the columns exist, so drop them only if present
bind = op.get_bind()
drop_in_use = _has_column(bind, "proxies", "in_use")
drop_last_fail = _has_column(bind, "proxies", "last_fail")
if drop_in_use or drop_last_fail:
with op.batch_alter_table("proxies") as batch:
if drop_last_fail:
batch.drop_column("last_fail")
if drop_in_use:
batch.drop_column("in_use")

View File

@@ -0,0 +1,37 @@
"""add first_visit column to users
Revision ID: e03269ce4058
Revises: 957c893a8a67
Create Date: 2025-05-01 06:58:05.118501
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'e03269ce4058'
down_revision: Union[str, None] = '957c893a8a67'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade():
# 1) add nullable column with UTC default
op.add_column(
"users",
sa.Column(
"first_visit",
sa.TIMESTAMP(timezone=True),
nullable=True,
server_default=sa.text("timezone('utc', now())")
),
)
# 2) once its there, make it NOT NULL and drop the default
op.alter_column("users", "first_visit", nullable=False, server_default=None)
def downgrade():
op.drop_column("users", "first_visit")

View File

@@ -0,0 +1,41 @@
from alembic import op
import sqlalchemy as sa
from sqlalchemy.engine.reflection import Inspector
revision = "ff38ddad43af"
down_revision = "7064708f684e"
branch_labels = None
depends_on = None
def upgrade():
conn = op.get_bind()
insp = Inspector.from_engine(conn)
cols = {c["name"]: c for c in insp.get_columns("dl_stats")}
# ── 1. guarantee “id” exists and is the PK ──────────────────────
if "id" not in cols:
op.add_column("dl_stats", sa.Column("id", sa.Integer(), primary_key=True))
# rely on PostgreSQL's implicit sequence; no ALTER ... ADD GENERATED
op.create_primary_key("dl_stats_pkey", "dl_stats", ["id"])
else:
pk_cols = insp.get_pk_constraint("dl_stats")["constrained_columns"]
if "id" not in pk_cols:
op.drop_constraint("dl_stats_pkey", "dl_stats", type_="primary")
op.create_primary_key("dl_stats_pkey", "dl_stats", ["id"])
# do **not** attempt to alter the columns default/identity
# ── 2. add “ok” boolean if missing, back-fill from “success” ───
if "ok" not in cols:
op.add_column(
"dl_stats",
sa.Column("ok", sa.Boolean(), nullable=False,
server_default=sa.text("false")),
)
if "success" in cols:
op.execute("UPDATE dl_stats SET ok = success")
def downgrade():
op.drop_column("dl_stats", "ok")
op.drop_constraint("dl_stats_pkey", "dl_stats", type_="primary")
op.drop_column("dl_stats", "id")

View File

@@ -0,0 +1,10 @@
revision = 'ffae4495003d'
down_revision = '55327cbf08df' # or whatever its real parent was
branch_labels = None
depends_on = None
def upgrade():
pass
def downgrade():
pass

View File

@@ -0,0 +1,45 @@
"""default zeros for new user counters"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.engine.reflection import Inspector
revision = "user_counters_defaults_old"
down_revision = "add_ok_to_dl_stats"
branch_labels = None
depends_on = None
def _has_column(table: str, column: str, conn) -> bool:
insp = Inspector.from_engine(conn)
return column in [c["name"] for c in insp.get_columns(table)]
def _add(column: str, coltype, default_sql: str, conn):
if not _has_column("users", column, conn):
op.add_column(
"users",
sa.Column(column, coltype, nullable=True, server_default=sa.text(default_sql)),
)
# Whether it was just added or already existed, be sure it is NOT NULL and no default remains
op.alter_column("users", column, nullable=False, server_default=None)
def upgrade():
conn = op.get_bind()
_add("videos_downloaded", sa.Integer(), "0", conn)
_add("mb_usage", sa.Float(), "0", conn)
_add("level", sa.Integer(), "1", conn)
_add("xp", sa.Integer(), "0", conn)
_add("tier", sa.Integer(), "0", conn)
_add("ban_status", sa.Boolean(), "false", conn)
_add("soft_banned", sa.Boolean(), "false", conn)
def downgrade():
for col in (
"soft_banned", "ban_status", "tier",
"xp", "level", "mb_usage", "videos_downloaded",
):
op.drop_column("users", col)