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 @@
|
||||
"""Pydantic request/response schemas for the JSON API."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, field_validator
|
||||
|
||||
|
||||
class FeedIn(BaseModel):
|
||||
url: str
|
||||
title: str = ""
|
||||
ntfy_server: str = ""
|
||||
ntfy_topic: str = ""
|
||||
ntfy_token: str = ""
|
||||
ntfy_username: str = ""
|
||||
ntfy_password: str = ""
|
||||
priority: int = 3
|
||||
tags: str = ""
|
||||
attach_image: bool = True
|
||||
to_telegram: bool = False
|
||||
to_webhook: bool = False
|
||||
filter_include: str = ""
|
||||
filter_exclude: str = ""
|
||||
interval: int = 0
|
||||
enabled: bool = True
|
||||
|
||||
@field_validator("url")
|
||||
@classmethod
|
||||
def _url_required(cls, v: str) -> str:
|
||||
v = v.strip()
|
||||
if not v:
|
||||
raise ValueError("URL ленты обязателен")
|
||||
if not v.startswith(("http://", "https://")):
|
||||
raise ValueError("URL должен начинаться с http:// или https://")
|
||||
return v
|
||||
|
||||
@field_validator("priority")
|
||||
@classmethod
|
||||
def _priority_range(cls, v: int) -> int:
|
||||
return min(5, max(1, v))
|
||||
|
||||
@field_validator("interval")
|
||||
@classmethod
|
||||
def _interval_nonneg(cls, v: int) -> int:
|
||||
return max(0, v)
|
||||
|
||||
|
||||
class SettingsIn(BaseModel):
|
||||
default_ntfy_server: str = "https://ntfy.sh"
|
||||
check_interval: int = 5
|
||||
auth_enabled: bool = False
|
||||
# Telegram
|
||||
telegram_enabled: bool = False
|
||||
telegram_token: str = ""
|
||||
telegram_chat_id: str = ""
|
||||
# Webhook
|
||||
webhook_enabled: bool = False
|
||||
webhook_url: str = ""
|
||||
# Admin alerts
|
||||
alerts_enabled: bool = False
|
||||
alert_topic: str = ""
|
||||
alert_threshold: int = 3
|
||||
|
||||
@field_validator("check_interval")
|
||||
@classmethod
|
||||
def _interval_min(cls, v: int) -> int:
|
||||
return max(1, v)
|
||||
|
||||
@field_validator("alert_threshold")
|
||||
@classmethod
|
||||
def _threshold_min(cls, v: int) -> int:
|
||||
return max(1, v)
|
||||
|
||||
|
||||
class TestIn(BaseModel):
|
||||
server: str = ""
|
||||
topic: str
|
||||
|
||||
|
||||
class PreviewIn(BaseModel):
|
||||
url: str
|
||||
filter_include: str = ""
|
||||
filter_exclude: str = ""
|
||||
|
||||
@field_validator("url")
|
||||
@classmethod
|
||||
def _url_required(cls, v: str) -> str:
|
||||
v = v.strip()
|
||||
if not v.startswith(("http://", "https://")):
|
||||
raise ValueError("URL должен начинаться с http:// или https://")
|
||||
return v
|
||||
|
||||
|
||||
class UserIn(BaseModel):
|
||||
username: str
|
||||
password: str = "" # empty on edit = keep existing
|
||||
role: str = "admin"
|
||||
|
||||
@field_validator("username")
|
||||
@classmethod
|
||||
def _username_required(cls, v: str) -> str:
|
||||
v = v.strip()
|
||||
if not v:
|
||||
raise ValueError("Логин обязателен")
|
||||
return v
|
||||
|
||||
@field_validator("role")
|
||||
@classmethod
|
||||
def _role_valid(cls, v: str) -> str:
|
||||
return v if v in ("admin", "viewer") else "viewer"
|
||||
Reference in New Issue
Block a user