Files
professional-website/docker-compose.swarm.yml
Jason Woltje 486bbc8cf8
Some checks failed
ci/woodpecker/push/web Pipeline failed
ci/woodpecker/pr/web Pipeline failed
feat(site): port stitch design system + seed-ready content model
Ports the "Technical Editorial" design sample into real TSX wired to
Payload globals/collections. Home/About/Projects (list+detail)/Contact
pages render against Payload data. Expands schemas (Home principles,
About timeline/skills/gear, Contact channels) to cover the full design
surface. Adds seed script that populates realistic AI-drafted content
for first boot. Defers writing/resume routes per scope cut.

- Design tokens: Material-3 palette + Space Grotesk/Inter typography
  scale + dot-grid + glassmorphism utilities
- Shared layout: Nav, Footer, StatusTerminal, GridOverlay, Button,
  TechChip in src/components/site
- Schemas: expand 5 globals + 6 collections; add auto-slug hook
- Seed: scripts/seed.ts — idempotent upsert for media, categories,
  6 projects, 8 gear, 3 posts, 5 globals; generates placeholder admin
- Contact: form + /api/contact route with optional Turnstile verify
- Rename TURNSTILE_SITE_KEY -> NEXT_PUBLIC_TURNSTILE_SITE_KEY (client)
- Remove dead src/components/SiteHeader|SiteFooter|StatusTerminal
2026-04-14 18:57:53 -05:00

111 lines
4.0 KiB
YAML

# =============================================================================
# jasonwoltje.com — Docker Swarm / Portainer stack
# =============================================================================
#
# Deploy target: w-docker0 (10.1.1.45), Portainer endpoint 7.
# Routing:
# jasonwoltje.com / www.jasonwoltje.com -> web (Next.js + Payload CMS)
#
# Ingress pattern (mirrors MosaicStack):
# Edge Traefik (10.1.1.43) terminates TLS
# -> per-swarm Traefik on w-docker0 on entrypoint "web" (HTTP)
# -> web:3000
#
# Usage (Portainer):
# Stacks -> Add Stack -> Git repository
# URL: https://git.mosaicstack.dev/jason.woltje/professional-website
# Compose path: docker-compose.swarm.yml
# Env vars: see .env.example (all required unless marked optional)
# Deploy
#
# Image tag rule: WEB_IMAGE_TAG MUST be an immutable tag (sha-<8> or vX.Y.Z).
# Never point this stack at `latest`.
# =============================================================================
services:
web:
image: git.mosaicstack.dev/jason.woltje/professional-website:${WEB_IMAGE_TAG}
environment:
DATABASE_URI: postgresql://${PAYLOAD_POSTGRES_USER}:${PAYLOAD_POSTGRES_PASSWORD}@jasonwoltje_postgres:5432/${PAYLOAD_POSTGRES_DB}
PAYLOAD_SECRET: ${PAYLOAD_SECRET}
PAYLOAD_PUBLIC_SERVER_URL: https://${SITE_DOMAIN:-jasonwoltje.com}
NEXT_PUBLIC_SITE_URL: https://${SITE_DOMAIN:-jasonwoltje.com}
NEXT_PUBLIC_BUILD_SHA: ${WEB_IMAGE_TAG}
NEXT_PUBLIC_BUILD_REV: ${WEB_IMAGE_TAG}
NEXT_PUBLIC_TURNSTILE_SITE_KEY: ${NEXT_PUBLIC_TURNSTILE_SITE_KEY:-}
TURNSTILE_SECRET_KEY: ${TURNSTILE_SECRET_KEY:-}
NEXT_PUBLIC_UMAMI_SRC: ${NEXT_PUBLIC_UMAMI_SRC:-}
NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${NEXT_PUBLIC_UMAMI_WEBSITE_ID:-}
RESEND_API_KEY: ${RESEND_API_KEY:-}
RESEND_FROM: ${RESEND_FROM:-no-reply@jasonwoltje.com}
RESEND_TO: ${RESEND_TO:-jason@diversecanvas.com}
MAUTIC_FORM_URL: ${MAUTIC_FORM_URL:-}
volumes:
- media:/app/media
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:3000/api/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
networks:
- internal
- traefik-public
deploy:
replicas: 1
update_config:
parallelism: 1
delay: 30s
order: start-first
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 5
window: 120s
labels:
- "traefik.enable=true"
- "traefik.http.routers.jasonwoltje.rule=Host(`${SITE_DOMAIN:-jasonwoltje.com}`) || Host(`www.${SITE_DOMAIN:-jasonwoltje.com}`)"
- "traefik.http.routers.jasonwoltje.entrypoints=web"
- "traefik.http.services.jasonwoltje.loadbalancer.server.port=3000"
# www -> apex 301
- "traefik.http.middlewares.jasonwoltje-www-redirect.redirectregex.regex=^https?://www\\.${SITE_DOMAIN:-jasonwoltje.com}/(.*)"
- "traefik.http.middlewares.jasonwoltje-www-redirect.redirectregex.replacement=https://${SITE_DOMAIN:-jasonwoltje.com}/$${1}"
- "traefik.http.middlewares.jasonwoltje-www-redirect.redirectregex.permanent=true"
- "traefik.http.routers.jasonwoltje.middlewares=jasonwoltje-www-redirect"
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: ${PAYLOAD_POSTGRES_DB:-payload}
POSTGRES_USER: ${PAYLOAD_POSTGRES_USER:-payload}
POSTGRES_PASSWORD: ${PAYLOAD_POSTGRES_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
networks:
- internal
deploy:
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 5
placement:
constraints:
- node.role == manager
volumes:
postgres-data:
media:
networks:
internal:
driver: overlay
traefik-public:
external: true