RSS/Atom -> ntfy bridge with web UI, OPML import/export and RU/EN localization

Web-managed fork of nurefexc/rss-bridge-ntfy: Flask UI + REST API, background
sync engine (SQLite dedup, quiet hours, filters, flood protection, images),
OPML import/export and switchable interface/notification language.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 19:34:53 +08:00
commit 3f9b108482
15 changed files with 2076 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
"""Entry point: wire up the store, the background engine and the web server.
A single process hosts both the Flask web UI/API and the RSS->ntfy engine
(running in its own daemon thread). Served by waitress so it works the same on
Linux containers and Windows.
"""
import logging
import os
import signal
import sys
from waitress import serve
import store as store_mod
from engine import Engine
from webapp import create_app
def setup_logging(engine: Engine):
logger = logging.getLogger("bridge")
logger.setLevel(logging.INFO)
fmt = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s", "%Y-%m-%d %H:%M:%S")
stream = logging.StreamHandler(sys.stdout)
stream.setFormatter(fmt)
logger.addHandler(stream)
file_handler = logging.FileHandler(store_mod.LOG_PATH, encoding="utf-8")
file_handler.setFormatter(fmt)
logger.addHandler(file_handler)
logger.addHandler(engine.ring) # in-memory buffer for the web UI
logger.propagate = False
def main():
host = os.environ.get("HOST", "0.0.0.0")
port = int(os.environ.get("PORT", "8080"))
store = store_mod.Store()
engine = Engine(store)
setup_logging(engine)
engine.start()
app = create_app(store, engine)
def shutdown(signum, frame): # noqa: ARG001
logging.getLogger("bridge").info("Shutting down (signal %s)", signum)
engine.stop()
sys.exit(0)
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
logging.getLogger("bridge").info("Web UI on http://%s:%s", host, port)
serve(app, host=host, port=port, threads=8)
if __name__ == "__main__":
main()