Added path to unsubscribe from RSS feeds and exception handler to propagate FeedErrors
This commit is contained in:
1 parent
6ffce24bc7
commit
6f1f024e90
1 file changed
+69
-2
+69
-2
@@ -4,6 +4,7 @@ from contextlib import asynccontextmanager
|
|||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request
|
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request
|
||||||
|
from fastapi.exceptions import RequestValidationError
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from requests.exceptions import RequestException
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
@@ -63,6 +64,27 @@ api = FastAPI(
|
|||||||
async def feed_error_handler(request: Request, exc: FeedError):
|
async def feed_error_handler(request: Request, exc: FeedError):
|
||||||
return JSONResponse(status_code=exc.status_code, content={"detail":str(exc)})
|
return JSONResponse(status_code=exc.status_code, content={"detail":str(exc)})
|
||||||
|
|
||||||
|
@api.exception_handler(RequestValidationError)
|
||||||
|
async def validation_error_handler(request: Request, exc: RequestValidationError):
|
||||||
|
errors = exc.errors()
|
||||||
|
|
||||||
|
# 1) If one of our own FeedErrors caused it, use its message as is
|
||||||
|
for err in errors:
|
||||||
|
original = (err.get("ctx") or {}).get("error")
|
||||||
|
if isinstance(original, FeedError):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=original.status_code,
|
||||||
|
content={"detail": str(original)},
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2) Anything else (missing field, bad enum value, bad header): flatten to one string
|
||||||
|
messages = []
|
||||||
|
for err in errors:
|
||||||
|
field = ".".join(str(p) for p in err["loc"] if p != "body")
|
||||||
|
msg = err["msg"].removeprefix("Value error, ")
|
||||||
|
messages.append(f"{field}: {msg}" if field else msg)
|
||||||
|
return JSONResponse(status_code=422, content={"detail": "; ".join(messages)})
|
||||||
|
|
||||||
@api.middleware("http")
|
@api.middleware("http")
|
||||||
async def prometheus_middleware(request: Request, call_next):
|
async def prometheus_middleware(request: Request, call_next):
|
||||||
logger.debug(f"[FastAPI] Incoming request: {request.method} {request.url.path}")
|
logger.debug(f"[FastAPI] Incoming request: {request.method} {request.url.path}")
|
||||||
@@ -319,12 +341,57 @@ def add_feed(
|
|||||||
db.commit()
|
db.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
logger.exception(f"[FastAPI] Failed to subscribe to user_id={user_id} to {url}")
|
logger.exception(f"[FastAPI] Failed to subscribe user_id={user_id} to {url}")
|
||||||
raise HTTPException(status_code=500) from e
|
raise HTTPException(status_code=500) from e
|
||||||
|
|
||||||
logger.info(f"[FastAPI] user_id={user_id} subscribed to feed_id={feed_id}, url={url}")
|
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}
|
return {"status":"ok","detail":"Subscribed to feed","feed_url":url}
|
||||||
|
|
||||||
|
@user_router.post("/delete/feed")
|
||||||
|
def delete_feed(
|
||||||
|
user_id: UserId,
|
||||||
|
feed_entry: FeedEntry,
|
||||||
|
db = Depends(get_db),
|
||||||
|
):
|
||||||
|
url = feed_entry.rss_feed_url
|
||||||
|
logger.info(f"[FastAPI] Unsubscribe request: RSS URL={url}, user_id={user_id}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with db.cursor() as cursor:
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT id FROM rss_feeds WHERE rss_feed = %s",
|
||||||
|
(url,),
|
||||||
|
)
|
||||||
|
known = cursor.fetchone()
|
||||||
|
|
||||||
|
if known is None:
|
||||||
|
db.rollback()
|
||||||
|
return {"status":"ok","detail":"Unsubscribed to feed","feed_url":url}
|
||||||
|
|
||||||
|
feed_id = known[0]
|
||||||
|
cursor.execute(
|
||||||
|
"UPDATE rss_feed_subscriptions SET active = 0 "
|
||||||
|
"WHERE user_id = %s AND rss_feed_id = %s",
|
||||||
|
(user_id, feed_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT 1 from rss_feed_subscriptions "
|
||||||
|
"WHERE rss_feed_id = %s AND active = 1 LIMIT 1",
|
||||||
|
(feed_id,),
|
||||||
|
)
|
||||||
|
if cursor.fetchone() is None:
|
||||||
|
cursor.execute(
|
||||||
|
"UPDATE rss_feeds SET active = 0 WHERE id = %s",
|
||||||
|
(feed_id,),
|
||||||
|
)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
except Exception as e:
|
||||||
|
db.rollback()
|
||||||
|
logger.exception(f"[FastAPI] Failed to unsubscribe user_id={user_id} to {url}")
|
||||||
|
raise HTTPException(status_code=500) from e
|
||||||
|
return {"status":"ok","detail":"Unsubscribed to feed","feed_url":url}
|
||||||
api.include_router(user_router)
|
api.include_router(user_router)
|
||||||
api.include_router(scheduler_router)
|
api.include_router(scheduler_router)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user