10 changed files with 291 additions and 86 deletions
-
15api/.env.example
-
72api/Dockerfile
-
9api/src/main.ts
-
2api/src/users/schemas/user.schema.ts
-
163docker-compose.yaml
-
2web/.dockerignore
-
2web/.env.example
-
90web/Dockerfile
-
8web/lib/auth.ts
-
14web/lib/httpServerClient.ts
@ -1,25 +1,61 @@ |
|||
FROM node:18-alpine AS base |
|||
RUN npm i -g pnpm |
|||
# Stage 1: Dependencies |
|||
FROM node:22-alpine AS deps |
|||
WORKDIR /app |
|||
|
|||
# Install pnpm |
|||
RUN corepack enable && corepack prepare pnpm@latest --activate |
|||
|
|||
# Copy package.json and pnpm-lock.yaml |
|||
COPY package.json pnpm-lock.yaml ./ |
|||
RUN pnpm i |
|||
COPY . . |
|||
|
|||
FROM base AS dev |
|||
ENV NODE_ENV=development |
|||
ENTRYPOINT ["pnpm", "start:dev"] |
|||
# Install dependencies |
|||
RUN pnpm install --frozen-lockfile |
|||
|
|||
# Stage 2: Builder |
|||
FROM node:22-alpine AS builder |
|||
WORKDIR /app |
|||
|
|||
# Install pnpm |
|||
RUN corepack enable && corepack prepare pnpm@latest --activate |
|||
|
|||
# Copy dependencies from deps stage |
|||
COPY --from=deps /app/node_modules ./node_modules |
|||
COPY . . |
|||
|
|||
FROM base AS build |
|||
ENV NODE_ENV=production |
|||
# Build the application |
|||
RUN pnpm build |
|||
|
|||
FROM node:18-alpine AS prod |
|||
ENV NODE_ENV=production |
|||
EXPOSE 3005 |
|||
# Stage 3: Production |
|||
FROM node:22-alpine AS runner |
|||
WORKDIR /app |
|||
RUN npm i -g pnpm |
|||
COPY --from=build /app/.env ./.env |
|||
COPY --from=build /app/dist ./dist |
|||
COPY --from=build /app/package.json /app/pnpm-lock.yaml ./ |
|||
RUN pnpm i --prod |
|||
ENTRYPOINT ["pnpm", "start"] |
|||
|
|||
# Install pnpm |
|||
RUN corepack enable && corepack prepare pnpm@latest --activate |
|||
|
|||
# Set NODE_ENV to production |
|||
ENV NODE_ENV production |
|||
|
|||
# Copy necessary files for production |
|||
COPY --from=builder /app/dist ./dist |
|||
COPY --from=builder /app/package.json ./ |
|||
COPY --from=builder /app/pnpm-lock.yaml ./ |
|||
|
|||
# Install only production dependencies |
|||
RUN pnpm install --prod --frozen-lockfile |
|||
|
|||
# Add a non-root user |
|||
RUN addgroup --system --gid 1001 nodejs && \ |
|||
adduser --system --uid 1001 nestjs && \ |
|||
chown -R nestjs:nodejs /app |
|||
USER nestjs |
|||
|
|||
# Expose the port specified by the PORT environment variable (default: 3001) |
|||
ENV PORT 300 |
|||
EXPOSE ${PORT} |
|||
|
|||
# Health check to verify app is running |
|||
# HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ |
|||
# CMD wget -q -O - http://localhost:${PORT}/api/v1/health || exit 1 |
|||
|
|||
# Command to run the application |
|||
CMD ["node", "dist/main"] |
|||
@ -1,56 +1,141 @@ |
|||
|
|||
services: |
|||
web: |
|||
container_name: web |
|||
build: |
|||
context: ./web |
|||
dockerfile: Dockerfile |
|||
# MongoDB service |
|||
textbee-db: |
|||
container_name: textbee-db |
|||
image: mongo:latest |
|||
restart: always |
|||
environment: |
|||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_ROOT_USER:-adminUser} |
|||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_ROOT_PASS:-adminPassword} |
|||
- MONGO_INITDB_DATABASE=textbee |
|||
# - MONGO_DB_USERNAME=${MONGO_USER:-textbeeUser} |
|||
# - MONGO_DB_PASSWORD=${MONGO_PASS:-textbeePassword} |
|||
volumes: |
|||
# - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro |
|||
- mongodb_data:/data/db |
|||
ports: |
|||
- "3000:3000" |
|||
depends_on: |
|||
- mongo |
|||
# only allow access from the same machine, and use port 27018 to avoid conflict with default mongo port 27017 |
|||
# - "127.0.0.1:${MONGO_PORT:-27018}:27017" |
|||
- "${MONGO_PORT:-27018}:27017" |
|||
networks: |
|||
- textbee-network |
|||
healthcheck: |
|||
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
retries: 3 |
|||
start_period: 20s |
|||
|
|||
# MongoDB Express (optional admin UI) |
|||
mongo-express: |
|||
container_name: textbee-mongo-express |
|||
image: mongo-express:latest |
|||
restart: always |
|||
ports: |
|||
- "${MONGO_EXPRESS_PORT:-8081}:8081" |
|||
environment: |
|||
NODE_ENV: production |
|||
- ME_CONFIG_MONGODB_ADMINUSERNAME=${MONGO_ROOT_USER:-adminUser} |
|||
- ME_CONFIG_MONGODB_ADMINPASSWORD=${MONGO_ROOT_PASS:-adminPassword} |
|||
- ME_CONFIG_MONGODB_SERVER=textbee-db |
|||
depends_on: |
|||
textbee-db: |
|||
condition: service_healthy |
|||
networks: |
|||
- textbee-network |
|||
|
|||
api: |
|||
container_name: api |
|||
# NestJS API |
|||
textbee-api: |
|||
container_name: textbee-api |
|||
build: |
|||
context: ./api |
|||
dockerfile: Dockerfile |
|||
target: prod |
|||
restart: always |
|||
ports: |
|||
- "3005:3005" |
|||
depends_on: |
|||
- mongo |
|||
- "${PORT:-3001}:3001" |
|||
env_file: |
|||
- ./api/.env |
|||
environment: |
|||
NODE_ENV: production |
|||
- PORT=${PORT:-3001} |
|||
# - MONGO_URI=${MONGO_URI:-mongodb://${MONGO_USER:-textbeeUser}:${MONGO_PASS:-textbeePassword}@textbee-db:27018/TextBee} |
|||
# - MONGO_URI=mongodb://adminUser:adminPassword@textbee-db:27018/textbee |
|||
# - FRONTEND_URL=${NEXT_PUBLIC_SITE_URL:-http://localhost:3000} |
|||
# - JWT_SECRET=${JWT_SECRET:-your_jwt_secret_here} |
|||
# - JWT_EXPIRATION=${JWT_EXPIRATION:-60d} |
|||
# - MAIL_HOST=${MAIL_HOST} |
|||
# - MAIL_PORT=${MAIL_PORT} |
|||
# - MAIL_USER=${MAIL_USER} |
|||
# - MAIL_PASS=${MAIL_PASS} |
|||
# - MAIL_FROM=${MAIL_FROM} |
|||
# - USE_SMS_QUEUE=${USE_SMS_QUEUE:-false} |
|||
# - REDIS_HOST=${REDIS_HOST:-redis} |
|||
# - REDIS_PORT=${REDIS_PORT:-6379} |
|||
# - FIREBASE_PROJECT_ID=${FIREBASE_PROJECT_ID} |
|||
# - FIREBASE_PRIVATE_KEY_ID=${FIREBASE_PRIVATE_KEY_ID} |
|||
# - FIREBASE_PRIVATE_KEY=${FIREBASE_PRIVATE_KEY} |
|||
# - FIREBASE_CLIENT_EMAIL=${FIREBASE_CLIENT_EMAIL} |
|||
# - FIREBASE_CLIENT_ID=${FIREBASE_CLIENT_ID} |
|||
# - FIREBASE_CLIENT_C509_CERT_URL=${FIREBASE_CLIENT_C509_CERT_URL} |
|||
depends_on: |
|||
textbee-db: |
|||
condition: service_healthy |
|||
networks: |
|||
- textbee-network |
|||
|
|||
mongo: |
|||
container_name: mongo |
|||
image: mongo |
|||
# Next.js Web |
|||
textbee-web: |
|||
container_name: textbee-web |
|||
build: |
|||
context: ./web |
|||
dockerfile: Dockerfile |
|||
restart: always |
|||
ports: |
|||
- "27017:27017" |
|||
- "${PORT:-3000}:3000" |
|||
env_file: |
|||
- ./web/.env |
|||
environment: |
|||
MONGO_INITDB_ROOT_USERNAME: adminUser |
|||
MONGO_INITDB_ROOT_PASSWORD: adminPassword |
|||
MONGO_INITDB_DATABASE: TextBee |
|||
volumes: |
|||
- textbee-db-data:/data/db |
|||
# THe following scripts creates TextBee DB automatically, also the user which web and api are connecting with. |
|||
- ./mongo-init:/docker-entrypoint-initdb.d:ro |
|||
mongo-express: |
|||
container_name: mongo-ee |
|||
image: mongo-express |
|||
- PORT=${PORT:-3000} |
|||
# - NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL:-http://localhost:3000} |
|||
- NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL:-http://localhost:3001/api/v1} |
|||
# - AUTH_SECRET=${AUTH_SECRET:-generate_a_secure_random_string_here} |
|||
# - DATABASE_URL=mongodb://adminUser:adminPassword@textbee-db:27018/textbee |
|||
# - DATABASE_URL=mongodb://adminUser:adminPassword@textbee-db:27018/textbee |
|||
# - MAIL_HOST=${MAIL_HOST} |
|||
# - MAIL_PORT=${MAIL_PORT} |
|||
# - MAIL_USER=${MAIL_USER} |
|||
# - MAIL_PASS=${MAIL_PASS} |
|||
# - MAIL_FROM=${MAIL_FROM} |
|||
# - ADMIN_EMAIL=${ADMIN_EMAIL} |
|||
# - NEXT_PUBLIC_GOOGLE_CLIENT_ID=${NEXT_PUBLIC_GOOGLE_CLIENT_ID} |
|||
# - NEXT_PUBLIC_TAWKTO_EMBED_URL=${NEXT_PUBLIC_TAWKTO_EMBED_URL} |
|||
depends_on: |
|||
- textbee-api |
|||
networks: |
|||
- textbee-network |
|||
|
|||
# Redis (if SMS queue is needed) |
|||
redis: |
|||
container_name: textbee-redis |
|||
image: redis:alpine |
|||
restart: always |
|||
ports: |
|||
- "8081:8081" |
|||
environment: |
|||
ME_CONFIG_MONGODB_ADMINUSERNAME: adminUser |
|||
ME_CONFIG_MONGODB_ADMINPASSWORD: adminPassword |
|||
ME_CONFIG_MONGODB_URL: mongodb://adminUser:adminPassword@mongo:27017/ |
|||
ME_CONFIG_BASICAUTH: "false" |
|||
depends_on: |
|||
- mongo |
|||
- "${REDIS_PORT:-6379}:6379" |
|||
volumes: |
|||
- redis_data:/data |
|||
networks: |
|||
- textbee-network |
|||
command: redis-server --appendonly yes |
|||
healthcheck: |
|||
test: ["CMD", "redis-cli", "ping"] |
|||
interval: 30s |
|||
timeout: 10s |
|||
retries: 3 |
|||
start_period: 20s |
|||
|
|||
networks: |
|||
textbee-network: |
|||
driver: bridge |
|||
|
|||
volumes: |
|||
textbee-db-data: |
|||
mongodb_data: |
|||
redis_data: |
|||
@ -0,0 +1,2 @@ |
|||
node_modules |
|||
.git |
|||
@ -1,20 +1,82 @@ |
|||
FROM node:18-alpine AS base |
|||
|
|||
# Stage 1: Install web dependencies |
|||
FROM base AS web-deps |
|||
# Stage 1: Dependencies |
|||
FROM node:22-alpine AS deps |
|||
WORKDIR /app |
|||
COPY .env ./.env |
|||
|
|||
# Install pnpm and required OpenSSL dependencies |
|||
RUN apk add --no-cache openssl openssl-dev |
|||
RUN corepack enable && corepack prepare pnpm@latest --activate |
|||
|
|||
# Copy package.json and pnpm-lock.yaml |
|||
COPY package.json pnpm-lock.yaml ./ |
|||
RUN corepack enable pnpm && pnpm install --frozen-lockfile |
|||
|
|||
# Stage 2: Build the web application |
|||
FROM base AS web-builder |
|||
ENV NODE_ENV=production |
|||
EXPOSE 3000 |
|||
# Install dependencies |
|||
RUN pnpm install --frozen-lockfile |
|||
|
|||
# Stage 2: Builder |
|||
FROM node:22-alpine AS builder |
|||
WORKDIR /app |
|||
|
|||
# Install pnpm and required OpenSSL dependencies |
|||
RUN apk add --no-cache openssl openssl-dev |
|||
RUN corepack enable && corepack prepare pnpm@latest --activate |
|||
|
|||
# Copy dependencies from deps stage |
|||
COPY --from=deps /app/node_modules ./node_modules |
|||
|
|||
# Copy all files |
|||
COPY . . |
|||
COPY --from=web-deps /app/node_modules ./node_modules |
|||
COPY --from=web-deps /app/.env .env |
|||
RUN corepack enable pnpm && pnpm run vercel-build |
|||
|
|||
CMD ["pnpm", "start"] |
|||
# Set environment variables for building |
|||
ENV NEXT_TELEMETRY_DISABLED 1 |
|||
|
|||
# Generate prisma client - make sure it exists |
|||
RUN pnpm prisma generate |
|||
|
|||
# Build the application |
|||
RUN pnpm build |
|||
|
|||
# Stage 3: Production runner |
|||
FROM node:22-alpine AS runner |
|||
WORKDIR /app |
|||
|
|||
# Install pnpm and required OpenSSL dependencies |
|||
RUN apk add --no-cache openssl openssl-dev |
|||
RUN corepack enable && corepack prepare pnpm@latest --activate |
|||
|
|||
# Set environment variables |
|||
ENV NODE_ENV production |
|||
ENV NEXT_TELEMETRY_DISABLED 1 |
|||
ENV CONTAINER_RUNTIME docker |
|||
|
|||
# Add a non-root user to run the app |
|||
RUN addgroup --system --gid 1001 nodejs && \ |
|||
adduser --system --uid 1001 nextjs && \ |
|||
chown -R nextjs:nodejs /app |
|||
|
|||
# Copy necessary files for the standalone app |
|||
COPY --from=builder --chown=nextjs:nodejs /app/next.config.js ./ |
|||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public |
|||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ |
|||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static |
|||
|
|||
# Copy Prisma schema and generate client during runtime |
|||
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma |
|||
COPY --from=builder --chown=nextjs:nodejs /app/package.json ./ |
|||
COPY --from=builder --chown=nextjs:nodejs /app/pnpm-lock.yaml ./ |
|||
|
|||
# Install only production dependencies, including Prisma, and generate Prisma client |
|||
RUN pnpm install --prod --frozen-lockfile && \ |
|||
pnpm prisma generate |
|||
|
|||
# Switch to non-root user |
|||
USER nextjs |
|||
|
|||
# Expose the port the app will run on |
|||
ENV PORT 3000 |
|||
EXPOSE ${PORT} |
|||
|
|||
# Health check to verify app is running |
|||
# HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ |
|||
# CMD wget -q -O - http://localhost:${PORT}/api/health || exit 1 |
|||
|
|||
CMD ["node", "server.js"] |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue