diff --git a/.env.example b/.env.example index 892824c..2b9fc11 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,8 @@ IMAGE_QUALITY=85 OPENROUTER_API_KEY= UMAMI_SCRIPT_URL= UMAMI_WEBSITE_ID= +GITHUB_REPO_URL= +CONTACT_EMAIL= RETENTION_DAYS=30 ROYALTY_IMAGE_PROVIDER=picsum ROYALTY_IMAGE_MCP_ENDPOINT= diff --git a/backend/config.py b/backend/config.py index 7e63434..3b71461 100644 --- a/backend/config.py +++ b/backend/config.py @@ -10,6 +10,8 @@ IMAGE_QUALITY = int(os.getenv("IMAGE_QUALITY", "85")) RETENTION_DAYS = int(os.getenv("RETENTION_DAYS", "30")) UMAMI_SCRIPT_URL = os.getenv("UMAMI_SCRIPT_URL", "") UMAMI_WEBSITE_ID = os.getenv("UMAMI_WEBSITE_ID", "") +GITHUB_REPO_URL = os.getenv("GITHUB_REPO_URL", "") +CONTACT_EMAIL = os.getenv("CONTACT_EMAIL", "") ROYALTY_IMAGE_MCP_ENDPOINT = os.getenv("ROYALTY_IMAGE_MCP_ENDPOINT", "") ROYALTY_IMAGE_API_KEY = os.getenv("ROYALTY_IMAGE_API_KEY", "") ROYALTY_IMAGE_PROVIDER = os.getenv("ROYALTY_IMAGE_PROVIDER", "picsum") diff --git a/backend/main.py b/backend/main.py index aee4b1c..06e3cbd 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,13 +1,15 @@ import logging import os +import random from apscheduler.schedulers.background import BackgroundScheduler from fastapi import Depends, FastAPI, Query, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware from fastapi.staticfiles import StaticFiles -from fastapi.responses import FileResponse +from fastapi.responses import FileResponse, HTMLResponse, JSONResponse from sqlalchemy.orm import Session +from starlette.exceptions import HTTPException as StarletteHTTPException from backend import config from backend.database import get_db, init_db @@ -33,6 +35,77 @@ logger = logging.getLogger(__name__) app = FastAPI(title="ClawFort News API", version="0.1.0") +_ERROR_MESSAGES = { + 404: [ + "Oh no! This page wandered off to train a tiny model.", + "Oh no! We looked everywhere, even in the latent space.", + "Oh no! The link took a creative detour.", + "Oh no! This route is currently off doing research.", + "Oh no! The page you asked for is not in this timeline.", + ], + 500: [ + "Oh no! The server hit a logic knot and needs a quick reset.", + "Oh no! Our robots dropped a semicolon somewhere important.", + "Oh no! A background process got stage fright.", + "Oh no! The AI took an unexpected coffee break.", + "Oh no! Something internal blinked at the wrong moment.", + ], +} + + +def _render_error_page(status_code: int) -> str: + message = random.choice(_ERROR_MESSAGES.get(status_code, _ERROR_MESSAGES[500])) + return f""" + + + + + {status_code} - ClawFort + + + + +
+
+

{status_code}

+

Oh no!

+

{message}

+ Back to ClawFort +
+
+ +""" + + +@app.exception_handler(StarletteHTTPException) +async def http_exception_handler(request: Request, exc: StarletteHTTPException): + if request.url.path.startswith("/api/"): + return JSONResponse(status_code=exc.status_code, content={"detail": exc.detail}) + + if exc.status_code == 404: + return HTMLResponse(_render_error_page(404), status_code=404) + + return JSONResponse(status_code=exc.status_code, content={"detail": exc.detail}) + + +@app.exception_handler(Exception) +async def unhandled_exception_handler(request: Request, exc: Exception): + logger.exception("Unhandled server error: %s", exc) + if request.url.path.startswith("/api/"): + return JSONResponse(status_code=500, content={"detail": "Internal Server Error"}) + return HTMLResponse(_render_error_page(500), status_code=500) + + app.add_middleware( CORSMiddleware, allow_origins=["*"], @@ -216,6 +289,8 @@ async def serve_config() -> dict: return { "umami_script_url": config.UMAMI_SCRIPT_URL, "umami_website_id": config.UMAMI_WEBSITE_ID, + "github_repo_url": config.GITHUB_REPO_URL, + "contact_email": config.CONTACT_EMAIL, "supported_languages": config.SUPPORTED_LANGUAGES, "default_language": "en", } diff --git a/backend/static/images/favicon-ai.svg b/backend/static/images/favicon-ai.svg new file mode 100644 index 0000000..1a9d2e3 --- /dev/null +++ b/backend/static/images/favicon-ai.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/data/clawfort.db b/data/clawfort.db index 576e688..99ba3a5 100644 Binary files a/data/clawfort.db and b/data/clawfort.db differ diff --git a/frontend/attribution.html b/frontend/attribution.html index 001d714..beba287 100644 --- a/frontend/attribution.html +++ b/frontend/attribution.html @@ -15,6 +15,7 @@ + Attribution and Ownership Disclaimer - ClawFort diff --git a/frontend/index.html b/frontend/index.html index ff3c0ae..a1b6400 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -20,10 +20,11 @@ - ClawFort — AI News + ClawFort AI News + - +