import type { AuthenticatedEnvironment } from "@internal/run-engine"; import type { Prisma, PrismaClientOrTransaction, RuntimeEnvironment } from "@trigger.dev/database"; import { $replica, prisma } from "~/db.server"; import { logger } from "~/services/logger.server"; import { getUsername } from "~/utils/username"; import { sanitizeBranchName } from "~/v3/gitBranch"; export type { RuntimeEnvironment }; export async function findEnvironmentByApiKey( apiKey: string, branchName: string | undefined ): Promise { const environment = await $replica.runtimeEnvironment.findFirst({ where: { apiKey, }, include: { project: true, organization: true, orgMember: true, childEnvironments: branchName ? { where: { branchName: sanitizeBranchName(branchName), archivedAt: null, }, } : undefined, }, }); //don't return deleted projects if (environment?.project.deletedAt !== null) { return null; } if (environment.type === "PREVIEW") { if (!branchName) { logger.warn("findEnvironmentByApiKey(): Preview env with no branch name provided", { environmentId: environment.id, }); return null; } const childEnvironment = environment?.childEnvironments.at(0); if (childEnvironment) { return { ...childEnvironment, apiKey: environment.apiKey, orgMember: environment.orgMember, organization: environment.organization, project: environment.project, }; } //A branch was specified but no child environment was found return null; } return environment; } /** @deprecated We don't use public api keys anymore */ export async function findEnvironmentByPublicApiKey( apiKey: string, branchName: string | undefined ): Promise { const environment = await $replica.runtimeEnvironment.findFirst({ where: { pkApiKey: apiKey, }, include: { project: true, organization: true, orgMember: true, }, }); //don't return deleted projects if (environment?.project.deletedAt !== null) { return null; } return environment; } export async function findEnvironmentById( id: string ): Promise<(AuthenticatedEnvironment & { parentEnvironment: { apiKey: string } | null }) | null> { const environment = await $replica.runtimeEnvironment.findFirst({ where: { id, }, include: { project: true, organization: true, orgMember: true, parentEnvironment: { select: { apiKey: true, }, }, }, }); //don't return deleted projects if (environment?.project.deletedAt !== null) { return null; } return environment; } export async function findEnvironmentBySlug( projectId: string, envSlug: string, userId: string ): Promise { return $replica.runtimeEnvironment.findFirst({ where: { projectId: projectId, slug: envSlug, OR: [ { type: { in: ["PREVIEW", "STAGING", "PRODUCTION"], }, }, { type: "DEVELOPMENT", orgMember: { userId, }, }, ], }, include: { project: true, organization: true, orgMember: true, }, }); } export async function findEnvironmentFromRun( runId: string, tx?: PrismaClientOrTransaction ): Promise { const taskRun = await (tx ?? $replica).taskRun.findFirst({ where: { id: runId, }, include: { runtimeEnvironment: { include: { project: true, organization: true, orgMember: true, }, }, }, }); if (!taskRun) { return null; } return taskRun?.runtimeEnvironment; } export async function createNewSession(environment: RuntimeEnvironment, ipAddress: string) { const session = await prisma.runtimeEnvironmentSession.create({ data: { environmentId: environment.id, ipAddress, }, }); await prisma.runtimeEnvironment.update({ where: { id: environment.id, }, data: { currentSessionId: session.id, }, }); return session; } export async function disconnectSession(environmentId: string) { const environment = await prisma.runtimeEnvironment.findFirst({ where: { id: environmentId, }, }); if (!environment || !environment.currentSessionId) { return null; } const session = await prisma.runtimeEnvironmentSession.update({ where: { id: environment.currentSessionId, }, data: { disconnectedAt: new Date(), }, }); await prisma.runtimeEnvironment.update({ where: { id: environment.id, }, data: { currentSessionId: null, }, }); return session; } export async function findLatestSession(environmentId: string) { const session = await $replica.runtimeEnvironmentSession.findFirst({ where: { environmentId, }, orderBy: { createdAt: "desc", }, }); return session; } export type DisplayableInputEnvironment = Prisma.RuntimeEnvironmentGetPayload<{ select: { id: true; type: true; slug: true; orgMember: { select: { user: { select: { id: true; name: true; displayName: true; }; }; }; }; }; }>; export function displayableEnvironment( environment: DisplayableInputEnvironment, userId: string | undefined ) { let userName: string | undefined = undefined; if (environment.type === "DEVELOPMENT") { if (!environment.orgMember) { userName = "Deleted"; } else if (environment.orgMember.user.id !== userId) { userName = getUsername(environment.orgMember.user); } } return { id: environment.id, type: environment.type, slug: environment.slug, userName, }; } export async function findDisplayableEnvironment( environmentId: string, userId: string | undefined ) { const environment = await $replica.runtimeEnvironment.findFirst({ where: { id: environmentId, }, select: { id: true, type: true, slug: true, orgMember: { select: { user: { select: { id: true, name: true, displayName: true, }, }, }, }, }, }); if (!environment) { return; } return displayableEnvironment(environment, userId); } export async function hasAccessToEnvironment({ environmentId, projectId, organizationId, userId, }: { environmentId: string; projectId: string; organizationId: string; userId: string; }): Promise { const environment = await $replica.runtimeEnvironment.findFirst({ where: { id: environmentId, projectId: projectId, organizationId: organizationId, organization: { members: { some: { userId } } }, }, }); return environment !== null; }