Test isolation fixes: - auth_db.get_db(): read TURF_SAAS_DB dynamically (not frozen at import) - api_v1/utils.get_db(): read TURF_SAAS_DB dynamically (not frozen at import) - api_tokens_db.get_db(): read TURF_SAAS_DB dynamically (not frozen at import) - tests/test_history.py: enforce _tmp_db.name + call init_auth_tables() in fixtures - tests/test_user_tokens.py: enforce _tmp_db.name + call migrate_api_tokens_tables() in app fixture Auth compatibility fixes: - api_v1/routes/history.py: use auth.jwt_required_middleware (flask_jwt_extended) with saas_auth fallback for portal_server context - api_v1/routes/ml_feedback.py: same auth import strategy - api_v1/routes/user.py: same auth import strategy Dependencies: - requirements.txt: add optuna>=4.0.0 (used in ML ensemble tests and training) Co-Authored-By: Paperclip <noreply@paperclip.ing>
104 lines
3.8 KiB
Python
104 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Auth DB — users and subscriptions schema for turf_saas.db
|
|
Sprint 2-3: Auth JWT + Multi-tenant (HRT-28)
|
|
HRT-79: migration Telegram columns
|
|
"""
|
|
|
|
import sqlite3
|
|
import os
|
|
|
|
# NOTE: DB_PATH kept for backward compat, but get_db() reads env at call time
|
|
# so test isolation works correctly when TURF_SAAS_DB is set per-module.
|
|
DB_PATH = os.environ.get("TURF_SAAS_DB", "/home/h3r7/turf_saas/turf_saas.db")
|
|
|
|
|
|
def get_db():
|
|
# Read env dynamically so test overrides of TURF_SAAS_DB are respected
|
|
db_path = os.environ.get("TURF_SAAS_DB", DB_PATH)
|
|
conn = sqlite3.connect(db_path)
|
|
conn.row_factory = sqlite3.Row
|
|
return conn
|
|
|
|
|
|
def init_auth_tables():
|
|
"""Create users and subscriptions tables if they don't exist."""
|
|
conn = get_db()
|
|
c = conn.cursor()
|
|
|
|
c.executescript("""
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
email TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
plan TEXT NOT NULL DEFAULT 'free'
|
|
CHECK(plan IN ('free','premium','pro')),
|
|
created_at DATETIME NOT NULL DEFAULT (datetime('now')),
|
|
is_active INTEGER NOT NULL DEFAULT 1,
|
|
daily_usage INTEGER NOT NULL DEFAULT 0,
|
|
last_usage_date TEXT DEFAULT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS subscriptions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
plan TEXT NOT NULL CHECK(plan IN ('free','premium','pro')),
|
|
start_date DATETIME NOT NULL DEFAULT (datetime('now')),
|
|
end_date DATETIME,
|
|
stripe_customer_id TEXT,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS refresh_tokens (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id),
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
created_at DATETIME NOT NULL DEFAULT (datetime('now')),
|
|
expires_at DATETIME NOT NULL,
|
|
revoked INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_user ON subscriptions(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user ON refresh_tokens(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_hash ON refresh_tokens(token_hash);
|
|
""")
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
print("[auth_db] Tables users, subscriptions, refresh_tokens created/verified.")
|
|
|
|
# Apply Telegram columns migration (idempotent)
|
|
migrate_telegram_columns()
|
|
|
|
|
|
def migrate_telegram_columns():
|
|
"""
|
|
Migration idempotente : ajoute les colonnes Telegram à la table users.
|
|
Utilise ALTER TABLE ... ADD COLUMN avec try/except OperationalError
|
|
pour être safe si les colonnes existent déjà (SQLite ne supporte pas IF NOT EXISTS).
|
|
HRT-79
|
|
"""
|
|
conn = get_db()
|
|
c = conn.cursor()
|
|
columns = [
|
|
("telegram_chat_id", "TEXT DEFAULT NULL"),
|
|
("alert_value_bets", "INTEGER DEFAULT 1"),
|
|
("alert_top1", "INTEGER DEFAULT 1"),
|
|
("alert_quinte_only", "INTEGER DEFAULT 0"),
|
|
]
|
|
for col, definition in columns:
|
|
try:
|
|
c.execute(f"ALTER TABLE users ADD COLUMN {col} {definition}")
|
|
print(f"[auth_db] Colonne '{col}' ajoutée.")
|
|
except sqlite3.OperationalError:
|
|
# Column already exists — safe to ignore
|
|
pass
|
|
conn.commit()
|
|
conn.close()
|
|
print("[auth_db] Migration Telegram columns OK.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
init_auth_tables()
|