mirror of
https://github.com/elisiariocouto/leggen.git
synced 2025-12-13 13:42:19 +00:00
This major update transforms leggen from CLI-only to a web-ready architecture while maintaining full CLI compatibility. New Features: - FastAPI backend service (leggend) with comprehensive REST API - Background job scheduler with configurable cron (replaces Ofelia) - All CLI commands refactored to use API endpoints - Docker configuration updated for new services - API client with health checks and error handling API Endpoints: - /api/v1/banks/* - Bank connections and institutions - /api/v1/accounts/* - Account management and balances - /api/v1/transactions/* - Transaction retrieval with filtering - /api/v1/sync/* - Manual sync and scheduler configuration - /api/v1/notifications/* - Notification settings management CLI Enhancements: - New --api-url option and LEGGEND_API_URL environment variable - Enhanced sync command with --wait and --force options - Improved transactions command with --full and --limit options - Automatic fallback and health checking Breaking Changes: - compose.yml structure updated (leggend service added) - Ofelia scheduler removed (internal scheduler used instead) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
192 lines
7.5 KiB
Python
192 lines
7.5 KiB
Python
from typing import Optional
|
|
from fastapi import APIRouter, HTTPException
|
|
from loguru import logger
|
|
|
|
from leggend.api.models.common import APIResponse
|
|
from leggend.api.models.notifications import (
|
|
NotificationSettings,
|
|
NotificationTest,
|
|
DiscordConfig,
|
|
TelegramConfig,
|
|
NotificationFilters
|
|
)
|
|
from leggend.services.notification_service import NotificationService
|
|
from leggend.config import config
|
|
|
|
router = APIRouter()
|
|
notification_service = NotificationService()
|
|
|
|
|
|
@router.get("/notifications/settings", response_model=APIResponse)
|
|
async def get_notification_settings() -> APIResponse:
|
|
"""Get current notification settings"""
|
|
try:
|
|
notifications_config = config.notifications_config
|
|
filters_config = config.filters_config
|
|
|
|
# Build response safely without exposing secrets
|
|
discord_config = notifications_config.get("discord", {})
|
|
telegram_config = notifications_config.get("telegram", {})
|
|
|
|
settings = NotificationSettings(
|
|
discord=DiscordConfig(
|
|
webhook="***" if discord_config.get("webhook") else "",
|
|
enabled=discord_config.get("enabled", True)
|
|
) if discord_config.get("webhook") else None,
|
|
telegram=TelegramConfig(
|
|
token="***" if telegram_config.get("token") else "",
|
|
chat_id=telegram_config.get("chat_id", 0),
|
|
enabled=telegram_config.get("enabled", True)
|
|
) if telegram_config.get("token") else None,
|
|
filters=NotificationFilters(
|
|
case_insensitive=filters_config.get("case-insensitive", {}),
|
|
case_sensitive=filters_config.get("case-sensitive"),
|
|
amount_threshold=filters_config.get("amount_threshold"),
|
|
keywords=filters_config.get("keywords", [])
|
|
)
|
|
)
|
|
|
|
return APIResponse(
|
|
success=True,
|
|
data=settings,
|
|
message="Notification settings retrieved successfully"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to get notification settings: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Failed to get notification settings: {str(e)}")
|
|
|
|
|
|
@router.put("/notifications/settings", response_model=APIResponse)
|
|
async def update_notification_settings(settings: NotificationSettings) -> APIResponse:
|
|
"""Update notification settings"""
|
|
try:
|
|
# Update notifications config
|
|
notifications_config = {}
|
|
|
|
if settings.discord:
|
|
notifications_config["discord"] = {
|
|
"webhook": settings.discord.webhook,
|
|
"enabled": settings.discord.enabled
|
|
}
|
|
|
|
if settings.telegram:
|
|
notifications_config["telegram"] = {
|
|
"token": settings.telegram.token,
|
|
"chat_id": settings.telegram.chat_id,
|
|
"enabled": settings.telegram.enabled
|
|
}
|
|
|
|
# Update filters config
|
|
filters_config = {}
|
|
if settings.filters.case_insensitive:
|
|
filters_config["case-insensitive"] = settings.filters.case_insensitive
|
|
if settings.filters.case_sensitive:
|
|
filters_config["case-sensitive"] = settings.filters.case_sensitive
|
|
if settings.filters.amount_threshold:
|
|
filters_config["amount_threshold"] = settings.filters.amount_threshold
|
|
if settings.filters.keywords:
|
|
filters_config["keywords"] = settings.filters.keywords
|
|
|
|
# Save to config
|
|
if notifications_config:
|
|
config.update_section("notifications", notifications_config)
|
|
if filters_config:
|
|
config.update_section("filters", filters_config)
|
|
|
|
return APIResponse(
|
|
success=True,
|
|
data={"updated": True},
|
|
message="Notification settings updated successfully"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to update notification settings: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Failed to update notification settings: {str(e)}")
|
|
|
|
|
|
@router.post("/notifications/test", response_model=APIResponse)
|
|
async def test_notification(test_request: NotificationTest) -> APIResponse:
|
|
"""Send a test notification"""
|
|
try:
|
|
success = await notification_service.send_test_notification(
|
|
test_request.service,
|
|
test_request.message
|
|
)
|
|
|
|
if success:
|
|
return APIResponse(
|
|
success=True,
|
|
data={"sent": True},
|
|
message=f"Test notification sent to {test_request.service} successfully"
|
|
)
|
|
else:
|
|
return APIResponse(
|
|
success=False,
|
|
message=f"Failed to send test notification to {test_request.service}"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to send test notification: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Failed to send test notification: {str(e)}")
|
|
|
|
|
|
@router.get("/notifications/services", response_model=APIResponse)
|
|
async def get_notification_services() -> APIResponse:
|
|
"""Get available notification services and their status"""
|
|
try:
|
|
notifications_config = config.notifications_config
|
|
|
|
services = {
|
|
"discord": {
|
|
"name": "Discord",
|
|
"enabled": bool(notifications_config.get("discord", {}).get("webhook")),
|
|
"configured": bool(notifications_config.get("discord", {}).get("webhook")),
|
|
"active": notifications_config.get("discord", {}).get("enabled", True)
|
|
},
|
|
"telegram": {
|
|
"name": "Telegram",
|
|
"enabled": bool(
|
|
notifications_config.get("telegram", {}).get("token") and
|
|
notifications_config.get("telegram", {}).get("chat_id")
|
|
),
|
|
"configured": bool(
|
|
notifications_config.get("telegram", {}).get("token") and
|
|
notifications_config.get("telegram", {}).get("chat_id")
|
|
),
|
|
"active": notifications_config.get("telegram", {}).get("enabled", True)
|
|
}
|
|
}
|
|
|
|
return APIResponse(
|
|
success=True,
|
|
data=services,
|
|
message="Notification services status retrieved successfully"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to get notification services: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Failed to get notification services: {str(e)}")
|
|
|
|
|
|
@router.delete("/notifications/settings/{service}", response_model=APIResponse)
|
|
async def delete_notification_service(service: str) -> APIResponse:
|
|
"""Delete/disable a notification service"""
|
|
try:
|
|
if service not in ["discord", "telegram"]:
|
|
raise HTTPException(status_code=400, detail="Service must be 'discord' or 'telegram'")
|
|
|
|
notifications_config = config.notifications_config.copy()
|
|
if service in notifications_config:
|
|
del notifications_config[service]
|
|
config.update_section("notifications", notifications_config)
|
|
|
|
return APIResponse(
|
|
success=True,
|
|
data={"deleted": service},
|
|
message=f"{service.capitalize()} notification service deleted successfully"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to delete notification service {service}: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Failed to delete notification service: {str(e)}") |