316 lines
11 KiB
Python
316 lines
11 KiB
Python
|
|
#!/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)
|