Files
Gestion_sondes/app/Monitor_connexions.py
2026-04-26 17:02:13 +02:00

228 lines
6.4 KiB
Python

import os
import time
import logging
import json
from typing import Optional
from pathlib import Path
import pymysql
import requests
from dotenv import load_dotenv
BASE_DIR = Path("/home/debian/Gestion_sondes")
load_dotenv(BASE_DIR / ".env")
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s"
)
log = logging.getLogger("journal_connexions")
def env_str(name: str, default: Optional[str] = None) -> Optional[str]:
value = os.getenv(name, default)
if value is None:
return None
value = value.strip()
return value if value else default
DB_CONFIG = {
"host": env_str("DB_HOST"),
"port": int(env_str("DB_PORT", "3306")),
"user": env_str("DB_USER2"),
"password": env_str("DB_PASS2"),
"database": env_str("DB_NAME2", "Acces"),
"charset": "utf8mb4",
"cursorclass": pymysql.cursors.DictCursor,
"autocommit": True,
}
SYNO_CHAT_WEBHOOK = env_str("SYNO_CHAT_WEBHOOK_CONNEXIONS")
SYNO_CHAT_BOTNAME = env_str("SYNO_CHAT_BOTNAME_CONNEXIONS", "Journal Connexions")
SYNO_CHAT_WEBHOOK_SIMPLE = env_str("SYNO_CHAT_WEBHOOK_CONNEXIONS_SIMPLE")
SYNO_CHAT_BOTNAME_SIMPLE = env_str("SYNO_CHAT_BOTNAME_CONNEXIONS_SIMPLE", "Connexions Simples")
POLL_INTERVAL = int(env_str("POLL_INTERVAL", "10"))
def get_connection():
return pymysql.connect(**DB_CONFIG)
def format_message(row: dict) -> str:
return (
f"[Connexion MySQL]\n"
f"Utilisateur : {row.get('NomUtilisateur', '')}\n"
f"Poste : {row.get('PosteClient', '')}\n"
f"Tableur : {row.get('TableurSource', '')}\n"
f"Windows : {row.get('UtilisateurWindows', '')}\n"
f"Site : {row.get('SiteDemande', '')}\n"
f"Service : {row.get('ServiceDemande', '')}\n"
f"DSN : {row.get('DSN', '')}\n"
f"BDD : {row.get('BDD', '')}\n"
f"Statut : {row.get('Statut', '')}\n"
f"Motif : {row.get('Motif', '')}\n"
f"Heure : {row.get('DateHeure', '')}\n"
f"Session : {row.get('SessionID', '')}"
)
def format_simple_message(row: dict) -> str:
user = row.get("NomUtilisateur", "")
site = row.get("SiteDemande", "")
statut = row.get("Statut", "")
return f"[Connexion site]\n{user} -> {site} ({statut})"
def send_synology_chat(message: str) -> None:
if not SYNO_CHAT_WEBHOOK:
raise RuntimeError("SYNO_CHAT_WEBHOOK_CONNEXIONS non configure")
syno_payload = {
"text": message,
"botName": SYNO_CHAT_BOTNAME
}
response = requests.post(
SYNO_CHAT_WEBHOOK,
data={"payload": json.dumps(syno_payload, ensure_ascii=False)},
timeout=10
)
log.info("Synology Chat DETAIL HTTP=%s body=%s", response.status_code, response.text)
response.raise_for_status()
body = response.json()
if not body.get("success", False):
raise RuntimeError(f"Synology Chat DETAIL erreur: {body}")
def send_synology_chat_simple(message: str) -> None:
if not SYNO_CHAT_WEBHOOK_SIMPLE:
return
syno_payload = {
"text": message,
"botName": SYNO_CHAT_BOTNAME_SIMPLE
}
response = requests.post(
SYNO_CHAT_WEBHOOK_SIMPLE,
data={"payload": json.dumps(syno_payload, ensure_ascii=False)},
timeout=10
)
log.info("Synology Chat SIMPLE HTTP=%s body=%s", response.status_code, response.text)
response.raise_for_status()
body = response.json()
if not body.get("success", False):
raise RuntimeError(f"Synology Chat SIMPLE erreur: {body}")
def fetch_pending_rows(conn) -> list[dict]:
sql = """
SELECT
Id_Journal,
DateHeure,
NomUtilisateur,
PosteClient,
TableurSource,
UtilisateurWindows,
SiteDemande,
ServiceDemande,
DSN,
BDD,
Statut,
Motif,
SessionID
FROM `Acces`.`JournalConnexions`
WHERE NotificationEnvoyee = 0
ORDER BY DateHeure ASC, Id_Journal ASC
LIMIT 50
"""
with conn.cursor() as cur:
cur.execute(sql)
return cur.fetchall()
def mark_sent(conn, row_id: int) -> None:
sql = """
UPDATE `Acces`.`JournalConnexions`
SET NotificationEnvoyee = 1,
DateNotification = NOW(),
ErreurNotification = NULL
WHERE Id_Journal = %s
"""
with conn.cursor() as cur:
cur.execute(sql, (row_id,))
def mark_error(conn, row_id: int, error_msg: str) -> None:
sql = """
UPDATE `Acces`.`JournalConnexions`
SET ErreurNotification = %s
WHERE Id_Journal = %s
"""
with conn.cursor() as cur:
cur.execute(sql, (error_msg[:255], row_id))
def process_once() -> None:
with get_connection() as conn:
rows = fetch_pending_rows(conn)
if not rows:
log.info("Aucune nouvelle connexion à notifier.")
return
log.info("%s connexion(s) à notifier.", len(rows))
for row in rows:
row_id = row["Id_Journal"]
try:
detailed_message = format_message(row)
send_synology_chat(detailed_message)
# Envoi simplifie uniquement pour la vraie connexion site,
# pas pour la ligne meta Domo91 / Acces
if row.get("SiteDemande") and row.get("DSN") != "Domo91":
simple_message = format_simple_message(row)
send_synology_chat_simple(simple_message)
mark_sent(conn, row_id)
log.info("Notification envoyee pour Id_Journal=%s", row_id)
except Exception as exc:
log.exception("Erreur d'envoi pour Id_Journal=%s", row_id)
try:
mark_error(conn, row_id, str(exc))
except Exception:
log.exception("Impossible d'ecrire ErreurNotification pour Id_Journal=%s", row_id)
def main():
missing = [k for k, v in DB_CONFIG.items() if v is None and k != "port"]
if missing:
raise RuntimeError(f"Variables d'environnement manquantes : {', '.join(missing)}")
if not SYNO_CHAT_WEBHOOK:
raise RuntimeError("SYNO_CHAT_WEBHOOK_CONNEXIONS manquant")
log.info("Surveillance de JournalConnexions demarree.")
while True:
try:
process_once()
except Exception:
log.exception("Erreur generale dans la boucle de surveillance")
time.sleep(POLL_INTERVAL)
if __name__ == "__main__":
main()