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>
225 lines
6.7 KiB
Python
225 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
User route for API v1 — Telegram alert configuration
|
|
HRT-79: Alertes Telegram configurables (Premium)
|
|
|
|
GET /api/v1/user/telegram-config — Lire la config Telegram de l'utilisateur connecté
|
|
POST /api/v1/user/telegram-config — Mettre à jour la config Telegram
|
|
|
|
Accès : Premium / Pro uniquement (@jwt_required_middleware + @plan_required)
|
|
"""
|
|
|
|
import sqlite3
|
|
from flask import Blueprint, jsonify, request
|
|
|
|
from api_v1.utils import internal_error, bad_request
|
|
# Auth: try flask_jwt_extended (app_v1) first, fall back to saas_auth (portal_server)
|
|
try:
|
|
from auth import jwt_required_middleware
|
|
except ImportError:
|
|
from saas_auth import require_auth as jwt_required_middleware
|
|
try:
|
|
from auth import plan_required
|
|
except ImportError:
|
|
plan_required = lambda *a, **kw: (lambda f: f)
|
|
|
|
user_bp = Blueprint("v1_user", __name__, url_prefix="/api/v1/user")
|
|
|
|
# DB_PATH est résolu via la même variable d'env que auth_db.py
|
|
import os
|
|
|
|
_DB_PATH = os.environ.get("TURF_SAAS_DB", "/home/h3r7/turf_saas/turf_saas.db")
|
|
|
|
|
|
def _get_db():
|
|
conn = sqlite3.connect(_DB_PATH)
|
|
conn.row_factory = sqlite3.Row
|
|
return conn
|
|
|
|
|
|
# ── GET /api/v1/user/telegram-config ──────────────────────────────────────────
|
|
|
|
|
|
@user_bp.route("/telegram-config", methods=["GET"])
|
|
@jwt_required_middleware
|
|
@plan_required("premium", "pro")
|
|
def get_telegram_config():
|
|
"""
|
|
Retourne la configuration Telegram de l'utilisateur connecté.
|
|
---
|
|
tags:
|
|
- Utilisateur
|
|
summary: Lire la config alertes Telegram (premium+)
|
|
security:
|
|
- Bearer: []
|
|
responses:
|
|
200:
|
|
description: Configuration Telegram courante
|
|
schema:
|
|
properties:
|
|
telegram_chat_id:
|
|
type: string
|
|
nullable: true
|
|
alert_value_bets:
|
|
type: boolean
|
|
alert_top1:
|
|
type: boolean
|
|
alert_quinte_only:
|
|
type: boolean
|
|
401:
|
|
description: Token invalide
|
|
403:
|
|
description: Plan insuffisant
|
|
"""
|
|
user_id = request.user_id # injecté par jwt_required_middleware
|
|
|
|
conn = _get_db()
|
|
try:
|
|
row = conn.execute(
|
|
"""
|
|
SELECT telegram_chat_id, alert_value_bets, alert_top1, alert_quinte_only
|
|
FROM users
|
|
WHERE id = ?
|
|
""",
|
|
(user_id,),
|
|
).fetchone()
|
|
|
|
if not row:
|
|
return jsonify({"error": "Utilisateur introuvable"}), 404
|
|
|
|
return jsonify(
|
|
{
|
|
"telegram_chat_id": row["telegram_chat_id"],
|
|
"alert_value_bets": bool(row["alert_value_bets"]),
|
|
"alert_top1": bool(row["alert_top1"]),
|
|
"alert_quinte_only": bool(row["alert_quinte_only"]),
|
|
}
|
|
), 200
|
|
|
|
except sqlite3.OperationalError as exc:
|
|
# Colonnes absentes : migration non appliquée
|
|
return jsonify(
|
|
{
|
|
"telegram_chat_id": None,
|
|
"alert_value_bets": True,
|
|
"alert_top1": True,
|
|
"alert_quinte_only": False,
|
|
"_warning": "Migration Telegram non appliquée",
|
|
}
|
|
), 200
|
|
except Exception as exc:
|
|
return internal_error(str(exc))
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
# ── POST /api/v1/user/telegram-config ─────────────────────────────────────────
|
|
|
|
|
|
@user_bp.route("/telegram-config", methods=["POST"])
|
|
@jwt_required_middleware
|
|
@plan_required("premium", "pro")
|
|
def update_telegram_config():
|
|
"""
|
|
Met à jour la configuration Telegram de l'utilisateur connecté.
|
|
---
|
|
tags:
|
|
- Utilisateur
|
|
summary: Configurer les alertes Telegram (premium+)
|
|
security:
|
|
- Bearer: []
|
|
parameters:
|
|
- in: body
|
|
name: body
|
|
required: true
|
|
schema:
|
|
properties:
|
|
telegram_chat_id:
|
|
type: string
|
|
description: Chat ID Telegram (ou null pour désactiver)
|
|
alert_value_bets:
|
|
type: boolean
|
|
default: true
|
|
alert_top1:
|
|
type: boolean
|
|
default: true
|
|
alert_quinte_only:
|
|
type: boolean
|
|
default: false
|
|
responses:
|
|
200:
|
|
description: Configuration mise à jour
|
|
400:
|
|
description: Paramètres invalides
|
|
401:
|
|
description: Token invalide
|
|
403:
|
|
description: Plan insuffisant
|
|
"""
|
|
user_id = request.user_id # injecté par jwt_required_middleware
|
|
|
|
data = request.get_json(silent=True)
|
|
if not data:
|
|
return bad_request("Corps JSON requis")
|
|
|
|
# Validation et extraction des champs
|
|
telegram_chat_id = data.get("telegram_chat_id")
|
|
if telegram_chat_id is not None and not isinstance(telegram_chat_id, str):
|
|
return bad_request("telegram_chat_id doit être une chaîne ou null")
|
|
if isinstance(telegram_chat_id, str):
|
|
telegram_chat_id = telegram_chat_id.strip() or None
|
|
|
|
alert_value_bets = data.get("alert_value_bets", True)
|
|
alert_top1 = data.get("alert_top1", True)
|
|
alert_quinte_only = data.get("alert_quinte_only", False)
|
|
|
|
if not isinstance(alert_value_bets, bool):
|
|
return bad_request("alert_value_bets doit être un booléen")
|
|
if not isinstance(alert_top1, bool):
|
|
return bad_request("alert_top1 doit être un booléen")
|
|
if not isinstance(alert_quinte_only, bool):
|
|
return bad_request("alert_quinte_only doit être un booléen")
|
|
|
|
conn = _get_db()
|
|
try:
|
|
conn.execute(
|
|
"""
|
|
UPDATE users
|
|
SET telegram_chat_id = ?,
|
|
alert_value_bets = ?,
|
|
alert_top1 = ?,
|
|
alert_quinte_only = ?
|
|
WHERE id = ?
|
|
""",
|
|
(
|
|
telegram_chat_id,
|
|
int(alert_value_bets),
|
|
int(alert_top1),
|
|
int(alert_quinte_only),
|
|
user_id,
|
|
),
|
|
)
|
|
conn.commit()
|
|
|
|
return jsonify(
|
|
{
|
|
"status": "ok",
|
|
"telegram_chat_id": telegram_chat_id,
|
|
"alert_value_bets": alert_value_bets,
|
|
"alert_top1": alert_top1,
|
|
"alert_quinte_only": alert_quinte_only,
|
|
}
|
|
), 200
|
|
|
|
except sqlite3.OperationalError as exc:
|
|
return jsonify(
|
|
{
|
|
"error": "Migration Telegram non appliquée — contacter le support",
|
|
"detail": str(exc),
|
|
}
|
|
), 500
|
|
except Exception as exc:
|
|
return internal_error(str(exc))
|
|
finally:
|
|
conn.close()
|