Added authentication header and multi user support to the royalroad check
This commit is contained in:
1 parent
5a2e1096de
commit
146c229e45
7 files changed
+113
-37
No files matched your search
+58
-21
@@ -3,14 +3,16 @@ from contextlib import asynccontextmanager
|
||||
import os
|
||||
|
||||
import uvicorn
|
||||
from fastapi import Depends, FastAPI, HTTPException, Request
|
||||
from fastapi import Depends, FastAPI, HTTPException, Request, APIRouter
|
||||
|
||||
from db_module import close_connection_pool, create_connection_pool, get_db, start_healthcheck_thread
|
||||
from feed_handler import grab_gitea_release_information, grab_royal_road_chapter_information, build_gitea_runner
|
||||
from feed_handler import grab_gitea_release_information, grab_royal_road_chapter_information, build_gitea_runner, RoyalRoadStory
|
||||
from metrics_server import REQUEST_COUNTER, metrics_api
|
||||
from secret_manager import cleanup_secret_files, get_secret
|
||||
from send_notifications import NotificationClient
|
||||
from send_notifications import NotificationClient, NotificationUnavailableError, NotificationError
|
||||
from simple_logger_handler import LOG_LEVEL, setup_logger
|
||||
from auth_config import Caller, auth
|
||||
from service_auth import UserId
|
||||
|
||||
logger = setup_logger(__name__)
|
||||
|
||||
@@ -19,7 +21,7 @@ logger = setup_logger(__name__)
|
||||
async def lifespan(app: FastAPI):
|
||||
logger.info("[FastAPI] Starting application...")
|
||||
app.state.notifier = NotificationClient()
|
||||
app.state.gitea_token = get_secret(os.getenv("GITEA_TOKEN",""),"/etc/secrets/gitea_token")
|
||||
app.state.gitea_token = get_secret("GITEA_TOKEN","/etc/secrets/gitea_token")
|
||||
|
||||
logger.info("[FastAPI] Creating MySQL connection pool...")
|
||||
create_connection_pool("royalRoadPool")
|
||||
@@ -44,7 +46,8 @@ api = FastAPI(
|
||||
title="Docker Repository Query",
|
||||
description="Queries Dockerhub and GHCR for new docker images",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan
|
||||
lifespan=lifespan,
|
||||
dependencies=[Depends(auth.authenticate)],
|
||||
)
|
||||
|
||||
@api.middleware("http")
|
||||
@@ -63,7 +66,11 @@ async def prometheus_middleware(request: Request, call_next):
|
||||
logger.debug(f"[FastAPI] Counter incremented for {request.method} {request.url.path} [{status}]")
|
||||
return response
|
||||
|
||||
@api.get("/gitea")
|
||||
|
||||
user_router = APIRouter(dependencies=[Depends(auth.require(Caller.GATEWAY))])
|
||||
scheduler_router = APIRouter(dependencies=[Depends(auth.require(Caller.SCHEDULER))])
|
||||
|
||||
@scheduler_router.get("/gitea")
|
||||
def get_release_information(request: Request, db = Depends(get_db)):
|
||||
"""
|
||||
Checks for new Gitea releases and updates the database accordingly.
|
||||
@@ -133,7 +140,7 @@ def get_release_information(request: Request, db = Depends(get_db)):
|
||||
logger.info(f"[FastAPI] Gitea check complete: {results}")
|
||||
return results
|
||||
|
||||
@api.get("/royalroad")
|
||||
@scheduler_router.get("/royalroad")
|
||||
def get_chapters(request: Request, db = Depends(get_db)):
|
||||
"""
|
||||
Checks for new Royalroad chapters and updates the database accordingly.
|
||||
@@ -160,26 +167,56 @@ def get_chapters(request: Request, db = Depends(get_db)):
|
||||
feeds = cursor.fetchall()
|
||||
logger.debug(f"[FastAPI] Found {len(feeds)} active stories to check.")
|
||||
|
||||
cache: dict[str, str | None] = {}
|
||||
for feed_id, rss_feed, last_entry in feeds:
|
||||
results["checked"] += 1
|
||||
try:
|
||||
chapter_title, link, story_title, online_latest_entry = grab_royal_road_chapter_information(rss_feed)
|
||||
logger.debug(f"[FastAPI] ID:{feed_id},RSS Feed {rss_feed}: db={last_entry}, online={online_latest_entry}")
|
||||
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}")
|
||||
|
||||
if online_latest_entry == last_entry:
|
||||
if story.chapter_id == last_entry:
|
||||
continue
|
||||
|
||||
logger.info(f"[FastAPI] New chapter detected for story ID {feed_id}: {story_title}")
|
||||
request.app.state.notifier.send(
|
||||
title=story_title,
|
||||
body=chapter_title,
|
||||
link=link,
|
||||
category="royal-road"
|
||||
)
|
||||
cursor.execute(
|
||||
"UPDATE rss_feeds SET last_entry = %s WHERE id = %s",
|
||||
(online_latest_entry, feed_id),
|
||||
)
|
||||
logger.info(f"[FastAPI] New chapter detected for story ID {feed_id}: {story.story_title}")
|
||||
with db.cursor() as cursor:
|
||||
cursor.execute (
|
||||
"SELECT user_id, feed_name "
|
||||
"FROM rss_feed_subscriptions "
|
||||
"WHERE rss_feed_id = %s AND active = 1",
|
||||
(feed_id,),
|
||||
)
|
||||
subscribers = cursor.fetchall()
|
||||
|
||||
sent = 0
|
||||
failed = 0
|
||||
for user_id, feed_name in subscribers:
|
||||
try:
|
||||
request.app.state.notifier.send(
|
||||
title=feed_name or story.story_title,
|
||||
body=story.chapter_title,
|
||||
link=story.link,
|
||||
category="royal-road",
|
||||
user_id=user_id,
|
||||
)
|
||||
send += 1
|
||||
except NotificationUnavailableError as e:
|
||||
failed = len(subscribers) - sent
|
||||
logger.error (f"[FastAPI] Notifications backend unavailable, skipping"
|
||||
f"{failed} remaining user(s) for story={story.story_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}"
|
||||
f"status={getattr(e, 'status_code',None)}")
|
||||
if subscribers and sent == 0:
|
||||
results["errors"]+=failed
|
||||
logger.error(f"[FastAPI] All notifications failed for Feed ID:{feed_id}, will retry later")
|
||||
continue
|
||||
with db.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"UPDATE rss_feeds SET last_entry = %s WHERE id = %s",
|
||||
(story.chapter_id, feed_id),
|
||||
)
|
||||
db.commit()
|
||||
logger.debug(f"[FastAPI] Notification sent and database updated for story ID {feed_id}")
|
||||
results["new"] += 1
|
||||
|
||||
Reference in new issue
Block a user