106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
import json
|
|
import logging
|
|
import requests
|
|
from datetime import datetime, timezone, timedelta
|
|
from sqlalchemy.orm import Session
|
|
|
|
from config import get_settings
|
|
from models import Computer, Notification
|
|
|
|
settings = get_settings()
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def create_notification(db: Session, computer: Computer, ntype: str, title: str, message: str) -> Notification:
|
|
n = Notification(
|
|
computer_id=computer.id,
|
|
type=ntype,
|
|
title=title,
|
|
message=message,
|
|
)
|
|
db.add(n)
|
|
db.commit()
|
|
db.refresh(n)
|
|
send_external_notifications(n, computer)
|
|
return n
|
|
|
|
|
|
def check_offline_computers(db: Session):
|
|
"""Mark online computers that missed the threshold as offline and create notifications."""
|
|
threshold = datetime.now(timezone.utc) - timedelta(minutes=settings.OFFLINE_THRESHOLD_MINUTES)
|
|
stale = (
|
|
db.query(Computer)
|
|
.filter(Computer.status == "online", Computer.last_seen < threshold)
|
|
.all()
|
|
)
|
|
for computer in stale:
|
|
computer.status = "offline"
|
|
db.add(computer)
|
|
# avoid duplicate notifications for the same offline event
|
|
existing = (
|
|
db.query(Notification)
|
|
.filter(
|
|
Notification.computer_id == computer.id,
|
|
Notification.type == "offline",
|
|
Notification.is_read == False,
|
|
)
|
|
.first()
|
|
)
|
|
if not existing:
|
|
msg = (
|
|
f"ПК {computer.hostname} не присылал данные более "
|
|
f"{settings.OFFLINE_THRESHOLD_MINUTES} минут. "
|
|
f"Последний пользователь: {computer.current_user or '-'}, "
|
|
f"IP: {computer.current_ip or '-'}"
|
|
)
|
|
create_notification(db, computer, "offline", f"ПК {computer.hostname} офлайн", msg)
|
|
db.commit()
|
|
|
|
|
|
def send_external_notifications(notification: Notification, computer: Computer):
|
|
if settings.NOTIFICATION_WEBHOOK_URL:
|
|
try:
|
|
requests.post(
|
|
settings.NOTIFICATION_WEBHOOK_URL,
|
|
json={
|
|
"type": notification.type,
|
|
"title": notification.title,
|
|
"message": notification.message,
|
|
"hostname": computer.hostname,
|
|
"user": computer.current_user,
|
|
"ip": computer.current_ip,
|
|
},
|
|
timeout=10,
|
|
)
|
|
except Exception as e:
|
|
logger.error("Webhook notification failed: %s", e)
|
|
|
|
if settings.NOTIFICATION_TELEGRAM_BOT_TOKEN and settings.NOTIFICATION_TELEGRAM_CHAT_ID:
|
|
try:
|
|
text = f"*{notification.title}*\n\n{notification.message}"
|
|
requests.post(
|
|
f"https://api.telegram.org/bot{settings.NOTIFICATION_TELEGRAM_BOT_TOKEN}/sendMessage",
|
|
json={
|
|
"chat_id": settings.NOTIFICATION_TELEGRAM_CHAT_ID,
|
|
"text": text,
|
|
"parse_mode": "Markdown",
|
|
},
|
|
timeout=10,
|
|
)
|
|
except Exception as e:
|
|
logger.error("Telegram notification failed: %s", e)
|
|
|
|
|
|
def mark_notification_read(db: Session, notification_id: int) -> bool:
|
|
n = db.query(Notification).filter(Notification.id == notification_id).first()
|
|
if not n:
|
|
return False
|
|
n.is_read = True
|
|
n.read_at = datetime.now(timezone.utc)
|
|
db.commit()
|
|
return True
|
|
|
|
|
|
def get_unread_count(db: Session) -> int:
|
|
return db.query(Notification).filter(Notification.is_read == False).count()
|