import AdminJS from 'adminjs' import AdminJSFastify from '@adminjs/fastify' import { Database, Resource, getModelByName } from '@adminjs/prisma' import bcrypt from 'bcrypt' import { exec } from 'child_process' import { prisma } from './src/lib/db.mjs' AdminJS.registerAdapter({ Database, Resource }) // ── Auto-build (prod only) ────────────────────────────────────────────────── let buildTimeout = null let buildInProgress = false function triggerBuild() { if (process.env.NODE_ENV !== 'production') { console.log('[admin] Dev mode — skipping build') return } if (buildTimeout) clearTimeout(buildTimeout) buildTimeout = setTimeout(() => { if (buildInProgress) return buildInProgress = true console.log('[admin] Building site...') exec('pnpm build', { cwd: process.cwd(), timeout: 120_000 }, (err, stdout, stderr) => { buildInProgress = false if (err) console.error('[admin] Build FAILED:', stderr) else console.log('[admin] Build OK') }) }, 5_000) } const afterBuildHook = async (response) => { triggerBuild(); return response } // ── AdminJS setup ─────────────────────────────────────────────────────────── export async function setupAdmin(app) { const admin = new AdminJS({ rootPath: '/admin', resources: [ { resource: { model: getModelByName('Product'), client: prisma }, options: { navigation: { name: 'Contenu', icon: 'Package' }, listProperties: ['sortOrder', 'name', 'type', 'price', 'isPublished', 'updatedAt'], editProperties: [ 'slug', 'sortOrder', 'index', 'name', 'type', 'materials', 'year', 'status', 'description', 'specs', 'notes', 'imagePath', 'imageAlt', 'seoTitle', 'seoDescription', 'ogImage', 'productDisplayName', 'price', 'currency', 'availability', 'stripePriceId', 'stripeKey', 'isPublished', ], properties: { description: { type: 'textarea' }, specs: { type: 'textarea' }, notes: { type: 'textarea' }, seoDescription: { type: 'textarea' }, }, actions: { new: { after: [afterBuildHook] }, edit: { after: [afterBuildHook] }, delete: { after: [afterBuildHook] }, }, }, }, { resource: { model: getModelByName('Order'), client: prisma }, options: { navigation: { name: 'Commerce', icon: 'CreditCard' }, listProperties: ['stripeSessionId', 'status', 'amount', 'customerEmail', 'productSlug', 'createdAt'], actions: { new: { isAccessible: false }, edit: { isAccessible: false }, delete: { isAccessible: false }, }, }, }, ], branding: { companyName: 'REBOURS Studio', logo: false, withMadeWithLove: false, }, }) await AdminJSFastify.buildAuthenticatedRouter( admin, { authenticate: async (email, password) => { const user = await prisma.adminUser.findUnique({ where: { email } }) if (!user) return null const valid = await bcrypt.compare(password, user.passwordHash) return valid ? { email: user.email, id: user.id } : null }, cookiePassword: process.env.COOKIE_SECRET ?? 'super-secret-cookie-password-at-least-32-chars', cookieName: 'adminjs', }, app, { saveUninitialized: false, cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', }, } ) return admin }