diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..35abbaf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +node_modules +.next +.git +.env* +!.env.local.example +!.env.docker.example +docker +supabase +*.md diff --git a/.env.docker.example b/.env.docker.example new file mode 100644 index 0000000..c3a6a40 --- /dev/null +++ b/.env.docker.example @@ -0,0 +1,12 @@ +# Le Tribunal — Docker deployment with managed Supabase + +# --- Domain --- +APP_DOMAIN=acropole.alxczl.fr + +# --- Traefik --- +TRAEFIK_NETWORK=proxy + +# --- Supabase (managed) --- +# From your Supabase project: Settings -> API +SUPABASE_URL= +SUPABASE_ANON_KEY= diff --git a/.gitignore b/.gitignore index b721bff..a0b0be5 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* !.env.local.example +!.env.docker.example # vercel .vercel diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4658b3a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM node:22-alpine AS base +WORKDIR /app + +FROM base AS deps +COPY package.json package-lock.json ./ +RUN npm ci + +FROM base AS build +COPY --from=deps /app/node_modules ./node_modules +COPY . . +ARG NEXT_PUBLIC_SUPABASE_URL +ARG NEXT_PUBLIC_SUPABASE_ANON_KEY +ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL +ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY +RUN npm run build + +FROM base AS runner +ENV NODE_ENV=production +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs +COPY --from=build /app/public ./public +COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=build --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"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..60ad884 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +services: + app: + build: + context: . + args: + NEXT_PUBLIC_SUPABASE_URL: ${SUPABASE_URL} + NEXT_PUBLIC_SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY} + restart: unless-stopped + labels: + - "traefik.enable=true" + - "traefik.http.routers.tribunal.rule=Host(`${APP_DOMAIN}`)" + - "traefik.http.routers.tribunal.entrypoints=https" + - "traefik.http.routers.tribunal.tls.certresolver=letsencrypt" + - "traefik.http.routers.tribunal.middlewares=compression@file,hsts-headers@file" + - "traefik.http.services.tribunal.loadbalancer.server.port=3000" + networks: + - proxy + +networks: + proxy: + external: true + name: ${TRAEFIK_NETWORK} diff --git a/next.config.ts b/next.config.ts index 6e35e0d..3e93ab6 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,11 +1,16 @@ import type { NextConfig } from "next"; +const supabaseHostname = process.env.NEXT_PUBLIC_SUPABASE_URL + ? new URL(process.env.NEXT_PUBLIC_SUPABASE_URL).hostname + : "localhost"; + const nextConfig: NextConfig = { + output: "standalone", images: { remotePatterns: [ { protocol: "https", - hostname: "kuichrzrbdwydixbdfon.supabase.co", + hostname: supabaseHostname, pathname: "/storage/v1/object/public/**", }, ],