What a mess

This commit is contained in:
Joan
2025-11-07 15:27:13 +01:00
parent 0b79b3ae59
commit 33cc9586c2
130 changed files with 29819 additions and 1175 deletions

View File

@@ -0,0 +1,174 @@
"""
Migration: Fix telegram_id to allow NULL for web users
Changes:
- Drop existing primary key constraint on telegram_id
- Make telegram_id nullable
- Ensure id column exists and is unique
- Add constraint that either telegram_id OR username must be NOT NULL
Run this migration to allow web-only users without telegram_id.
"""
import asyncio
import os
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy import text
DB_USER = os.getenv("POSTGRES_USER")
DB_PASS = os.getenv("POSTGRES_PASSWORD")
DB_NAME = os.getenv("POSTGRES_DB")
DB_HOST = os.getenv("POSTGRES_HOST", "localhost")
DB_PORT = os.getenv("POSTGRES_PORT", "5432")
DATABASE_URL = f"postgresql+psycopg://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
async def migrate():
engine = create_async_engine(DATABASE_URL)
async with engine.begin() as conn:
print("Starting migration: Fix telegram_id to allow NULL...")
try:
# First, check current state
result = await conn.execute(text("""
SELECT
c.column_name,
c.is_nullable,
c.column_default
FROM information_schema.columns c
WHERE c.table_name = 'players'
AND c.column_name IN ('telegram_id', 'id', 'username')
ORDER BY c.ordinal_position
"""))
print("\nCurrent columns:")
for row in result:
print(f" - {row[0]}: nullable={row[1]}, default={row[2]}")
# Store foreign keys that reference players.telegram_id
print("\nFinding foreign keys that reference players(telegram_id)...")
result = await conn.execute(text("""
SELECT
tc.constraint_name,
tc.table_name,
kcu.column_name
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu
ON tc.constraint_name = ccu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND ccu.table_name = 'players'
AND ccu.column_name = 'telegram_id'
"""))
fk_constraints = list(result)
# Drop foreign key constraints temporarily
for fk_name, table_name, column_name in fk_constraints:
print(f" Dropping FK: {table_name}.{fk_name}")
await conn.execute(text(f"ALTER TABLE {table_name} DROP CONSTRAINT {fk_name}"))
# Check for primary key constraint
result = await conn.execute(text("""
SELECT constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_name = 'players' AND constraint_type = 'PRIMARY KEY'
"""))
pk_constraints = list(result)
if pk_constraints:
pk_name = pk_constraints[0][0]
print(f"\nDropping PRIMARY KEY constraint: {pk_name}")
await conn.execute(text(f"ALTER TABLE players DROP CONSTRAINT {pk_name}"))
else:
print("\n✓ No PRIMARY KEY constraint found")
# Make telegram_id nullable
print("Making telegram_id nullable...")
await conn.execute(text("""
ALTER TABLE players
ALTER COLUMN telegram_id DROP NOT NULL
"""))
# Make telegram_id unique if not already
print("Ensuring telegram_id is unique...")
try:
await conn.execute(text("""
ALTER TABLE players
ADD CONSTRAINT players_telegram_id_unique UNIQUE (telegram_id)
"""))
except Exception as e:
if "already exists" in str(e):
print("✓ telegram_id unique constraint already exists")
else:
raise
# Ensure id column exists and is unique
result = await conn.execute(text("""
SELECT column_name
FROM information_schema.columns
WHERE table_name='players' AND column_name='id'
"""))
if not list(result):
print("Adding id column...")
await conn.execute(text("""
ALTER TABLE players
ADD COLUMN id SERIAL UNIQUE
"""))
else:
print("✓ id column exists")
# Make sure it's unique
try:
await conn.execute(text("""
ALTER TABLE players
ADD CONSTRAINT players_id_unique UNIQUE (id)
"""))
except Exception as e:
if "already exists" in str(e):
print("✓ id unique constraint already exists")
else:
raise
# Add check constraint that either telegram_id OR username must be NOT NULL
print("Adding constraint: either telegram_id OR username must be NOT NULL...")
try:
await conn.execute(text("""
ALTER TABLE players
ADD CONSTRAINT players_id_check
CHECK (telegram_id IS NOT NULL OR username IS NOT NULL)
"""))
except Exception as e:
if "already exists" in str(e):
print("✓ Check constraint already exists")
else:
raise
# Recreate foreign key constraints
print("\nRecreating foreign key constraints...")
for fk_name, table_name, column_name in fk_constraints:
print(f" Adding FK: {table_name}.{fk_name}")
await conn.execute(text(f"""
ALTER TABLE {table_name}
ADD CONSTRAINT {fk_name}
FOREIGN KEY ({column_name})
REFERENCES players(telegram_id)
ON DELETE CASCADE
"""))
print("\n✅ Migration completed successfully!")
print("\nNow players can be created with:")
print(" - telegram_id (Telegram users)")
print(" - username + password_hash (web users)")
print(" - Both telegram_id is unique and id is unique")
except Exception as e:
print(f"\n❌ Migration failed: {e}")
raise
await engine.dispose()
if __name__ == "__main__":
asyncio.run(migrate())