diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..8d6bf42 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +node_modules +.next +.git +.gitignore +*.md +.env +.env.* +!.env.example +.vercel +.tools +coverage +npm-debug.log* +webhook-debug.log +drizzle +docker-compose*.yml +.github diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..27592aa --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,50 @@ +name: CD + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + publish: + name: Build and push image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Lowercase image name + run: echo "IMAGE=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV" + + - name: Image metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.IMAGE }} + tags: | + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + + - uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NEXT_PUBLIC_APP_URL=${{ vars.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }} + NEXT_PUBLIC_STREAM_VIDEO_API_KEY=${{ secrets.NEXT_PUBLIC_STREAM_VIDEO_API_KEY || 'placeholder' }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..36c8f4a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +jobs: + quality: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run lint + + docker: + name: Docker build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + - uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: false + tags: nexora-ai:ci + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NEXT_PUBLIC_APP_URL=http://localhost:3000 + NEXT_PUBLIC_STREAM_VIDEO_API_KEY=placeholder diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..538c0fb --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +# syntax=docker/dockerfile:1 + +FROM node:20-alpine AS base +RUN apk add --no-cache libc6-compat +WORKDIR /app + +FROM base AS deps +COPY package.json package-lock.json ./ +RUN npm ci + +FROM base AS builder +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +ARG NEXT_PUBLIC_APP_URL=http://localhost:3000 +ARG NEXT_PUBLIC_STREAM_VIDEO_API_KEY=placeholder +ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL +ENV NEXT_PUBLIC_STREAM_VIDEO_API_KEY=$NEXT_PUBLIC_STREAM_VIDEO_API_KEY +ENV NEXT_TELEMETRY_DISABLED=1 +ENV DATABASE_URL=postgresql://build:build@127.0.0.1:5432/build +ENV BETTER_AUTH_SECRET=docker-build-placeholder-secret-32chars +ENV BETTER_AUTH_URL=$NEXT_PUBLIC_APP_URL +ENV STREAM_VIDEO_SECRET_KEY=docker-build-placeholder-stream-secret +ENV GEMINI_API_KEY=docker-build-placeholder-gemini-key + +RUN npm run build + +FROM base AS runner +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 + +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..10d0dc4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + web: + build: + context: . + dockerfile: Dockerfile + args: + NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-http://localhost:3000} + NEXT_PUBLIC_STREAM_VIDEO_API_KEY: ${NEXT_PUBLIC_STREAM_VIDEO_API_KEY:-placeholder} + ports: + - "3000:3000" + env_file: + - .env + environment: + NODE_ENV: production + PORT: 3000 + HOSTNAME: 0.0.0.0 + restart: unless-stopped diff --git a/next.config.ts b/next.config.ts index cb651cd..68a6c64 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,5 +1,7 @@ import type { NextConfig } from "next"; -const nextConfig: NextConfig = {}; +const nextConfig: NextConfig = { + output: "standalone", +}; export default nextConfig;