Prisma迁移阶段执行数据转换脚本(如密码加密)的实现方案咨询
嘿,刚好我之前处理过类似的Prisma密码迁移需求,给你梳理下可行的方案:
首先明确说一句:Prisma的原生迁移系统(不管是prisma migrate dev还是migrate deploy)并不直接支持在迁移流程中运行自定义的TypeScript/JS脚本——因为它的核心是处理SQL层面的Schema变更,没法直接嵌入业务逻辑类的转换操作。不过我们可以通过分步操作来实现你的需求,完全可控。
下面是针对你的场景的具体步骤:
1. 先扩展Schema,新增哈希密码字段
首先不要直接删掉原有的password明文字段,而是新增一个可选的hashedPassword字段来存储bcrypt哈希值(bcrypt生成的哈希通常是60位左右,所以设@db.VarChar(60)刚好)。修改后的User模型如下:
model User { id String @id @unique @db.Char(32) // unsigned UUID name String @unique @db.VarChar(16) password String @db.VarChar(32) // 暂时保留明文密码字段 hashedPassword String? @db.VarChar(60) // 新增可选的哈希密码字段 articles Article[] createdAt DateTime @default(now()) }
然后执行命令生成并运行迁移:
prisma migrate dev --name add-hashed-password-field
这一步会在数据库中新增hashedPassword字段,不会影响现有数据。
2. 编写自定义脚本,批量转换明文密码为哈希值
接下来我们写一个TypeScript脚本,用Prisma Client读取所有用户的明文密码,生成bcrypt哈希后更新到新字段。比如创建migrate-passwords.ts:
import { PrismaClient } from '@prisma/client'; import bcrypt from 'bcrypt'; const prisma = new PrismaClient(); async function migratePasswords() { // 只获取还没生成哈希密码的用户 const users = await prisma.user.findMany({ where: { password: { not: null }, hashedPassword: null } }); // 循环处理每个用户 for (const user of users) { if (user.password) { // bcrypt默认用10轮盐值,这个参数可以根据需求调整 const hashed = await bcrypt.hash(user.password, 10); await prisma.user.update({ where: { id: user.id }, data: { hashedPassword: hashed } }); console.log(`已处理用户: ${user.name}`); } } console.log('所有密码哈希转换完成!'); await prisma.$disconnect(); } // 执行脚本并处理异常 migratePasswords().catch(async (err) => { console.error('转换过程出错:', err); await prisma.$disconnect(); process.exit(1); });
然后执行这个脚本:
npx ts-node migrate-passwords.ts
如果是JavaScript环境,直接用node migrate-passwords.js即可,记得先安装依赖bcrypt(或者bcryptjs,看你偏好)。
3. 验证业务逻辑切换
在删除原明文密码字段之前,一定要先修改你的业务代码——比如登录接口,把验证逻辑从对比明文密码,改成用bcrypt.compare()对比用户输入和hashedPassword字段。确认所有登录、用户相关的流程都能正常工作,再进行下一步。
4. 清理Schema和数据库字段
当你确认哈希密码完全可用后,就可以移除原有的password明文字段了。修改后的最终User模型:
model User { id String @id @unique @db.Char(32) // unsigned UUID name String @unique @db.VarChar(16) hashedPassword String @db.VarChar(60) // 现在设为必填字段 articles Article[] createdAt DateTime @default(now()) }
然后生成并运行最终的迁移:
prisma migrate dev --name remove-plaintext-password-field
额外注意事项
- 备份优先:执行任何数据变更前,一定要先备份数据库,避免操作失误导致数据丢失。
- 批量优化:如果你的用户量很大,建议在脚本中加入分页逻辑(用
skip和take),避免一次性加载所有用户导致内存溢出。 - 生产环境流程:生产环境用
prisma migrate deploy时,流程和本地一致:先部署新增字段的迁移,在服务器上执行密码转换脚本,验证后再部署移除原字段的迁移,尽量选低峰期操作。
内容来源于stack exchange

