Add Apprise alerting capabilities (backend)

- Add name + apprise_url columns to NotificationConfig model
- Add AdminNotificationConfig model for system-wide alert channels
- Create NotificationService with send_user_notification,
  send_admin_notification, and test_notification helpers
- Replace stub notifications.py with full CRUD + /test endpoint
- Add admin notification config CRUD + /test to admin.py
- Update NotificationConfig schemas: name required, apprise_url
  optional, config optional (default {})
- Add AdminNotificationConfig* schemas
- Integrate send_user_notification into process_mail_account task
  for Gmail revocation, forwarding failures, and exceptions
- Update CHANGELOG.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-26 18:06:05 +00:00
parent a6c95bef4d
commit bbd2febfc1
7 changed files with 484 additions and 4 deletions
+31
View File
@@ -289,6 +289,11 @@ class NotificationConfig(Base):
Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False
)
name = Column(String(255), nullable=False, default="My Notification")
apprise_url = Column(
Text, nullable=True
) # The Apprise URL e.g. tgram://token/chatid
# Channel details
channel: Column[str] = Column(SQLEnum(NotificationChannel), nullable=False)
is_enabled = Column(Boolean, default=True)
@@ -590,3 +595,29 @@ class AppSetting(Base):
onupdate=lambda: datetime.now(timezone.utc),
nullable=False,
)
class AdminNotificationConfig(Base):
"""System-wide admin notification channels"""
__tablename__ = "admin_notification_configs"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(255), nullable=False)
apprise_url = Column(Text, nullable=False)
is_enabled = Column(Boolean, default=True)
notify_on_errors = Column(Boolean, default=True)
notify_on_system_events = Column(Boolean, default=True)
description = Column(Text, nullable=True)
created_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
nullable=False,
)
updated_at = Column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
nullable=False,
)