bee59de3a0
* Don't use the organization max concurrency anymore * Early draft of the concurrency page * WIP adding a new stepper input component * Move stepper to be alphabetical * When max value is reached, disabled the + button * Show placeholder if you delete all numbers * Make all the html input values available to the component * Adds size variants * Move stepper into its own component * Work on showing the extra concurrency * The purchase form styling and functionality (minus actually purchasing) * New style for outline input fields * Concurrency purchasing working * Purchasing concurrency and quota emails working * Improvements to the modal * Show cost breakdown in the modal * Fix for allocated concurrency including DEV * Improved types * Allocating concurrency is working * Live updates total env concurrency * Implemented reset * Fix for concurrency allocation editing across multiple projects * Tabular numbers * Added an error from allocating concurrency * Fixes for allocating concurrency where it didn't calculate correctly * "Increase limit" link to concurrency page * Indent environments * Added Preview limit when updating concurrency for an org * Show error when changing plan fails * Added maximumProjectCount column to Org * Limit project count and display a rich error toast (with title and button now) * Added title and button to toasts. Use it for new project error * @trigger.dev/platform 1.0.20 * Allow submitting zero concurrency so you can downgrade back to nothing * Use the server as the truth for omitted environments * Updated the pricing panels --------- Co-authored-by: James Ritchie <james@trigger.dev>
129 lines
3.1 KiB
TypeScript
129 lines
3.1 KiB
TypeScript
import type {
|
|
Organization,
|
|
OrgMember,
|
|
Project,
|
|
RuntimeEnvironment,
|
|
User,
|
|
} from "@trigger.dev/database";
|
|
import { customAlphabet } from "nanoid";
|
|
import { generate } from "random-words";
|
|
import slug from "slug";
|
|
import { prisma, type PrismaClientOrTransaction } from "~/db.server";
|
|
import { env } from "~/env.server";
|
|
import { featuresForUrl } from "~/features.server";
|
|
import { createApiKeyForEnv, createPkApiKeyForEnv, envSlug } from "./api-key.server";
|
|
import { getDefaultEnvironmentConcurrencyLimit } from "~/services/platform.v3.server";
|
|
export type { Organization };
|
|
|
|
const nanoid = customAlphabet("1234567890abcdef", 4);
|
|
|
|
export async function createOrganization(
|
|
{
|
|
title,
|
|
userId,
|
|
companySize,
|
|
}: Pick<Organization, "title" | "companySize"> & {
|
|
userId: User["id"];
|
|
},
|
|
attemptCount = 0
|
|
): Promise<Organization> {
|
|
if (typeof process.env.BLOCKED_USERS === "string" && process.env.BLOCKED_USERS.includes(userId)) {
|
|
throw new Error("Organization could not be created.");
|
|
}
|
|
|
|
const uniqueOrgSlug = `${slug(title)}-${nanoid(4)}`;
|
|
|
|
const orgWithSameSlug = await prisma.organization.findFirst({
|
|
where: { slug: uniqueOrgSlug },
|
|
});
|
|
|
|
if (attemptCount > 100) {
|
|
throw new Error(`Unable to create organization with slug ${uniqueOrgSlug} after 100 attempts`);
|
|
}
|
|
|
|
if (orgWithSameSlug) {
|
|
return createOrganization(
|
|
{
|
|
title,
|
|
userId,
|
|
companySize,
|
|
},
|
|
attemptCount + 1
|
|
);
|
|
}
|
|
|
|
const features = featuresForUrl(new URL(env.APP_ORIGIN));
|
|
|
|
const organization = await prisma.organization.create({
|
|
data: {
|
|
title,
|
|
slug: uniqueOrgSlug,
|
|
companySize,
|
|
maximumConcurrencyLimit: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT,
|
|
members: {
|
|
create: {
|
|
userId: userId,
|
|
role: "ADMIN",
|
|
},
|
|
},
|
|
v3Enabled: true,
|
|
},
|
|
include: {
|
|
members: true,
|
|
},
|
|
});
|
|
|
|
return { ...organization };
|
|
}
|
|
|
|
export async function createEnvironment({
|
|
organization,
|
|
project,
|
|
type,
|
|
isBranchableEnvironment = false,
|
|
member,
|
|
prismaClient = prisma,
|
|
}: {
|
|
organization: Pick<Organization, "id" | "maximumConcurrencyLimit">;
|
|
project: Pick<Project, "id">;
|
|
type: RuntimeEnvironment["type"];
|
|
isBranchableEnvironment?: boolean;
|
|
member?: OrgMember;
|
|
prismaClient?: PrismaClientOrTransaction;
|
|
}) {
|
|
const slug = envSlug(type);
|
|
const apiKey = createApiKeyForEnv(type);
|
|
const pkApiKey = createPkApiKeyForEnv(type);
|
|
const shortcode = createShortcode().join("-");
|
|
|
|
const limit = await getDefaultEnvironmentConcurrencyLimit(organization.id, type);
|
|
|
|
return await prismaClient.runtimeEnvironment.create({
|
|
data: {
|
|
slug,
|
|
apiKey,
|
|
pkApiKey,
|
|
shortcode,
|
|
autoEnableInternalSources: type !== "DEVELOPMENT",
|
|
maximumConcurrencyLimit: limit,
|
|
organization: {
|
|
connect: {
|
|
id: organization.id,
|
|
},
|
|
},
|
|
project: {
|
|
connect: {
|
|
id: project.id,
|
|
},
|
|
},
|
|
orgMember: member ? { connect: { id: member.id } } : undefined,
|
|
type,
|
|
isBranchableEnvironment,
|
|
},
|
|
});
|
|
}
|
|
|
|
function createShortcode() {
|
|
return generate({ exactly: 2 });
|
|
}
|