Dockerfile modif2
This commit is contained in:
parent
3548eabf68
commit
ed84f37e12
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.env
|
||||||
|
node_modules/
|
||||||
|
.next/
|
||||||
|
supabase/
|
||||||
|
.gitignore
|
||||||
34
Dockerfile
34
Dockerfile
@ -24,21 +24,22 @@ WORKDIR /app
|
|||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Variables publiques nécessaires à la compilation du bundle client.
|
# Les NEXT_PUBLIC_* sont baked dans le bundle client.
|
||||||
# Passer via --build-arg ou docker-compose (voir docker-compose.yml).
|
# → Définir dans Dokploy : Project > Build Variables
|
||||||
# Les valeurs vides sont acceptées : les pages sont dynamiques (cookies),
|
# → Les placeholders ci-dessous empêchent le SDK Supabase de lever une
|
||||||
# donc aucune pré-rendu statique ne les utilise au build.
|
# erreur de validation URL si les build-args ne sont pas fournis.
|
||||||
ARG NEXT_PUBLIC_SUPABASE_URL=""
|
ARG NEXT_PUBLIC_SUPABASE_URL=https://placeholder.supabase.co
|
||||||
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY=""
|
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY=placeholder-anon-key-for-build-only
|
||||||
|
|
||||||
ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \
|
ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \
|
NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \
|
||||||
NEXT_TELEMETRY_DISABLED=1 \
|
NEXT_TELEMETRY_DISABLED=1 \
|
||||||
NODE_ENV=production \
|
NODE_ENV=production \
|
||||||
# Allouer suffisamment de mémoire pour le build Next.js
|
NODE_OPTIONS="--max-old-space-size=2048"
|
||||||
NODE_OPTIONS="--max-old-space-size=4096"
|
|
||||||
|
|
||||||
RUN npm run build
|
# Affiche les vars actives et expose la sortie complète du build
|
||||||
|
RUN echo "→ NEXT_PUBLIC_SUPABASE_URL = $NEXT_PUBLIC_SUPABASE_URL" && \
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
@ -50,31 +51,26 @@ RUN apk update && apk upgrade --no-cache
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Utilisateur non-root dédié
|
|
||||||
RUN addgroup --system --gid 1001 nodejs && \
|
RUN addgroup --system --gid 1001 nodejs && \
|
||||||
adduser --system --uid 1001 nextjs
|
adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
# Fichiers publics (favicon, images statiques…)
|
|
||||||
COPY --from=builder /app/public ./public
|
COPY --from=builder /app/public ./public
|
||||||
|
|
||||||
# Serveur Next.js standalone (bundle minimal, sans node_modules complets)
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
|
||||||
# Assets statiques compilés (_next/static)
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
|
# Secrets et vars publiques injectés au runtime par Dokploy
|
||||||
|
# (Project > Environment Variables) :
|
||||||
|
# NEXT_PUBLIC_SUPABASE_URL
|
||||||
|
# NEXT_PUBLIC_SUPABASE_ANON_KEY
|
||||||
|
# SUPABASE_SERVICE_ROLE_KEY
|
||||||
ENV NODE_ENV=production \
|
ENV NODE_ENV=production \
|
||||||
NEXT_TELEMETRY_DISABLED=1 \
|
NEXT_TELEMETRY_DISABLED=1 \
|
||||||
PORT=3000 \
|
PORT=3000 \
|
||||||
HOSTNAME="0.0.0.0"
|
HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
# Secrets injectés au runtime uniquement (jamais baked dans l'image) :
|
|
||||||
# NEXT_PUBLIC_SUPABASE_URL
|
|
||||||
# NEXT_PUBLIC_SUPABASE_ANON_KEY
|
|
||||||
# SUPABASE_SERVICE_ROLE_KEY
|
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=20s --retries=3 \
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
services:
|
|
||||||
solyquiz:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
# Les variables NEXT_PUBLIC_* sont lues depuis le fichier .env
|
|
||||||
# grâce à env_file, puis transmises comme build args
|
|
||||||
args:
|
|
||||||
NEXT_PUBLIC_SUPABASE_URL: ${NEXT_PUBLIC_SUPABASE_URL}
|
|
||||||
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${NEXT_PUBLIC_SUPABASE_ANON_KEY}
|
|
||||||
image: solyquiz:latest
|
|
||||||
container_name: solyquiz
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
# Secrets injectés uniquement au runtime (non présents dans l'image)
|
|
||||||
env_file:
|
|
||||||
- .env
|
|
||||||
environment:
|
|
||||||
NODE_ENV: production
|
|
||||||
restart: unless-stopped
|
|
||||||
# Limite les ressources pour éviter les abus
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 512m
|
|
||||||
cpus: "1.0"
|
|
||||||
8
lib/supabase/client.ts
Normal file
8
lib/supabase/client.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createBrowserClient } from '@supabase/ssr'
|
||||||
|
|
||||||
|
export function createClient() {
|
||||||
|
return createBrowserClient(
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
||||||
|
)
|
||||||
|
}
|
||||||
62
lib/supabase/server.ts
Normal file
62
lib/supabase/server.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { createServerClient, type CookieOptions } from '@supabase/ssr'
|
||||||
|
import { createClient as createSupabaseClient } from '@supabase/supabase-js'
|
||||||
|
import { cookies } from 'next/headers'
|
||||||
|
|
||||||
|
export async function createClient() {
|
||||||
|
const cookieStore = await cookies()
|
||||||
|
|
||||||
|
return createServerClient(
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
||||||
|
{
|
||||||
|
cookies: {
|
||||||
|
getAll() {
|
||||||
|
return cookieStore.getAll()
|
||||||
|
},
|
||||||
|
setAll(cookiesToSet: { name: string; value: string; options?: CookieOptions }[]) {
|
||||||
|
try {
|
||||||
|
cookiesToSet.forEach(({ name, value, options }) =>
|
||||||
|
cookieStore.set(name, value, options)
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
// setAll appelé depuis un Server Component
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Client admin avec service_role — bypass RLS + accès auth.admin.* */
|
||||||
|
export function createAdminClient() {
|
||||||
|
return createSupabaseClient(
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
|
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
||||||
|
{ auth: { autoRefreshToken: false, persistSession: false } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createServiceClient() {
|
||||||
|
const cookieStore = await cookies()
|
||||||
|
|
||||||
|
return createServerClient(
|
||||||
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
||||||
|
process.env.SUPABASE_SERVICE_ROLE_KEY!,
|
||||||
|
{
|
||||||
|
cookies: {
|
||||||
|
getAll() {
|
||||||
|
return cookieStore.getAll()
|
||||||
|
},
|
||||||
|
setAll(cookiesToSet: { name: string; value: string; options?: CookieOptions }[]) {
|
||||||
|
try {
|
||||||
|
cookiesToSet.forEach(({ name, value, options }) =>
|
||||||
|
cookieStore.set(name, value, options)
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
// ignoré
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
36
lib/supabase/typed.ts
Normal file
36
lib/supabase/typed.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Ce fichier fournit des wrappers typés pour les opérations Supabase courantes.
|
||||||
|
* Les types sont définis manuellement en attendant la génération via `supabase gen types`.
|
||||||
|
* Note: Connecter votre projet Supabase et lancer `npx supabase gen types typescript`
|
||||||
|
* pour obtenir des types auto-générés qui remplaceront ce fichier.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {
|
||||||
|
Profile,
|
||||||
|
Category,
|
||||||
|
Subchapter,
|
||||||
|
Quiz,
|
||||||
|
Question,
|
||||||
|
Answer,
|
||||||
|
Session,
|
||||||
|
StudentParticipation,
|
||||||
|
StudentAnswer,
|
||||||
|
} from '@/lib/types/database'
|
||||||
|
|
||||||
|
export type {
|
||||||
|
Profile,
|
||||||
|
Category,
|
||||||
|
Subchapter,
|
||||||
|
Quiz,
|
||||||
|
Question,
|
||||||
|
Answer,
|
||||||
|
Session,
|
||||||
|
StudentParticipation,
|
||||||
|
StudentAnswer,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Types pour les retours de requêtes
|
||||||
|
export type SupabaseResponse<T> = {
|
||||||
|
data: T | null
|
||||||
|
error: { message: string; code?: string } | null
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user