diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1b64d94 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,64 @@ +# Dependencies +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Testing +coverage +.nyc_output + +# Next.js +.next +out +dist + +# Production +build + +# Misc +.DS_Store +*.pem + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Local env files +.env*.local +.env + +# Vercel +.vercel + +# Typescript +*.tsbuildinfo +next-env.d.ts + +# IDE +.vscode +.idea +*.swp +*.swo +*~ + +# Git +.git +.gitignore + +# Docker +Dockerfile +.dockerignore +docker-compose.yml + +# Database +*.db +*.db-journal +dev.db +prisma/dev.db + +# Prisma generated (will be regenerated in container) +prisma/generated + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9ee8bf5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,100 @@ +# Stage 1: Dependencies +FROM node:20-alpine AS deps +RUN apk add --no-cache libc6-compat python3 make g++ +WORKDIR /app + +# Install pnpm +RUN corepack enable && corepack prepare pnpm@latest --activate + +# Copy package files +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile + +# Stage 2: Builder +FROM node:20-alpine AS builder +RUN apk add --no-cache libc6-compat python3 make g++ +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=deps /app/package.json ./package.json +COPY --from=deps /app/pnpm-lock.yaml ./pnpm-lock.yaml + +# Copy application files +COPY . . + +# Rebuild better-sqlite3 for the target platform +RUN pnpm rebuild better-sqlite3 + +# Generate Prisma Client +# Set a dummy DATABASE_URL for generation (not used, but required by prisma.config.ts) +ENV DATABASE_URL=file:./prisma/dev.db +RUN pnpm exec prisma generate + + +# Build Next.js application +ENV NEXT_TELEMETRY_DISABLED=1 +RUN pnpm build + +# Stage 3: Runner +FROM node:20-alpine 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 necessary files +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/prisma ./prisma + +# Copy Prisma and better-sqlite3 dependencies from pnpm store +COPY --from=builder /app/node_modules/.pnpm/@prisma+client@* ./node_modules/.pnpm/@prisma+client@*/ +COPY --from=builder /app/node_modules/.pnpm/@prisma+adapter-better-sqlite3@* ./node_modules/.pnpm/@prisma+adapter-better-sqlite3@*/ +COPY --from=builder /app/node_modules/.pnpm/better-sqlite3@* ./node_modules/.pnpm/better-sqlite3@*/ + +# Copy generated Prisma client +COPY --from=builder /app/prisma/generated ./prisma/generated + +# Copy package.json and pnpm-lock.yaml for Prisma CLI +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/pnpm-lock.yaml ./pnpm-lock.yaml + +# Copy node_modules structure needed for Prisma +RUN mkdir -p node_modules/@prisma node_modules/better-sqlite3 node_modules/@prisma/adapter-better-sqlite3 && \ + ln -sf ../.pnpm/@prisma+client@*/node_modules/@prisma/* node_modules/@prisma/ && \ + ln -sf ../.pnpm/better-sqlite3@*/node_modules/better-sqlite3/* node_modules/better-sqlite3/ && \ + ln -sf ../.pnpm/@prisma+adapter-better-sqlite3@*/node_modules/@prisma/adapter-better-sqlite3/* node_modules/@prisma/adapter-better-sqlite3/ + +# Set permissions +RUN chown -R nextjs:nodejs /app + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +# Create entrypoint script to run migrations +RUN echo '#!/bin/sh' > /app/entrypoint.sh && \ + echo 'set -e' >> /app/entrypoint.sh && \ + echo 'if [ -f prisma/dev.db ]; then' >> /app/entrypoint.sh && \ + echo ' echo "Database exists, running migrations..."' >> /app/entrypoint.sh && \ + echo ' node node_modules/.bin/prisma migrate deploy || true' >> /app/entrypoint.sh && \ + echo 'else' >> /app/entrypoint.sh && \ + echo ' echo "Database does not exist, creating..."' >> /app/entrypoint.sh && \ + echo ' node node_modules/.bin/prisma migrate deploy || true' >> /app/entrypoint.sh && \ + echo 'fi' >> /app/entrypoint.sh && \ + echo 'exec node server.js' >> /app/entrypoint.sh && \ + chmod +x /app/entrypoint.sh + +CMD ["/app/entrypoint.sh"] + diff --git a/README.docker.md b/README.docker.md new file mode 100644 index 0000000..9507672 --- /dev/null +++ b/README.docker.md @@ -0,0 +1,60 @@ +# Docker Setup + +Ce projet inclut des fichiers Docker pour faciliter le déploiement. + +## Fichiers Docker + +- `Dockerfile` - Image de production optimisée (multi-stage build) +- `docker-compose.yml` - Configuration pour la production + +## Production + +Pour construire et démarrer l'application en production : + +```bash +# Construire l'image +docker-compose build + +# Démarrer les services +docker-compose up -d + +# Voir les logs +docker-compose logs -f +``` + +## Variables d'environnement + +Créez un fichier `.env` à la racine du projet avec les variables suivantes : + +```env +NEXTAUTH_SECRET=your-secret-key-here +NEXTAUTH_URL=http://localhost:3000 +DATABASE_URL=file:./prisma/dev.db +``` + +## Base de données + +La base de données SQLite est persistée via un volume Docker. Les migrations Prisma sont appliquées automatiquement au démarrage du conteneur. + +Pour appliquer manuellement les migrations : + +```bash +docker-compose exec app node node_modules/.bin/prisma migrate deploy +``` + +## Commandes utiles + +```bash +# Arrêter les conteneurs +docker-compose down + +# Reconstruire sans cache +docker-compose build --no-cache + +# Accéder au shell du conteneur +docker-compose exec app sh + +# Voir les logs en temps réel +docker-compose logs -f app +``` + diff --git a/app/admin/page.tsx b/app/admin/page.tsx index a4bbac6..b58a263 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -5,6 +5,8 @@ import { Role } from "@/prisma/generated/prisma/client"; import NavigationWrapper from "@/components/NavigationWrapper"; import AdminPanel from "@/components/AdminPanel"; +export const dynamic = "force-dynamic"; + export default async function AdminPage() { const session = await auth(); diff --git a/app/api/health/route.ts b/app/api/health/route.ts new file mode 100644 index 0000000..225b25f --- /dev/null +++ b/app/api/health/route.ts @@ -0,0 +1,6 @@ +import { NextResponse } from "next/server"; + +export async function GET() { + return NextResponse.json({ status: "ok" }); +} + diff --git a/app/events/page.tsx b/app/events/page.tsx index 643a6ad..07431aa 100644 --- a/app/events/page.tsx +++ b/app/events/page.tsx @@ -5,6 +5,8 @@ import { getBackgroundImage } from "@/lib/preferences"; import { auth } from "@/lib/auth"; import { calculateEventStatus } from "@/lib/eventStatus"; +export const dynamic = "force-dynamic"; + export default async function EventsPage() { const events = await prisma.event.findMany({ orderBy: { diff --git a/app/leaderboard/page.tsx b/app/leaderboard/page.tsx index 80e55cb..a01a8da 100644 --- a/app/leaderboard/page.tsx +++ b/app/leaderboard/page.tsx @@ -3,6 +3,8 @@ import LeaderboardSection from "@/components/LeaderboardSection"; import { prisma } from "@/lib/prisma"; import { getBackgroundImage } from "@/lib/preferences"; +export const dynamic = "force-dynamic"; + interface LeaderboardEntry { rank: number; username: string; diff --git a/app/page.tsx b/app/page.tsx index 2674fd6..f38bfb9 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,6 +3,8 @@ import HeroSection from "@/components/HeroSection"; import EventsSection from "@/components/EventsSection"; import { prisma } from "@/lib/prisma"; +export const dynamic = "force-dynamic"; + export default async function Home() { const events = await prisma.event.findMany({ orderBy: { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6ccf0e4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,34 @@ +version: "3.8" + +services: + app: + build: + context: . + dockerfile: Dockerfile + container_name: got-mc-app + ports: + - "3040:3000" + environment: + - NODE_ENV=production + - DATABASE_URL=file:./prisma/dev.db + - NEXTAUTH_URL=http://localhost:3000 + - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:-change-this-secret-in-production} + volumes: + # Persist database + - ./prisma/dev.db:/app/prisma/dev.db + - ./prisma/migrations:/app/prisma/migrations + restart: unless-stopped + healthcheck: + test: + [ + "CMD", + "wget", + "--quiet", + "--tries=1", + "--spider", + "http://localhost:3000/api/health || exit 1", + ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s diff --git a/next.config.js b/next.config.js index 91ef62f..f83dccb 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,7 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + output: 'standalone', }; module.exports = nextConfig;