Features: feed CRUD, per-feed ntfy target (incl. private servers), Telegram/webhook channels, keyword filters, image attachments, per-feed intervals, OPML import/export, notification history & stats, users with roles, admin alerts, RU/EN i18n, light/dark theme, notification preview, history search, activity chart. Dockerized. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+110
@@ -0,0 +1,110 @@
|
||||
"""Database models."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
def _utcnow() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class Feed(SQLModel, table=True):
|
||||
"""A single RSS/Atom feed to monitor."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
title: str = ""
|
||||
url: str = Field(index=True)
|
||||
|
||||
# --- ntfy target (per-feed; empty server falls back to global default) ---
|
||||
ntfy_server: str = ""
|
||||
ntfy_topic: str = ""
|
||||
# Optional access token / Basic-auth for private ntfy servers.
|
||||
ntfy_token: str = "" # bearer token (tk_...)
|
||||
ntfy_username: str = "" # OR basic auth user
|
||||
ntfy_password: str = "" # OR basic auth password
|
||||
priority: int = 3 # 1=min .. 5=max
|
||||
tags: str = "" # comma separated ntfy tags/emojis
|
||||
attach_image: bool = True # attach first image found in the entry
|
||||
|
||||
# --- alternative delivery channels (per-feed opt-in) ---
|
||||
to_telegram: bool = False
|
||||
to_webhook: bool = False
|
||||
|
||||
# --- keyword filters ---
|
||||
# Only entries containing at least one include keyword (if any) AND
|
||||
# none of the exclude keywords are forwarded. Comma separated, case-insensitive.
|
||||
filter_include: str = ""
|
||||
filter_exclude: str = ""
|
||||
|
||||
# --- scheduling ---
|
||||
# Per-feed interval in minutes. 0 = use the global default.
|
||||
interval: int = 0
|
||||
|
||||
enabled: bool = True
|
||||
|
||||
# --- state ---
|
||||
last_checked: Optional[datetime] = None
|
||||
last_status: str = "" # human readable result of last check
|
||||
error_streak: int = 0 # consecutive failures (for admin alerts)
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class SeenEntry(SQLModel, table=True):
|
||||
"""Tracks which feed entries have already been pushed to avoid duplicates."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
feed_id: int = Field(index=True, foreign_key="feed.id")
|
||||
entry_uid: str = Field(index=True)
|
||||
seen_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class Notification(SQLModel, table=True):
|
||||
"""History of dispatched (or failed) notifications."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
feed_id: int = Field(index=True, foreign_key="feed.id")
|
||||
feed_title: str = ""
|
||||
title: str = ""
|
||||
link: str = ""
|
||||
channels: str = "" # e.g. "ntfy,telegram"
|
||||
ok: bool = True
|
||||
detail: str = "" # error text when ok is False
|
||||
created_at: datetime = Field(default_factory=_utcnow, index=True)
|
||||
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
"""A web-panel user. Roles: 'admin' (full) or 'viewer' (read-only)."""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
username: str = Field(index=True)
|
||||
password_hash: str = ""
|
||||
role: str = "admin" # admin | viewer
|
||||
created_at: datetime = Field(default_factory=_utcnow)
|
||||
|
||||
|
||||
class Settings(SQLModel, table=True):
|
||||
"""Singleton settings row (id == 1)."""
|
||||
|
||||
id: Optional[int] = Field(default=1, primary_key=True)
|
||||
default_ntfy_server: str = "https://ntfy.sh"
|
||||
check_interval: int = 5 # minutes (global default)
|
||||
|
||||
# Auth toggle (per-user credentials live in the User table).
|
||||
auth_enabled: bool = False
|
||||
|
||||
# --- Telegram channel ---
|
||||
telegram_enabled: bool = False
|
||||
telegram_token: str = ""
|
||||
telegram_chat_id: str = ""
|
||||
|
||||
# --- Generic webhook channel ---
|
||||
webhook_enabled: bool = False
|
||||
webhook_url: str = ""
|
||||
|
||||
# --- Admin health alerts ---
|
||||
alerts_enabled: bool = False
|
||||
alert_topic: str = "" # ntfy topic to notify when a feed keeps failing
|
||||
alert_threshold: int = 3 # consecutive failures before alerting
|
||||
Reference in New Issue
Block a user