From 55d5fd7a39e306b4c88f4b75ef8ac6d5c5c258b6 Mon Sep 17 00:00:00 2001 From: streaper2 Date: Sat, 17 Oct 2026 14:51:59 +0200 Subject: [PATCH] feat: add admin application setup with authentication and deployment configurations --- admin/Dockerfile | 2 +- admin/nixpacks.toml | 2 +- admin/package.json | 3 +- admin/src/lib/auth.ts | 71 +++++++++++++++++++++++++++++++++----- package.json | 3 +- scripts/ensure-admin.cjs | 73 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 scripts/ensure-admin.cjs diff --git a/admin/Dockerfile b/admin/Dockerfile index 5942bb9..dfa84e2 100644 --- a/admin/Dockerfile +++ b/admin/Dockerfile @@ -19,4 +19,4 @@ RUN chown -R nextjs:nodejs /app USER nextjs # On lance directement via npm (avec push de la BD si besoin, ou juste start) -CMD ["sh", "-c", "npx prisma db push --schema=prisma/schema.prisma --accept-data-loss && npm start"] +CMD ["sh", "-c", "npx prisma db push --schema=prisma/schema.prisma --accept-data-loss && (node ../scripts/ensure-admin.cjs || node scripts/ensure-admin.cjs || true) && npm start"] diff --git a/admin/nixpacks.toml b/admin/nixpacks.toml index 3ca8f61..ccd5656 100644 --- a/admin/nixpacks.toml +++ b/admin/nixpacks.toml @@ -10,4 +10,4 @@ cmds = ["npm ci"] cmds = ["npx prisma generate", "npm run build"] [start] -cmd = "npx prisma db push --accept-data-loss && npm start" \ No newline at end of file +cmd = "npx prisma db push --accept-data-loss && (node ../scripts/ensure-admin.cjs || node scripts/ensure-admin.cjs || true) && npm start" \ No newline at end of file diff --git a/admin/package.json b/admin/package.json index fd01e9a..3d68faa 100644 --- a/admin/package.json +++ b/admin/package.json @@ -5,7 +5,8 @@ "scripts": { "dev": "next dev", "build": "prisma generate --schema=../prisma/schema.prisma && next build", - "start": "next start -p 3000", + "start": "node ../scripts/ensure-admin.cjs && next start -p 3000", + "db:init-admin": "node ../scripts/ensure-admin.cjs", "lint": "eslint" }, "dependencies": { diff --git a/admin/src/lib/auth.ts b/admin/src/lib/auth.ts index 7c0aa24..121b4dd 100644 --- a/admin/src/lib/auth.ts +++ b/admin/src/lib/auth.ts @@ -42,15 +42,68 @@ export async function loginAdmin( } try { - // Search user by email (case-insensitive) or by exact name - const user = await prisma.user.findFirst({ - where: { - OR: [ - { email: { equals: cleanIdentifier, mode: 'insensitive' } }, - { name: { equals: cleanIdentifier, mode: 'insensitive' } }, - ], - }, - }); + let user = null; + try { + // Search user by email (case-insensitive) or by exact name + user = await prisma.user.findFirst({ + where: { + OR: [ + { email: { equals: cleanIdentifier, mode: 'insensitive' } }, + { name: { equals: cleanIdentifier, mode: 'insensitive' } }, + ], + }, + }); + } catch (queryErr: any) { + // If column adminRole does not exist in production database yet, auto-heal schema! + if ( + queryErr?.message?.includes('adminRole') || + queryErr?.message?.includes('column') || + queryErr?.code === 'P2021' || + queryErr?.code === 'P2022' + ) { + console.log("⚠️ Colonnes manquantes détectées en production. Auto-migration de la table User..."); + try { + await prisma.$executeRawUnsafe(`ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "adminRole" TEXT DEFAULT 'ADMIN';`); + await prisma.$executeRawUnsafe(`ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "adminPermissions" TEXT[] DEFAULT ARRAY[]::TEXT[];`); + // Retry findFirst + user = await prisma.user.findFirst({ + where: { + OR: [ + { email: { equals: cleanIdentifier, mode: 'insensitive' } }, + { name: { equals: cleanIdentifier, mode: 'insensitive' } }, + ], + }, + }); + } catch (alterErr) { + console.error("Auto-alter failed:", alterErr); + } + } else { + throw queryErr; + } + } + + // Auto-bootstrap for production: If admin@afrohub.com is requested and does not exist in DB yet, + // create it automatically so that newly deployed production databases have a Super Admin immediately. + if (!user && (cleanIdentifier.toLowerCase() === 'admin@afrohub.com' || cleanIdentifier.toLowerCase() === 'admin')) { + try { + const initialPassword = process.env.ADMIN_INITIAL_PASSWORD || 'afrohub2025'; + const hashedPassword = await bcrypt.hash(initialPassword, 10); + user = await prisma.user.create({ + data: { + name: 'Admin Afrohub', + email: 'admin@afrohub.com', + password: hashedPassword, + role: 'ADMIN', + adminRole: 'SUPER_ADMIN', + adminPermissions: [], + emailVerified: true, + }, + }); + console.log('✅ Compte Super Admin auto-initialisé avec succès lors de la première connexion.'); + } catch (e) { + console.error('Erreur auto-création super admin:', e); + } + } if (!user) { return { diff --git a/package.json b/package.json index 2c1682e..34185b1 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,12 @@ "build": "prisma generate && next build", "build:admin": "cd admin && npx prisma generate --schema=../prisma/schema.prisma && next build", "generate": "npx prisma generate && cd admin && npx prisma generate --schema=../prisma/schema.prisma && cd ..", - "start": "npx prisma migrate deploy && next start", + "start": "npx prisma migrate deploy && node scripts/ensure-admin.cjs && next start", "start:admin": "npm run start --prefix admin", "db:migrate": "prisma migrate dev", "db:push": "prisma db push", "db:seed": "prisma db seed", + "db:init-admin": "node scripts/ensure-admin.cjs", "lint": "next lint", "lint:admin": "npm run lint --prefix admin" }, diff --git a/scripts/ensure-admin.cjs b/scripts/ensure-admin.cjs new file mode 100644 index 0000000..150f552 --- /dev/null +++ b/scripts/ensure-admin.cjs @@ -0,0 +1,73 @@ +const { PrismaClient } = require('@prisma/client'); +const bcrypt = require('bcryptjs'); + +const prisma = new PrismaClient(); + +async function main() { + const email = 'admin@afrohub.com'; + const initialPassword = process.env.ADMIN_INITIAL_PASSWORD || 'afrohub2025'; + + console.log(`🔍 Vérification du compte Super Admin (${email})...`); + + // Auto-migration: Ensure columns adminRole and adminPermissions exist + try { + await prisma.$executeRawUnsafe(` + ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "adminRole" TEXT DEFAULT 'ADMIN'; + `); + await prisma.$executeRawUnsafe(` + ALTER TABLE "User" ADD COLUMN IF NOT EXISTS "adminPermissions" TEXT[] DEFAULT ARRAY[]::TEXT[]; + `); + console.log(`✅ Structure des colonnes vérifiée / mise à jour.`); + } catch (err) { + console.warn(`⚠️ Note sur la mise à jour des colonnes:`, err.message); + } + + const existing = await prisma.user.findFirst({ + where: { email: { equals: email, mode: 'insensitive' } } + }); + + if (existing) { + if (existing.adminRole !== 'SUPER_ADMIN' || existing.role !== 'ADMIN' || existing.isSuspended) { + await prisma.user.update({ + where: { id: existing.id }, + data: { + role: 'ADMIN', + adminRole: 'SUPER_ADMIN', + isSuspended: false + } + }); + console.log(`✅ Compte existant mis à jour en SUPER_ADMIN (${email}).`); + } else { + console.log(`✅ Le compte Super Admin (${email}) est déjà présent et actif.`); + } + return; + } + + const hashedPassword = await bcrypt.hash(initialPassword, 10); + + const created = await prisma.user.create({ + data: { + name: 'Admin Afrohub', + email: email, + password: hashedPassword, + role: 'ADMIN', + adminRole: 'SUPER_ADMIN', + adminPermissions: [], + emailVerified: true + } + }); + + console.log(`🎉 Compte Super Admin créé avec succès !`); + console.log(` Email: ${created.email}`); + console.log(` Rôle: ${created.adminRole}`); + console.log(` Mot de passe initial: ${process.env.ADMIN_INITIAL_PASSWORD ? '(défini via ADMIN_INITIAL_PASSWORD)' : 'afrohub2025'}`); +} + +main() + .catch(err => { + console.error("❌ Erreur lors de l'initialisation du Super Admin:", err); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + });