diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..72e9aa425 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +Dockerfile +.dockerignore +node_modules +npm-debug.log +README.md +.next +.git \ No newline at end of file diff --git a/.env.example b/.env.example index 07a74e21e..e87506571 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,8 @@ ##### REQUIRED FOR DEV ##### # Supabase keys, learn more here: https://supabase.com/docs/guides/getting-started/quickstarts/nextjs -NEXT_PUBLIC_SUPABASE_URL= -NEXT_PUBLIC_SUPABASE_ANON_KEY= +NEXT_PUBLIC_SUPABASE_URL=your_supabase_url +NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key NEXT_PRIVATE_SUPABASE_SERVICE_ROLE_KEY= # needed for sending auth emails, learn more here: https://supabase.com/docs/guides/auth/auth-hooks?queryGroups=language&language=http @@ -12,8 +12,8 @@ SEND_EMAIL_HOOK_SECRET= RESEND_API_KEY= # Keys used to connect to PostHog, learn more here: https://posthog.com/docs/libraries/next-js?tab=App+router -NEXT_PUBLIC_POSTHOG_KEY= -NEXT_PUBLIC_POSTHOG_HOST= +NEXT_PUBLIC_POSTHOG_KEY=your_posthog_key +NEXT_PUBLIC_POSTHOG_HOST=your_posthog_host # This was inserted by `prisma init`: # Environment variables declared in this file are automatically made available to Prisma. @@ -23,7 +23,7 @@ NEXT_PUBLIC_POSTHOG_HOST= # See the documentation for all the connection string options: https://pris.ly/d/connection-strings # Connect to Supabase via connection pooling with Supavisor. -DATABASE_URL= +DATABASE_URL="postgresql://postgres:postgres@db:5432/techblitz" # Direct connection to the database. Used for migrations. DIRECT_URL= @@ -40,17 +40,17 @@ EXECUTE_CODE_URL= ##### OPTIONAL KEYS NOT REQUIRED FOR DEV ##### # Keys used to connect to Stripe (only needed for stripe development) -NEXT_PUBLIC_STRIPE_KEY= -NEXT_PRIVATE_STRIPE_SECRET_KEY= +NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key +NEXT_PRIVATE_STRIPE_SECRET_KEY=your_stripe_secret_key # webhook secret used for updating user details from stripe events, learn more here: https://docs.stripe.com/development/dashboard/webhooks?locale=en-GB NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET= # Keys used to connect to OpenAI (only needed for ai development) -OPENAI_API_KEY= +NEXT_PUBLIC_OPENAI_API_KEY=your_openai_api_key NEXT_PRIVATE_OPEN_AI_ORG= # Keys used to connect to Claude (only needed for ai development) -ANTHROPIC_API_KEY= +NEXT_PUBLIC_ANTHROPIC_API_KEY=your_anthropic_api_key # already preset. NEXT_PUBLIC_ENV=development @@ -63,3 +63,12 @@ REFERRAL_CODE= # the stripe product id for the premium product (used to create custom signup coupons) STRIPE_PREMIUM_PRODUCT_ID= + +# OAuth +NEXT_PUBLIC_GITHUB_OAUTH_CLIENT_ID=your_github_client_id +NEXT_PUBLIC_GITHUB_OAUTH_CLIENT_SECRET=your_github_client_secret +NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_ID=your_google_client_id +NEXT_PUBLIC_GOOGLE_OAUTH_CLIENT_SECRET=your_google_client_secret + +# Docker for self hosting +DOCKER=false \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..e8b9a040d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,77 @@ +# syntax=docker.io/docker/dockerfile:1 + +FROM node:18-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install pnpm +RUN npm install -g pnpm + +# Install dependencies +COPY package.json pnpm-lock.yaml ./ +# Use --no-frozen-lockfile to avoid issues with lockfile changes +RUN pnpm install --no-frozen-lockfile + +# Rebuild the source code only when needed +FROM base AS builder +# set working directory +WORKDIR /app + +# Install pnpm +RUN npm install -g pnpm + +# copy the dependencies from the deps stage +COPY --from=deps /app/node_modules ./node_modules + +# Copy the prisma directory containing schema files +COPY prisma ./prisma + +# copy the rest of the application code +COPY . . + +# Skip Prisma's postinstall script for the build phase to avoid schema errors +ENV PRISMA_SKIP_POSTINSTALL=1 +ENV NEXT_TELEMETRY_DISABLED=1 + +# Enables Hot Reloading Check https://github.com/vercel/next.js/issues/36774 for more information +ENV CHOKIDAR_USEPOLLING=true +ENV WATCHPACK_POLLING=true + +# Generate Prisma client +RUN pnpm exec prisma generate + +# Build the application +RUN pnpm build + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +RUN mkdir .next +RUN chown -R nextjs:nodejs .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] \ No newline at end of file diff --git a/app/api/health/route.ts b/app/api/health/route.ts new file mode 100644 index 000000000..7e7f17f28 --- /dev/null +++ b/app/api/health/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from 'next/server'; + +export async function GET() { + try { + return NextResponse.json( + { + status: 'ok', + timestamp: new Date().toISOString(), + environment: process.env.NODE_ENV, + }, + { status: 200 } + ); + } catch (error) { + console.error('Health check failed:', error); + + return NextResponse.json( + { + status: 'error', + message: 'Health check failed', + timestamp: new Date().toISOString(), + }, + { status: 500 } + ); + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..fce879c91 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,64 @@ +# Docker Compose file for TechBlitz application + +services: + app: + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + ports: + - '3000:3000' + environment: + - DATABASE_URL=postgresql://postgres:postgres@db:5432/techblitz + - NEXT_PUBLIC_URL=http://localhost:3000 + - NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL:-} + - NEXT_PUBLIC_SUPABASE_ANON_KEY=${NEXT_PUBLIC_SUPABASE_ANON_KEY:-} + - NEXT_PRIVATE_SUPABASE_SERVICE_ROLE_KEY=${NEXT_PRIVATE_SUPABASE_SERVICE_ROLE_KEY:-} + - NEXT_PUBLIC_POSTHOG_KEY=${NEXT_PUBLIC_POSTHOG_KEY:-} + - NEXT_PUBLIC_POSTHOG_HOST=${NEXT_PUBLIC_POSTHOG_HOST:-} + - NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:-} + - NEXT_PRIVATE_STRIPE_SECRET_KEY=${NEXT_PRIVATE_STRIPE_SECRET_KEY:-} + - NEXT_PUBLIC_OPENAI_API_KEY=${NEXT_PUBLIC_OPENAI_API_KEY:-} + - NEXT_PUBLIC_ANTHROPIC_API_KEY=${NEXT_PUBLIC_ANTHROPIC_API_KEY:-} + - DOCKER=true + - PRISMA_SKIP_POSTINSTALL=1 + - PRISMA_SCHEMA_PATH=prisma/schema/schema.prisma + depends_on: + - db + # Add healthcheck to ensure the application is running properly + healthcheck: + test: ['CMD', 'wget', '--no-verbose', '--spider', 'http://localhost:3000'] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + volumes: + # Use volume for node_modules to avoid rebuilding + - node_modules:/app/node_modules + # Mount prisma directory for schema access + - ./prisma:/app/prisma/schema + + db: + image: postgres:16-alpine + restart: unless-stopped + environment: + - POSTGRES_PASSWORD=postgres + - POSTGRES_USER=postgres + - POSTGRES_DB=techblitz + ports: + - '5432:5432' + volumes: + - postgres-data:/var/lib/postgresql/data + # Add healthcheck to ensure the database is running properly + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U postgres'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + +volumes: + postgres-data: + driver: local + node_modules: + driver: local diff --git a/next.config.js b/next.config.js index 25415c8dd..53d802153 100644 --- a/next.config.js +++ b/next.config.js @@ -3,6 +3,8 @@ const createMDX = require('@next/mdx'); /** @type {import('next').NextConfig} */ const nextConfig = { + // output: 'standalone' if the flag is set + output: process.env.DOCKER ? 'standalone' : undefined, pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'], images: { remotePatterns: [ diff --git a/package.json b/package.json index 7af1cc1d3..c3eb5a34d 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,9 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, + "prisma": { + "schema": "prisma/schema/schema.prisma" + }, "dependencies": { "@ai-sdk/anthropic": "1.1.13", "@ai-sdk/openai": "1.1.15",