diff --git a/Dockerfile b/Dockerfile index 4a8081a..5ee836c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,64 +1,54 @@ # ═══════════════════════════════════════════════ # Harat's Booking — Production Dockerfile # ═══════════════════════════════════════════════ -# Client is pre-built by deploy.ps1 (npm run build) -# This Dockerfile only handles server + copies dist -# ═══════════════════════════════════════════════ -# ── Stage 1: Server Dependencies ── -FROM node:20-alpine AS server-deps +# ── Stage 1: Install production dependencies ── +FROM node:20-alpine AS deps WORKDIR /app -COPY server/package.json server/package-lock.json* ./ -RUN npm install --omit=dev +COPY package.json package-lock.json ./ +COPY server/package.json ./server/package.json +COPY packages/shared/package.json ./packages/shared/package.json +COPY packages/shared/ ./packages/shared/ -# ── Stage 2: Prisma Generate ── -FROM node:20-alpine AS prisma-gen -WORKDIR /app +RUN npm install --omit=dev --workspace=server --workspace=packages/shared -COPY --from=server-deps /app/node_modules ./node_modules -COPY server/prisma ./prisma -COPY server/package.json ./ +COPY server/prisma ./server/prisma +RUN cd server && npx -y prisma generate -RUN npx prisma generate - -# ── Stage 3: Final Image ── +# ── Stage 2: Final Image ── FROM node:20-alpine WORKDIR /app -# Runtime deps + pm2 +# Create user + dirs FIRST +RUN addgroup -S harats && adduser -S harats -G harats \ + && mkdir -p /data/logs \ + && chown -R harats:harats /data /app + +# Runtime deps + pm2 (install as root, then fix ownership) RUN apk add --no-cache curl \ && npm install -g pm2@latest \ && pm2 install pm2-logrotate \ && pm2 set pm2-logrotate:max_size 10M \ && pm2 set pm2-logrotate:retain 7 \ - && pm2 set pm2-logrotate:compress true + && pm2 set pm2-logrotate:compress true \ + && pm2 kill -# Don't run as root -RUN addgroup -S harats && adduser -S harats -G harats \ - && mkdir -p /data/logs \ - && chown -R harats:harats /data +# Copy node_modules +COPY --chown=harats:harats --from=deps /app/node_modules ./node_modules -# Copy production node_modules (with generated Prisma client) -COPY --chown=harats:harats --from=prisma-gen /app/node_modules ./node_modules - -# Copy server compiled source +# Copy server compiled source (tsc outputs to dist/src/) COPY --chown=harats:harats server/dist ./dist COPY --chown=harats:harats server/prisma ./prisma -# Copy shared package (workspace dependency) -COPY --chown=harats:harats packages/shared/ ./node_modules/@harats/shared/ - # Copy pre-built client dist COPY --chown=harats:harats client/dist ./public -# Copy ecosystem config +# Copy ecosystem config + version COPY --chown=harats:harats ecosystem.config.cjs ./ecosystem.config.cjs - -# Copy version.json COPY --chown=harats:harats version.json ./version.json -# Version info (injected at build time) +# Version info ARG BUILD_VERSION=0.0.0 ARG BUILD_DATE=unknown RUN echo "{\"version\":\"${BUILD_VERSION}\",\"build\":\"$(echo $BUILD_DATE | cut -c1-16)\",\"buildDate\":\"${BUILD_DATE}\"}" > version.json diff --git a/deploy/deploy-remote.sh b/deploy/deploy-remote.sh index 24ed6ae..8edea60 100644 --- a/deploy/deploy-remote.sh +++ b/deploy/deploy-remote.sh @@ -45,7 +45,7 @@ export BUILD_VERSION="$DOCKER_TAG" export BUILD_DATE="$(date -Iseconds)" echo " → harats-server:$DOCKER_TAG" -docker compose -f "$DEPLOY_DIR/$COMPOSE_FILE" build \ +docker compose -f "$DEPLOY_DIR/$COMPOSE_FILE" build --no-cache \ --build-arg BUILD_VERSION="$TAG" \ --build-arg BUILD_DATE="$BUILD_DATE" \ server diff --git a/deploy/deploy.ps1 b/deploy/deploy.ps1 index 9a382da..ee2e1ee 100644 --- a/deploy/deploy.ps1 +++ b/deploy/deploy.ps1 @@ -177,9 +177,10 @@ try { server/dist/ ` server/prisma/ ` server/package.json ` - server/package-lock.json ` client/dist/ ` packages/shared/ ` + package.json ` + package-lock.json ` Dockerfile ` .dockerignore ` docker-compose.prod.yml ` diff --git a/ecosystem.config.cjs b/ecosystem.config.cjs index cdee28b..2288451 100644 --- a/ecosystem.config.cjs +++ b/ecosystem.config.cjs @@ -10,7 +10,7 @@ module.exports = { { name: 'harats-server', cwd: __dirname, - script: 'dist/index.js', + script: 'dist/src/index.js', instances: 1, exec_mode: 'fork', @@ -22,9 +22,9 @@ module.exports = { restart_delay: 2000, max_memory_restart: '512M', - // Logs - error_file: '/data/logs/pm2-error.log', - out_file: '/data/logs/pm2-out.log', + // Logs (stdout/stderr for Docker) + error_file: '/dev/stderr', + out_file: '/dev/stdout', merge_logs: true, time: true, log_date_format: 'YYYY-MM-DD HH:mm:ss Z', diff --git a/server/src/utils/jwt.ts b/server/src/utils/jwt.ts index 6fad770..aecd118 100644 --- a/server/src/utils/jwt.ts +++ b/server/src/utils/jwt.ts @@ -7,11 +7,11 @@ interface TokenPayload { } export function generateAccessToken(payload: TokenPayload): string { - return jwt.sign(payload, env.JWT_SECRET, { expiresIn: env.JWT_EXPIRES_IN }); + return jwt.sign(payload, env.JWT_SECRET, { expiresIn: env.JWT_EXPIRES_IN as jwt.SignOptions['expiresIn'] }); } export function generateRefreshToken(payload: TokenPayload): string { - return jwt.sign(payload, env.JWT_REFRESH_SECRET, { expiresIn: env.JWT_REFRESH_EXPIRES_IN }); + return jwt.sign(payload, env.JWT_REFRESH_SECRET, { expiresIn: env.JWT_REFRESH_EXPIRES_IN as jwt.SignOptions['expiresIn'] }); } export function verifyAccessToken(token: string): TokenPayload { diff --git a/version.json b/version.json index 83ebfd6..1e7ecdb 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "1.0.0", - "build": "", - "buildDate": "" -} + "version": "1.0.0", + "build": "20260608-1539", + "buildDate": "2026-06-08T15:39:12.1722623+07:00" +} \ No newline at end of file