76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
import { json } from "@remix-run/server-runtime";
|
|
import { type GetProjectEnvironmentsResponseBody } from "@trigger.dev/core/v3";
|
|
import { z } from "zod";
|
|
import { $replica } from "~/db.server";
|
|
import { findProjectByRef } from "~/models/project.server";
|
|
import { createLoaderPATApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
|
import { sortEnvironments } from "~/utils/environmentSort";
|
|
import { isBranchableEnvironment } from "~/utils/branchableEnvironment";
|
|
|
|
const ParamsSchema = z.object({
|
|
projectRef: z.string(),
|
|
});
|
|
|
|
export const loader = createLoaderPATApiRoute(
|
|
{
|
|
params: ParamsSchema,
|
|
corsStrategy: "all",
|
|
// Resolve projectRef → org so the PAT plugin can ground its role-floor
|
|
// calculation. Membership is enforced by the plugin (`authenticatePat`
|
|
// rejects users who aren't members of the target org) and again by
|
|
// `findProjectByRef` below.
|
|
context: async (params) => {
|
|
const project = await $replica.project.findFirst({
|
|
where: { externalRef: params.projectRef },
|
|
select: { organizationId: true },
|
|
});
|
|
return project ? { organizationId: project.organizationId } : {};
|
|
},
|
|
authorization: { action: "read", resource: () => ({ type: "environments" }) },
|
|
},
|
|
async ({ params, authentication }) => {
|
|
const project = await findProjectByRef(params.projectRef, authentication.userId);
|
|
|
|
if (!project) {
|
|
return json({ error: "Project not found" }, { status: 404 });
|
|
}
|
|
|
|
const environments = await $replica.runtimeEnvironment.findMany({
|
|
where: {
|
|
projectId: project.id,
|
|
// Only base/parent environments. Branch children (preview branches)
|
|
// are excluded — syncs target the parent and branches override elsewhere.
|
|
parentEnvironmentId: null,
|
|
archivedAt: null,
|
|
OR: [
|
|
{ type: { in: ["STAGING", "PRODUCTION", "PREVIEW"] } },
|
|
// dev is per-user: only return the caller's own dev environment
|
|
{ type: "DEVELOPMENT", orgMember: { userId: authentication.userId } },
|
|
],
|
|
},
|
|
select: {
|
|
id: true,
|
|
slug: true,
|
|
type: true,
|
|
isBranchableEnvironment: true,
|
|
parentEnvironmentId: true,
|
|
branchName: true,
|
|
paused: true,
|
|
},
|
|
});
|
|
|
|
const result: GetProjectEnvironmentsResponseBody = sortEnvironments(environments).map(
|
|
(env) => ({
|
|
id: env.id,
|
|
slug: env.slug,
|
|
type: env.type,
|
|
isBranchableEnvironment: isBranchableEnvironment(env),
|
|
branchName: env.branchName,
|
|
paused: env.paused,
|
|
})
|
|
);
|
|
|
|
return json(result);
|
|
}
|
|
);
|