diff --git a/pos/migrations/runner.py b/pos/migrations/runner.py new file mode 100755 index 0000000..4e6aee1 --- /dev/null +++ b/pos/migrations/runner.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# /home/Autopartes/pos/migrations/runner.py +"""Apply schema migrations to all tenant databases.""" + +import os +import sys +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from tenant_db import get_master_conn, get_tenant_conn_by_dbname + +MIGRATIONS_DIR = os.path.dirname(os.path.abspath(__file__)) + +# Migration registry: version -> filename +MIGRATIONS = { + 'v1.0': 'v1.0_initial.sql', + # Future: 'v1.1': 'v1.1_add_xyz.sql', +} + + +def get_all_tenants(): + """Get all tenants with their current schema version.""" + conn = get_master_conn() + cur = conn.cursor() + cur.execute(""" + SELECT t.id, t.db_name, t.name, COALESCE(v.version, 'v0.0') as version + FROM tenants t + LEFT JOIN tenant_schema_version v ON t.id = v.tenant_id + WHERE t.is_active = true + """) + tenants = cur.fetchall() + cur.close() + conn.close() + return tenants + + +def apply_migration(db_name, version): + """Apply a single migration to a tenant DB.""" + filename = MIGRATIONS[version] + filepath = os.path.join(MIGRATIONS_DIR, filename) + + if not os.path.exists(filepath): + print(f" ERROR: Migration file not found: {filepath}") + return False + + conn = get_tenant_conn_by_dbname(db_name) + cur = conn.cursor() + try: + with open(filepath) as f: + cur.execute(f.read()) + conn.commit() + return True + except Exception as e: + conn.rollback() + print(f" ERROR: {e}") + return False + finally: + cur.close() + conn.close() + + +def run_migrations(): + """Apply pending migrations to all tenants.""" + tenants = get_all_tenants() + sorted_versions = sorted(MIGRATIONS.keys()) + + print(f"Found {len(tenants)} active tenants") + print(f"Available migrations: {sorted_versions}") + + for tenant_id, db_name, name, current_version in tenants: + print(f"\n[{name}] (db={db_name}, current={current_version})") + + for version in sorted_versions: + if version <= current_version: + continue + + print(f" Applying {version}...", end=' ') + if apply_migration(db_name, version): + # Update version in master + master_conn = get_master_conn() + master_cur = master_conn.cursor() + master_cur.execute(""" + INSERT INTO tenant_schema_version (tenant_id, version) + VALUES (%s, %s) + ON CONFLICT (tenant_id) DO UPDATE SET version = %s, updated_at = NOW() + """, (tenant_id, version, version)) + master_conn.commit() + master_cur.close() + master_conn.close() + print("OK") + else: + print("FAILED — stopping migrations for this tenant") + break + + print("\nDone.") + + +if __name__ == '__main__': + run_migrations()