feat: (PBAC) Add db seed (#22704)
* add a seed for pbac * remove readme * add team feature * add seed script to json * log seed detials
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env tsx
|
||||
|
||||
/**
|
||||
* Cleanup script to remove PBAC demo organization and related data
|
||||
* Run with: npx tsx packages/prisma/cleanup-pbac.ts
|
||||
*/
|
||||
import prisma from ".";
|
||||
|
||||
async function cleanupPBACOrganization() {
|
||||
console.log("🧹 Cleaning up PBAC Demo Organization...");
|
||||
|
||||
try {
|
||||
// Find the organization
|
||||
const organization = await prisma.team.findFirst({
|
||||
where: {
|
||||
slug: "pbac-demo-org",
|
||||
isOrganization: true,
|
||||
},
|
||||
include: {
|
||||
members: true,
|
||||
roles: true,
|
||||
children: true, // teams within the organization
|
||||
},
|
||||
});
|
||||
|
||||
if (!organization) {
|
||||
console.log("ℹ️ PBAC Demo Organization not found, nothing to clean up");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found organization: ${organization.name} (ID: ${organization.id})`);
|
||||
|
||||
// Delete users created for PBAC demo
|
||||
const pbacUserEmails = [
|
||||
"owner@pbac-demo.com",
|
||||
"events@pbac-demo.com",
|
||||
"analytics@pbac-demo.com",
|
||||
"coordinator@pbac-demo.com",
|
||||
"support@pbac-demo.com",
|
||||
];
|
||||
|
||||
const pbacUsers = await prisma.user.findMany({
|
||||
where: {
|
||||
email: { in: pbacUserEmails },
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Found ${pbacUsers.length} PBAC users to delete`);
|
||||
|
||||
// Delete users (this will cascade delete memberships, profiles, etc.)
|
||||
for (const user of pbacUsers) {
|
||||
await prisma.user.delete({
|
||||
where: { id: user.id },
|
||||
});
|
||||
console.log(` ✅ Deleted user: ${user.email}`);
|
||||
}
|
||||
|
||||
// Delete custom roles (this will cascade delete permissions)
|
||||
for (const role of organization.roles) {
|
||||
await prisma.role.delete({
|
||||
where: { id: role.id },
|
||||
});
|
||||
console.log(` ✅ Deleted role: ${role.name}`);
|
||||
}
|
||||
|
||||
// Delete child teams
|
||||
for (const team of organization.children) {
|
||||
await prisma.team.delete({
|
||||
where: { id: team.id },
|
||||
});
|
||||
console.log(` ✅ Deleted team: ${team.name}`);
|
||||
}
|
||||
|
||||
// Delete the organization (this will cascade delete organization settings)
|
||||
await prisma.team.delete({
|
||||
where: { id: organization.id },
|
||||
});
|
||||
|
||||
console.log(` ✅ Deleted organization: ${organization.name}`);
|
||||
|
||||
// Clean up any temp org redirects
|
||||
await prisma.tempOrgRedirect.deleteMany({
|
||||
where: {
|
||||
OR: [
|
||||
{ from: { in: ["owner", "events", "analytics", "coordinator", "support"] } },
|
||||
{ toUrl: { contains: "pbac-demo-org" } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
console.log(" ✅ Cleaned up temp org redirects");
|
||||
|
||||
console.log("\n🎉 PBAC Demo Organization cleanup completed successfully!");
|
||||
} catch (error) {
|
||||
console.error("❌ Error during cleanup:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
cleanupPBACOrganization()
|
||||
.then(() => {
|
||||
console.log("✅ Cleanup completed!");
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("❌ Cleanup failed:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -18,7 +18,8 @@
|
||||
"post-install": "yarn generate-schemas",
|
||||
"seed-app-store": "ts-node --transpile-only ./seed-app-store.ts",
|
||||
"delete-app": "ts-node --transpile-only ./delete-app.ts",
|
||||
"seed-insights": "ts-node --transpile-only ./seed-insights.ts"
|
||||
"seed-insights": "ts-node --transpile-only ./seed-insights.ts",
|
||||
"seed-pbac": "ts-node --transpile-only ./seed-pbac-organization.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "9.2.0",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env tsx
|
||||
|
||||
/**
|
||||
* Standalone script to create a PBAC-enabled organization with custom roles
|
||||
* Run with: npx tsx packages/prisma/seed-pbac-only.ts
|
||||
*/
|
||||
import { createPBACOrganization } from "./seed-pbac-organization";
|
||||
|
||||
async function main() {
|
||||
console.log("🚀 Starting PBAC organization seed...");
|
||||
|
||||
try {
|
||||
const result = await createPBACOrganization();
|
||||
|
||||
console.log("\n🎉 PBAC Organization created successfully!");
|
||||
console.log("\n📋 Summary:");
|
||||
console.log(`Organization: ${result.organization.name} (${result.organization.slug})`);
|
||||
console.log(`Custom Roles: ${Object.keys(result.customRoles).length}`);
|
||||
console.log(`Users: ${result.users.length}`);
|
||||
console.log(`Team: ${result.team?.name || "None"} (${result.team?.slug || "N/A"})`);
|
||||
|
||||
console.log("\n🔐 Login Credentials:");
|
||||
result.users.forEach(({ user, role, customRole }) => {
|
||||
const roleText = customRole ? `${role} + ${customRole}` : role;
|
||||
console.log(` - ${user.name}: ${user.email} / password (${roleText})`);
|
||||
});
|
||||
|
||||
console.log(`\n🌐 Access URLs:`);
|
||||
console.log(`Organization: ${process.env.NEXT_PUBLIC_WEBAPP_URL}/org/${result.organization.slug}`);
|
||||
if (result.team) {
|
||||
console.log(`Team: ${process.env.NEXT_PUBLIC_WEBAPP_URL}/team/${result.team.slug}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Error creating PBAC organization:", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => {
|
||||
console.log("\n✅ Seed completed successfully!");
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("❌ Seed failed:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,706 @@
|
||||
import { uuid } from "short-uuid";
|
||||
|
||||
import { hashPassword } from "@calcom/features/auth/lib/hashPassword";
|
||||
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
|
||||
import { MembershipRole, RoleType } from "@calcom/prisma/enums";
|
||||
|
||||
import prisma from ".";
|
||||
|
||||
/**
|
||||
* Creates an organization with custom roles and PBAC (Permission-Based Access Control) enabled
|
||||
* This demonstrates how to set up fine-grained permissions for team members
|
||||
*/
|
||||
export async function createPBACOrganization() {
|
||||
console.log("🏢 Creating PBAC-enabled organization with custom roles...");
|
||||
|
||||
// Check if organization already exists
|
||||
const existingOrg = await prisma.team.findFirst({
|
||||
where: {
|
||||
slug: "pbac-demo-org",
|
||||
isOrganization: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (existingOrg) {
|
||||
console.log("⚠️ PBAC Demo Organization already exists, skipping creation");
|
||||
return {
|
||||
organization: existingOrg,
|
||||
customRoles: {},
|
||||
users: [],
|
||||
team: null,
|
||||
};
|
||||
}
|
||||
|
||||
// First, create the organization
|
||||
const organization = await prisma.team.create({
|
||||
data: {
|
||||
name: "PBAC Demo Organization",
|
||||
slug: "pbac-demo-org",
|
||||
isOrganization: true,
|
||||
metadata: {
|
||||
isOrganization: true,
|
||||
},
|
||||
organizationSettings: {
|
||||
create: {
|
||||
isOrganizationVerified: true,
|
||||
orgAutoAcceptEmail: "pbac-demo.com",
|
||||
isAdminAPIEnabled: true,
|
||||
isAdminReviewed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Add the feature flag
|
||||
await prisma.teamFeatures.create({
|
||||
data: {
|
||||
featureId: "pbac",
|
||||
teamId: organization.id,
|
||||
assignedBy: "system (Seed script)",
|
||||
assignedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Created organization: ${organization.name} (ID: ${organization.id})`);
|
||||
|
||||
// Create custom roles with specific permissions
|
||||
const customRoles = await createCustomRoles(organization.id);
|
||||
|
||||
// Create users with different roles
|
||||
const users = await createUsersWithRoles(organization.id, customRoles);
|
||||
|
||||
// Create a team within the organization
|
||||
const team = await createTeamWithCustomRoles(organization.id, users, customRoles);
|
||||
|
||||
console.log("🎉 PBAC organization setup complete!");
|
||||
console.log(`Organization URL: ${process.env.NEXT_PUBLIC_WEBAPP_URL}/org/${organization.slug}`);
|
||||
|
||||
// Display created users with their credentials and permissions
|
||||
console.log("\n📋 Created Users - Login Credentials & Permissions:");
|
||||
console.log("=".repeat(80));
|
||||
users.forEach(({ user, role, customRole }) => {
|
||||
const password = getPasswordForUser(user.email);
|
||||
const permissions = getPermissionsForRole(user.email, customRoles);
|
||||
|
||||
console.log(`👤 ${user.name}`);
|
||||
console.log(` 📧 Email: ${user.email}`);
|
||||
console.log(` 🔑 Password: ${password}`);
|
||||
console.log(` 👔 Role: ${role}${customRole ? ` (${customRole})` : ""}`);
|
||||
|
||||
if (permissions.length > 0) {
|
||||
console.log(` 🔐 Permissions:`);
|
||||
permissions.forEach((permission) => {
|
||||
console.log(` • ${permission.resource}:${permission.action}`);
|
||||
});
|
||||
} else {
|
||||
console.log(` 🔐 Permissions: Full access (Owner role)`);
|
||||
}
|
||||
console.log("");
|
||||
});
|
||||
console.log("=".repeat(80));
|
||||
|
||||
return {
|
||||
organization,
|
||||
customRoles,
|
||||
users,
|
||||
team,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates custom roles with specific permissions for the organization
|
||||
*/
|
||||
async function createCustomRoles(organizationId: number) {
|
||||
console.log("🔐 Creating custom roles with PBAC permissions...");
|
||||
|
||||
// Check if roles already exist
|
||||
const existingRoles = await prisma.role.findMany({
|
||||
where: { teamId: organizationId },
|
||||
});
|
||||
|
||||
if (existingRoles.length > 0) {
|
||||
console.log("⚠️ Custom roles already exist for this organization, skipping creation");
|
||||
return {
|
||||
eventManager: existingRoles.find((r) => r.name === "Event Manager"),
|
||||
analytics: existingRoles.find((r) => r.name === "Analytics Specialist"),
|
||||
teamCoordinator: existingRoles.find((r) => r.name === "Team Coordinator"),
|
||||
supportAgent: existingRoles.find((r) => r.name === "Support Agent"),
|
||||
};
|
||||
}
|
||||
|
||||
// Event Manager Role - Can manage event types and bookings
|
||||
const eventManagerRole = await prisma.role.create({
|
||||
data: {
|
||||
id: `event_manager_${organizationId}`,
|
||||
name: "Event Manager",
|
||||
description: "Can create and manage event types, view bookings and recordings",
|
||||
color: "#3B82F6", // Blue
|
||||
teamId: organizationId,
|
||||
type: RoleType.CUSTOM,
|
||||
permissions: {
|
||||
create: [
|
||||
// Event Type permissions
|
||||
{ resource: "eventType", action: "create" },
|
||||
{ resource: "eventType", action: "read" },
|
||||
{ resource: "eventType", action: "update" },
|
||||
{ resource: "eventType", action: "delete" },
|
||||
|
||||
// Booking permissions
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "booking", action: "update" },
|
||||
{ resource: "booking", action: "readRecordings" },
|
||||
|
||||
// Team read access
|
||||
{ resource: "team", action: "read" },
|
||||
|
||||
// Availability override
|
||||
{ resource: "availability", action: "override" },
|
||||
],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
permissions: true,
|
||||
},
|
||||
});
|
||||
// Analytics Specialist Role - Can view insights and reports
|
||||
const analyticsRole = await prisma.role.create({
|
||||
data: {
|
||||
id: `analytics_specialist_${organizationId}`,
|
||||
name: "Analytics Specialist",
|
||||
description: "Can view insights, reports, and booking analytics",
|
||||
color: "#10B981", // Green
|
||||
teamId: organizationId,
|
||||
type: RoleType.CUSTOM,
|
||||
permissions: {
|
||||
create: [
|
||||
// Insights permissions
|
||||
{ resource: "insights", action: "read" },
|
||||
|
||||
// Booking read access
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "booking", action: "readTeamBookings" },
|
||||
{ resource: "booking", action: "readOrgBookings" },
|
||||
|
||||
// Event Type read access
|
||||
{ resource: "eventType", action: "read" },
|
||||
|
||||
// Team and organization read access
|
||||
{ resource: "team", action: "read" },
|
||||
{ resource: "organization", action: "read" },
|
||||
{ resource: "organization", action: "listMembers" },
|
||||
|
||||
// Routing forms for conversion tracking
|
||||
{ resource: "routingForm", action: "read" },
|
||||
],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
permissions: true,
|
||||
},
|
||||
});
|
||||
// Team Coordinator Role - Can manage team members and workflows
|
||||
const teamCoordinatorRole = await prisma.role.create({
|
||||
data: {
|
||||
id: `team_coordinator_${organizationId}`,
|
||||
name: "Team Coordinator",
|
||||
description: "Can invite/remove team members, manage workflows and routing forms",
|
||||
color: "#8B5CF6", // Purple
|
||||
teamId: organizationId,
|
||||
type: RoleType.CUSTOM,
|
||||
permissions: {
|
||||
create: [
|
||||
// Team management
|
||||
{ resource: "team", action: "read" },
|
||||
{ resource: "team", action: "update" },
|
||||
{ resource: "team", action: "invite" },
|
||||
{ resource: "team", action: "remove" },
|
||||
{ resource: "team", action: "changeMemberRole" },
|
||||
|
||||
// Organization member management
|
||||
{ resource: "organization", action: "read" },
|
||||
{ resource: "organization", action: "listMembers" },
|
||||
|
||||
// Workflow management
|
||||
{ resource: "workflow", action: "create" },
|
||||
{ resource: "workflow", action: "read" },
|
||||
{ resource: "workflow", action: "update" },
|
||||
{ resource: "workflow", action: "delete" },
|
||||
|
||||
// Routing form management
|
||||
{ resource: "routingForm", action: "create" },
|
||||
{ resource: "routingForm", action: "read" },
|
||||
{ resource: "routingForm", action: "update" },
|
||||
{ resource: "routingForm", action: "delete" },
|
||||
|
||||
// Basic booking access
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "eventType", action: "read" },
|
||||
],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
permissions: true,
|
||||
},
|
||||
});
|
||||
// Support Agent Role - Limited access for customer support
|
||||
const supportAgentRole = await prisma.role.create({
|
||||
data: {
|
||||
id: `support_agent_${organizationId}`,
|
||||
name: "Support Agent",
|
||||
description: "Can view bookings and basic event information for customer support",
|
||||
color: "#F59E0B", // Amber
|
||||
teamId: organizationId,
|
||||
type: RoleType.CUSTOM,
|
||||
permissions: {
|
||||
create: [
|
||||
// Limited booking access
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "booking", action: "update" }, // For rescheduling/canceling
|
||||
|
||||
// Event Type read access
|
||||
{ resource: "eventType", action: "read" },
|
||||
|
||||
// Team read access
|
||||
{ resource: "team", action: "read" },
|
||||
|
||||
// Organization read access
|
||||
{ resource: "organization", action: "read" },
|
||||
],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
permissions: true,
|
||||
},
|
||||
});
|
||||
console.log("✅ Created custom roles:");
|
||||
console.log(` - Event Manager (${eventManagerRole.permissions.length} permissions)`);
|
||||
console.log(` - Analytics Specialist (${analyticsRole.permissions.length} permissions)`);
|
||||
console.log(` - Team Coordinator (${teamCoordinatorRole.permissions.length} permissions)`);
|
||||
console.log(` - Support Agent (${supportAgentRole.permissions.length} permissions)`);
|
||||
|
||||
return {
|
||||
eventManager: eventManagerRole,
|
||||
analytics: analyticsRole,
|
||||
teamCoordinator: teamCoordinatorRole,
|
||||
supportAgent: supportAgentRole,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates users with different roles in the organization
|
||||
*/
|
||||
async function createUsersWithRoles(organizationId: number, customRoles: any) {
|
||||
console.log("👥 Creating users with custom roles...");
|
||||
|
||||
const users: Array<{
|
||||
user: any;
|
||||
role: MembershipRole;
|
||||
customRole: string | null;
|
||||
}> = [];
|
||||
|
||||
// Check if users already exist
|
||||
const existingUsers = await prisma.user.findMany({
|
||||
where: {
|
||||
email: {
|
||||
in: [
|
||||
"owner@pbac-demo.com",
|
||||
"events@pbac-demo.com",
|
||||
"analytics@pbac-demo.com",
|
||||
"coordinator@pbac-demo.com",
|
||||
"support@pbac-demo.com",
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (existingUsers.length > 0) {
|
||||
console.log("⚠️ Some PBAC users already exist, skipping user creation");
|
||||
return existingUsers.map((user) => ({
|
||||
user,
|
||||
role: MembershipRole.MEMBER,
|
||||
customRole: null,
|
||||
}));
|
||||
}
|
||||
|
||||
// Organization Owner
|
||||
const owner = await createUser({
|
||||
email: "owner@pbac-demo.com",
|
||||
username: "pbac-owner",
|
||||
name: "Organization Owner",
|
||||
password: "pbac-owner-2024!",
|
||||
});
|
||||
|
||||
await createMembership({
|
||||
userId: owner.id,
|
||||
teamId: organizationId,
|
||||
role: MembershipRole.OWNER,
|
||||
customRoleId: null, // Uses default owner role
|
||||
});
|
||||
|
||||
await createProfile({
|
||||
userId: owner.id,
|
||||
organizationId,
|
||||
username: "owner",
|
||||
});
|
||||
|
||||
users.push({ user: owner, role: MembershipRole.OWNER, customRole: null });
|
||||
|
||||
// Event Manager
|
||||
const eventManager = await createUser({
|
||||
email: "events@pbac-demo.com",
|
||||
username: "pbac-events",
|
||||
name: "Event Manager",
|
||||
password: "events-2024!",
|
||||
});
|
||||
|
||||
await createMembership({
|
||||
userId: eventManager.id,
|
||||
teamId: organizationId,
|
||||
role: MembershipRole.MEMBER,
|
||||
customRoleId: customRoles.eventManager.id,
|
||||
});
|
||||
|
||||
await createProfile({
|
||||
userId: eventManager.id,
|
||||
organizationId,
|
||||
username: "events",
|
||||
});
|
||||
|
||||
users.push({ user: eventManager, role: MembershipRole.MEMBER, customRole: "Event Manager" });
|
||||
|
||||
// Analytics Specialist
|
||||
const analytics = await createUser({
|
||||
email: "analytics@pbac-demo.com",
|
||||
username: "pbac-analytics",
|
||||
name: "Analytics Specialist",
|
||||
password: "analytics-2024!",
|
||||
});
|
||||
|
||||
await createMembership({
|
||||
userId: analytics.id,
|
||||
teamId: organizationId,
|
||||
role: MembershipRole.MEMBER,
|
||||
customRoleId: customRoles.analytics.id,
|
||||
});
|
||||
|
||||
await createProfile({
|
||||
userId: analytics.id,
|
||||
organizationId,
|
||||
username: "analytics",
|
||||
});
|
||||
|
||||
users.push({ user: analytics, role: MembershipRole.MEMBER, customRole: "Analytics Specialist" });
|
||||
|
||||
// Team Coordinator
|
||||
const coordinator = await createUser({
|
||||
email: "coordinator@pbac-demo.com",
|
||||
username: "pbac-coordinator",
|
||||
name: "Team Coordinator",
|
||||
password: "coordinator-2024!",
|
||||
});
|
||||
|
||||
await createMembership({
|
||||
userId: coordinator.id,
|
||||
teamId: organizationId,
|
||||
role: MembershipRole.ADMIN,
|
||||
customRoleId: customRoles.teamCoordinator.id,
|
||||
});
|
||||
|
||||
await createProfile({
|
||||
userId: coordinator.id,
|
||||
organizationId,
|
||||
username: "coordinator",
|
||||
});
|
||||
|
||||
users.push({ user: coordinator, role: MembershipRole.ADMIN, customRole: "Team Coordinator" });
|
||||
|
||||
// Support Agent
|
||||
const support = await createUser({
|
||||
email: "support@pbac-demo.com",
|
||||
username: "pbac-support",
|
||||
name: "Support Agent",
|
||||
password: "support-2024!",
|
||||
});
|
||||
|
||||
await createMembership({
|
||||
userId: support.id,
|
||||
teamId: organizationId,
|
||||
role: MembershipRole.MEMBER,
|
||||
customRoleId: customRoles.supportAgent.id,
|
||||
});
|
||||
|
||||
await createProfile({
|
||||
userId: support.id,
|
||||
organizationId,
|
||||
username: "support",
|
||||
});
|
||||
|
||||
users.push({ user: support, role: MembershipRole.MEMBER, customRole: "Support Agent" });
|
||||
|
||||
console.log("✅ Created users with roles:");
|
||||
users.forEach(({ user, role, customRole }) => {
|
||||
console.log(` - ${user.name} (${user.email}) - ${role}${customRole ? ` + ${customRole}` : ""}`);
|
||||
});
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a team within the organization with custom role assignments
|
||||
*/
|
||||
async function createTeamWithCustomRoles(organizationId: number, users: any[], customRoles: any) {
|
||||
console.log("🏢 Creating team with custom role assignments...");
|
||||
|
||||
const team = await prisma.team.create({
|
||||
data: {
|
||||
name: "Sales Team",
|
||||
slug: "sales-team",
|
||||
parentId: organizationId,
|
||||
metadata: {},
|
||||
},
|
||||
});
|
||||
|
||||
// Add team members with their custom roles
|
||||
for (const { user } of users) {
|
||||
let customRoleId: string | null = null;
|
||||
|
||||
// Assign appropriate roles for the team
|
||||
if (user.email === "owner@pbac-demo.com") {
|
||||
customRoleId = "owner_role";
|
||||
} else if (user.email === "coordinator@pbac-demo.com") {
|
||||
customRoleId = customRoles.teamCoordinator.id;
|
||||
} else if (user.email === "events@pbac-demo.com") {
|
||||
customRoleId = customRoles.eventManager.id;
|
||||
} else if (user.email === "analytics@pbac-demo.com") {
|
||||
customRoleId = customRoles.analytics.id;
|
||||
} else if (user.email === "support@pbac-demo.com") {
|
||||
customRoleId = customRoles.supportAgent.id;
|
||||
}
|
||||
|
||||
await prisma.membership.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
role: MembershipRole.MEMBER,
|
||||
customRoleId,
|
||||
accepted: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Create some event types for the team
|
||||
await prisma.eventType.create({
|
||||
data: {
|
||||
title: "Sales Consultation",
|
||||
slug: "sales-consultation",
|
||||
length: 30,
|
||||
teamId: team.id,
|
||||
userId: users[0].user.id, // Owner as the creator
|
||||
users: {
|
||||
connect: users.map(({ user }) => ({ id: user.id })),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.eventType.create({
|
||||
data: {
|
||||
title: "Product Demo",
|
||||
slug: "product-demo",
|
||||
length: 45,
|
||||
teamId: team.id,
|
||||
userId: users[0].user.id, // Owner as the creator
|
||||
users: {
|
||||
connect: users.slice(0, 3).map(({ user }) => ({ id: user.id })), // First 3 users
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`✅ Created team: ${team.name} with ${users.length} members`);
|
||||
console.log(`Team URL: ${process.env.NEXT_PUBLIC_WEBAPP_URL}/team/${team.slug}`);
|
||||
|
||||
return team;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a user
|
||||
*/
|
||||
async function createUser(userData: { email: string; username: string; name: string; password: string }) {
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
email: userData.email,
|
||||
username: userData.username,
|
||||
name: userData.name,
|
||||
emailVerified: new Date(),
|
||||
completedOnboarding: true,
|
||||
locale: "en",
|
||||
schedules: {
|
||||
create: {
|
||||
name: "Working Hours",
|
||||
availability: {
|
||||
createMany: {
|
||||
data: getAvailabilityFromSchedule(DEFAULT_SCHEDULE),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.userPassword.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
hash: await hashPassword(userData.password),
|
||||
},
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a membership
|
||||
*/
|
||||
async function createMembership({
|
||||
userId,
|
||||
teamId,
|
||||
role,
|
||||
customRoleId,
|
||||
}: {
|
||||
userId: number;
|
||||
teamId: number;
|
||||
role: MembershipRole;
|
||||
customRoleId: string | null;
|
||||
}) {
|
||||
return prisma.membership.create({
|
||||
data: {
|
||||
userId,
|
||||
teamId,
|
||||
role,
|
||||
customRoleId,
|
||||
accepted: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to create a profile
|
||||
*/
|
||||
async function createProfile({
|
||||
userId,
|
||||
organizationId,
|
||||
username,
|
||||
}: {
|
||||
userId: number;
|
||||
organizationId: number;
|
||||
username: string;
|
||||
}) {
|
||||
const profile = await prisma.profile.create({
|
||||
data: {
|
||||
uid: uuid(),
|
||||
userId,
|
||||
organizationId,
|
||||
username,
|
||||
},
|
||||
});
|
||||
|
||||
// Update user to point to this profile
|
||||
await prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: { movedToProfileId: profile.id },
|
||||
});
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get password for a user based on their email
|
||||
*/
|
||||
function getPasswordForUser(email: string): string {
|
||||
const passwordMap: Record<string, string> = {
|
||||
"owner@pbac-demo.com": "pbac-owner-2024!",
|
||||
"events@pbac-demo.com": "events-2024!",
|
||||
"analytics@pbac-demo.com": "analytics-2024!",
|
||||
"coordinator@pbac-demo.com": "coordinator-2024!",
|
||||
"support@pbac-demo.com": "support-2024!",
|
||||
};
|
||||
|
||||
return passwordMap[email] || "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get permissions for a role based on user email
|
||||
*/
|
||||
function getPermissionsForRole(email: string, customRoles: any): Array<{ resource: string; action: string }> {
|
||||
// Owner has full access, no specific permissions to list
|
||||
if (email === "owner@pbac-demo.com") {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Map email to role and return permissions
|
||||
const rolePermissionMap: Record<string, Array<{ resource: string; action: string }>> = {
|
||||
"events@pbac-demo.com": [
|
||||
{ resource: "eventType", action: "create" },
|
||||
{ resource: "eventType", action: "read" },
|
||||
{ resource: "eventType", action: "update" },
|
||||
{ resource: "eventType", action: "delete" },
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "booking", action: "update" },
|
||||
{ resource: "booking", action: "readRecordings" },
|
||||
{ resource: "team", action: "read" },
|
||||
{ resource: "availability", action: "override" },
|
||||
],
|
||||
"analytics@pbac-demo.com": [
|
||||
{ resource: "insights", action: "read" },
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "booking", action: "readTeamBookings" },
|
||||
{ resource: "booking", action: "readOrgBookings" },
|
||||
{ resource: "eventType", action: "read" },
|
||||
{ resource: "team", action: "read" },
|
||||
{ resource: "organization", action: "read" },
|
||||
{ resource: "organization", action: "listMembers" },
|
||||
{ resource: "routingForm", action: "read" },
|
||||
],
|
||||
"coordinator@pbac-demo.com": [
|
||||
{ resource: "team", action: "read" },
|
||||
{ resource: "team", action: "update" },
|
||||
{ resource: "team", action: "invite" },
|
||||
{ resource: "team", action: "remove" },
|
||||
{ resource: "team", action: "changeMemberRole" },
|
||||
{ resource: "organization", action: "read" },
|
||||
{ resource: "organization", action: "listMembers" },
|
||||
{ resource: "workflow", action: "create" },
|
||||
{ resource: "workflow", action: "read" },
|
||||
{ resource: "workflow", action: "update" },
|
||||
{ resource: "workflow", action: "delete" },
|
||||
{ resource: "routingForm", action: "create" },
|
||||
{ resource: "routingForm", action: "read" },
|
||||
{ resource: "routingForm", action: "update" },
|
||||
{ resource: "routingForm", action: "delete" },
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "eventType", action: "read" },
|
||||
],
|
||||
"support@pbac-demo.com": [
|
||||
{ resource: "booking", action: "read" },
|
||||
{ resource: "booking", action: "update" },
|
||||
{ resource: "eventType", action: "read" },
|
||||
{ resource: "team", action: "read" },
|
||||
{ resource: "organization", action: "read" },
|
||||
],
|
||||
};
|
||||
|
||||
return rolePermissionMap[email] || [];
|
||||
}
|
||||
// Run the function if this file is executed directly
|
||||
if (require.main === module) {
|
||||
createPBACOrganization()
|
||||
.then(() => {
|
||||
console.log("✅ PBAC organization created successfully!");
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("❌ Error creating PBAC organization:", error);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
}
|
||||
+13
-2
@@ -342,8 +342,19 @@ async function createOrganizationAndAddMembersAndTeams({
|
||||
orgProfile: member.orgProfile,
|
||||
};
|
||||
|
||||
await prisma.tempOrgRedirect.create({
|
||||
data: {
|
||||
// Create temp org redirect with upsert to handle duplicates
|
||||
await prisma.tempOrgRedirect.upsert({
|
||||
where: {
|
||||
from_type_fromOrgId: {
|
||||
from: member.memberData.username,
|
||||
type: RedirectType.User,
|
||||
fromOrgId: 0,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
toUrl: `${getOrgFullOrigin(orgData.slug)}/${member.orgProfile.username}`,
|
||||
},
|
||||
create: {
|
||||
fromOrgId: 0,
|
||||
type: RedirectType.User,
|
||||
from: member.memberData.username,
|
||||
|
||||
Reference in New Issue
Block a user