Removed circular import

This commit is contained in:
florian committed 2026-10-31 15:47:38 +01:00
1 parent a6befcf99b
commit ee21a77a69
3 files changed
+87 -34

No files matched your search

+71 -19
View File
@@ -4,22 +4,26 @@ from contextlib import asynccontextmanager
import uvicorn
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
from auth_config import Caller, auth
from db_module import close_connection_pool, create_connection_pool, get_db, start_healthcheck_thread
from feed_errors import FeedError
from feed_handler import (
build_gitea_runner,
ensure_feed_exists,
grab_gitea_release_information,
grab_royal_road_chapter_information,
)
from metrics_server import REQUEST_COUNTER, metrics_api
from models import FeedEntry, fetch_latest
from secret_manager import cleanup_secret_files, get_secret
from send_notifications import NotificationClient, NotificationError, NotificationUnavailableError
from service_auth import UserId
from simple_logger_handler import LOG_LEVEL, setup_logger
logger = setup_logger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("[FastAPI] Starting application...")
@@ -53,6 +57,10 @@ api = FastAPI(
dependencies=[Depends(auth.authenticate)],
)
@api.exception_handler(FeedError)
async def feed_error_handler(request: Request, exc: FeedError):
return JSONResponse(status_code=exc.status_code, content={"detail":str(exc)})
@api.middleware("http")
async def prometheus_middleware(request: Request, call_next):
logger.debug(f"[FastAPI] Incoming request: {request.method} {request.url.path}")
@@ -103,13 +111,13 @@ def get_release_information(request: Request, db = Depends(get_db)):
for feed_id, rss_feed, last_entry in feeds:
results["checked"] += 1
try:
release_title, link = grab_gitea_release_information(rss_feed)
logger.debug(f"[FastAPI] ID:{feed_id},RSS Feed {rss_feed}: db={last_entry}, online={link}")
gitea_release_information = grab_gitea_release_information(rss_feed)
logger.debug(f"[FastAPI] ID:{feed_id},RSS Feed {rss_feed}: db={last_entry}, online={gitea_release_information.link}")
if link == last_entry:
if gitea_release_information.link == last_entry:
continue
logger.info(f"[FastAPI] Release change for ID {feed_id}: {release_title}")
logger.info(f"[FastAPI] Release change for ID {feed_id}: {gitea_release_information.release_title}")
with db.cursor() as cursor:
cursor.execute(
@@ -124,11 +132,11 @@ def get_release_information(request: Request, db = Depends(get_db)):
failed = 0
for user_id,feed_name in subscribers:
try:
repo = link.split("/releases")[0].removeprefix("https://")
repo = gitea_release_information.link.split("/releases")[0].removeprefix("https://")
request.app.state.notifier.send(
title=feed_name or repo,
body=release_title,
link=link,
body=gitea_release_information.release_title,
link=gitea_release_information.link,
category="utility",
user_id=user_id,
)
@@ -149,14 +157,14 @@ def get_release_information(request: Request, db = Depends(get_db)):
with db.cursor() as cursor:
cursor.execute(
"UPDATE rss_feeds SET last_entry = %s WHERE id = %s",
(link, feed_id),
(gitea_release_information.link, feed_id),
)
db.commit()
logger.debug(f"[FastAPI] Notification sent and database updated for Gitea feed ID {feed_id}")
results["new"] += 1
if link.startswith("https://gitea.com/gitea/runner/releases/tag/v"):
gitea_build_success = build_gitea_runner(release_title, request.app.state.gitea_token)
if gitea_release_information.link.startswith("https://gitea.com/gitea/runner/releases/tag/v"):
gitea_build_success = build_gitea_runner(gitea_release_information.release_title, request.app.state.gitea_token)
logger.info(f"[FastAPI] Gitea build ran successfully:{gitea_build_success}")
except Exception as e:
@@ -205,12 +213,12 @@ def get_chapters(request: Request, db = Depends(get_db)):
results["checked"] += 1
try:
story = grab_royal_road_chapter_information(rss_feed)
logger.debug(f"[FastAPI] ID:{feed_id},RSS Feed {rss_feed}: db={last_entry}, online={story.chapter_id}")
logger.debug(f"[FastAPI] ID:{feed_id},RSS Feed {rss_feed}: db={last_entry}, online={story.entry_id}")
if story.chapter_id == last_entry:
if story.entry_id == last_entry:
continue
logger.info(f"[FastAPI] New chapter detected for story ID {feed_id}: {story.story_title}")
logger.info(f"[FastAPI] New chapter detected for story ID {feed_id}: {story.feed_title}")
with db.cursor() as cursor:
cursor.execute (
"SELECT user_id, feed_name "
@@ -225,8 +233,8 @@ def get_chapters(request: Request, db = Depends(get_db)):
for user_id, feed_name in subscribers:
try:
request.app.state.notifier.send(
title=feed_name or story.story_title,
body=story.chapter_title,
title=feed_name or story.feed_title,
body=story.body_title,
link=story.link,
category="royal-road",
user_id=user_id,
@@ -235,11 +243,11 @@ def get_chapters(request: Request, db = Depends(get_db)):
except NotificationUnavailableError:
failed = len(subscribers) - sent
logger.error (f"[FastAPI] Notifications backend unavailable, skipping "
f"{failed} remaining user(s) for story={story.story_title}")
f"{failed} remaining user(s) for story={story.feed_title}")
break
except NotificationError as e:
failed += 1
logger.error(f"[FastAPI] Failed to send notification to user_id={user_id} for story={story.story_title} "
logger.error(f"[FastAPI] Failed to send notification to user_id={user_id} for story={story.feed_title} "
f"status={getattr(e, 'status_code',None)}")
if subscribers and sent == 0:
results["errors"] += 1
@@ -248,7 +256,7 @@ def get_chapters(request: Request, db = Depends(get_db)):
with db.cursor() as cursor:
cursor.execute(
"UPDATE rss_feeds SET last_entry = %s WHERE id = %s",
(story.chapter_id, feed_id),
(story.entry_id, feed_id),
)
db.commit()
logger.debug(f"[FastAPI] Notification sent and database updated for story ID {feed_id}")
@@ -269,6 +277,50 @@ def get_chapters(request: Request, db = Depends(get_db)):
logger.info(f"[FastAPI] Royalroad check complete: {results}")
return results
@user_router.post("/add/feed")
def add_feed(
user_id: UserId,
feed_entry: FeedEntry,
db = Depends(get_db)
):
url = feed_entry.rss_feed_url
logger.info(f"[FastAPI] Subscribe request: RSS URL={url}, user_id={user_id}")
with db.cursor() as cursor:
cursor.execute("SELECT id FROM rss_feeds WHERE rss_feed = %s", (feed_entry.rss_feed_url,),)
known = cursor.fetchone()
last_entry = None
if known is None:
ensure_feed_exists(url)
logger.info(f"[FastApi] Adding new RSS feed, type={feed_entry.type}, rss_feed_url={url}")
try:
last_entry = fetch_latest(feed_entry.type, url)
except ValueError:
logger.warning(f"[FastAPI] Could not read latest entry for {url}, storing none")
try:
with db.cursor() as cursor:
cursor.execute(
"INSERT into rss_feeds (type,rss_feed,last_entry) VALUES (%s,%s,%s) "
"ON DUPLICATE KEY UPDATE id = LAST_INSERT_ID(id), active = 1",
(feed_entry.type, url, last_entry.entry_id),
)
feed_id = cursor.lastrowid
cursor.execute (
"INSERT INTO rss_feed_subscriptions (user_id, rss_feed_id, feed_name) "
"VALUES (%s, %s, %s) "
"ON DUPLICATE KEY UPDATE active = 1, feed_name = VALUES(feed_name)",
(user_id, feed_id, feed_entry.custom_name),
)
db.commit()
except Exception as e:
db.rollback()
logger.exception(f"[FastAPI] Failed to subscribe to user_id={user_id} to {url}")
raise HTTPException(status_code=500) from e
logger.info(f"[FastAPI] user_id={user_id} subscribed to feed_id={feed_id}, url={url}")
return {"status":"ok","detail":"Subscribed to feed","feed_id":{feed_id}}
api.include_router(user_router)
api.include_router(scheduler_router)