hp-prod-tracker/scripts/backfill-org-ids.ts
Leivur R. Djurhuus 40028b7ced feat: add pipeline stage resolver and organization access control
- Implemented `stage-resolver.ts` to unify old and new pipeline stage definitions.
- Created `org-scope.ts` for organization access verification and scoping queries.
- Added role-based permissions management in `permissions.ts` and `rbac-service.ts`.
- Introduced invitation management in `invitation-service.ts` with validation schemas.
- Developed custom field and notification rule services with respective validators.
- Established pipeline template CRUD operations in `pipeline-template-service.ts`.
- Added Zustand store for managing pipeline builder state in `pipeline-builder-store.ts`.
2026-03-14 22:43:43 -05:00

99 lines
2.9 KiB
TypeScript

/**
* Backfill Organization IDs Script
*
* Populates the new organizationId columns on Deliverable and DeliverableStage
* by copying from their parent Project's organizationId.
*
* Run with: npx tsx scripts/backfill-org-ids.ts
*/
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../src/generated/prisma/client";
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! });
const prisma = new PrismaClient({ adapter });
async function main() {
console.log("Backfilling organizationId on deliverables...");
// Get all deliverables missing organizationId
const deliverables = await prisma.deliverable.findMany({
where: { organizationId: null },
select: { id: true, projectId: true },
});
if (deliverables.length === 0) {
console.log("No deliverables need backfill.");
} else {
// Group by projectId for efficiency
const projectIds = [...new Set(deliverables.map((d) => d.projectId))];
const projects = await prisma.project.findMany({
where: { id: { in: projectIds } },
select: { id: true, organizationId: true },
});
const projectOrgMap = new Map(projects.map((p) => [p.id, p.organizationId]));
let updated = 0;
for (const deliverable of deliverables) {
const orgId = projectOrgMap.get(deliverable.projectId);
if (!orgId) {
console.warn(` Skipping deliverable ${deliverable.id} — project ${deliverable.projectId} has no org`);
continue;
}
await prisma.deliverable.update({
where: { id: deliverable.id },
data: { organizationId: orgId },
});
updated++;
}
console.log(` Updated ${updated}/${deliverables.length} deliverables.`);
}
console.log("Backfilling organizationId on deliverable stages...");
const stages = await prisma.deliverableStage.findMany({
where: { organizationId: null },
select: {
id: true,
deliverable: {
select: { project: { select: { organizationId: true } } },
},
},
});
if (stages.length === 0) {
console.log("No stages need backfill.");
} else {
let updated = 0;
// Batch update for efficiency
const batchSize = 100;
for (let i = 0; i < stages.length; i += batchSize) {
const batch = stages.slice(i, i + batchSize);
await prisma.$transaction(
batch.map((stage) =>
prisma.deliverableStage.update({
where: { id: stage.id },
data: { organizationId: stage.deliverable.project.organizationId },
})
)
);
updated += batch.length;
if (updated % 500 === 0) {
console.log(` Progress: ${updated}/${stages.length}`);
}
}
console.log(` Updated ${updated}/${stages.length} stages.`);
}
console.log("Backfill complete!");
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});