From 451a337e0f231dbaeb2d57aa330f19fb12430227 Mon Sep 17 00:00:00 2001 From: Aditya Tripathi Date: Sun, 4 May 2025 18:33:18 +0000 Subject: [PATCH 1/4] feat: improve docker image, compose file, add migrations image, etc. --- .dockerignore | 48 ++++++++++++++ Dockerfile | 34 ---------- apps/mail/next.config.ts | 1 + docker-compose.yaml | 78 ++++++++++++++++++---- docker/app/Dockerfile | 138 +++++++++++++++++++++++++++++++++++++++ docker/db/Dockerfile | 34 ++++++++++ 6 files changed, 286 insertions(+), 47 deletions(-) create mode 100644 .dockerignore delete mode 100644 Dockerfile create mode 100644 docker/app/Dockerfile create mode 100644 docker/db/Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..27323f50a2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,48 @@ +# dependencies +/node_modules +/apps/*/node_modules +/packages/*/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* +bun-debug.log* +# env files (can opt-in for committing if needed) +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# ide +.idea +.vscode +.turbo +i18n.cache +apps/mail/scripts.ts diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 65e434bdf2..0000000000 --- a/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM oven/bun:canary - -WORKDIR /app - -# Install turbo globally -RUN bun install -g next turbo - - -COPY package.json bun.lock turbo.json ./ - -RUN mkdir -p apps packages - -COPY apps/*/package.json ./apps/ -COPY packages/*/package.json ./packages/ -COPY packages/tsconfig/ ./packages/tsconfig/ - -RUN bun install - -COPY . . - -# Installing with full context. Prevent missing dependencies error. -RUN bun install - - -RUN bun run build - -ENV NODE_ENV=production - -# Resolve Nextjs TextEncoder error. -ENV NODE_OPTIONS=--no-experimental-fetch - -EXPOSE 3000 - -CMD ["bun", "run", "start", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/apps/mail/next.config.ts b/apps/mail/next.config.ts index 4c1a2ed1b9..b24b5c485f 100644 --- a/apps/mail/next.config.ts +++ b/apps/mail/next.config.ts @@ -3,6 +3,7 @@ import type { NextConfig } from 'next'; const nextConfig: NextConfig = { // devIndicators: false, + output: process.env.DOCKER_BUILD ? 'standalone' : undefined, compiler: { removeConsole: process.env.NODE_ENV === 'production' diff --git a/docker-compose.yaml b/docker-compose.yaml index 8e82c35c6d..df16c04bb7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,38 +1,90 @@ services: + zero: + build: + context: . + dockerfile: docker/app/Dockerfile + args: + NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-http://localhost:3000} + DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-zerodotemail} + BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:-my-better-auth-secret} + BETTER_AUTH_URL: ${BETTER_AUTH_URL:-http://localhost:3000} + BETTER_AUTH_TRUSTED_ORIGINS: ${BETTER_AUTH_TRUSTED_ORIGINS:-http://localhost:3000} + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} + REDIS_URL: http://upstash-proxy:80 + REDIS_TOKEN: ${REDIS_TOKEN:-upstash-local-token} + RESEND_API_KEY: ${RESEND_API_KEY} + OPENAI_API_KEY: ${OPENAI_API_KEY} + AI_SYSTEM_PROMPT: ${AI_SYSTEM_PROMPT} + GROQ_API_KEY: ${GROQ_API_KEY} + GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY} + NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0} + depends_on: + - db + - migrations + - valkey + - upstash-proxy + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:3000/healthcheck'] + interval: 90s + timeout: 5s + retries: 3 + start_period: 10s + ports: + - 3000:3000 + + migrations: + build: + context: . + dockerfile: docker/db/Dockerfile + args: + DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-zerodotemail} + depends_on: + - db + command: ['bun', 'run', 'db:migrate'] + restart: 'no' + db: - container_name: zerodotemail-db - image: postgres:17 + image: postgres:17-alpine restart: unless-stopped environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: zerodotemail + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + POSTGRES_DB: ${POSTGRES_DB:-zerodotemail} PGDATA: /var/lib/postgresql/data/pgdata - ports: - - 5432:5432 volumes: - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'zerodotemail'] + interval: 10s + timeout: 5s + retries: 5 valkey: - container_name: zerodotemail-redis image: docker.io/bitnami/valkey:8.0 environment: - ALLOW_EMPTY_PASSWORD=yes - VALKEY_DISABLE_COMMANDS=FLUSHDB,FLUSHALL - ports: - - 6379:6379 volumes: - valkey-data:/bitnami/valkey/data + healthcheck: + test: ['CMD', 'redis-cli', '-h', 'localhost', '-p', '6379', 'ping'] + interval: 10s + timeout: 5s + retries: 5 upstash-proxy: container_name: zerodotemail-upstash-proxy image: hiett/serverless-redis-http:latest environment: SRH_MODE: env - SRH_TOKEN: ${REDIS_TOKEN} + SRH_TOKEN: ${REDIS_TOKEN:-upstash-local-token} SRH_CONNECTION_STRING: 'redis://valkey:6379' - ports: - - 8079:80 + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:80'] + interval: 10s + timeout: 5s + retries: 5 volumes: valkey-data: diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile new file mode 100644 index 0000000000..df70d867e5 --- /dev/null +++ b/docker/app/Dockerfile @@ -0,0 +1,138 @@ +# ======================================== +# Base Stage: Alpine Linux with Bun +# ======================================== +FROM oven/bun:alpine AS base + +# ======================================== +# Dependencies Stage: Install Dependencies +# ======================================== +FROM base AS deps +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install turbo globally +RUN bun install -g turbo + +COPY package.json bun.lock turbo.json ./ +RUN mkdir -p apps packages +COPY apps/*/package.json ./apps/ +COPY packages/*/package.json ./packages/ +COPY packages/tsconfig/ ./packages/tsconfig/ + +RUN bun install + +# ======================================== +# Builder Stage: Build the Application +# ======================================== +FROM base AS builder +WORKDIR /app + +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Installing with full context to prevent missing dependencies error +RUN bun install + +# Define build arguments +ARG NEXT_PUBLIC_APP_URL \ + DATABASE_URL \ + BETTER_AUTH_SECRET \ + BETTER_AUTH_URL \ + BETTER_AUTH_TRUSTED_ORIGINS \ + GOOGLE_CLIENT_ID \ + GOOGLE_CLIENT_SECRET \ + REDIS_URL \ + REDIS_TOKEN \ + RESEND_API_KEY \ + OPENAI_API_KEY \ + AI_SYSTEM_PROMPT \ + GROQ_API_KEY \ + GOOGLE_GENERATIVE_AI_API_KEY \ + NEXT_TELEMETRY_DISABLED + +# Set environment variables for build +ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} \ + DATABASE_URL=${DATABASE_URL} \ + BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} \ + BETTER_AUTH_URL=${BETTER_AUTH_URL} \ + BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS} \ + GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} \ + GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} \ + REDIS_URL=${REDIS_URL} \ + REDIS_TOKEN=${REDIS_TOKEN} \ + RESEND_API_KEY=${RESEND_API_KEY} \ + OPENAI_API_KEY=${OPENAI_API_KEY} \ + AI_SYSTEM_PROMPT=${AI_SYSTEM_PROMPT} \ + GROQ_API_KEY=${GROQ_API_KEY} \ + GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY} \ + NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED} \ + DOCKER_BUILD=true + +# Required for standalone nextjs build +WORKDIR /app/apps/mail +RUN bun install sharp + +WORKDIR /app +RUN bun run build + +# ======================================== +# Runner Stage: Production Environment +# ======================================== +FROM base AS runner +WORKDIR /app + +# Define build arguments +ARG NEXT_PUBLIC_APP_URL \ + DATABASE_URL \ + BETTER_AUTH_SECRET \ + BETTER_AUTH_URL \ + BETTER_AUTH_TRUSTED_ORIGINS \ + GOOGLE_CLIENT_ID \ + GOOGLE_CLIENT_SECRET \ + REDIS_URL \ + REDIS_TOKEN \ + RESEND_API_KEY \ + OPENAI_API_KEY \ + AI_SYSTEM_PROMPT \ + GROQ_API_KEY \ + GOOGLE_GENERATIVE_AI_API_KEY \ + NEXT_TELEMETRY_DISABLED + +# Set environment variables for build +ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} \ + DATABASE_URL=${DATABASE_URL} \ + BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} \ + BETTER_AUTH_URL=${BETTER_AUTH_URL} \ + BETTER_AUTH_TRUSTED_ORIGINS=${BETTER_AUTH_TRUSTED_ORIGINS} \ + GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} \ + GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} \ + REDIS_URL=${REDIS_URL} \ + REDIS_TOKEN=${REDIS_TOKEN} \ + RESEND_API_KEY=${RESEND_API_KEY} \ + OPENAI_API_KEY=${OPENAI_API_KEY} \ + AI_SYSTEM_PROMPT=${AI_SYSTEM_PROMPT} \ + GROQ_API_KEY=${GROQ_API_KEY} \ + GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY} \ + NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED} \ + NODE_ENV=production + +RUN addgroup -S -g 1001 bunjs && \ + adduser -S -u 1001 nextjs -G bunjs + +# Copy public assets +COPY --from=builder --chown=nextjs:bunjs /app/apps/mail/public ./apps/mail/public + +# Leverage output traces to reduce image size (standalone output) +COPY --from=builder --chown=nextjs:bunjs /app/apps/mail/.next/standalone ./ +COPY --from=builder --chown=nextjs:bunjs /app/apps/mail/.next/static ./apps/mail/.next/static + +# Switch to non-root user +USER nextjs + +# Set server port and host +EXPOSE 3000 +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +# Start the server +CMD ["bun", "apps/mail/server.js"] \ No newline at end of file diff --git a/docker/db/Dockerfile b/docker/db/Dockerfile new file mode 100644 index 0000000000..c1a5863c0f --- /dev/null +++ b/docker/db/Dockerfile @@ -0,0 +1,34 @@ +# ======================================== +# Dependencies Stage: Install Dependencies +# ======================================== +FROM oven/bun:alpine AS deps +WORKDIR /app + +# Copy only package files needed for migrations +COPY package.json bun.lock turbo.json ./ +COPY packages/db/package.json ./packages/db/ +COPY packages/tsconfig/base.json ./packages/tsconfig/base.json +COPY packages/tsconfig/package.json ./packages/tsconfig/ + +# Install minimal dependencies in one layer +RUN bun install && \ + bun install drizzle-kit drizzle-orm postgres typescript + +# ======================================== +# Runner Stage: Production Environment +# ======================================== +FROM oven/bun:alpine AS runner +WORKDIR /app + +# Copy only the necessary files from deps +COPY --from=deps /app/node_modules ./node_modules +COPY packages/db/drizzle.config.ts ./packages/db/drizzle.config.ts +COPY packages/db/src ./packages/db/src +COPY packages/db/migrations ./packages/db/migrations +COPY packages/db/package.json ./packages/db/package.json + +# Define build arguments and environment variables +ARG DATABASE_URL +ENV DATABASE_URL=${DATABASE_URL} + +WORKDIR /app/packages/db \ No newline at end of file From 606e7b2b580a04675e59828de8240dba525e33d7 Mon Sep 17 00:00:00 2001 From: Aditya Tripathi Date: Mon, 5 May 2025 22:36:11 +0000 Subject: [PATCH 2/4] chore(ci): fix redis proxy healthcheck + add db compose for development --- README.md | 6 +-- docker-compose.db.yaml | 48 +++++++++++++++++++ ...r-compose.yaml => docker-compose.prod.yaml | 8 ++-- docker/app/Dockerfile | 35 +++++++------- docker/db/Dockerfile | 8 +--- entrypoint.sh | 8 ---- package.json | 8 ++-- 7 files changed, 77 insertions(+), 44 deletions(-) create mode 100644 docker-compose.db.yaml rename docker-compose.yaml => docker-compose.prod.yaml (89%) delete mode 100644 entrypoint.sh diff --git a/README.md b/README.md index 036666a312..270b34b6e8 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ You can set up Zero in two ways: bun install # Start database locally - bun docker:up + bun docker:db:up ``` 2. **Set Up Environment** @@ -80,7 +80,7 @@ You can set up Zero in two ways: cp .env.example .env ``` - Configure your environment variables (see below) - - Start the database with the provided docker compose setup: `bun docker:up` + - Start the database with the provided docker compose setup: `bun docker:db:up` - Initialize the database: `bun db:push` 3. **Start the App** @@ -198,7 +198,7 @@ Zero uses PostgreSQL for storing data. Here's how to set it up: Run this command to start a local PostgreSQL instance: ```bash - bun docker:up + bun docker:db:up ``` This creates a database with: diff --git a/docker-compose.db.yaml b/docker-compose.db.yaml new file mode 100644 index 0000000000..40cc5e8504 --- /dev/null +++ b/docker-compose.db.yaml @@ -0,0 +1,48 @@ +services: + db: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + POSTGRES_DB: ${POSTGRES_DB:-zerodotemail} + PGDATA: /var/lib/postgresql/data/pgdata + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'zerodotemail'] + interval: 10s + timeout: 5s + retries: 5 + + valkey: + image: docker.io/bitnami/valkey:8.0 + environment: + - ALLOW_EMPTY_PASSWORD=yes + - VALKEY_DISABLE_COMMANDS=FLUSHDB,FLUSHALL + volumes: + - valkey-data:/bitnami/valkey/data + healthcheck: + test: ['CMD', 'redis-cli', '-h', 'localhost', '-p', '6379', 'ping'] + interval: 10s + timeout: 5s + retries: 5 + + upstash-proxy: + container_name: zerodotemail-upstash-proxy + image: hiett/serverless-redis-http:latest + environment: + SRH_MODE: env + SRH_TOKEN: ${REDIS_TOKEN:-upstash-local-token} + SRH_CONNECTION_STRING: 'redis://valkey:6379' + healthcheck: + test: timeout 10s bash -c ':> /dev/tcp/127.0.0.1/80' || exit 1 + interval: 10s + timeout: 5s + retries: 5 + ports: + - 8079:80 + +volumes: + valkey-data: + postgres-data: diff --git a/docker-compose.yaml b/docker-compose.prod.yaml similarity index 89% rename from docker-compose.yaml rename to docker-compose.prod.yaml index df16c04bb7..d615e7874b 100644 --- a/docker-compose.yaml +++ b/docker-compose.prod.yaml @@ -18,7 +18,6 @@ services: AI_SYSTEM_PROMPT: ${AI_SYSTEM_PROMPT} GROQ_API_KEY: ${GROQ_API_KEY} GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY} - NEXT_TELEMETRY_DISABLED: ${NEXT_TELEMETRY_DISABLED:-0} depends_on: - db - migrations @@ -37,8 +36,8 @@ services: build: context: . dockerfile: docker/db/Dockerfile - args: - DATABASE_URL: postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-zerodotemail} + environment: + - DATABASE_URL=postgresql://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-zerodotemail} depends_on: - db command: ['bun', 'run', 'db:migrate'] @@ -74,14 +73,13 @@ services: retries: 5 upstash-proxy: - container_name: zerodotemail-upstash-proxy image: hiett/serverless-redis-http:latest environment: SRH_MODE: env SRH_TOKEN: ${REDIS_TOKEN:-upstash-local-token} SRH_CONNECTION_STRING: 'redis://valkey:6379' healthcheck: - test: ['CMD', 'curl', '-f', 'http://localhost:80'] + test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:80'] interval: 10s timeout: 5s retries: 5 diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile index df70d867e5..ca9a06c6ea 100644 --- a/docker/app/Dockerfile +++ b/docker/app/Dockerfile @@ -19,7 +19,7 @@ COPY apps/*/package.json ./apps/ COPY packages/*/package.json ./packages/ COPY packages/tsconfig/ ./packages/tsconfig/ -RUN bun install +RUN bun install --omit dev --ignore-scripts # ======================================== # Builder Stage: Build the Application @@ -31,7 +31,11 @@ COPY --from=deps /app/node_modules ./node_modules COPY . . # Installing with full context to prevent missing dependencies error -RUN bun install +RUN bun install --omit dev --ignore-scripts + +# Required for standalone nextjs build +WORKDIR /app/apps/mail +RUN bun install sharp # Define build arguments ARG NEXT_PUBLIC_APP_URL \ @@ -47,8 +51,7 @@ ARG NEXT_PUBLIC_APP_URL \ OPENAI_API_KEY \ AI_SYSTEM_PROMPT \ GROQ_API_KEY \ - GOOGLE_GENERATIVE_AI_API_KEY \ - NEXT_TELEMETRY_DISABLED + GOOGLE_GENERATIVE_AI_API_KEY # Set environment variables for build ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} \ @@ -65,13 +68,10 @@ ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} \ AI_SYSTEM_PROMPT=${AI_SYSTEM_PROMPT} \ GROQ_API_KEY=${GROQ_API_KEY} \ GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY} \ - NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED} \ + NEXT_TELEMETRY_DISABLED=1 \ + NODE_ENV=production \ DOCKER_BUILD=true -# Required for standalone nextjs build -WORKDIR /app/apps/mail -RUN bun install sharp - WORKDIR /app RUN bun run build @@ -81,6 +81,9 @@ RUN bun run build FROM base AS runner WORKDIR /app +RUN addgroup -S -g 1001 bunjs && \ + adduser -S -u 1001 nextjs -G bunjs + # Define build arguments ARG NEXT_PUBLIC_APP_URL \ DATABASE_URL \ @@ -95,8 +98,7 @@ ARG NEXT_PUBLIC_APP_URL \ OPENAI_API_KEY \ AI_SYSTEM_PROMPT \ GROQ_API_KEY \ - GOOGLE_GENERATIVE_AI_API_KEY \ - NEXT_TELEMETRY_DISABLED + GOOGLE_GENERATIVE_AI_API_KEY # Set environment variables for build ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} \ @@ -113,11 +115,10 @@ ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} \ AI_SYSTEM_PROMPT=${AI_SYSTEM_PROMPT} \ GROQ_API_KEY=${GROQ_API_KEY} \ GOOGLE_GENERATIVE_AI_API_KEY=${GOOGLE_GENERATIVE_AI_API_KEY} \ - NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED} \ - NODE_ENV=production - -RUN addgroup -S -g 1001 bunjs && \ - adduser -S -u 1001 nextjs -G bunjs + NODE_ENV=production \ + PORT=3000 \ + HOSTNAME="0.0.0.0" \ + NEXT_TELEMETRY_DISABLED=1 # Copy public assets COPY --from=builder --chown=nextjs:bunjs /app/apps/mail/public ./apps/mail/public @@ -131,8 +132,6 @@ USER nextjs # Set server port and host EXPOSE 3000 -ENV PORT=3000 -ENV HOSTNAME="0.0.0.0" # Start the server CMD ["bun", "apps/mail/server.js"] \ No newline at end of file diff --git a/docker/db/Dockerfile b/docker/db/Dockerfile index c1a5863c0f..3d3829f10f 100644 --- a/docker/db/Dockerfile +++ b/docker/db/Dockerfile @@ -11,8 +11,8 @@ COPY packages/tsconfig/base.json ./packages/tsconfig/base.json COPY packages/tsconfig/package.json ./packages/tsconfig/ # Install minimal dependencies in one layer -RUN bun install && \ - bun install drizzle-kit drizzle-orm postgres typescript +RUN bun install --omit dev --ignore-scripts && \ + bun install --omit dev --ignore-scripts drizzle-kit drizzle-orm postgres # ======================================== # Runner Stage: Production Environment @@ -27,8 +27,4 @@ COPY packages/db/src ./packages/db/src COPY packages/db/migrations ./packages/db/migrations COPY packages/db/package.json ./packages/db/package.json -# Define build arguments and environment variables -ARG DATABASE_URL -ENV DATABASE_URL=${DATABASE_URL} - WORKDIR /app/packages/db \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100644 index 6c81ee1114..0000000000 --- a/entrypoint.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -set -e - -echo "Running database migrations..." -bun run db:migrate - -echo "Starting the app..." -bun run start --host "0.0.0.0" \ No newline at end of file diff --git a/package.json b/package.json index 6756d729bf..bf349157a0 100644 --- a/package.json +++ b/package.json @@ -6,16 +6,16 @@ "scripts": { "prepare": "husky", "dev": "dotenv -- turbo run dev", - "build": "dotenv -- turbo run build", + "build": "dotenv -- turbo run --filter=@zero/mail build", "start": "dotenv -- turbo run start", "lint": "dotenv -- turbo run lint", "format": "prettier --write apps/**/*.{ts,tsx} --log-level silent", "check": "bun run check:format && bun run lint", "check:format": "prettier . --check", "lint-staged": "prettier --write --ignore-unknown", - "docker:up": "docker-compose up -d", - "docker:down": "docker-compose down", - "docker:clean": "docker-compose down -v", + "docker:db:up": "docker-compose -f docker-compose.db.yaml up -d", + "docker:db:down": "docker-compose -f docker-compose.db.yaml down", + "docker:db:clean": "docker-compose -f docker-compose.db.yaml down -v", "db:generate": "dotenv -- turbo run db:generate", "db:migrate": "dotenv -- turbo run db:migrate", "db:push": "dotenv -- turbo run db:push", From 44b0bdb9c24c0f35bdf50ba610ed6a73690210e9 Mon Sep 17 00:00:00 2001 From: Aditya Tripathi Date: Tue, 6 May 2025 11:45:50 +0000 Subject: [PATCH 3/4] chore(docker): fix health checks and dependency status checks --- docker-compose.prod.yaml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml index d615e7874b..8b1ea173a5 100644 --- a/docker-compose.prod.yaml +++ b/docker-compose.prod.yaml @@ -19,12 +19,16 @@ services: GROQ_API_KEY: ${GROQ_API_KEY} GOOGLE_GENERATIVE_AI_API_KEY: ${GOOGLE_GENERATIVE_AI_API_KEY} depends_on: - - db - - migrations - - valkey - - upstash-proxy + db: + condition: service_healthy + migrations: + condition: service_completed_successfully + valkey: + condition: service_healthy + upstash-proxy: + condition: service_healthy healthcheck: - test: ['CMD', 'curl', '-f', 'http://localhost:3000/healthcheck'] + test: ['CMD', 'wget', '--spider', '--quiet', 'http://127.0.0.1:3000'] interval: 90s timeout: 5s retries: 3 From 37a8a4ff42c33c727aa927eb4eaaf13f0bb2d9e5 Mon Sep 17 00:00:00 2001 From: Aditya Tripathi Date: Fri, 16 May 2025 00:03:13 +0000 Subject: [PATCH 4/4] feat: agnostic image with runtime variables + typed config --- README.md | 2 +- apps/mail/app/(auth)/login/login-client.tsx | 4 +- apps/mail/app/(auth)/login/page.tsx | 22 +++--- .../(routes)/settings/connections/page.tsx | 3 +- apps/mail/app/layout.tsx | 2 + apps/mail/app/og-api/home/route.tsx | 11 ++- apps/mail/components/connection/add.tsx | 3 +- apps/mail/components/create/ai-chat.tsx | 3 +- apps/mail/components/create/voice.tsx | 5 +- apps/mail/components/party.tsx | 3 +- apps/mail/instrumentation.ts | 5 +- apps/mail/lib/auth-client.ts | 3 +- apps/mail/lib/constants.ts | 4 +- apps/mail/lib/email-utils.client.tsx | 16 +++-- apps/mail/lib/env.ts | 42 +++++++++++ apps/mail/lib/groq.ts | 7 +- apps/mail/lib/posthog-provider.tsx | 7 +- apps/mail/lib/services.ts | 7 +- apps/mail/lib/site-config.ts | 7 +- apps/mail/lib/utils.ts | 5 +- apps/mail/middleware.ts | 3 +- apps/mail/next.config.ts | 9 +-- apps/mail/package.json | 2 + apps/mail/providers/query-provider.tsx | 3 +- bun.lock | 46 ++++++++++++ docker-compose.prod.yaml | 32 ++++----- docker/app/Dockerfile | 72 +++---------------- scripts/docker/entrypoint.sh | 10 +++ scripts/docker/replace-placeholder.sh | 36 ++++++++++ 29 files changed, 244 insertions(+), 130 deletions(-) create mode 100644 apps/mail/lib/env.ts create mode 100644 scripts/docker/entrypoint.sh create mode 100644 scripts/docker/replace-placeholder.sh diff --git a/README.md b/README.md index 29610f2f17..2d802f91c5 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ You can set up Zero in two ways: ``` - Configure your environment variables (see below) - Setup cloudflare with `bun run cf-install`, you will need to run this everytime there is a `.env` change - - Start the database with the provided docker compose setup: `bun docker:up` + - Start the database with the provided docker compose setup: `bun docker:db:up` - Initialize the database: `bun db:push` 3. **Start the App** diff --git a/apps/mail/app/(auth)/login/login-client.tsx b/apps/mail/app/(auth)/login/login-client.tsx index 9af92b1d5b..7ea98b1559 100644 --- a/apps/mail/app/(auth)/login/login-client.tsx +++ b/apps/mail/app/(auth)/login/login-client.tsx @@ -10,7 +10,7 @@ import { TriangleAlert } from 'lucide-react'; import { useRouter } from 'next/navigation'; import Image from 'next/image'; import { toast } from 'sonner'; -import Link from 'next/link'; +import { env } from '@/lib/env'; interface EnvVarStatus { name: string; @@ -121,7 +121,7 @@ function LoginClientContent({ providers, isProd }: LoginClientProps) { toast.promise( signIn.social({ provider: provider.id as any, - callbackURL: `${process.env.NEXT_PUBLIC_APP_URL}/mail`, + callbackURL: `${env.NEXT_PUBLIC_APP_URL}/mail`, }), { error: 'Login redirect failed', diff --git a/apps/mail/app/(auth)/login/page.tsx b/apps/mail/app/(auth)/login/page.tsx index 2512986a9c..cb6655a014 100644 --- a/apps/mail/app/(auth)/login/page.tsx +++ b/apps/mail/app/(auth)/login/page.tsx @@ -1,24 +1,28 @@ import { authProviders, customProviders, isProviderEnabled } from '@zero/server/auth-providers'; import { LoginClient } from './login-client'; +import { env } from '@/lib/env'; export default function LoginPage() { - const envNodeEnv = process.env.NODE_ENV; + const envNodeEnv = env.NODE_ENV; const isProd = envNodeEnv === 'production'; - const authProviderStatus = authProviders(process.env as Record).map( + const authProviderStatus = authProviders(env as unknown as Record).map( (provider) => { const envVarStatus = - provider.envVarInfo?.map((envVar) => ({ - name: envVar.name, - set: !!process.env[envVar.name], - source: envVar.source, - defaultValue: envVar.defaultValue, - })) || []; + provider.envVarInfo?.map((envVar) => { + const envVarName = envVar.name as keyof typeof env; + return { + name: envVar.name, + set: !!env[envVarName], + source: envVar.source, + defaultValue: envVar.defaultValue, + }; + }) || []; return { id: provider.id, name: provider.name, - enabled: isProviderEnabled(provider, process.env as Record), + enabled: isProviderEnabled(provider, env as Record), required: provider.required, envVarInfo: provider.envVarInfo, envVarStatus, diff --git a/apps/mail/app/(routes)/settings/connections/page.tsx b/apps/mail/app/(routes)/settings/connections/page.tsx index da11b5a620..28266134e9 100644 --- a/apps/mail/app/(routes)/settings/connections/page.tsx +++ b/apps/mail/app/(routes)/settings/connections/page.tsx @@ -24,6 +24,7 @@ import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { useTranslations } from 'next-intl'; import { useState } from 'react'; +import { env } from '@/lib/env'; import Image from 'next/image'; import { toast } from 'sonner'; @@ -147,7 +148,7 @@ export default function ConnectionsPage() { onClick={async () => { await authClient.linkSocial({ provider: connection.providerId, - callbackURL: `${process.env.NEXT_PUBLIC_APP_URL}/settings/connections`, + callbackURL: `${env.NEXT_PUBLIC_APP_URL}/settings/connections`, }); }} > diff --git a/apps/mail/app/layout.tsx b/apps/mail/app/layout.tsx index bdf968d022..cdbbcee57e 100644 --- a/apps/mail/app/layout.tsx +++ b/apps/mail/app/layout.tsx @@ -2,6 +2,7 @@ import { ClientProviders } from '@/providers/client-providers'; import { ServerProviders } from '@/providers/server-providers'; import { SpeedInsights } from '@vercel/speed-insights/next'; import { Geist, Geist_Mono } from 'next/font/google'; +import { PublicEnvScript } from 'next-runtime-env'; import { siteConfig } from '@/lib/site-config'; import type { PropsWithChildren } from 'react'; import type { Viewport } from 'next'; @@ -33,6 +34,7 @@ export default async function RootLayout({ children }: PropsWithChildren) {