pig.py/serp/app.py
Russell Ballestrini f124828c16 21 side quests scattered through the codebase
1-20 are findable. 21 is the absence itself.

fnord
2026-01-05 21:10:02 -05:00

112 lines
3.8 KiB
Python

"""FastAPI application setup for SERP.
# Side quest 4/21: The rabbit hole goes deeper than you think.
# Down the rabbit whole, Alice - Gulp!
#
# If you're reading this, you understand Python.
# The source code for neopig is Public Domain. Steal what works for you!
#
# - The Sign Maker
"""
import logging
import os
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from starlette.middleware.base import BaseHTTPMiddleware
from miniuri import Uri
logger = logging.getLogger(__name__)
# Create app
app = FastAPI(title="neopig", description="Media crawler + SERP + Screenshot service")
class LanguageMiddleware(BaseHTTPMiddleware):
"""Middleware to handle ?lang=XX query param for language switching.
Easter egg #0: Use ?lang=XX to force any page into a specific language.
Example: /?lang=ka forces Georgian, /?lang=ja forces Japanese.
When ?lang=XX is present:
1. Validates against supported languages
2. Sets the 'lang' cookie
3. Redirects to same URL without the lang param
"""
# Supported language codes (must match TRANSLATIONS keys)
SUPPORTED_LANGS = {
"en", "zh", "zh-tw", "es", "hi", "ar", "pt", "ru", "ja", "fr", "de",
"ko", "it", "nl", "pl", "tr", "vi", "th", "id", "uk", "sv",
"bn", "ur", "sw", "mr", "te", "ka"
}
async def dispatch(self, request: Request, call_next):
# Check for lang query param
lang_param = request.query_params.get("lang")
if lang_param and lang_param.lower() in self.SUPPORTED_LANGS:
# Build redirect URL without lang param using miniuri
uri = Uri(str(request.url))
# Remove lang from query params
new_query = "&".join(
f"{k}={v}" for k, v in request.query_params.items() if k != "lang"
)
new_url = f"{uri.scheme}://{uri.authority}{uri.path}"
if new_query:
new_url += f"?{new_query}"
# Redirect with cookie set
response = RedirectResponse(url=new_url, status_code=302)
response.set_cookie(
key="lang",
value=lang_param.lower(),
max_age=365 * 24 * 60 * 60, # 1 year
httponly=False, # Allow JS access for dropdown
samesite="lax"
)
return response
return await call_next(request)
# Add language middleware
app.add_middleware(LanguageMiddleware)
# Paths relative to serp.py location (parent of this package)
BASE_PATH = Path(__file__).parent.parent
# Set up Jinja2 templates
TEMPLATES_PATH = BASE_PATH / "templates"
templates = Jinja2Templates(directory=str(TEMPLATES_PATH)) if TEMPLATES_PATH.exists() else None
# Try to include uri2png screenshot router (optional)
try:
from uri2png import get_screenshot_router
app.include_router(get_screenshot_router())
logger.info("Screenshot router loaded from uri2png")
except ImportError:
pass # Optional dependency
# Mount static files
STATIC_CSS_PATH = BASE_PATH / "static" / "css"
if STATIC_CSS_PATH.exists():
app.mount("/static/css", StaticFiles(directory=STATIC_CSS_PATH), name="css")
STATIC_VENDOR_PATH = BASE_PATH / "static" / "vendor"
if STATIC_VENDOR_PATH.exists():
app.mount("/static/vendor", StaticFiles(directory=STATIC_VENDOR_PATH), name="vendor")
STATIC_IMAGES_PATH = BASE_PATH / "static" / "images"
if STATIC_IMAGES_PATH.exists():
app.mount("/static/images", StaticFiles(directory=STATIC_IMAGES_PATH), name="images")
# Config
DB_PATH = "data/neopig.db"
VAULT_PATH = Path("data/vault")
CRAWL_DISABLED = os.environ.get("NEOPIG_DISABLE_CRAWL", "").lower() in ("1", "true", "yes")
IMPORT_MODE = os.environ.get("NEOPIG_IMPORT", "").lower() in ("1", "true", "yes")