Fix master-api Docker runtime: flat deps, explicit env, longer health start period.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
96a0bbface
commit
4e232e8ef6
3 changed files with 62 additions and 41 deletions
|
|
@ -25,13 +25,13 @@
|
||||||
"fastify": "^5.2.1",
|
"fastify": "^5.2.1",
|
||||||
"ioredis": "^5.4.2",
|
"ioredis": "^5.4.2",
|
||||||
"nanoid": "^5.0.9",
|
"nanoid": "^5.0.9",
|
||||||
|
"prisma": "^6.3.1",
|
||||||
"stremio-addon-sdk": "^1.6.10",
|
"stremio-addon-sdk": "^1.6.10",
|
||||||
"zod": "^3.24.1"
|
"zod": "^3.24.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.10.7",
|
"@types/node": "^22.10.7",
|
||||||
"@types/ws": "^8.5.14",
|
"@types/ws": "^8.5.14",
|
||||||
"prisma": "^6.3.1",
|
|
||||||
"tsx": "^4.19.2",
|
"tsx": "^4.19.2",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "^5.7.3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,33 +2,49 @@ FROM node:20-alpine AS base
|
||||||
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
|
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
FROM base AS deps
|
|
||||||
COPY package.json pnpm-workspace.yaml ./
|
|
||||||
COPY apps/master-api/package.json ./apps/master-api/
|
|
||||||
COPY packages/shared-types/package.json ./packages/shared-types/
|
|
||||||
COPY packages/protocol/package.json ./packages/protocol/
|
|
||||||
RUN pnpm install --no-frozen-lockfile
|
|
||||||
|
|
||||||
FROM base AS builder
|
FROM base AS builder
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
|
||||||
COPY --from=deps /app/apps ./apps
|
|
||||||
COPY --from=deps /app/packages ./packages
|
|
||||||
COPY package.json pnpm-workspace.yaml ./
|
COPY package.json pnpm-workspace.yaml ./
|
||||||
COPY packages ./packages
|
|
||||||
COPY apps/master-api ./apps/master-api
|
COPY apps/master-api ./apps/master-api
|
||||||
RUN pnpm --filter @media-cluster/shared-types build \
|
COPY packages ./packages
|
||||||
&& pnpm --filter @media-cluster/protocol build
|
RUN pnpm install --no-frozen-lockfile
|
||||||
|
RUN pnpm --filter @media-cluster/shared-types build
|
||||||
|
RUN pnpm --filter @media-cluster/protocol build
|
||||||
WORKDIR /app/apps/master-api
|
WORKDIR /app/apps/master-api
|
||||||
RUN pnpm exec prisma generate && pnpm build
|
RUN pnpm exec prisma generate
|
||||||
|
RUN pnpm build
|
||||||
|
|
||||||
|
# Assemble a flat runtime directory (avoids broken pnpm symlinks in final image)
|
||||||
|
WORKDIR /app
|
||||||
|
RUN mkdir -p /out/node_modules/@media-cluster \
|
||||||
|
&& cp -R apps/master-api/dist /out/dist \
|
||||||
|
&& cp -R apps/master-api/prisma /out/prisma \
|
||||||
|
&& cp apps/master-api/package.json /out/package.json \
|
||||||
|
&& cp -R packages/shared-types /out/node_modules/@media-cluster/shared-types \
|
||||||
|
&& cp -R packages/protocol /out/node_modules/@media-cluster/protocol
|
||||||
|
|
||||||
FROM node:20-alpine AS runner
|
FROM node:20-alpine AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
RUN apk add --no-cache openssl wget
|
RUN apk add --no-cache openssl wget python3 make g++
|
||||||
COPY --from=builder /app/apps/master-api/dist ./dist
|
COPY --from=builder /out/ ./
|
||||||
COPY --from=builder /app/apps/master-api/package.json ./package.json
|
|
||||||
COPY --from=builder /app/apps/master-api/prisma ./prisma
|
# Drop workspace: protocol deps; keep prisma CLI for migrate
|
||||||
COPY --from=builder /app/node_modules ./node_modules
|
RUN node -e "\
|
||||||
COPY --from=builder /app/packages ./packages
|
const fs=require('fs');\
|
||||||
|
const p=JSON.parse(fs.readFileSync('package.json','utf8'));\
|
||||||
|
delete p.dependencies['@media-cluster/protocol'];\
|
||||||
|
delete p.dependencies['@media-cluster/shared-types'];\
|
||||||
|
p.dependencies.prisma = (p.devDependencies && p.devDependencies.prisma) || '6.3.1';\
|
||||||
|
delete p.devDependencies;\
|
||||||
|
delete p.scripts;\
|
||||||
|
fs.writeFileSync('package.json', JSON.stringify(p, null, 2));"
|
||||||
|
|
||||||
|
RUN npm install --omit=dev \
|
||||||
|
&& npx prisma generate \
|
||||||
|
&& apk del python3 make g++
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=5 \
|
||||||
|
CMD wget -qO- http://127.0.0.1:3000/health || exit 1
|
||||||
|
|
||||||
CMD ["sh", "-c", "npx prisma migrate deploy && node dist/app.js"]
|
CMD ["sh", "-c", "npx prisma migrate deploy && node dist/app.js"]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
# Docker Compose tuned for Dokploy
|
# Docker Compose for Dokploy
|
||||||
# Deploy as: Compose application → Git provider → compose path:
|
# Compose path: deploy/docker/docker-compose.dokploy.yml
|
||||||
# deploy/docker/docker-compose.dokploy.yml
|
|
||||||
#
|
#
|
||||||
# Set environment variables in the Dokploy UI (Environment tab).
|
# Put secrets in Dokploy Environment tab (NOT env_file).
|
||||||
# Dokploy writes them to .env next to this compose file.
|
# Dokploy injects them for ${VAR} substitution.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
|
@ -11,15 +10,16 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: ${POSTGRES_USER:-media}
|
POSTGRES_USER: ${POSTGRES_USER:-media}
|
||||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-media_cluster}
|
POSTGRES_DB: ${POSTGRES_DB:-media_cluster}
|
||||||
volumes:
|
volumes:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-media} -d $${POSTGRES_DB:-media_cluster}"]
|
test: ["CMD-SHELL", "pg_isready -U \"$$POSTGRES_USER\" -d \"$$POSTGRES_DB\""]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 10
|
||||||
|
start_period: 10s
|
||||||
networks:
|
networks:
|
||||||
- media-cluster
|
- media-cluster
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ services:
|
||||||
test: ["CMD", "redis-cli", "ping"]
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 10
|
||||||
networks:
|
networks:
|
||||||
- media-cluster
|
- media-cluster
|
||||||
|
|
||||||
|
|
@ -41,28 +41,35 @@ services:
|
||||||
context: ../..
|
context: ../..
|
||||||
dockerfile: deploy/docker/Dockerfile.master-api
|
dockerfile: deploy/docker/Dockerfile.master-api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
HOST: 0.0.0.0
|
HOST: 0.0.0.0
|
||||||
PORT: 3000
|
PORT: "3000"
|
||||||
DATABASE_URL: postgresql://${POSTGRES_USER:-media}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-media_cluster}
|
DATABASE_URL: postgresql://${POSTGRES_USER:-media}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-media_cluster}
|
||||||
REDIS_URL: redis://redis:6379
|
REDIS_URL: redis://redis:6379
|
||||||
|
SESSION_SECRET: ${SESSION_SECRET:?SESSION_SECRET is required}
|
||||||
|
ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@localhost}
|
||||||
|
ADMIN_PASSWORD: ${ADMIN_PASSWORD:-changeme123}
|
||||||
|
PUBLIC_URL: ${PUBLIC_URL:-https://master.vonas.nl}
|
||||||
|
TMDB_API_KEY: ${TMDB_API_KEY:-}
|
||||||
|
PLAYBACK_IDLE_TIMEOUT_SECONDS: ${PLAYBACK_IDLE_TIMEOUT_SECONDS:-1200}
|
||||||
|
PLAYBACK_ABSOLUTE_LIFETIME_SECONDS: ${PLAYBACK_ABSOLUTE_LIFETIME_SECONDS:-43200}
|
||||||
|
NODE_HEARTBEAT_INTERVAL_SECONDS: ${NODE_HEARTBEAT_INTERVAL_SECONDS:-30}
|
||||||
|
NODE_OFFLINE_THRESHOLD_SECONDS: ${NODE_OFFLINE_THRESHOLD_SECONDS:-90}
|
||||||
|
HIDE_UNAVAILABLE_TITLES: ${HIDE_UNAVAILABLE_TITLES:-false}
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
redis:
|
redis:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
# Host ports (avoid Dokploy UI on :3000)
|
|
||||||
ports:
|
ports:
|
||||||
- "3100:3000"
|
- "3100:3000"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
|
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/health"]
|
||||||
interval: 30s
|
interval: 15s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 3
|
retries: 10
|
||||||
start_period: 40s
|
start_period: 90s
|
||||||
networks:
|
networks:
|
||||||
media-cluster:
|
media-cluster:
|
||||||
aliases:
|
aliases:
|
||||||
|
|
@ -78,12 +85,10 @@ services:
|
||||||
environment:
|
environment:
|
||||||
HOSTNAME: 0.0.0.0
|
HOSTNAME: 0.0.0.0
|
||||||
PORT: "3000"
|
PORT: "3000"
|
||||||
# Prefer compose DNS; fallback via published host port
|
|
||||||
MASTER_API_URL: http://master-api:3000
|
MASTER_API_URL: http://master-api:3000
|
||||||
NEXT_PUBLIC_API_URL: http://master-api:3000
|
NEXT_PUBLIC_API_URL: http://master-api:3000
|
||||||
depends_on:
|
depends_on:
|
||||||
master-api:
|
- master-api
|
||||||
condition: service_healthy
|
|
||||||
ports:
|
ports:
|
||||||
- "3101:3000"
|
- "3101:3000"
|
||||||
networks:
|
networks:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue