pig.py/serp/app.py
Russell Ballestrini 027f5afc57 Add public domain license and headers to all source files
- Add LICENSE file (public domain, permacomputer values)
- Add license headers to all 27 Python source files
- Restore full about_ch9_p2 with "terminated 🟣" content
- Translate about_ch9_p2 to all 26 languages

The permacomputer is community-owned infrastructure optimized around:
TRUTH, FREEDOM, HARMONY, LOVE
2026-01-08 15:38:28 -05:00

138 lines
4.7 KiB
Python

# PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
#
# This is free public domain 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, and 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
#
# Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
# software, either in source code form or as a compiled binary, for any purpose,
# commercial or non-commercial, and by any means.
#
# NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
#
# Copyright 2025 TimeHexOn & foxhop & russell@unturf
# https://git.unturf.com/engineering/unturf/pig.py
"""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")