pig.py/serp/app.py

131 lines
4.4 KiB
Python

# This is free software for the public good of a permacomputer hosted at
# permacomputer.com, an always-on computer by the people, for the people.
# One which is durable, easy to repair, & distributed like tap water
# for machine learning intelligence.
#
# The permacomputer is community-owned infrastructure optimized around
# four values:
#
# TRUTH First principles, math & science, open source code freely distributed
# FREEDOM Voluntary partnerships, freedom from tyranny & corporate control
# HARMONY Minimal waste, self-renewing systems with diverse thriving connections
# LOVE Be yourself without hurting others, cooperation through natural law
#
# This software contributes to that vision by archiving the web, preserving digital knowledge before it disappears.
# Code is seeds to sprout on any abandoned technology.
"""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, Cookie, Header
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from .i18n import TRANSLATIONS
logger = logging.getLogger(__name__)
# Create app
app = FastAPI(title="neopig", description="Media crawler + SERP + Screenshot service")
# Supported language codes
SUPPORTED_LANGS = set(TRANSLATIONS.keys())
async def get_language(
request: Request,
lang: str = Cookie(None),
accept_language: str = Header(None)
) -> str:
"""Dependency: resolve language from ?lang=XX > cookie > Accept-Language.
Easter egg #0: Use ?lang=XX to force any page into a specific language.
Cookie is set via middleware (LanguageCookieMiddleware).
"""
# Query param takes priority
lang_query = request.query_params.get("lang")
if lang_query and lang_query.lower() in SUPPORTED_LANGS:
return lang_query.lower()
# Cookie next
if lang and lang in SUPPORTED_LANGS:
return lang
# Accept-Language header
if accept_language:
for part in accept_language.split(','):
code = part.split(';')[0].strip().split('-')[0].lower()
if code in SUPPORTED_LANGS:
return code
return "en"
from starlette.middleware.base import BaseHTTPMiddleware
class LanguageCookieMiddleware(BaseHTTPMiddleware):
"""Middleware to set lang cookie when ?lang=XX is used."""
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
# Set cookie if ?lang= query param is present and valid
lang_query = request.query_params.get("lang")
if lang_query and lang_query.lower() in SUPPORTED_LANGS:
response.set_cookie(
key="lang",
value=lang_query.lower(),
max_age=365 * 24 * 60 * 60,
httponly=False,
samesite="lax"
)
return response
app.add_middleware(LanguageCookieMiddleware)
# 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")