diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..71e2cad --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.next +node_modules +.env.local +.env.prod +.git diff --git a/Dockerfile b/Dockerfile index 8fbd336..fecf18a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,16 @@ FROM node:22-alpine AS base RUN corepack enable && corepack prepare pnpm@latest --activate -# ── deps ───────────────────────────────────────────────────────────────────── -FROM base AS deps -WORKDIR /app -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ -RUN pnpm install --frozen-lockfile - # ── builder ─────────────────────────────────────────────────────────────────── FROM base AS builder WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules +# Install deps first (cached until lockfile changes) +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN pnpm install --frozen-lockfile +# Copy source and build COPY . . -# Dummy env so Next.js build doesn't fail on missing vars at build time. -# Real values are injected at runtime via docker-compose env. ENV NEXT_TELEMETRY_DISABLED=1 +# Dummy build-time vars — real values injected at runtime via docker-compose. ENV DATABASE_URL=postgresql://placeholder:placeholder@localhost:5432/curio ENV REDIS_URL=redis://localhost:6379 ENV AUTH_SECRET=build-placeholder @@ -29,7 +25,6 @@ ENV NEXT_TELEMETRY_DISABLED=1 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 @@ -41,7 +36,8 @@ CMD ["node", "server.js"] # ── worker (BullMQ — needs full source + tsx) ───────────────────────────────── FROM base AS worker WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +RUN pnpm install --frozen-lockfile COPY . . ENV NODE_ENV=production CMD ["node_modules/.bin/tsx", "jobs/bootstrap.ts"] diff --git a/src/app/api/cron/review-digest/route.ts b/src/app/api/cron/review-digest/route.ts index b370710..3dbc05b 100644 --- a/src/app/api/cron/review-digest/route.ts +++ b/src/app/api/cron/review-digest/route.ts @@ -9,7 +9,7 @@ import { sendEmail } from '@/lib/email'; const BASE_URL = process.env.AUTH_URL ?? 'http://localhost:3000'; -export function signUserId(userId: string): string { +function signUserId(userId: string): string { const secret = process.env.UNSUBSCRIBE_SECRET ?? 'dev-secret-change-in-prod'; return createHmac('sha256', secret).update(userId).digest('hex'); } diff --git a/src/app/map/map-client.tsx b/src/app/map/map-client.tsx index 056f8cf..677ea82 100644 --- a/src/app/map/map-client.tsx +++ b/src/app/map/map-client.tsx @@ -305,9 +305,9 @@ export function ThemeMapClient() { ) : listThemes.length === 0 ? (
-

Aucun thème exploré pour l'instant.

+

Aucun thème exploré pour l'instant.

- Commencer à apprendre → + Commencer à apprendre →

) : ( diff --git a/src/auth.ts b/src/auth.ts index 1e14197..91582bb 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -75,7 +75,7 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ async createUser({ user }) { // First ever user becomes admin — covers OAuth sign-up path const [{ total }] = await db.select({ total: count() }).from(users); - if (total === 1) { + if (total === 1 && user.id) { await db.update(users).set({ role: 'admin' }).where(eq(users.id, user.id)); } }, diff --git a/src/lib/db/queries.ts b/src/lib/db/queries.ts index 470a9b2..451bede 100644 --- a/src/lib/db/queries.ts +++ b/src/lib/db/queries.ts @@ -1,4 +1,4 @@ -import { eq, asc, lte, and, inArray, lt, desc, not, isNull, sql, count, countDistinct } from 'drizzle-orm'; +import { eq, asc, lte, gte, and, inArray, lt, desc, not, isNull, sql, count, countDistinct } from 'drizzle-orm'; import { db } from './index'; import { blueprints, @@ -1049,7 +1049,7 @@ export async function getOnlineGradeMetrics(windowDays = 30): Promise = {}; @@ -1060,7 +1060,7 @@ export async function getOnlineGradeMetrics(windowDays = 30): Promise