feat: add miniPaint stack (paint.pivoine.art)

Added new paint service stack to the docker-compose infrastructure:

- **Paint stack** (paint.pivoine.art):
  - miniPaint: Web-based image editor built from GitHub
  - Multi-stage Docker build clones from https://github.com/viliusle/miniPaint
  - Features: layers, filters, drawing tools, text, shapes support
  - Client-side processing with no server uploads
  - Stateless architecture (no backups needed)

Infrastructure updates:
- Created paint/compose.yaml with Traefik routing and SSL
- Created paint/Dockerfile with Node.js build stage and nginx serve
- Added PAINT environment variables to arty.yml
- Updated compose.yaml include list
- Updated CLAUDE.md documentation

All services integrated with Traefik for SSL termination and include
Watchtower auto-update labels.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-06 19:44:14 +01:00
parent dac3e89f47
commit ab35b2bca1
5 changed files with 82 additions and 0 deletions

View File

@@ -23,6 +23,7 @@ Root `compose.yaml` uses Docker Compose's `include` directive to orchestrate mul
- **vault**: Vaultwarden password manager (SQLite)
- **joplin**: Joplin Server note-taking and sync platform (PostgreSQL)
- **vert**: VERT file format converter (WebAssembly-based, stateless)
- **paint**: miniPaint web-based image editor (built from GitHub)
- **restic**: Backrest backup system with restic backend
- **sablier**: Dynamic scaling plugin for Traefik
- **vpn**: WireGuard VPN (wg-easy)
@@ -272,6 +273,27 @@ Simply access https://vert.pivoine.art and drag/drop files to convert between fo
**Note**: VERT is stateless and doesn't require backups as no data is persisted.
### Paint (paint/compose.yaml)
miniPaint web-based image editor built from GitHub:
- **paint**: miniPaint app exposed at `paint.pivoine.art:80`
- Online image editor with layer support
- Built directly from https://github.com/viliusle/miniPaint
- Supports PNG, JPG, GIF, WebP formats
- Features: layers, filters, drawing tools, text, shapes
- Client-side processing (no uploads to server)
- No persistent data storage required
- Stateless architecture
**Build Process**:
- Multi-stage Docker build clones from GitHub
- Builds using Node.js 18
- Serves static files via nginx
**Usage**:
Access https://paint.pivoine.art to use the image editor. All editing happens in the browser - images are not uploaded to the server.
**Note**: miniPaint is stateless and doesn't require backups as no data is persisted.
### Restic (restic/compose.yaml)
Backrest backup system with restic backend:
- **backrest**: Backrest web UI exposed at `restic.pivoine.art:9898`

View File

@@ -116,6 +116,10 @@ envs:
VERT_IMAGE: ghcr.io/vert-sh/vert:latest
VERT_TRAEFIK_HOST: vert.pivoine.art
VERT_SABLIER_ENABLED: true
# Paint
PAINT_TRAEFIK_ENABLED: true
PAINT_COMPOSE_PROJECT_NAME: paint
PAINT_TRAEFIK_HOST: paint.pivoine.art
# Proxy
PROXY_COMPOSE_PROJECT_NAME: proxy
PROXY_DOCKER_IMAGE: traefik:latest

View File

@@ -11,6 +11,7 @@ include:
- vault/compose.yaml
- joplin/compose.yaml
- vert/compose.yaml
- paint/compose.yaml
- restic/compose.yaml
- umami/compose.yaml
- sablier/compose.yaml

23
paint/Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
# Build miniPaint from GitHub repository
FROM node:18-alpine AS builder
WORKDIR /app
# Clone the repository
RUN apk add --no-cache git && \
git clone https://github.com/viliusle/miniPaint.git . && \
npm install && \
npm run build
# Production stage with nginx
FROM nginx:alpine
# Copy built files from builder
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx configuration if needed
COPY --from=builder /app /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

32
paint/compose.yaml Normal file
View File

@@ -0,0 +1,32 @@
services:
paint:
build:
context: .
dockerfile: Dockerfile
image: minipaint:latest
container_name: ${PAINT_COMPOSE_PROJECT_NAME}_app
restart: unless-stopped
networks:
- compose_network
labels:
- 'traefik.enable=${PAINT_TRAEFIK_ENABLED}'
# HTTP to HTTPS redirect
- 'traefik.http.middlewares.${PAINT_COMPOSE_PROJECT_NAME}-redirect-web-secure.redirectscheme.scheme=https'
- 'traefik.http.routers.${PAINT_COMPOSE_PROJECT_NAME}-web.middlewares=${PAINT_COMPOSE_PROJECT_NAME}-redirect-web-secure'
- 'traefik.http.routers.${PAINT_COMPOSE_PROJECT_NAME}-web.rule=Host(`${PAINT_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${PAINT_COMPOSE_PROJECT_NAME}-web.entrypoints=web'
# HTTPS router
- 'traefik.http.routers.${PAINT_COMPOSE_PROJECT_NAME}-web-secure.rule=Host(`${PAINT_TRAEFIK_HOST}`)'
- 'traefik.http.routers.${PAINT_COMPOSE_PROJECT_NAME}-web-secure.tls.certresolver=resolver'
- 'traefik.http.routers.${PAINT_COMPOSE_PROJECT_NAME}-web-secure.entrypoints=web-secure'
- 'traefik.http.routers.${PAINT_COMPOSE_PROJECT_NAME}-web-secure.middlewares=security-headers@file'
# Service
- 'traefik.http.services.${PAINT_COMPOSE_PROJECT_NAME}-web-secure.loadbalancer.server.port=80'
- 'traefik.docker.network=${NETWORK_NAME}'
# Watchtower
- 'com.centurylinklabs.watchtower.enable=${WATCHTOWER_LABEL_ENABLE}'
networks:
compose_network:
name: ${NETWORK_NAME}
external: true