Add dark mode palettes + Woodpecker CI pipeline

- Add 2 dark palettes (Nuit, Ocean) to DisplaySettings with full SVG
  theme tokens — all hardcoded SVG colors (grids, legends, text fills,
  pills, dot strokes, drag handles) replaced with reactive bindings
- Update scoped CSS to use var(--color-*) and var(--svg-*) throughout
- Add Woodpecker CI pipeline (.woodpecker.yml): build → docker push → deploy
- Add multi-stage Dockerfiles for backend (Python) and frontend (Nuxt)
- Add production docker-compose with Traefik labels + dev override
- Remove old single-stage Dockerfiles and root docker-compose.yml
- Update Makefile with docker-dev target
- Exclude data files (pdf, xls, ipynb) from git

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-02-23 21:36:31 +01:00
parent 5dc42af33e
commit 4ba5e78e58
16 changed files with 510 additions and 176 deletions

38
docker/backend.Dockerfile Normal file
View File

@@ -0,0 +1,38 @@
# syntax = docker/dockerfile:1
FROM python:3.11-slim AS base
WORKDIR /app
# Build (install dependencies)
FROM base AS build
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY backend/ .
# Production
FROM base AS production
ENV PYTHONUNBUFFERED=1
COPY --from=build /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
COPY --from=build /usr/local/bin/uvicorn /usr/local/bin/uvicorn
COPY --from=build /usr/local/bin/alembic /usr/local/bin/alembic
COPY --from=build /app /app
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/docs')" || exit 1
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
# Development
FROM base AS development
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
WORKDIR /app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

View File

@@ -0,0 +1,26 @@
services:
backend:
build:
target: development
environment:
DATABASE_URL: sqlite+aiosqlite:///./sejeteralo.db
SECRET_KEY: dev-secret-key
DEBUG: "true"
CORS_ORIGINS: '["http://localhost:3000"]'
ports: !override
- "8000:8000"
volumes:
- ../backend:/app
labels: []
frontend:
build:
target: development
environment:
NUXT_PUBLIC_API_BASE: http://localhost:8000/api/v1
ports: !override
- "3000:3000"
- "24678:24678"
volumes:
- ../frontend:/app
labels: []

43
docker/docker-compose.yml Normal file
View File

@@ -0,0 +1,43 @@
name: sejeteralo
services:
backend:
build:
context: ../
dockerfile: docker/backend.Dockerfile
target: production
environment:
DATABASE_URL: sqlite+aiosqlite:///./sejeteralo.db
SECRET_KEY: ${SECRET_KEY}
DEBUG: "false"
CORS_ORIGINS: '["https://${DOMAIN:-sejeteralo.org}"]'
volumes:
- backend-data:/app
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.routers.sejeteralo-api.rule=Host(`${DOMAIN:-sejeteralo.org}`) && PathPrefix(`/api`)"
- "traefik.http.routers.sejeteralo-api.entrypoints=websecure"
- "traefik.http.routers.sejeteralo-api.tls.certresolver=letsencrypt"
- "traefik.http.services.sejeteralo-api.loadbalancer.server.port=8000"
frontend:
build:
context: ../
dockerfile: docker/frontend.Dockerfile
target: production
environment:
NODE_ENV: production
NUXT_PUBLIC_API_BASE: http://backend:8000/api/v1
depends_on:
- backend
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.routers.sejeteralo.rule=Host(`${DOMAIN:-sejeteralo.org}`)"
- "traefik.http.routers.sejeteralo.entrypoints=websecure"
- "traefik.http.routers.sejeteralo.tls.certresolver=letsencrypt"
- "traefik.http.services.sejeteralo.loadbalancer.server.port=3000"
volumes:
backend-data:

View File

@@ -0,0 +1,36 @@
# syntax = docker/dockerfile:1
ARG NODE_VERSION=20-slim
FROM node:${NODE_VERSION} AS base
WORKDIR /src
# Build
FROM base AS build
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci
COPY frontend/ .
RUN npm run build
# Production
FROM base AS production
ENV PORT=3000
ENV NODE_ENV=production
COPY --from=build /src/.output /src/.output
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:${PORT}/ || exit 1
EXPOSE $PORT
CMD [ "node", ".output/server/index.mjs" ]
# Development
FROM base AS development
WORKDIR /app
ENTRYPOINT [ "npm", "run", "dev" ]