UrNetwork-Stats-Dashboard-r.../migrate.py

316 lines
11 KiB
Python
Raw Permalink Normal View History

2025-11-21 22:39:41 +01:00
#!/usr/bin/env python3
"""
Database Migration Script for UrNetwork Stats Dashboard v2.0
Migrates from single-account to multi-account structure
"""
import sqlite3
import os
import sys
from getpass import getpass
def print_header(text):
print("\n" + "=" * 60)
print(f" {text}")
print("=" * 60)
def print_success(text):
print(f"{text}")
def print_warning(text):
print(f"{text}")
def print_error(text):
print(f"{text}")
def backup_files():
"""Create backups of existing files"""
print_header("Vytváření záloh")
if os.path.exists('.env'):
os.system('cp .env .env.backup')
print_success("Zazálohován .env → .env.backup")
# Check for database in multiple locations
db_locations = ['transfer_stats.db', 'instance/transfer_stats.db']
for db_path in db_locations:
if os.path.exists(db_path):
backup_path = db_path + '.backup'
os.system(f'cp {db_path} {backup_path}')
print_success(f"Zazálohována databáze → {backup_path}")
break
def migrate_database():
"""Migrate database to new structure"""
print_header("Migrace databáze")
# Check for database in multiple locations
db_path = None
possible_paths = [
'transfer_stats.db',
'instance/transfer_stats.db',
'../transfer_stats.db'
]
for path in possible_paths:
if os.path.exists(path):
db_path = path
print_success(f"Nalezena databáze: {db_path}")
break
if not db_path:
print_error("Databáze transfer_stats.db nenalezena!")
print_warning("Hledáno v: " + ", ".join(possible_paths))
return False
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# 1. Create accounts table
cursor.execute('''
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
nickname TEXT,
is_active BOOLEAN DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
''')
print_success("Vytvořena tabulka accounts")
# 2. Add account_id column to stats
try:
cursor.execute('ALTER TABLE stats ADD COLUMN account_id INTEGER')
print_success("Přidán sloupec account_id do tabulky stats")
except sqlite3.OperationalError as e:
if "duplicate column" in str(e).lower():
print_warning("Sloupec account_id již existuje")
else:
raise
# 3. Migrate existing account from .env
if os.path.exists('.env'):
username = None
password = None
with open('.env', 'r') as f:
for line in f:
if line.startswith('UR_USER='):
username = line.split('=', 1)[1].strip()
elif line.startswith('UR_PASS='):
password = line.split('=', 1)[1].strip()
if username and password:
cursor.execute('SELECT COUNT(*) FROM accounts WHERE username = ?', (username,))
if cursor.fetchone()[0] == 0:
print(f"\nNalezen existující účet: {username}")
nickname = input("Zadejte přezdívku pro tento účet (nebo stiskněte Enter pro přeskočení): ").strip()
cursor.execute(
'INSERT INTO accounts (username, password, nickname, is_active) VALUES (?, ?, ?, 1)',
(username, password, nickname if nickname else None)
)
account_id = cursor.lastrowid
# Update existing stats
cursor.execute('UPDATE stats SET account_id = ? WHERE account_id IS NULL', (account_id,))
updated_count = cursor.rowcount
print_success(f"Migrován účet: {username}")
if nickname:
print_success(f" Přezdívka: {nickname}")
print_success(f" Aktualizováno {updated_count} statistických záznamů")
else:
print_warning(f"Účet {username} již existuje v databázi")
else:
print_warning("Nenalezeny credentials v .env souboru")
else:
print_warning(".env soubor nenalezen")
conn.commit()
conn.close()
print_success("Migrace databáze dokončena!")
return True
except Exception as e:
print_error(f"Chyba při migraci databáze: {e}")
return False
def update_env_file():
"""Update .env file with admin password"""
print_header("Aktualizace .env souboru")
if not os.path.exists('.env'):
print_warning(".env soubor nenalezen, bude vytvořen nový")
env_lines = []
else:
with open('.env', 'r') as f:
env_lines = f.readlines()
# Check if ADMIN_PASSWORD exists
has_admin_pass = any(line.startswith('ADMIN_PASSWORD=') for line in env_lines)
if has_admin_pass:
print_warning("ADMIN_PASSWORD již existuje v .env")
response = input("Chcete nastavit nové heslo? (y/n): ").lower()
if response != 'y':
return True
# Remove old password
env_lines = [line for line in env_lines if not line.startswith('ADMIN_PASSWORD=')]
print("\nNastavte administrátorské heslo pro přístup do dashboardu.")
print("(Toto je oddělené od vašich UrNetwork credentials)")
while True:
admin_pass = getpass("\nAdministrátorské heslo: ")
admin_pass_confirm = getpass("Potvrďte heslo: ")
if admin_pass == admin_pass_confirm:
if len(admin_pass) < 6:
print_error("Heslo musí mít alespoň 6 znaků!")
continue
break
else:
print_error("Hesla se neshodují!")
# Add admin password
if not any(line.strip() == "# Admin Access" for line in env_lines):
env_lines.insert(0, "# Admin Access\n")
env_lines.insert(1, f"ADMIN_PASSWORD={admin_pass}\n")
env_lines.insert(2, "\n")
# Write updated .env
with open('.env', 'w') as f:
f.writelines(env_lines)
print_success("Administrátorské heslo nastaveno")
return True
def verify_migration():
"""Verify that migration was successful"""
print_header("Ověření migrace")
# Find database
db_path = None
for path in ['transfer_stats.db', 'instance/transfer_stats.db']:
if os.path.exists(path):
db_path = path
break
if not db_path:
print_error("Databáze nenalezena pro ověření")
return False
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Check accounts table
cursor.execute("SELECT COUNT(*) FROM accounts")
account_count = cursor.fetchone()[0]
print_success(f"Nalezeno {account_count} účtů v databázi")
# Check stats with account_id
cursor.execute("SELECT COUNT(*) FROM stats WHERE account_id IS NOT NULL")
stats_count = cursor.fetchone()[0]
print_success(f"Nalezeno {stats_count} statistických záznamů s account_id")
# Check orphaned stats
cursor.execute("SELECT COUNT(*) FROM stats WHERE account_id IS NULL")
orphaned_count = cursor.fetchone()[0]
if orphaned_count > 0:
print_warning(f"Nalezeno {orphaned_count} statistik bez přiřazeného účtu")
conn.close()
# Check .env
if os.path.exists('.env'):
with open('.env', 'r') as f:
env_content = f.read()
if 'ADMIN_PASSWORD=' in env_content:
print_success("ADMIN_PASSWORD nalezeno v .env")
else:
print_warning("ADMIN_PASSWORD chybí v .env")
return True
except Exception as e:
print_error(f"Chyba při ověření: {e}")
return False
def print_next_steps():
"""Print next steps after migration"""
print_header("Další kroky")
print("""
1. Restartujte aplikaci:
pkill -f main.py
python3 main.py
2. Přihlaste se pomocí nového admin hesla
3. Přejděte do "Správa účtů" pro přidání dalších UrNetwork účtů
4. Pokud něco nefunguje, obnovte zálohy:
cp .env.backup .env
cp transfer_stats.db.backup transfer_stats.db
Dokumentace: README_CZ.md
""")
def main():
print("""
UrNetwork Stats Dashboard - Migration Script v2.0
Migrace z single-account na multi-account strukturu
""")
print("\nTento skript provede následující:")
print(" • Vytvoří zálohy .env a databáze")
print(" • Přidá podporu pro více účtů")
print(" • Nastaví administrátorské heslo")
print(" • Migruje existující data")
response = input("\nPokračovat? (y/n): ").lower()
if response != 'y':
print("Migrace zrušena.")
sys.exit(0)
# Run migration steps
backup_files()
if not migrate_database():
print_error("\nMigrace selhala při aktualizaci databáze!")
print("Obnovte zálohy a zkuste to znovu.")
sys.exit(1)
if not update_env_file():
print_error("\nMigrace selhala při aktualizaci .env!")
print("Obnovte zálohy a zkuste to znovu.")
sys.exit(1)
if not verify_migration():
print_warning("\nOvěření migrace selhalo, ale data by měla být OK")
print_next_steps()
print_header("Migrace úspěšně dokončena! 🎉")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\nMigrace přerušena uživatelem.")
sys.exit(1)
except Exception as e:
print_error(f"\nNeočekávaná chyba: {e}")
print("Obnovte zálohy pomocí:")
print(" cp .env.backup .env")
print(" cp transfer_stats.db.backup transfer_stats.db")
sys.exit(1)