68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
/**
|
|
* Migrate existing plain-text messages to encrypted form.
|
|
*
|
|
* Run once after enabling ENCRYPTION_KEY:
|
|
* npx ts-node prisma/encrypt-existing.ts
|
|
*
|
|
* Safe to re-run — skips already-encrypted messages ("enc:v1:" prefix).
|
|
*/
|
|
import '../src/config'; // loads .env & initialises encryption
|
|
import { PrismaClient } from '@prisma/client';
|
|
import { encryptText, isEncryptionEnabled } from '../src/encrypt';
|
|
|
|
// Use raw PrismaClient to bypass the encryption middleware (avoid double-encryption)
|
|
const rawPrisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
if (!isEncryptionEnabled()) {
|
|
console.error('❌ ENCRYPTION_KEY не задан в .env — сначала укажите ключ шифрования.');
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log('🔒 Начало шифрования существующих сообщений…\n');
|
|
|
|
// We bypass the Prisma middleware by using $queryRawUnsafe for the SELECT,
|
|
// then use raw UPDATE to avoid double-encryption via middleware.
|
|
const messages: Array<{ id: string; content: string | null; quote: string | null }> =
|
|
await rawPrisma.$queryRaw`
|
|
SELECT id, content, quote FROM "Message"
|
|
WHERE (content IS NOT NULL AND content != '' AND content NOT LIKE 'enc:v1:%')
|
|
OR (quote IS NOT NULL AND quote != '' AND quote NOT LIKE 'enc:v1:%')
|
|
`;
|
|
|
|
console.log(`📝 Найдено ${messages.length} незашифрованных сообщений`);
|
|
|
|
let encrypted = 0;
|
|
const BATCH_SIZE = 500;
|
|
|
|
for (let i = 0; i < messages.length; i += BATCH_SIZE) {
|
|
const batch = messages.slice(i, i + BATCH_SIZE);
|
|
await rawPrisma.$transaction(
|
|
batch.map((msg) => {
|
|
const newContent = msg.content && !msg.content.startsWith('enc:v1:')
|
|
? encryptText(msg.content) : null;
|
|
const newQuote = msg.quote && !msg.quote.startsWith('enc:v1:')
|
|
? encryptText(msg.quote) : null;
|
|
|
|
return rawPrisma.$executeRaw`
|
|
UPDATE "Message"
|
|
SET content = COALESCE(${newContent}::text, content),
|
|
quote = COALESCE(${newQuote}::text, quote)
|
|
WHERE id = ${msg.id}
|
|
`;
|
|
})
|
|
);
|
|
encrypted += batch.length;
|
|
process.stdout.write(` ✔ ${encrypted}/${messages.length}\r`);
|
|
}
|
|
|
|
console.log(`\n\n✅ Готово! Зашифровано ${encrypted} сообщений.`);
|
|
console.log('⚠ СОХРАНИТЕ КЛЮЧ ENCRYPTION_KEY В НАДЁЖНОМ МЕСТЕ — без него данные не восстановить!');
|
|
await rawPrisma.$disconnect();
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error('Ошибка миграции:', e);
|
|
process.exit(1);
|
|
});
|