131 lines
4.4 KiB
Python
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")
|