init_db.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. #!/usr/bin/env python3
  2. """Initialise (ou migre) la base de données SQLite. Appelé par les scripts install et upgrade."""
  3. import os
  4. import sqlite3
  5. import sys
  6. config_path = sys.argv[1] if len(sys.argv) > 1 else None
  7. if config_path:
  8. os.environ["BACKUPMANAGER_CONFIG"] = config_path
  9. # Lire DB_PATH directement depuis le fichier de config (sans importer app/SQLAlchemy)
  10. # pour pouvoir migrer le schéma AVANT que l'import de app ne tente de requêter la DB.
  11. _cfg = {}
  12. if config_path and os.path.exists(config_path):
  13. with open(config_path) as _f:
  14. exec(compile(_f.read(), config_path, "exec"), _cfg)
  15. db_path = _cfg.get("DB_PATH") or os.path.join(
  16. os.path.dirname(os.path.abspath(__file__)), "backupmanager.db"
  17. )
  18. # Migrations SQLite directes — avant tout import de SQLAlchemy/app
  19. if os.path.exists(db_path):
  20. _conn = sqlite3.connect(db_path)
  21. _cur = _conn.execute("PRAGMA table_info(jobs)")
  22. existing_cols = {row[1] for row in _cur.fetchall()}
  23. migrations = [
  24. ("destination_id", "ALTER TABLE jobs ADD COLUMN destination_id INTEGER REFERENCES destinations(id)"),
  25. ("remote_instance_id","ALTER TABLE jobs ADD COLUMN remote_instance_id INTEGER REFERENCES remote_instances(id)"),
  26. ("keep_local_archive","ALTER TABLE jobs ADD COLUMN keep_local_archive BOOLEAN NOT NULL DEFAULT 1"),
  27. ]
  28. for col, sql in migrations:
  29. if col not in existing_cols:
  30. _conn.execute(sql)
  31. _conn.commit()
  32. print(f"Migration : colonne {col} ajoutée à jobs.")
  33. # Table many-to-many job_destinations
  34. tables = {r[0] for r in _conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()}
  35. if "job_destinations" not in tables:
  36. _conn.execute("""
  37. CREATE TABLE job_destinations (
  38. id INTEGER PRIMARY KEY AUTOINCREMENT,
  39. job_id INTEGER NOT NULL REFERENCES jobs(id),
  40. dest_type TEXT NOT NULL,
  41. dest_id INTEGER NOT NULL
  42. )
  43. """)
  44. # Migrer les données existantes depuis destination_id / remote_instance_id
  45. if "destination_id" in existing_cols:
  46. for job_id, dest_id, inst_id in _conn.execute(
  47. "SELECT id, destination_id, remote_instance_id FROM jobs"
  48. ).fetchall():
  49. if dest_id:
  50. _conn.execute(
  51. "INSERT INTO job_destinations (job_id, dest_type, dest_id) VALUES (?, 'ssh', ?)",
  52. (job_id, dest_id),
  53. )
  54. if inst_id:
  55. _conn.execute(
  56. "INSERT INTO job_destinations (job_id, dest_type, dest_id) VALUES (?, 'instance', ?)",
  57. (job_id, inst_id),
  58. )
  59. _conn.commit()
  60. print("Migration : table job_destinations créée et données migrées.")
  61. _conn.close()
  62. # Import de app après les migrations — SQLAlchemy peut désormais requêter la DB
  63. from app import app, db
  64. with app.app_context():
  65. db.create_all()
  66. print("Base de données initialisée.")