🚀 ci(nutriphi): add production deployment configuration

- Add backend Dockerfile with multi-stage build and health checks
- Add web Dockerfile with SvelteKit static env vars
- Add docker-entrypoint.sh for automatic DB migration
- Add nutriphi-backend and nutriphi-web to docker-compose.macmini.yml
- Add CI/CD detection and build jobs for nutriphi
- Update CORS origins in mana-core-auth to include nutriphi.mana.how
- Include nutriphi in deploy:landing:all script

Ports: Backend 3023, Web 5189
Domain: nutriphi.mana.how / nutriphi-api.mana.how
This commit is contained in:
Till-JS 2026-01-28 15:29:43 +01:00
parent 43991383a1
commit c031540ef1
6 changed files with 312 additions and 2 deletions

View file

@ -66,6 +66,8 @@ jobs:
storage-backend: ${{ steps.changes.outputs.storage-backend }}
storage-web: ${{ steps.changes.outputs.storage-web }}
telegram-stats-bot: ${{ steps.changes.outputs.telegram-stats-bot }}
nutriphi-backend: ${{ steps.changes.outputs.nutriphi-backend }}
nutriphi-web: ${{ steps.changes.outputs.nutriphi-web }}
any-changes: ${{ steps.changes.outputs.any-changes }}
steps:
- name: Checkout code
@ -96,6 +98,8 @@ jobs:
echo "storage-backend=true" >> $GITHUB_OUTPUT
echo "storage-web=true" >> $GITHUB_OUTPUT
echo "telegram-stats-bot=true" >> $GITHUB_OUTPUT
echo "nutriphi-backend=true" >> $GITHUB_OUTPUT
echo "nutriphi-web=true" >> $GITHUB_OUTPUT
echo "any-changes=true" >> $GITHUB_OUTPUT
exit 0
fi
@ -130,6 +134,8 @@ jobs:
echo "storage-backend=true" >> $GITHUB_OUTPUT
echo "storage-web=true" >> $GITHUB_OUTPUT
echo "telegram-stats-bot=true" >> $GITHUB_OUTPUT
echo "nutriphi-backend=true" >> $GITHUB_OUTPUT
echo "nutriphi-web=true" >> $GITHUB_OUTPUT
echo "any-changes=true" >> $GITHUB_OUTPUT
exit 0
fi
@ -297,6 +303,22 @@ jobs:
echo "telegram-stats-bot=false" >> $GITHUB_OUTPUT
fi
# nutriphi-backend
NUTRIPHI_BACKEND_CHANGED=$(check_pattern "apps/nutriphi/apps/backend/|apps/nutriphi/packages/")
if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$NUTRIPHI_BACKEND_CHANGED" == "true" ]; then
echo "nutriphi-backend=true" >> $GITHUB_OUTPUT
else
echo "nutriphi-backend=false" >> $GITHUB_OUTPUT
fi
# nutriphi-web
NUTRIPHI_WEB_CHANGED=$(check_pattern "apps/nutriphi/apps/web/|apps/nutriphi/packages/")
if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$NUTRIPHI_WEB_CHANGED" == "true" ]; then
echo "nutriphi-web=true" >> $GITHUB_OUTPUT
else
echo "nutriphi-web=false" >> $GITHUB_OUTPUT
fi
# Check if any service needs building
if grep -q "=true" $GITHUB_OUTPUT; then
echo "any-changes=true" >> $GITHUB_OUTPUT
@ -327,6 +349,8 @@ jobs:
echo "| storage-backend | ${{ steps.changes.outputs.storage-backend }} |" >> $GITHUB_STEP_SUMMARY
echo "| storage-web | ${{ steps.changes.outputs.storage-web }} |" >> $GITHUB_STEP_SUMMARY
echo "| telegram-stats-bot | ${{ steps.changes.outputs.telegram-stats-bot }} |" >> $GITHUB_STEP_SUMMARY
echo "| nutriphi-backend | ${{ steps.changes.outputs.nutriphi-backend }} |" >> $GITHUB_STEP_SUMMARY
echo "| nutriphi-web | ${{ steps.changes.outputs.nutriphi-web }} |" >> $GITHUB_STEP_SUMMARY
# ===========================================
# Validation job - runs on PRs
@ -858,3 +882,61 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-nutriphi-backend:
name: Build nutriphi-backend
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.nutriphi-backend == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/nutriphi-backend
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
file: apps/nutriphi/apps/backend/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-nutriphi-web:
name: Build nutriphi-web
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.nutriphi-web == 'true'
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository_owner }}/nutriphi-web
tags: type=raw,value=latest
- uses: docker/build-push-action@v5
with:
context: .
file: apps/nutriphi/apps/web/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max

View file

@ -0,0 +1,64 @@
# Build stage
FROM node:20-alpine AS builder
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
# Copy root workspace files
COPY pnpm-workspace.yaml ./
COPY package.json ./
COPY pnpm-lock.yaml ./
# Copy shared packages
COPY packages/shared-nestjs-auth ./packages/shared-nestjs-auth
# Copy nutriphi packages and backend
COPY apps/nutriphi/packages ./apps/nutriphi/packages
COPY apps/nutriphi/apps/backend ./apps/nutriphi/apps/backend
# Install dependencies
RUN pnpm install --frozen-lockfile
# Build shared packages first
WORKDIR /app/packages/shared-nestjs-auth
RUN pnpm build
# Build the backend
WORKDIR /app/apps/nutriphi/apps/backend
RUN pnpm build
# Production stage
FROM node:20-alpine AS production
# Install pnpm and postgresql-client for health checks
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate \
&& apk add --no-cache postgresql-client
WORKDIR /app
# Copy everything from builder (including node_modules)
COPY --from=builder /app/pnpm-workspace.yaml ./
COPY --from=builder /app/package.json ./
COPY --from=builder /app/pnpm-lock.yaml ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/packages ./packages
COPY --from=builder /app/apps/nutriphi ./apps/nutriphi
# Copy entrypoint script
COPY apps/nutriphi/apps/backend/docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
WORKDIR /app/apps/nutriphi/apps/backend
# Expose port
EXPOSE 3023
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3023/api/v1/health || exit 1
# Run entrypoint script
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node", "dist/main.js"]

View file

@ -0,0 +1,23 @@
#!/bin/sh
set -e
echo "=== NutriPhi Backend Entrypoint ==="
# Wait for PostgreSQL to be ready
echo "Waiting for PostgreSQL..."
until pg_isready -h ${DB_HOST:-postgres} -p ${DB_PORT:-5432} -U ${DB_USER:-postgres} 2>/dev/null; do
echo "PostgreSQL is unavailable - sleeping"
sleep 2
done
echo "PostgreSQL is up!"
cd /app/apps/nutriphi/apps/backend
# Run schema push
echo "Pushing database schema..."
npx drizzle-kit push --force
echo "Schema push completed!"
# Execute the main command
echo "Starting application..."
exec "$@"

View file

@ -0,0 +1,86 @@
# Build stage
FROM node:20-alpine AS builder
# Build arguments for SvelteKit static env vars
ARG PUBLIC_BACKEND_URL=http://nutriphi-backend:3023
ARG PUBLIC_MANA_CORE_AUTH_URL=http://mana-core-auth:3001
# Set as environment variables for build
ENV PUBLIC_BACKEND_URL=$PUBLIC_BACKEND_URL
ENV PUBLIC_MANA_CORE_AUTH_URL=$PUBLIC_MANA_CORE_AUTH_URL
# Install pnpm
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
# Copy root workspace files
COPY pnpm-workspace.yaml ./
COPY package.json ./
COPY pnpm-lock.yaml ./
# Copy shared packages needed by nutriphi web
COPY packages/shared-types ./packages/shared-types
COPY packages/shared-auth ./packages/shared-auth
COPY packages/shared-auth-ui ./packages/shared-auth-ui
COPY packages/shared-branding ./packages/shared-branding
COPY packages/shared-feedback-service ./packages/shared-feedback-service
COPY packages/shared-feedback-types ./packages/shared-feedback-types
COPY packages/shared-feedback-ui ./packages/shared-feedback-ui
COPY packages/shared-i18n ./packages/shared-i18n
COPY packages/shared-icons ./packages/shared-icons
COPY packages/shared-tailwind ./packages/shared-tailwind
COPY packages/shared-theme ./packages/shared-theme
COPY packages/shared-theme-ui ./packages/shared-theme-ui
COPY packages/shared-subscription-types ./packages/shared-subscription-types
COPY packages/shared-subscription-ui ./packages/shared-subscription-ui
COPY packages/shared-profile-ui ./packages/shared-profile-ui
COPY packages/shared-ui ./packages/shared-ui
COPY packages/shared-utils ./packages/shared-utils
# Copy nutriphi packages and web
COPY apps/nutriphi/packages ./apps/nutriphi/packages
COPY apps/nutriphi/apps/web ./apps/nutriphi/apps/web
# Install dependencies
RUN pnpm install --frozen-lockfile
# Build shared packages that need building
WORKDIR /app/packages/shared-auth
RUN pnpm build || true
# Build the web app
WORKDIR /app/apps/nutriphi/apps/web
RUN pnpm exec svelte-kit sync
RUN pnpm build
# Production stage
FROM node:20-alpine AS production
# Keep same directory structure as builder so pnpm symlinks resolve correctly
WORKDIR /app/apps/nutriphi/apps/web
# Copy the pnpm store that symlinks point to (at /app/node_modules/.pnpm)
COPY --from=builder /app/node_modules/.pnpm /app/node_modules/.pnpm
# Copy the app's node_modules (contains symlinks to the pnpm store)
COPY --from=builder /app/apps/nutriphi/apps/web/node_modules ./node_modules
# Copy built application
COPY --from=builder /app/apps/nutriphi/apps/web/build ./build
COPY --from=builder /app/apps/nutriphi/apps/web/package.json ./
# Expose port
EXPOSE 5189
# Set environment variables
ENV NODE_ENV=production
ENV PORT=5189
ENV HOST=0.0.0.0
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:5189/health || exit 1
# Run the app
CMD ["node", "build"]

View file

@ -89,7 +89,7 @@ services:
SMTP_USER: ${SMTP_USER:-94cde5002@smtp-brevo.com}
SMTP_PASSWORD: ${SMTP_PASSWORD}
SMTP_FROM: ManaCore <noreply@mana.how>
CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://todo.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://storage.mana.how,https://presi.mana.how
CORS_ORIGINS: https://mana.how,https://chat.mana.how,https://todo.mana.how,https://calendar.mana.how,https://clock.mana.how,https://contacts.mana.how,https://storage.mana.how,https://presi.mana.how,https://nutriphi.mana.how
# DuckDB Analytics (Business Metrics)
DUCKDB_PATH: /data/analytics/metrics.duckdb
volumes:
@ -534,6 +534,61 @@ services:
retries: 3
start_period: 40s
# ============================================
# NutriPhi App (AI Nutrition Tracking)
# ============================================
nutriphi-backend:
image: ghcr.io/memo-2023/nutriphi-backend:latest
container_name: nutriphi-backend
restart: always
depends_on:
mana-core-auth:
condition: service_healthy
postgres:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 3023
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-manacore123}@postgres:5432/nutriphi
DB_HOST: postgres
DB_PORT: 5432
DB_USER: postgres
MANA_CORE_AUTH_URL: http://mana-core-auth:3001
GEMINI_API_KEY: ${GEMINI_API_KEY:-}
CORS_ORIGINS: https://nutriphi.mana.how,https://mana.how
ports:
- "3023:3023"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3023/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
nutriphi-web:
image: ghcr.io/memo-2023/nutriphi-web:latest
container_name: nutriphi-web
restart: always
depends_on:
nutriphi-backend:
condition: service_healthy
environment:
NODE_ENV: production
PORT: 5189
PUBLIC_BACKEND_URL: http://nutriphi-backend:3023
PUBLIC_MANA_CORE_AUTH_URL: http://mana-core-auth:3001
PUBLIC_BACKEND_URL_CLIENT: https://nutriphi-api.mana.how
PUBLIC_MANA_CORE_AUTH_URL_CLIENT: https://auth.mana.how
ports:
- "5189:5189"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:5189/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ============================================
# Monitoring Stack
# ============================================

View file

@ -206,7 +206,7 @@
"deploy:landing:clock": "pnpm --filter @clock/landing build && npx wrangler pages deploy apps/clock/apps/landing/dist --project-name=clocks-landing",
"deploy:landing:mail": "pnpm --filter @mail/landing build && npx wrangler pages deploy apps/mail/apps/landing/dist --project-name=mail-landing",
"deploy:landing:moodlit": "pnpm --filter @moodlit/landing build && npx wrangler pages deploy apps/moodlit/apps/landing/dist --project-name=moodlit-landing",
"deploy:landing:all": "pnpm deploy:landing:calendar && pnpm deploy:landing:chat && pnpm deploy:landing:picture && pnpm deploy:landing:manacore && pnpm deploy:landing:manadeck && pnpm deploy:landing:zitare && pnpm deploy:landing:presi && pnpm deploy:landing:clock && pnpm deploy:landing:mail",
"deploy:landing:all": "pnpm deploy:landing:calendar && pnpm deploy:landing:chat && pnpm deploy:landing:picture && pnpm deploy:landing:manacore && pnpm deploy:landing:manadeck && pnpm deploy:landing:zitare && pnpm deploy:landing:presi && pnpm deploy:landing:clock && pnpm deploy:landing:mail && pnpm deploy:landing:nutriphi",
"cf:login": "npx wrangler login",
"cf:projects:list": "npx wrangler pages project list",
"cf:projects:create": "echo 'Creating Cloudflare Pages projects...' && npx wrangler pages project create chat-landing --production-branch=main && npx wrangler pages project create picture-landing --production-branch=main && npx wrangler pages project create manacore-landing --production-branch=main && npx wrangler pages project create manadeck-landing --production-branch=main && npx wrangler pages project create zitare-landing --production-branch=main",