build: dockerfile, compose-stack, entrypoint und update-skript

Multi-Stage-Build mit standalone-Output, non-root Nutzer node (UID 1000)
und Healthcheck auf /api/health. Der Entrypoint prueft zuerst, ob /data
beschreibbar ist, und sagt bei einem Rechteproblem genau, was zu tun
ist; danach laufen die Migrationen und erst dann startet die App.

Der Migrations-Runner wird beim Build mit esbuild zu einer einzelnen
CJS-Datei gebuendelt. Damit braucht das Runtime-Image weder drizzle-kit
noch tsx - nur das native better-sqlite3, das ohnehin dabei ist.

update.sh sichert data und .env, holt den neuen Stand, baut, startet und
wartet auf den Healthcheck. Wird der Container nicht gesund, rollt es
Code und Container auf den vorherigen Commit zurueck und nennt den Pfad
der Sicherung.

.gitattributes erzwingt LF: eine entrypoint.sh mit CRLF scheitert im
Container mit "no such file or directory" auf der Shebang-Zeile.

Verifiziert: Image gebaut (358 MB), Container healthy, Migration laeuft,
Registrierung des ersten Nutzers funktioniert, sharp arbeitet unter musl.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
unknown
2026-09-07 00:09:03 +02:00
co-authored by Claude Opus 5
parent b0152b8667
commit 61b1249afd
15 changed files with 976 additions and 36 deletions
+46
View File
@@ -0,0 +1,46 @@
# ---------------------------------------------------------------------------
# Bricktrack - ein Service, ein Datenverzeichnis.
#
# TLS terminiert der Reverse Proxy (z. B. Nginx Proxy Manager). Der Container
# spricht nur HTTP und muss nicht nach aussen erreichbar sein.
# ---------------------------------------------------------------------------
services:
app:
build:
context: .
dockerfile: Dockerfile
image: bricktrack:latest
container_name: bricktrack
restart: unless-stopped
env_file:
- .env
environment:
# Im Container liegen die Daten immer unter /data - der Pfad auf dem Host
# wird ueber den Bind-Mount unten bestimmt.
DATA_DIR: /data
PORT: 3000
ports:
# Nur an localhost binden: erreichbar wird die App ueber den Reverse
# Proxy. Laeuft der Proxy in einem anderen Docker-Netz, siehe README.
- "127.0.0.1:3000:3000"
volumes:
# Das eine Verzeichnis, das gesichert werden muss.
- ./data:/data
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000/api/health"]
interval: 30s
timeout: 5s
start_period: 20s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"