diff --git a/.env.example b/.env.example index 44dcb4e..953e3eb 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ -DATABASE_URL="postgresql://postgres:postgres@localhost:5432/moh_sass?schema=public" -NEXT_PUBLIC_APP_URL="http://localhost:3000" +DATABASE_URL="postgresql://postgres:postgres@db:5432/moh_sass?schema=public" +NEXT_PUBLIC_APP_URL="https://mohfarawati.de" NEXT_TELEMETRY_DISABLED="1" ADMIN_PASSWORD="123Yolo!321" ADMIN_AUTH_SECRET="Us0z76jwlTQLOeQWAGGxAxDcc0rHwp4q" diff --git a/Dockerfile b/Dockerfile index a1338fb..f5dcd21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,25 @@ -FROM node:22-alpine - +FROM node:22-alpine AS deps WORKDIR /app - COPY package*.json ./ -RUN npm install +RUN npm ci +FROM node:22-alpine AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules COPY . . +RUN npm run build + +FROM node:22-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +COPY --from=builder /app/package*.json ./ +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/public ./public +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/next.config.mjs ./next.config.mjs EXPOSE 3000 -CMD ["npm", "run", "dev", "--", "--hostname", "0.0.0.0", "--port", "3000"] +CMD ["npm", "run", "start", "--", "--hostname", "0.0.0.0", "--port", "3000"] diff --git a/Makefile b/Makefile index 0a6d610..f64ed9e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: start stop restart logs build ps port health clean-orphans app-shell db-shell db-init db-migrate db-seed prisma-generate prisma-migrate help +.PHONY: start stop restart deploy logs build ps port health clean-orphans app-shell db-shell db-init db-migrate db-seed prisma-generate prisma-migrate help MIGRATION_NAME ?= init @@ -10,6 +10,11 @@ stop: restart: stop start +deploy: + git pull + docker compose up -d --build + @git log -1 --oneline + logs: docker compose logs -f --tail=200 @@ -20,7 +25,7 @@ ps: docker compose ps port: - docker compose port app 3000 + @echo "https://mohfarawati.de" clean-orphans: docker compose up -d --remove-orphans @@ -47,18 +52,18 @@ prisma-migrate: docker compose exec app npx prisma migrate dev --name $(MIGRATION_NAME) health: - @PORT=$$(docker compose port app 3000 | sed 's/.*://'); \ - curl -sS "http://localhost:$$PORT/api/health" + curl -sS https://mohfarawati.de/api/health help: @echo "Available targets:" @echo " make start Start all containers" @echo " make stop Stop and remove containers" @echo " make restart Restart all containers" + @echo " make deploy Pull latest code and deploy updated stack" @echo " make logs Follow container logs" @echo " make build Build images" @echo " make ps Show container status" - @echo " make port Show random host port for app" + @echo " make port Show app public URL" @echo " make clean-orphans Remove orphaned old containers" @echo " make app-shell Open shell in app container" @echo " make db-shell Open PostgreSQL shell" @@ -67,4 +72,4 @@ help: @echo " make db-seed Seed database data" @echo " make prisma-generate Run prisma generate" @echo " make prisma-migrate Create/apply dev migration" - @echo " make health Check app health endpoint" + @echo " make health Check app health endpoint via public domain" diff --git a/docker-compose.yml b/docker-compose.yml index 88e80cc..eace4c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,28 +4,40 @@ services: context: . dockerfile: Dockerfile container_name: moh-sass-app - command: sh -c "npm install && npm run dev -- --hostname 0.0.0.0 --port 3000" + restart: unless-stopped environment: + NODE_ENV: production NEXT_TELEMETRY_DISABLED: "1" - WATCHPACK_POLLING: "true" - DATABASE_URL: postgresql://postgres:postgres@db:5432/moh_sass?schema=public + DATABASE_URL: ${DATABASE_URL} ADMIN_PASSWORD: ${ADMIN_PASSWORD:-change-me} ADMIN_AUTH_SECRET: ${ADMIN_AUTH_SECRET:-change-me-long-secret} ROOT_BASIC_AUTH_USER: ${ROOT_BASIC_AUTH_USER:-root} ROOT_BASIC_AUTH_PASS: ${ROOT_BASIC_AUTH_PASS:-change-me-root} - volumes: - - .:/app - - app_node_modules:/app/node_modules - - app_next:/app/.next - ports: - - "3000:3000" depends_on: db: condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:3000/api/health || exit 1"] + interval: 15s + timeout: 5s + retries: 10 + start_period: 20s + labels: + - "traefik.enable=true" + - "traefik.docker.network=proxy" + - "traefik.http.routers.mohfarawati.rule=Host(`mohfarawati.de`) || Host(`www.mohfarawati.de`)" + - "traefik.http.routers.mohfarawati.entrypoints=websecure" + - "traefik.http.routers.mohfarawati.tls=true" + - "traefik.http.routers.mohfarawati.tls.certresolver=cf" + - "traefik.http.services.mohfarawati.loadbalancer.server.port=3000" + networks: + - appnet + - proxy db: image: postgres:16-alpine container_name: moh-sass-db + restart: unless-stopped environment: POSTGRES_DB: moh_sass POSTGRES_USER: postgres @@ -37,8 +49,14 @@ services: interval: 5s timeout: 5s retries: 20 + networks: + - appnet volumes: - app_node_modules: - app_next: postgres_data: + +networks: + appnet: + proxy: + external: true + name: proxy