- Add notify_update_available() method to NotificationService - Add translations for update notifications (ru/en) - Add 'update' type to default notification types - Add check_for_panel_updates() background task (runs every 6 hours) - Notifications include link to settings page for easy update access - Prevent duplicate notifications for same version
725 lines
27 KiB
Python
725 lines
27 KiB
Python
"""
|
|
Notification Service - Business logic for notifications
|
|
"""
|
|
|
|
from typing import List, Optional, Dict, Any
|
|
from datetime import datetime, timedelta, timezone
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import and_, or_, desc
|
|
from loguru import logger
|
|
|
|
|
|
def utcnow() -> datetime:
|
|
"""Get current UTC time as timezone-aware datetime"""
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
from ..models import Notification, NotificationPreference, User, PanelSettings
|
|
from ..schemas import (
|
|
NotificationCreate,
|
|
NotificationUpdate,
|
|
NotificationPreferenceUpdate
|
|
)
|
|
from ..i18n import t
|
|
|
|
|
|
def get_panel_language(db: Session) -> str:
|
|
"""Get panel language from settings"""
|
|
setting = db.query(PanelSettings).filter(PanelSettings.key == "language").first()
|
|
return setting.value if setting else "ru"
|
|
|
|
|
|
class NotificationService:
|
|
"""Service for managing notifications"""
|
|
|
|
@staticmethod
|
|
def create_notification(
|
|
db: Session,
|
|
notification_data: NotificationCreate
|
|
) -> Notification:
|
|
"""
|
|
Create a new notification
|
|
|
|
Args:
|
|
db: Database session
|
|
notification_data: Notification data
|
|
|
|
Returns:
|
|
Created notification
|
|
"""
|
|
user_id = notification_data.user_id
|
|
level = notification_data.level
|
|
title = notification_data.title
|
|
|
|
# Check user preferences
|
|
prefs = NotificationService.get_user_preferences(db, user_id)
|
|
|
|
if not prefs or not prefs.enabled:
|
|
# User has disabled notifications
|
|
logger.info(f"[NOTIFICATION SKIPPED] User {user_id}: notifications disabled | Level: {level} | Title: {title}")
|
|
return None
|
|
|
|
# Check if notification level is allowed
|
|
if notification_data.level not in prefs.notification_levels:
|
|
logger.info(f"[NOTIFICATION SKIPPED] User {user_id}: level '{level}' not in allowed levels {prefs.notification_levels} | Title: {title}")
|
|
return None
|
|
|
|
# Check if notification type is allowed
|
|
if notification_data.type not in prefs.notification_types:
|
|
logger.info(f"[NOTIFICATION SKIPPED] User {user_id}: type '{notification_data.type}' not in allowed types | Title: {title}")
|
|
return None
|
|
|
|
# Check quiet hours
|
|
if NotificationService._is_quiet_hours(prefs):
|
|
# Only critical notifications during quiet hours
|
|
if notification_data.level != "critical":
|
|
logger.info(f"[NOTIFICATION SKIPPED] User {user_id}: quiet hours active, only critical allowed | Level: {level} | Title: {title}")
|
|
return None
|
|
|
|
# Create notification
|
|
notification = Notification(
|
|
user_id=notification_data.user_id,
|
|
type=notification_data.type,
|
|
level=notification_data.level,
|
|
title=notification_data.title,
|
|
message=notification_data.message,
|
|
data=notification_data.data,
|
|
link=notification_data.link,
|
|
source=notification_data.source,
|
|
source_id=notification_data.source_id,
|
|
expires_at=notification_data.expires_at or (utcnow() + timedelta(days=7))
|
|
)
|
|
|
|
db.add(notification)
|
|
db.commit()
|
|
db.refresh(notification)
|
|
|
|
logger.info(f"[NOTIFICATION CREATED] ID: {notification.id} | User: {user_id} | Level: {level} | Type: {notification_data.type} | Title: {title}")
|
|
|
|
return notification
|
|
|
|
@staticmethod
|
|
def get_user_notifications(
|
|
db: Session,
|
|
user_id: int,
|
|
unread_only: bool = False,
|
|
level: Optional[str] = None,
|
|
limit: int = 50,
|
|
offset: int = 0
|
|
) -> tuple[List[Notification], int]:
|
|
"""
|
|
Get user notifications with filters
|
|
|
|
Args:
|
|
db: Database session
|
|
user_id: User ID
|
|
unread_only: Only unread notifications
|
|
level: Filter by level (critical, warning, info, success)
|
|
limit: Maximum number of notifications
|
|
offset: Offset for pagination
|
|
|
|
Returns:
|
|
Tuple of (notifications, total_count)
|
|
"""
|
|
query = db.query(Notification).filter(Notification.user_id == user_id)
|
|
|
|
if unread_only:
|
|
query = query.filter(Notification.read == False)
|
|
|
|
if level:
|
|
query = query.filter(Notification.level == level)
|
|
|
|
# Filter out expired notifications
|
|
query = query.filter(
|
|
or_(
|
|
Notification.expires_at.is_(None),
|
|
Notification.expires_at > utcnow()
|
|
)
|
|
)
|
|
|
|
total = query.count()
|
|
notifications = query.order_by(desc(Notification.created_at)).limit(limit).offset(offset).all()
|
|
|
|
return notifications, total
|
|
|
|
@staticmethod
|
|
def get_unread_count(db: Session, user_id: int) -> int:
|
|
"""Get count of unread notifications"""
|
|
return db.query(Notification).filter(
|
|
and_(
|
|
Notification.user_id == user_id,
|
|
Notification.read == False,
|
|
or_(
|
|
Notification.expires_at.is_(None),
|
|
Notification.expires_at > utcnow()
|
|
)
|
|
)
|
|
).count()
|
|
|
|
@staticmethod
|
|
def mark_as_read(
|
|
db: Session,
|
|
notification_id: int,
|
|
user_id: int
|
|
) -> Optional[Notification]:
|
|
"""Mark notification as read"""
|
|
notification = db.query(Notification).filter(
|
|
and_(
|
|
Notification.id == notification_id,
|
|
Notification.user_id == user_id
|
|
)
|
|
).first()
|
|
|
|
if notification and not notification.read:
|
|
notification.read = True
|
|
notification.read_at = utcnow()
|
|
db.commit()
|
|
db.refresh(notification)
|
|
|
|
return notification
|
|
|
|
@staticmethod
|
|
def mark_all_as_read(db: Session, user_id: int) -> int:
|
|
"""Mark all notifications as read for user"""
|
|
count = db.query(Notification).filter(
|
|
and_(
|
|
Notification.user_id == user_id,
|
|
Notification.read == False
|
|
)
|
|
).update({
|
|
"read": True,
|
|
"read_at": utcnow()
|
|
})
|
|
db.commit()
|
|
return count
|
|
|
|
@staticmethod
|
|
def delete_notification(
|
|
db: Session,
|
|
notification_id: int,
|
|
user_id: int
|
|
) -> bool:
|
|
"""Delete a notification"""
|
|
notification = db.query(Notification).filter(
|
|
and_(
|
|
Notification.id == notification_id,
|
|
Notification.user_id == user_id
|
|
)
|
|
).first()
|
|
|
|
if notification:
|
|
db.delete(notification)
|
|
db.commit()
|
|
return True
|
|
return False
|
|
|
|
@staticmethod
|
|
def delete_all_read(db: Session, user_id: int) -> int:
|
|
"""Delete all read notifications for user"""
|
|
count = db.query(Notification).filter(
|
|
and_(
|
|
Notification.user_id == user_id,
|
|
Notification.read == True
|
|
)
|
|
).delete()
|
|
db.commit()
|
|
return count
|
|
|
|
@staticmethod
|
|
def cleanup_expired(db: Session) -> int:
|
|
"""Delete all expired notifications"""
|
|
count = db.query(Notification).filter(
|
|
and_(
|
|
Notification.expires_at.isnot(None),
|
|
Notification.expires_at < utcnow()
|
|
)
|
|
).delete()
|
|
db.commit()
|
|
return count
|
|
|
|
# Preferences management
|
|
|
|
@staticmethod
|
|
def get_user_preferences(
|
|
db: Session,
|
|
user_id: int
|
|
) -> Optional[NotificationPreference]:
|
|
"""Get user notification preferences"""
|
|
prefs = db.query(NotificationPreference).filter(
|
|
NotificationPreference.user_id == user_id
|
|
).first()
|
|
|
|
# Create default preferences if not exist
|
|
if not prefs:
|
|
prefs = NotificationPreference(
|
|
user_id=user_id,
|
|
enabled=True,
|
|
email_enabled=False,
|
|
email_critical_only=True,
|
|
telegram_enabled=False,
|
|
notification_levels=["critical", "warning", "info", "success"],
|
|
notification_types=["vm_status", "resource_alert", "system", "ipam", "template"]
|
|
)
|
|
db.add(prefs)
|
|
db.commit()
|
|
db.refresh(prefs)
|
|
|
|
return prefs
|
|
|
|
@staticmethod
|
|
def update_user_preferences(
|
|
db: Session,
|
|
user_id: int,
|
|
prefs_data: NotificationPreferenceUpdate
|
|
) -> NotificationPreference:
|
|
"""Update user notification preferences"""
|
|
prefs = NotificationService.get_user_preferences(db, user_id)
|
|
|
|
# Update fields
|
|
update_data = prefs_data.model_dump(exclude_unset=True)
|
|
for field, value in update_data.items():
|
|
setattr(prefs, field, value)
|
|
|
|
prefs.updated_at = utcnow()
|
|
db.commit()
|
|
db.refresh(prefs)
|
|
|
|
return prefs
|
|
|
|
# Helper methods
|
|
|
|
@staticmethod
|
|
def _is_quiet_hours(prefs: NotificationPreference) -> bool:
|
|
"""Check if current time is in quiet hours"""
|
|
if not prefs.quiet_hours_start or not prefs.quiet_hours_end:
|
|
return False
|
|
|
|
now = utcnow().time()
|
|
start = datetime.strptime(prefs.quiet_hours_start, "%H:%M").time()
|
|
end = datetime.strptime(prefs.quiet_hours_end, "%H:%M").time()
|
|
|
|
if start <= end:
|
|
return start <= now <= end
|
|
else:
|
|
# Crosses midnight
|
|
return now >= start or now <= end
|
|
|
|
# Convenience methods for creating specific notification types
|
|
|
|
@staticmethod
|
|
def notify_vm_status(
|
|
db: Session,
|
|
user_id: int,
|
|
vm_id: int,
|
|
vm_name: str,
|
|
status: str,
|
|
level: str = "info",
|
|
server_name: str = "",
|
|
old_status: str = ""
|
|
) -> Optional[Notification]:
|
|
"""Create VM status notification"""
|
|
lang = get_panel_language(db)
|
|
|
|
# Determine title and message based on status
|
|
if status == "running" and old_status == "stopped":
|
|
title = t("notify_vm_started_title", lang, vm_name=vm_name)
|
|
message = t("notify_vm_started_message", lang, vm_name=vm_name, server_name=server_name)
|
|
elif status == "stopped" and old_status == "running":
|
|
title = t("notify_vm_stopped_title", lang, vm_name=vm_name)
|
|
message = t("notify_vm_stopped_message", lang, vm_name=vm_name, server_name=server_name)
|
|
else:
|
|
title = t("notify_vm_status_change_title", lang, vm_name=vm_name)
|
|
message = t("notify_vm_status_change_message", lang, vm_name=vm_name, old_status=old_status, new_status=status)
|
|
|
|
return NotificationService.create_notification(
|
|
db,
|
|
NotificationCreate(
|
|
user_id=user_id,
|
|
type="vm_status",
|
|
level=level,
|
|
title=title,
|
|
message=message,
|
|
data={"vm_id": vm_id, "vm_name": vm_name, "status": status, "old_status": old_status},
|
|
link=f"/vms/instance/{vm_id}",
|
|
source="proxmox",
|
|
source_id=str(vm_id)
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def notify_resource_alert(
|
|
db: Session,
|
|
user_id: int,
|
|
resource_type: str,
|
|
resource_name: str,
|
|
usage_percent: float,
|
|
threshold: float
|
|
) -> Optional[Notification]:
|
|
"""Create resource usage alert"""
|
|
lang = get_panel_language(db)
|
|
level = "critical" if usage_percent >= 90 else "warning"
|
|
|
|
title = t("notify_resource_alert_title", lang, resource_type=resource_type)
|
|
message = t("notify_resource_alert_message", lang,
|
|
resource_name=resource_name,
|
|
usage=f"{usage_percent:.1f}",
|
|
threshold=f"{threshold:.0f}")
|
|
|
|
return NotificationService.create_notification(
|
|
db,
|
|
NotificationCreate(
|
|
user_id=user_id,
|
|
type="resource_alert",
|
|
level=level,
|
|
title=title,
|
|
message=message,
|
|
data={
|
|
"resource_type": resource_type,
|
|
"resource_name": resource_name,
|
|
"usage": usage_percent,
|
|
"threshold": threshold
|
|
},
|
|
source="system"
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def notify_system_event(
|
|
db: Session,
|
|
user_id: int,
|
|
title: str,
|
|
message: str,
|
|
level: str = "info",
|
|
data: Optional[dict] = None
|
|
) -> Optional[Notification]:
|
|
"""Create generic system notification"""
|
|
return NotificationService.create_notification(
|
|
db,
|
|
NotificationCreate(
|
|
user_id=user_id,
|
|
type="system",
|
|
level=level,
|
|
title=title,
|
|
message=message,
|
|
data=data,
|
|
source="system"
|
|
)
|
|
)
|
|
|
|
@staticmethod
|
|
def notify_update_available(
|
|
db: Session,
|
|
user_id: int,
|
|
current_version: str,
|
|
new_version: str,
|
|
changelog: Optional[str] = None
|
|
) -> Optional[Notification]:
|
|
"""Create notification about new panel version"""
|
|
lang = get_panel_language(db)
|
|
|
|
title = t("notify_update_available_title", lang, new_version=new_version)
|
|
message = t("notify_update_available_message", lang,
|
|
new_version=new_version,
|
|
current_version=current_version)
|
|
|
|
data = {
|
|
"current_version": current_version,
|
|
"new_version": new_version,
|
|
"changelog": changelog
|
|
}
|
|
|
|
return NotificationService.create_notification(
|
|
db,
|
|
NotificationCreate(
|
|
user_id=user_id,
|
|
type="update",
|
|
level="info",
|
|
title=title,
|
|
message=message,
|
|
data=data,
|
|
link="/settings#updates",
|
|
source="system",
|
|
source_id=f"update_{new_version}"
|
|
)
|
|
)
|
|
|
|
# ============================================
|
|
# Async methods for multi-channel notifications
|
|
# ============================================
|
|
|
|
@staticmethod
|
|
async def create_and_send(
|
|
db: Session,
|
|
user_id: int,
|
|
notification_type: str,
|
|
level: str,
|
|
title: str,
|
|
message: str,
|
|
data: Optional[Dict[str, Any]] = None,
|
|
link: Optional[str] = None,
|
|
source: Optional[str] = None,
|
|
source_id: Optional[str] = None,
|
|
force_send: bool = False
|
|
) -> Optional[Notification]:
|
|
"""
|
|
Create notification and send through enabled channels (Email, Telegram)
|
|
|
|
Args:
|
|
db: Database session
|
|
user_id: User ID
|
|
notification_type: Type of notification
|
|
level: Level (critical, warning, info, success)
|
|
title: Notification title
|
|
message: Notification message
|
|
data: Additional data
|
|
link: Optional link
|
|
source: Source system
|
|
source_id: Source entity ID
|
|
force_send: If True, send to external channels even if "Critical Only" is enabled
|
|
|
|
Returns:
|
|
Created notification
|
|
"""
|
|
# Create in-app notification
|
|
notification = NotificationService.create_notification(
|
|
db,
|
|
NotificationCreate(
|
|
user_id=user_id,
|
|
type=notification_type,
|
|
level=level,
|
|
title=title,
|
|
message=message,
|
|
data=data,
|
|
link=link,
|
|
source=source,
|
|
source_id=source_id
|
|
)
|
|
)
|
|
|
|
if not notification:
|
|
logger.warning(f"[NOTIFICATION] In-app notification was not created for user {user_id} | Level: {level} | Title: {title}")
|
|
return None
|
|
|
|
# Get user preferences for external channels
|
|
prefs = NotificationService.get_user_preferences(db, user_id)
|
|
if not prefs or not prefs.enabled:
|
|
logger.info(f"[EXTERNAL CHANNELS SKIPPED] User {user_id}: preferences disabled | Notification ID: {notification.id}")
|
|
return notification
|
|
|
|
# Get user for email
|
|
user = db.query(User).filter(User.id == user_id).first()
|
|
|
|
# Prepare notification dict for formatting
|
|
notification_dict = {
|
|
"title": title,
|
|
"message": message,
|
|
"level": level,
|
|
"source": source or "system",
|
|
"created_at": notification.created_at.strftime("%Y-%m-%d %H:%M:%S") if notification.created_at else "now"
|
|
}
|
|
|
|
# Log channel delivery attempt details
|
|
logger.info(f"[EXTERNAL CHANNELS] Processing notification {notification.id} | Level: {level} | Email enabled: {prefs.email_enabled} | Telegram enabled: {prefs.telegram_enabled} | Critical only: {prefs.email_critical_only} | Force send: {force_send}")
|
|
|
|
# Send Email
|
|
if prefs.email_enabled and user and user.email:
|
|
# Check critical only setting (bypass if force_send is True)
|
|
if prefs.email_critical_only and level != "critical" and not force_send:
|
|
logger.info(f"[EMAIL SKIPPED] User {user_id}: critical_only=True but level='{level}' | Notification ID: {notification.id}")
|
|
else:
|
|
try:
|
|
from .notification_channels import get_email_channel
|
|
email_ch = get_email_channel()
|
|
logger.info(f"[EMAIL] Attempting to send | To: {user.email} | SMTP configured: {email_ch.is_configured()} | Host: {email_ch.smtp_host}")
|
|
if email_ch.is_configured():
|
|
subject, body, html_body, embedded_images = email_ch.format_notification(notification_dict)
|
|
result = await email_ch.send(user.email, subject, body, html_body, embedded_images)
|
|
if result:
|
|
logger.info(f"[EMAIL SUCCESS] Sent to {user.email} | Notification ID: {notification.id}")
|
|
else:
|
|
logger.error(f"[EMAIL FAILED] Could not send to {user.email} | Notification ID: {notification.id}")
|
|
else:
|
|
logger.warning(f"[EMAIL SKIPPED] SMTP not configured | Host: {email_ch.smtp_host} | User: {email_ch.smtp_user}")
|
|
except Exception as e:
|
|
logger.error(f"[EMAIL ERROR] Failed to send to {user.email}: {e} | Notification ID: {notification.id}")
|
|
else:
|
|
if not prefs.email_enabled:
|
|
logger.debug(f"[EMAIL SKIPPED] User {user_id}: email_enabled=False")
|
|
elif not user or not user.email:
|
|
logger.warning(f"[EMAIL SKIPPED] User {user_id}: no email address configured")
|
|
|
|
# Send Telegram
|
|
if prefs.telegram_enabled and prefs.telegram_chat_id:
|
|
# Check critical only for Telegram too if set (bypass if force_send is True)
|
|
if prefs.email_critical_only and level != "critical" and not force_send:
|
|
logger.info(f"[TELEGRAM SKIPPED] User {user_id}: critical_only=True but level='{level}' | Notification ID: {notification.id}")
|
|
else:
|
|
try:
|
|
from .notification_channels import get_telegram_channel
|
|
tg_ch = get_telegram_channel()
|
|
logger.info(f"[TELEGRAM] Attempting to send | Chat ID: {prefs.telegram_chat_id} | Bot configured: {tg_ch.is_configured()}")
|
|
if tg_ch.is_configured():
|
|
tg_message, logo_bytes = tg_ch.format_notification(notification_dict)
|
|
if logo_bytes:
|
|
result = await tg_ch.send_photo(prefs.telegram_chat_id, logo_bytes, tg_message)
|
|
else:
|
|
result = await tg_ch.send(prefs.telegram_chat_id, tg_message)
|
|
if result:
|
|
logger.info(f"[TELEGRAM SUCCESS] Sent to {prefs.telegram_chat_id} | Notification ID: {notification.id}")
|
|
else:
|
|
logger.error(f"[TELEGRAM FAILED] Could not send to {prefs.telegram_chat_id} | Notification ID: {notification.id}")
|
|
else:
|
|
logger.warning(f"[TELEGRAM SKIPPED] Bot not configured")
|
|
except Exception as e:
|
|
logger.error(f"[TELEGRAM ERROR] Failed to send to {prefs.telegram_chat_id}: {e} | Notification ID: {notification.id}")
|
|
else:
|
|
if not prefs.telegram_enabled:
|
|
logger.debug(f"[TELEGRAM SKIPPED] User {user_id}: telegram_enabled=False")
|
|
elif not prefs.telegram_chat_id:
|
|
logger.warning(f"[TELEGRAM SKIPPED] User {user_id}: no telegram_chat_id configured")
|
|
|
|
return notification
|
|
|
|
@staticmethod
|
|
async def send_test_notification(
|
|
db: Session,
|
|
user_id: int,
|
|
channel: str = "all"
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Send test notification through specified channels
|
|
|
|
Args:
|
|
db: Database session
|
|
user_id: User ID
|
|
channel: Channel to test (all, email, telegram, inapp)
|
|
|
|
Returns:
|
|
Dict with results for each channel
|
|
"""
|
|
results = {"inapp": False, "email": False, "telegram": False, "errors": []}
|
|
|
|
user = db.query(User).filter(User.id == user_id).first()
|
|
prefs = NotificationService.get_user_preferences(db, user_id)
|
|
|
|
# Get panel language
|
|
lang = get_panel_language(db)
|
|
|
|
# Use local time instead of UTC
|
|
local_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
test_notification = {
|
|
"title": t("notify_test_title", lang),
|
|
"message": t("notify_test_message", lang),
|
|
"level": "info",
|
|
"source": "system",
|
|
"created_at": local_time
|
|
}
|
|
|
|
# In-app notification
|
|
if channel in ["all", "inapp"]:
|
|
try:
|
|
notification = NotificationService.create_notification(
|
|
db,
|
|
NotificationCreate(
|
|
user_id=user_id,
|
|
type="system",
|
|
level="info",
|
|
title=test_notification["title"],
|
|
message=test_notification["message"],
|
|
source="system"
|
|
)
|
|
)
|
|
results["inapp"] = notification is not None
|
|
except Exception as e:
|
|
results["errors"].append(f"In-app: {str(e)}")
|
|
|
|
# Email
|
|
if channel in ["all", "email"]:
|
|
try:
|
|
from .notification_channels import get_email_channel
|
|
email_ch = get_email_channel()
|
|
if email_ch.is_configured():
|
|
if user and user.email:
|
|
subject, body, html_body, embedded_images = email_ch.format_notification(test_notification)
|
|
results["email"] = await email_ch.send(user.email, subject, body, html_body, embedded_images)
|
|
else:
|
|
results["errors"].append("Email: User has no email address")
|
|
else:
|
|
results["errors"].append("Email: SMTP not configured")
|
|
except Exception as e:
|
|
results["errors"].append(f"Email: {str(e)}")
|
|
|
|
# Telegram
|
|
if channel in ["all", "telegram"]:
|
|
try:
|
|
from .notification_channels import get_telegram_channel
|
|
tg_ch = get_telegram_channel()
|
|
if tg_ch.is_configured():
|
|
if prefs and prefs.telegram_chat_id:
|
|
tg_message, logo_bytes = tg_ch.format_notification(test_notification)
|
|
if logo_bytes:
|
|
results["telegram"] = await tg_ch.send_photo(prefs.telegram_chat_id, logo_bytes, tg_message)
|
|
else:
|
|
results["telegram"] = await tg_ch.send(prefs.telegram_chat_id, tg_message)
|
|
else:
|
|
results["errors"].append("Telegram: Chat ID not configured")
|
|
else:
|
|
results["errors"].append("Telegram: Bot token not configured")
|
|
except Exception as e:
|
|
results["errors"].append(f"Telegram: {str(e)}")
|
|
|
|
return results
|
|
|
|
@staticmethod
|
|
async def get_channels_status(db: Session, user_id: int) -> Dict[str, Any]:
|
|
"""
|
|
Get status of notification channels
|
|
|
|
Args:
|
|
db: Database session
|
|
user_id: User ID
|
|
|
|
Returns:
|
|
Dict with channel status information
|
|
"""
|
|
from .notification_channels import get_email_channel, get_telegram_channel
|
|
|
|
user = db.query(User).filter(User.id == user_id).first()
|
|
prefs = NotificationService.get_user_preferences(db, user_id)
|
|
|
|
email_ch = get_email_channel()
|
|
tg_ch = get_telegram_channel()
|
|
|
|
# Get Telegram bot info if configured
|
|
telegram_bot_info = None
|
|
if tg_ch.is_configured():
|
|
try:
|
|
telegram_bot_info = await tg_ch.get_bot_info()
|
|
except Exception:
|
|
pass
|
|
|
|
return {
|
|
"email": {
|
|
"configured": email_ch.is_configured(),
|
|
"enabled": prefs.email_enabled if prefs else False,
|
|
"user_email": user.email if user else None,
|
|
"smtp_host": email_ch.smtp_host if email_ch.is_configured() else None,
|
|
"critical_only": prefs.email_critical_only if prefs else True
|
|
},
|
|
"telegram": {
|
|
"configured": tg_ch.is_configured(),
|
|
"enabled": prefs.telegram_enabled if prefs else False,
|
|
"chat_id": prefs.telegram_chat_id if prefs else None,
|
|
"bot_info": telegram_bot_info
|
|
},
|
|
"inapp": {
|
|
"configured": True,
|
|
"enabled": prefs.enabled if prefs else True
|
|
},
|
|
"quiet_hours": {
|
|
"start": prefs.quiet_hours_start if prefs else None,
|
|
"end": prefs.quiet_hours_end if prefs else None
|
|
}
|
|
}
|