feat: add admin application setup with authentication and deployment configurations
This commit is contained in:
@@ -19,4 +19,4 @@ RUN chown -R nextjs:nodejs /app
|
|||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
# On lance directement via npm (avec push de la BD si besoin, ou juste start)
|
# 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"]
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ cmds = ["npm ci"]
|
|||||||
cmds = ["npx prisma generate", "npm run build"]
|
cmds = ["npx prisma generate", "npm run build"]
|
||||||
|
|
||||||
[start]
|
[start]
|
||||||
cmd = "npx prisma db push --accept-data-loss && npm start"
|
cmd = "npx prisma db push --accept-data-loss && (node ../scripts/ensure-admin.cjs || node scripts/ensure-admin.cjs || true) && npm start"
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "prisma generate --schema=../prisma/schema.prisma && next build",
|
"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"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -42,15 +42,68 @@ export async function loginAdmin(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Search user by email (case-insensitive) or by exact name
|
let user = null;
|
||||||
const user = await prisma.user.findFirst({
|
try {
|
||||||
where: {
|
// Search user by email (case-insensitive) or by exact name
|
||||||
OR: [
|
user = await prisma.user.findFirst({
|
||||||
{ email: { equals: cleanIdentifier, mode: 'insensitive' } },
|
where: {
|
||||||
{ name: { equals: cleanIdentifier, mode: 'insensitive' } },
|
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) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -16,11 +16,12 @@
|
|||||||
"build": "prisma generate && next build",
|
"build": "prisma generate && next build",
|
||||||
"build:admin": "cd admin && npx prisma generate --schema=../prisma/schema.prisma && 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 ..",
|
"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",
|
"start:admin": "npm run start --prefix admin",
|
||||||
"db:migrate": "prisma migrate dev",
|
"db:migrate": "prisma migrate dev",
|
||||||
"db:push": "prisma db push",
|
"db:push": "prisma db push",
|
||||||
"db:seed": "prisma db seed",
|
"db:seed": "prisma db seed",
|
||||||
|
"db:init-admin": "node scripts/ensure-admin.cjs",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"lint:admin": "npm run lint --prefix admin"
|
"lint:admin": "npm run lint --prefix admin"
|
||||||
},
|
},
|
||||||
|
|||||||
73
scripts/ensure-admin.cjs
Normal file
73
scripts/ensure-admin.cjs
Normal file
@@ -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();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user