f1-circuits: become a nox mesh module #4

Merged
mesh-admin merged 1 commits from nox-mesh into master 2026-09-30 15:32:08 +00:00
4 changed files with 125 additions and 10 deletions
+82
View File
@@ -0,0 +1,82 @@
{
"module": "f1-circuits",
"slug": "f1",
"version": "1",
"capabilities": [
"container-runtime"
],
"requires": [
"route"
],
"contributes": {
"route": {
"label": "f1-circuits",
"endpoint": "web"
}
},
"binds": {
"route": "${dir:state}/route.json"
},
"listens": [
{
"name": "web",
"port": 8000,
"protocol": "tcp",
"from": "mesh",
"why": "the track-layout CDN, the race posters and the Kometa metadata over http; its public name is a route grant and the proxy reaches it here. Kometa fetches the metadata and every poster by that public name"
}
],
"resources": [
{
"id": "state",
"type": "directory",
"mode": "0700",
"place": "."
},
{
"id": "cache",
"type": "directory",
"mode": "0700"
},
{
"id": "site-env",
"type": "file",
"path": "${dir:state}/site.env",
"mode": "0644",
"content": "F1_CIRCUITS_BASE_URL=https://${bound:route:name}\n"
},
{
"id": "server",
"type": "container",
"name": "f1-circuits",
"artifact": "cdn",
"ports": [
"8000"
],
"env-file": [
"${dir:state}/site.env"
],
"volumes": [
"${dir:cache}:/app/cache"
],
"restart-on": [
"site-env"
]
}
],
"build": {
"on": [
{
"arg": "PYTHON_BASE",
"image": "python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df"
}
],
"artifacts": [
{
"name": "cdn",
"kind": "image",
"from": "service/Dockerfile"
}
]
}
}
+8 -2
View File
@@ -16,6 +16,7 @@ import io
import json import json
import logging import logging
import math import math
import os
import urllib.request import urllib.request
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
@@ -61,8 +62,13 @@ TILE_PROVIDERS = {
}, },
} }
# The address this service is reached at, which the Kometa metadata links every poster to. The
# mesh writes it from the name the route is served under (F1_CIRCUITS_BASE_URL); the fallback is
# the address HAL served it at, so a deployment that sets nothing behaves as before.
PUBLIC_BASE_URL = os.environ.get("F1_CIRCUITS_BASE_URL", "https://f1-circuits.zurag.be").rstrip("/")
# Polite UA — both providers expect one and Esri throttles aggressively without it. # Polite UA — both providers expect one and Esri throttles aggressively without it.
TILE_USER_AGENT = "f1-circuits-posters/1.0 (+https://f1-circuits.zurag.be)" TILE_USER_AGENT = f"f1-circuits-posters/1.0 (+{PUBLIC_BASE_URL})"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -641,7 +647,7 @@ class PostersService:
# ------------------------------------------------------------------ kometa metadata # ------------------------------------------------------------------ kometa metadata
BASE_URL = "https://f1-circuits.zurag.be" BASE_URL = PUBLIC_BASE_URL
def render_kometa_metadata(self, first_season: int = 1950, last_offset: int = 1) -> str: def render_kometa_metadata(self, first_season: int = 1950, last_offset: int = 1) -> str:
"""Emit the Kometa metadata YAML for the Formula 1 library, covering """Emit the Kometa metadata YAML for the Formula 1 library, covering
+8 -8
View File
@@ -1,4 +1,8 @@
FROM python:3.12-alpine # The base arrives pinned from module.json's build.on (python 3.12.13 on alpine 3.24); the mesh
# builds this image from this repository and a commit (novox/hq ADR 0069). The build context is
# the repository root, so every COPY names its path from there.
ARG PYTHON_BASE
FROM ${PYTHON_BASE}
WORKDIR /app WORKDIR /app
@@ -14,13 +18,9 @@ RUN apk add --no-cache \
# All pip deps ship musllinux wheels — no build-deps needed. # All pip deps ship musllinux wheels — no build-deps needed.
# numpy is pulled in by models/geo_json/geometry.py for vector ops at import time. # numpy is pulled in by models/geo_json/geometry.py for vector ops at import time.
RUN pip install --no-cache-dir \ # The versions are the ones the running deployment had (service/requirements.txt).
fastapi \ COPY service/requirements.txt ./requirements.txt
uvicorn \ RUN pip install --no-cache-dir -r requirements.txt
python-slugify \
pillow \
cairosvg \
numpy
COPY cdn-api.py circuits_service.py ergast_service.py posters_service.py ./ COPY cdn-api.py circuits_service.py ergast_service.py posters_service.py ./
COPY models/ ./models/ COPY models/ ./models/
+27
View File
@@ -0,0 +1,27 @@
# The exact set the deployment ace ran on 2026-09-30 was built with (pip freeze inside it).
# Pinned so a rebuild from this commit installs what was running, not whatever is newest.
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.13.0
cairocffi==1.7.1
CairoSVG==2.9.0
cffi==2.0.0
click==8.4.1
cssselect2==0.9.0
defusedxml==0.7.1
fastapi==0.137.0
h11==0.16.0
idna==3.18
numpy==2.4.6
pillow==12.2.0
pycparser==3.0
pydantic==2.13.4
pydantic_core==2.46.4
python-slugify==8.0.4
starlette==1.3.1
text-unidecode==1.3
tinycss2==1.5.1
typing-inspection==0.4.2
typing_extensions==4.15.0
uvicorn==0.49.0
webencodings==0.5.1