Jinja2 mit HTMX, Tailwind über die Standalone-Binärdatei im Docker-Build - kein Node.js, kein npm. Mobile-First, Tippflächen mindestens 44 px hoch. Seiten: Galerie mit Filterleiste, Detailseite, Erfassungsformular, Stapel-Import mit Entwurfsliste, Anmeldung. HTMX tauscht beim Filtern nur die Liste aus statt die ganze Seite neu zu laden. Aus den Entscheidungen im Plan folgt konkret: - Der Stapel-Import steht ZUERST im Formular: wer zwanzig Teile hat, will nicht zwanzigmal ein Formular ausfüllen. Bewusst ohne capture-Attribut, damit sich wahlweise fotografieren oder aus der Mediathek wählen lässt - mit capture ginge nur je ein Foto pro Vorgang. - Grösse, Kategorie, Geschlecht, Saison und Zustand werden vom zuletzt gespeicherten Teil übernommen. Beim Abarbeiten einer Kiste sind das fünf Felder weniger pro Stück. - Nach dem Nachtragen eines Entwurfs geht es direkt zum nächsten, nicht auf die Detailseite - das ist der eigentliche Arbeitsablauf. - Titel bleibt leer erlaubt; angezeigt wird dann Kategorie und Grösse. - reserved_by erscheint öffentlich nur als "reserviert". Den Namen sieht ausschliesslich der angemeldete Betreiber. - Nach dem Reservieren wird der Freigabe-Link angezeigt, als vollständige Adresse zum Aufbewahren. - Post/Redirect/Get überall, damit ein Neuladen nicht erneut abschickt. - CSRF-Token in jedem Formular, zusätzlich zu SameSite=Lax. HTMX liegt lokal im Repository, nicht vom CDN: die CSP erlaubt nur script-src 'self', und ein CDN-Aufruf gäbe die IP jedes Besuchers weiter - dieselbe Überlegung wie bei Swagger und bei den Schriften der Kantone-App. Ein Test prüft für alle Seiten, dass nichts von aussen geladen wird. Nebenbei TemplateResponse auf die aktuelle Signatur umgestellt (64 Deprecation-Warnungen weniger). 77 Tests, alle grün. Zusätzlich der ganze Weg gegen den Container: anmelden, drei Fotos als Stapel, Entwurf nachtragen, Werte werden für das nächste Teil übernommen, als Gast reservieren, Name bleibt verborgen, Selbst-Freigabe über den Link. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GR4bNaj9GtRu57J4Niii8o
62 lines
2.5 KiB
Docker
62 lines
2.5 KiB
Docker
# --------------------------------------------------------------- CSS ---
|
|
# Tailwind über die Standalone-Binärdatei - kein Node.js, kein npm. Das
|
|
# Ergebnis ist eine einzige style.css; die Binärdatei bleibt in dieser
|
|
# Stufe und landet nicht im fertigen Image.
|
|
FROM debian:bookworm-slim AS css
|
|
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
WORKDIR /build
|
|
ARG TAILWIND_VERSION=v3.4.17
|
|
RUN curl -sSLo /usr/local/bin/tailwindcss \
|
|
"https://github.com/tailwindlabs/tailwindcss/releases/download/${TAILWIND_VERSION}/tailwindcss-linux-x64" \
|
|
&& chmod +x /usr/local/bin/tailwindcss
|
|
COPY tailwind.config.js tailwind-input.css ./
|
|
COPY app/templates ./app/templates
|
|
RUN tailwindcss -c tailwind.config.js -i tailwind-input.css -o /build/style.css --minify
|
|
|
|
# ----------------------------------------------------------- Anwendung ---
|
|
FROM python:3.11-slim
|
|
|
|
# libheif wird von pillow-heif mitgeliefert; hier nur die Laufzeit-Bibliotheken,
|
|
# die Pillow für JPEG/PNG/WebP braucht.
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /app
|
|
|
|
COPY requirements.txt ./
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
|
|
COPY alembic.ini ./
|
|
COPY alembic/ ./alembic/
|
|
COPY app/ ./app/
|
|
COPY --from=css /build/style.css ./app/static/style.css
|
|
COPY docker-entrypoint.sh /usr/local/bin/kleiderboerse-entrypoint.sh
|
|
RUN chmod +x /usr/local/bin/kleiderboerse-entrypoint.sh
|
|
|
|
# Datenbank und Bilder liegen in Volumes, ausserhalb des Codes.
|
|
ENV DATABASE_PATH=/data/kleiderboerse.sqlite \
|
|
UPLOAD_PATH=/uploads \
|
|
PYTHONUNBUFFERED=1
|
|
|
|
# Nicht als root laufen. Die beiden Verzeichnisse gehören dem Dienstbenutzer,
|
|
# der Code dagegen root - so kann der Webdienst seinen eigenen Code nicht
|
|
# überschreiben.
|
|
RUN useradd --system --create-home --shell /usr/sbin/nologin kleider \
|
|
&& mkdir -p /data /uploads \
|
|
&& chown kleider:kleider /data /uploads
|
|
USER kleider
|
|
|
|
EXPOSE 8000
|
|
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
|
CMD curl -fsS http://localhost:8000/health || exit 1
|
|
|
|
ENTRYPOINT ["kleiderboerse-entrypoint.sh"]
|
|
# --proxy-headers: hinter einem Reverse-Proxy stünde sonst dessen IP in
|
|
# jedem Rate-Limit, und ein einzelner Besucher sperrte alle anderen aus.
|
|
# Ausserdem erkennt die App darüber, ob der Browser per HTTPS verbunden ist.
|
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", \
|
|
"--proxy-headers", "--forwarded-allow-ips", "*"]
|