chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import type { Prisma, Project } from "@trigger.dev/database";
|
||||
import { customAlphabet, nanoid } from "nanoid";
|
||||
import slug from "slug";
|
||||
import { $replica, prisma } from "~/db.server";
|
||||
import { projectCreated } from "~/services/projectCreated.server";
|
||||
import { type Organization, createEnvironment } from "./organization.server";
|
||||
export type { Project } from "@trigger.dev/database";
|
||||
|
||||
const externalRefGenerator = customAlphabet("abcdefghijklmnopqrstuvwxyz", 20);
|
||||
|
||||
type Options = {
|
||||
organizationSlug: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
version: "v2" | "v3";
|
||||
onboardingData?: Prisma.InputJsonValue;
|
||||
};
|
||||
|
||||
export class ExceededProjectLimitError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ExceededProjectLimitError";
|
||||
}
|
||||
}
|
||||
|
||||
export async function createProject(
|
||||
{ organizationSlug, name, userId, version, onboardingData }: Options,
|
||||
attemptCount = 0
|
||||
): Promise<Project & { organization: Organization }> {
|
||||
//check the user has permissions to do this
|
||||
const organization = await prisma.organization.findFirst({
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
isActivated: true,
|
||||
maximumConcurrencyLimit: true,
|
||||
maximumProjectCount: true,
|
||||
},
|
||||
where: {
|
||||
slug: organizationSlug,
|
||||
members: { some: { userId } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!organization) {
|
||||
throw new Error(
|
||||
`User ${userId} does not have permission to create a project in organization ${organizationSlug}`
|
||||
);
|
||||
}
|
||||
|
||||
if (version === "v3") {
|
||||
if (!organization.isActivated) {
|
||||
throw new Error(`Organization can't create v3 projects.`);
|
||||
}
|
||||
}
|
||||
|
||||
const projectCount = await prisma.project.count({
|
||||
where: {
|
||||
organizationId: organization.id,
|
||||
deletedAt: null,
|
||||
},
|
||||
});
|
||||
|
||||
if (projectCount >= organization.maximumProjectCount) {
|
||||
throw new ExceededProjectLimitError(
|
||||
`This organization has reached the maximum number of projects (${organization.maximumProjectCount}).`
|
||||
);
|
||||
}
|
||||
|
||||
//ensure the slug is globally unique
|
||||
const uniqueProjectSlug = `${slug(name)}-${nanoid(4)}`;
|
||||
const projectWithSameSlug = await prisma.project.findFirst({
|
||||
where: { slug: uniqueProjectSlug },
|
||||
});
|
||||
|
||||
if (attemptCount > 100) {
|
||||
throw new Error(`Unable to create project with slug ${uniqueProjectSlug} after 100 attempts`);
|
||||
}
|
||||
|
||||
if (projectWithSameSlug) {
|
||||
return createProject(
|
||||
{
|
||||
organizationSlug,
|
||||
name,
|
||||
userId,
|
||||
version,
|
||||
onboardingData,
|
||||
},
|
||||
attemptCount + 1
|
||||
);
|
||||
}
|
||||
|
||||
const project = await prisma.project.create({
|
||||
data: {
|
||||
name,
|
||||
slug: uniqueProjectSlug,
|
||||
organization: {
|
||||
connect: {
|
||||
slug: organizationSlug,
|
||||
},
|
||||
},
|
||||
externalRef: `proj_${externalRefGenerator()}`,
|
||||
version: version === "v3" ? "V3" : "V2",
|
||||
onboardingData,
|
||||
},
|
||||
include: {
|
||||
organization: {
|
||||
include: {
|
||||
members: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Create the dev and prod environments
|
||||
await createEnvironment({
|
||||
organization,
|
||||
project,
|
||||
type: "PRODUCTION",
|
||||
isBranchableEnvironment: false,
|
||||
});
|
||||
|
||||
for (const member of project.organization.members) {
|
||||
await createEnvironment({
|
||||
organization,
|
||||
project,
|
||||
type: "DEVELOPMENT",
|
||||
// We set this true but no backfill (yet!?) so never used
|
||||
// for dev environments
|
||||
isBranchableEnvironment: true,
|
||||
member,
|
||||
});
|
||||
}
|
||||
|
||||
await projectCreated(organization, project);
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
export async function findProjectBySlug(orgSlug: string, projectSlug: string, userId: string) {
|
||||
// Find the project scoped to the organization, making sure the user belongs to that org
|
||||
return await $replica.project.findFirst({
|
||||
where: {
|
||||
slug: projectSlug,
|
||||
organization: {
|
||||
slug: orgSlug,
|
||||
members: { some: { userId } },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function findProjectByRef(externalRef: string, userId: string) {
|
||||
// Find the project scoped to the organization, making sure the user belongs to that org
|
||||
return await $replica.project.findFirst({
|
||||
where: {
|
||||
externalRef,
|
||||
organization: {
|
||||
members: { some: { userId } },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user