import { type PrismaClient } from "@trigger.dev/database"; import { prisma } from "~/db.server"; import { BranchTrackingConfigSchema } from "~/v3/github"; import { env } from "~/env.server"; import { findProjectBySlug } from "~/models/project.server"; import { err, fromPromise, ok, ResultAsync } from "neverthrow"; import { BuildSettingsSchema } from "~/v3/buildSettings"; export class ProjectSettingsPresenter { #prismaClient: PrismaClient; constructor(prismaClient: PrismaClient = prisma) { this.#prismaClient = prismaClient; } getProjectSettings(organizationSlug: string, projectSlug: string, userId: string) { const githubAppEnabled = env.GITHUB_APP_ENABLED === "1"; const getProject = () => fromPromise(findProjectBySlug(organizationSlug, projectSlug, userId), (error) => ({ type: "other" as const, cause: error, })) .andThen((project) => { if (!project) { return err({ type: "project_not_found" as const }); } return ok(project); }) .map((project) => { const buildSettingsOrFailure = BuildSettingsSchema.safeParse(project.buildSettings); const buildSettings = buildSettingsOrFailure.success ? buildSettingsOrFailure.data : undefined; return { ...project, buildSettings }; }); if (!githubAppEnabled) { return getProject().map(({ buildSettings }) => ({ gitHubApp: { enabled: false, connectedRepository: undefined, installations: undefined, isPreviewEnvironmentEnabled: undefined, }, buildSettings, })); } const findConnectedGithubRepository = (projectId: string) => fromPromise( this.#prismaClient.connectedGithubRepository.findFirst({ where: { projectId, repository: { installation: { deletedAt: null, suspendedAt: null, }, }, }, select: { branchTracking: true, previewDeploymentsEnabled: true, createdAt: true, repository: { select: { id: true, name: true, fullName: true, htmlUrl: true, private: true, }, }, }, }), (error) => ({ type: "other" as const, cause: error, }) ).map((connectedGithubRepository) => { if (!connectedGithubRepository) { return undefined; } const branchTrackingOrFailure = BranchTrackingConfigSchema.safeParse( connectedGithubRepository.branchTracking ); const branchTracking = branchTrackingOrFailure.success ? branchTrackingOrFailure.data : undefined; return { ...connectedGithubRepository, branchTracking, }; }); const listGithubAppInstallations = (organizationId: string) => fromPromise( this.#prismaClient.githubAppInstallation.findMany({ where: { organizationId, deletedAt: null, suspendedAt: null, }, select: { id: true, accountHandle: true, targetType: true, appInstallationId: true, repositories: { select: { id: true, name: true, fullName: true, htmlUrl: true, private: true, }, // Most installations will only have a couple of repos so loading them here should be fine. // However, there might be outlier organizations so it's best to expose the installation repos // via a resource endpoint and filter on user input. take: 200, }, }, take: 20, orderBy: { createdAt: "desc", }, }), (error) => ({ type: "other" as const, cause: error, }) ); const isPreviewEnvironmentEnabled = (projectId: string) => fromPromise( this.#prismaClient.runtimeEnvironment.findFirst({ select: { id: true, }, where: { projectId: projectId, slug: "preview", }, }), (error) => ({ type: "other" as const, cause: error, }) ).map((previewEnvironment) => previewEnvironment !== null); return getProject().andThen((project) => ResultAsync.combine([ isPreviewEnvironmentEnabled(project.id), findConnectedGithubRepository(project.id), listGithubAppInstallations(project.organizationId), ]).map( ([isPreviewEnvironmentEnabled, connectedGithubRepository, githubAppInstallations]) => ({ gitHubApp: { enabled: true, connectedRepository: connectedGithubRepository, installations: githubAppInstallations, isPreviewEnvironmentEnabled, }, buildSettings: project.buildSettings, }) ) ); } }