65 lines
2.0 KiB
Python
65 lines
2.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Migration: add security columns.
|
|
users — totp_secret, totp_enabled
|
|
schwab_connections — refresh_token_expires_at
|
|
|
|
Run once: python scripts/add_security_columns.py
|
|
Safe to re-run — skips columns that already exist.
|
|
"""
|
|
import sys, os
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from app import create_app
|
|
from app.extensions import db
|
|
|
|
app = create_app()
|
|
|
|
COLUMNS = [
|
|
("users", "totp_secret",
|
|
"VARCHAR(64) NULL DEFAULT NULL"),
|
|
("users", "totp_enabled",
|
|
"TINYINT(1) NOT NULL DEFAULT 0"),
|
|
("schwab_connections", "refresh_token_expires_at",
|
|
"DATETIME NULL DEFAULT NULL"),
|
|
]
|
|
|
|
CREATE_TABLES = [
|
|
"""
|
|
CREATE TABLE IF NOT EXISTS audit_logs (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
action VARCHAR(64) NOT NULL,
|
|
description VARCHAR(255),
|
|
ip_address VARCHAR(45),
|
|
INDEX ix_audit_logs_timestamp (timestamp),
|
|
INDEX ix_audit_logs_action (action)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
"""
|
|
]
|
|
|
|
with app.app_context():
|
|
with db.engine.connect() as conn:
|
|
for table, column, definition in COLUMNS:
|
|
exists = conn.execute(db.text(
|
|
"SELECT COUNT(*) FROM information_schema.columns "
|
|
"WHERE table_schema = DATABASE() "
|
|
"AND table_name = :t AND column_name = :c"
|
|
), {"t": table, "c": column}).scalar()
|
|
|
|
if exists:
|
|
print(f" {table}.{column} already exists — skipped.")
|
|
else:
|
|
conn.execute(db.text(
|
|
f"ALTER TABLE {table} ADD COLUMN {column} {definition}"
|
|
))
|
|
conn.commit()
|
|
print(f" Added {table}.{column}.")
|
|
|
|
for sql in CREATE_TABLES:
|
|
conn.execute(db.text(sql))
|
|
conn.commit()
|
|
print(" Created audit_logs table (or already exists).")
|
|
|
|
print("Done.")
|