Replace all legacy dotted permission codes (vms.view, proxmox.manage, ...) in PermissionChecker/has_permission across app/api with the new resource:action format (vm:view, server:manage, ...), per LEGACY_PERMISSION_MAP. All 33 codes used by the API are now registered in the permission registry. Also switch the /api/auth/me admin branch to get_all_permissions_v2 so the endpoint emits permissions in the new format only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
254 lines
7.9 KiB
Python
254 lines
7.9 KiB
Python
"""
|
|
API routes for notifications
|
|
"""
|
|
|
|
from typing import List, Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..db import get_db
|
|
from ..auth import get_current_user, PermissionChecker
|
|
from ..models import User
|
|
from ..services.notification_service import NotificationService
|
|
from ..schemas import (
|
|
NotificationResponse,
|
|
NotificationListResponse,
|
|
NotificationUpdate,
|
|
NotificationPreferenceResponse,
|
|
NotificationPreferenceUpdate
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/notifications", tags=["notifications"])
|
|
|
|
|
|
@router.get("/", response_model=NotificationListResponse)
|
|
async def get_notifications(
|
|
unread_only: bool = Query(False, description="Only unread notifications"),
|
|
level: Optional[str] = Query(None, description="Filter by level: critical, warning, info, success"),
|
|
limit: int = Query(50, ge=1, le=100),
|
|
offset: int = Query(0, ge=0),
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Get user notifications with filters
|
|
|
|
- **unread_only**: Return only unread notifications
|
|
- **level**: Filter by notification level
|
|
- **limit**: Maximum number of notifications (1-100)
|
|
- **offset**: Offset for pagination
|
|
"""
|
|
notifications, total = NotificationService.get_user_notifications(
|
|
db,
|
|
user_id=current_user.id,
|
|
unread_only=unread_only,
|
|
level=level,
|
|
limit=limit,
|
|
offset=offset
|
|
)
|
|
|
|
unread_count = NotificationService.get_unread_count(db, current_user.id)
|
|
|
|
return {
|
|
"total": total,
|
|
"unread_count": unread_count,
|
|
"notifications": [n.to_dict() for n in notifications]
|
|
}
|
|
|
|
|
|
@router.get("/unread-count")
|
|
async def get_unread_count(
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Get count of unread notifications"""
|
|
count = NotificationService.get_unread_count(db, current_user.id)
|
|
return {"count": count}
|
|
|
|
|
|
@router.patch("/{notification_id}/read", response_model=NotificationResponse)
|
|
async def mark_notification_as_read(
|
|
notification_id: int,
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Mark notification as read"""
|
|
notification = NotificationService.mark_as_read(
|
|
db,
|
|
notification_id=notification_id,
|
|
user_id=current_user.id
|
|
)
|
|
|
|
if not notification:
|
|
raise HTTPException(status_code=404, detail="Notification not found")
|
|
|
|
return notification.to_dict()
|
|
|
|
|
|
@router.post("/mark-all-read")
|
|
async def mark_all_notifications_as_read(
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Mark all notifications as read"""
|
|
count = NotificationService.mark_all_as_read(db, current_user.id)
|
|
return {"message": f"{count} notifications marked as read"}
|
|
|
|
|
|
@router.delete("/{notification_id}")
|
|
async def delete_notification(
|
|
notification_id: int,
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Delete a notification"""
|
|
success = NotificationService.delete_notification(
|
|
db,
|
|
notification_id=notification_id,
|
|
user_id=current_user.id
|
|
)
|
|
|
|
if not success:
|
|
raise HTTPException(status_code=404, detail="Notification not found")
|
|
|
|
return {"message": "Notification deleted"}
|
|
|
|
|
|
@router.delete("/read/all")
|
|
async def delete_all_read_notifications(
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Delete all read notifications"""
|
|
count = NotificationService.delete_all_read(db, current_user.id)
|
|
return {"message": f"{count} notifications deleted"}
|
|
|
|
|
|
# Preferences endpoints
|
|
|
|
@router.get("/preferences", response_model=NotificationPreferenceResponse)
|
|
async def get_notification_preferences(
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Get user notification preferences"""
|
|
prefs = NotificationService.get_user_preferences(db, current_user.id)
|
|
return prefs.to_dict()
|
|
|
|
|
|
@router.put("/preferences", response_model=NotificationPreferenceResponse)
|
|
async def update_notification_preferences(
|
|
prefs_data: NotificationPreferenceUpdate,
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""Update user notification preferences"""
|
|
prefs = NotificationService.update_user_preferences(
|
|
db,
|
|
user_id=current_user.id,
|
|
prefs_data=prefs_data
|
|
)
|
|
return prefs.to_dict()
|
|
|
|
|
|
# ============================================
|
|
# Channel management endpoints
|
|
# ============================================
|
|
|
|
@router.get("/channels/status")
|
|
async def get_channels_status(
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Get status of all notification channels
|
|
|
|
Returns configuration and status for:
|
|
- Email (SMTP)
|
|
- Telegram
|
|
- In-app notifications
|
|
"""
|
|
status = await NotificationService.get_channels_status(db, current_user.id)
|
|
return status
|
|
|
|
|
|
@router.post("/test")
|
|
async def send_test_notification(
|
|
channel: str = Query("all", description="Channel to test: all, email, telegram, inapp"),
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Send a test notification through specified channel(s)
|
|
|
|
- **channel**: Which channel to test (all, email, telegram, inapp)
|
|
"""
|
|
if channel not in ["all", "email", "telegram", "inapp"]:
|
|
raise HTTPException(status_code=400, detail="Invalid channel. Use: all, email, telegram, inapp")
|
|
|
|
results = await NotificationService.send_test_notification(db, current_user.id, channel)
|
|
return results
|
|
|
|
|
|
@router.post("/telegram/verify")
|
|
async def verify_telegram_chat(
|
|
chat_id: str = Query(..., description="Telegram chat ID to verify"),
|
|
current_user: User = Depends(PermissionChecker("notification:manage")),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Verify Telegram chat ID and save to preferences
|
|
|
|
Steps to get your chat_id:
|
|
1. Start a conversation with @userinfobot in Telegram
|
|
2. It will tell you your chat ID
|
|
3. Make sure you've sent /start to your notification bot first
|
|
"""
|
|
from ..services.notification_channels import get_telegram_channel
|
|
|
|
tg_ch = get_telegram_channel()
|
|
if not tg_ch.is_configured():
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Telegram бот не настроен. Добавьте токен в Настройки → Уведомления."
|
|
)
|
|
|
|
# Try to send test message
|
|
success = await tg_ch.verify_chat_id(chat_id)
|
|
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Failed to send message to this chat_id. Make sure you've started the bot with /start"
|
|
)
|
|
|
|
# Save chat_id to preferences
|
|
prefs = NotificationService.get_user_preferences(db, current_user.id)
|
|
prefs.telegram_chat_id = chat_id
|
|
prefs.telegram_enabled = True
|
|
db.commit()
|
|
|
|
return {
|
|
"success": True,
|
|
"message": "Telegram verified and enabled!",
|
|
"chat_id": chat_id
|
|
}
|
|
|
|
|
|
@router.get("/telegram/bot-info")
|
|
async def get_telegram_bot_info(
|
|
current_user: User = Depends(PermissionChecker("notification:manage"))
|
|
):
|
|
"""Get information about the configured Telegram bot"""
|
|
from ..services.notification_channels import get_telegram_channel
|
|
|
|
tg_ch = get_telegram_channel()
|
|
if not tg_ch.is_configured():
|
|
return {"configured": False, "bot_info": None}
|
|
|
|
bot_info = await tg_ch.get_bot_info()
|
|
return {
|
|
"configured": True,
|
|
"bot_info": bot_info
|
|
}
|