✨ 8 major features: trafilatura, digest, ntfy actions, templates, FTS5 search, backup/restore, proxy, RSS reader
build-and-push / docker (push) Has been cancelled
build-and-push / docker (push) Has been cancelled
- Full article extraction via trafilatura (fetch_full_article)
- Digest mode with configurable period (digest_enabled, digest_period_hours)
- ntfy Actions buttons (Open article, Open feed)
- Notification templates with {title}, {body}, {link}, {source}, {image_url}
- FTS5 full-text search in notification history
- Database backup/restore (download/upload .db)
- HTTP/SOCKS proxy for RSS feed fetching (proxy_url setting)
- Built-in RSS reader tab with categories, unread counts, article detail view
- Auto-category 'Общее' for feeds without a category
- Article storage (Article table) for reader
- DigestEntry model for pending digest entries
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+37
-1
@@ -9,7 +9,7 @@ from sqlmodel import Session, SQLModel, create_engine, select
|
||||
|
||||
from . import config
|
||||
from .auth import hash_password
|
||||
from .models import Settings, User
|
||||
from .models import Category, Settings, User
|
||||
|
||||
engine = create_engine(
|
||||
config.DATABASE_URL,
|
||||
@@ -18,6 +18,33 @@ engine = create_engine(
|
||||
)
|
||||
|
||||
|
||||
def _setup_fts() -> None:
|
||||
"""Create FTS5 virtual table and triggers for full-text notification search."""
|
||||
with engine.begin() as conn:
|
||||
conn.execute(text(
|
||||
"CREATE VIRTUAL TABLE IF NOT EXISTS notification_fts "
|
||||
"USING fts5(title, body, content='notification', content_rowid='id')"
|
||||
))
|
||||
# Triggers to keep FTS index in sync
|
||||
for trigger_sql in [
|
||||
"""CREATE TRIGGER IF NOT EXISTS ntf_ai AFTER INSERT ON notification BEGIN
|
||||
INSERT INTO notification_fts(rowid, title, body)
|
||||
VALUES (new.id, new.title, new.feed_title);
|
||||
END""",
|
||||
"""CREATE TRIGGER IF NOT EXISTS ntf_ad AFTER DELETE ON notification BEGIN
|
||||
INSERT INTO notification_fts(notification_fts, rowid, title, body)
|
||||
VALUES ('delete', old.id, old.title, old.feed_title);
|
||||
END""",
|
||||
"""CREATE TRIGGER IF NOT EXISTS ntf_au AFTER UPDATE ON notification BEGIN
|
||||
INSERT INTO notification_fts(notification_fts, rowid, title, body)
|
||||
VALUES ('delete', old.id, old.title, old.feed_title);
|
||||
INSERT INTO notification_fts(rowid, title, body)
|
||||
VALUES (new.id, new.title, new.feed_title);
|
||||
END""",
|
||||
]:
|
||||
conn.execute(text(trigger_sql))
|
||||
|
||||
|
||||
def _migrate() -> None:
|
||||
"""Add any model columns missing from existing tables (SQLite ALTER ADD).
|
||||
|
||||
@@ -60,6 +87,7 @@ def init_db() -> None:
|
||||
"""Create tables, run migration, ensure settings + admin user exist."""
|
||||
SQLModel.metadata.create_all(engine)
|
||||
_migrate()
|
||||
_setup_fts()
|
||||
with Session(engine) as session:
|
||||
if session.get(Settings, 1) is None:
|
||||
session.add(
|
||||
@@ -83,6 +111,14 @@ def init_db() -> None:
|
||||
)
|
||||
session.commit()
|
||||
|
||||
# Bootstrap the "Общее" (General) category.
|
||||
general = session.exec(
|
||||
select(Category).where(Category.name == "Общее")
|
||||
).first()
|
||||
if general is None:
|
||||
session.add(Category(name="Общее", sort_order=0))
|
||||
session.commit()
|
||||
|
||||
|
||||
def get_settings(session: Session) -> Settings:
|
||||
settings = session.get(Settings, 1)
|
||||
|
||||
Reference in New Issue
Block a user