gongxiangp2/scripts/repair-permission-group-department.mjs

42 lines
2.8 KiB
JavaScript

// Explicit, reviewed identity repair. Default is read-only; no department-name mapping.
import { PrismaClient } from '@prisma/client'
import { mkdir, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { parseArgs } from 'node:util'
const { values } = parseArgs({ options: {
'association-id': { type: 'string' }, 'organization-unit-id': { type: 'string' },
'expected-pack-id': { type: 'string' }, 'expected-old-path': { type: 'string' },
'backup-dir': { type: 'string' }, apply: { type: 'boolean', default: false },
} })
for (const key of ['association-id', 'organization-unit-id', 'expected-pack-id', 'expected-old-path']) {
if (!values[key]) throw new Error(`Missing --${key}`)
}
if (values.apply && !values['backup-dir']) throw new Error('--apply requires --backup-dir')
const db = new PrismaClient()
try {
const row = await db.materialPermissionPackDepartment.findUnique({ where: { id: values['association-id'] } })
const unit = await db.organizationUnit.findUnique({ where: { id: values['organization-unit-id'] } })
if (!row || !unit?.isActive || row.packId !== values['expected-pack-id'] || row.departmentPath !== values['expected-old-path']) throw new Error('Reviewed association or current active department differs; stopped')
if (row.organizationUnitId && row.organizationUnitId !== unit.id) throw new Error('Association already bound to another identity; stopped')
const conflict = await db.materialPermissionPackDepartment.findUnique({ where: { organizationUnitId: unit.id } })
if (conflict && conflict.id !== row.id) throw new Error('Department identity already assigned; stopped')
const plan = { before: row, after: { organizationUnitId: unit.id, departmentPath: unit.path }, sourceDeptId: unit.sourceDeptId }
console.log(JSON.stringify({ mode: values.apply ? 'apply' : 'dry-run', ...plan }, null, 2))
if (values.apply) {
const dir = path.resolve(values['backup-dir'])
await mkdir(dir, { recursive: true, mode: 0o700 })
await writeFile(path.join(dir, `permission-department-${row.id}-${Date.now()}.json`), JSON.stringify(plan, null, 2), { mode: 0o600, flag: 'wx' })
await db.$transaction(async (tx) => {
const currentUnit = await tx.organizationUnit.findUnique({ where: { id: unit.id } })
if (!currentUnit?.isActive || currentUnit.path !== unit.path) throw new Error('Department changed during repair; stopped')
const result = await tx.materialPermissionPackDepartment.updateMany({
where: { id: row.id, packId: row.packId, departmentPath: row.departmentPath, organizationUnitId: row.organizationUnitId },
data: plan.after,
})
if (result.count !== 1) throw new Error('Association changed during repair; stopped')
}, { isolationLevel: 'Serializable' })
console.log('Verified identity repair applied; backup retained')
}
} finally { await db.$disconnect() }