/** * Diagnostic: vérifie si l'ENCRYPTION_SECRET actuel peut déchiffrer les secrets en DB. * * Usage: * ENCRYPTION_SECRET= DATABASE_URL= \ * npx ts-node apps/api/scratch/check_encryption.ts */ import 'dotenv/config'; import { PrismaClient } from '@prisma/client'; import crypto from 'crypto'; const prisma = new PrismaClient(); const SECRET = process.env.ENCRYPTION_SECRET || ''; if (SECRET.length < 32) { console.error('❌ ENCRYPTION_SECRET manquant ou trop court (min 32 chars)'); process.exit(1); } function tryDecrypt(value: string): { ok: boolean; result: string } { if (!value.startsWith('enc:')) return { ok: true, result: `[PLAINTEXT] ${value.slice(0, 20)}...` }; const [, ivHex, encHex] = value.split(':'); if (!ivHex || !encHex) return { ok: false, result: 'Format invalide' }; try { const iv = Buffer.from(ivHex, 'hex'); const enc = Buffer.from(encHex, 'hex'); const decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(SECRET.slice(0, 32)), iv); let dec = decipher.update(enc); dec = Buffer.concat([dec, decipher.final()]); const plain = dec.toString(); return { ok: true, result: `${plain.slice(0, 6)}...${plain.slice(-4)} (len=${plain.length})` }; } catch (err: any) { return { ok: false, result: err.message }; } } const FIELDS = ['systemUserToken', 'webhookSecret', 'openAiApiKey', 'googleAiApiKey'] as const; async function check() { const orgs = await prisma.organization.findMany({ select: { id: true, name: true, systemUserToken: true, webhookSecret: true, openAiApiKey: true, googleAiApiKey: true } }); for (const org of orgs) { console.log(`\n📦 Org: ${org.name} (${org.id})`); for (const field of FIELDS) { const val = (org as any)[field] as string | null; if (!val) { console.log(` ${field}: (vide)`); continue; } const { ok, result } = tryDecrypt(val); console.log(` ${ok ? '✅' : '❌'} ${field}: ${result}`); } } await prisma.$disconnect(); } check().catch(console.error);