From 055b07843c18af34b0d8fd8115394d56529588d6 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 19 Nov 2025 15:20:48 +0000 Subject: [PATCH] fix(api): Fix preview branch targeting in environment variable API routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixes the `x-trigger-branch` header support for targeting specific preview branches when managing environment variables. The header was documented but not actually being extracted or used in the environment variable API routes. Additionally, the query logic in `authenticatedEnvironmentForAuthentication` was fundamentally broken—it searched for environments with both `slug: "preview"` (parent environment property) AND a specific `branchName` (child environment property), which no environment could satisfy simultaneously. The fix extracts the branch name using `branchNameFromRequest()` and correctly queries for child branch environments using `type: "PREVIEW"` and the specific `branchName`. This ensures that environment variable operations (create, update, get, list) properly target individual preview branches instead of affecting all preview environments. --- .../routes/api.v1.projects.$projectRef.$env.ts | 4 +++- ...ctRef.background-workers.$envSlug.$version.ts | 4 +++- ...1.projects.$projectRef.envvars.$slug.$name.ts | 7 +++++-- .../api.v1.projects.$projectRef.envvars.$slug.ts | 7 +++++-- apps/webapp/app/services/apiAuth.server.ts | 16 ++++++++++------ apps/webapp/app/v3/gitBranch.ts | 2 +- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/apps/webapp/app/routes/api.v1.projects.$projectRef.$env.ts b/apps/webapp/app/routes/api.v1.projects.$projectRef.$env.ts index 53f2cad895..e0349aab55 100644 --- a/apps/webapp/app/routes/api.v1.projects.$projectRef.$env.ts +++ b/apps/webapp/app/routes/api.v1.projects.$projectRef.$env.ts @@ -5,6 +5,7 @@ import { env as processEnv } from "~/env.server"; import { authenticatedEnvironmentForAuthentication, authenticateRequest, + branchNameFromRequest, } from "~/services/apiAuth.server"; const ParamsSchema = z.object({ @@ -32,7 +33,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) { const environment = await authenticatedEnvironmentForAuthentication( authenticationResult, projectRef, - env + env, + branchNameFromRequest(request) ); const result: GetProjectEnvResponse = { diff --git a/apps/webapp/app/routes/api.v1.projects.$projectRef.background-workers.$envSlug.$version.ts b/apps/webapp/app/routes/api.v1.projects.$projectRef.background-workers.$envSlug.$version.ts index 90dc7a3661..6b044c9e83 100644 --- a/apps/webapp/app/routes/api.v1.projects.$projectRef.background-workers.$envSlug.$version.ts +++ b/apps/webapp/app/routes/api.v1.projects.$projectRef.background-workers.$envSlug.$version.ts @@ -4,6 +4,7 @@ import { prisma } from "~/db.server"; import { authenticateRequest, authenticatedEnvironmentForAuthentication, + branchNameFromRequest, } from "~/services/apiAuth.server"; import zlib from "node:zlib"; @@ -29,7 +30,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) { const environment = await authenticatedEnvironmentForAuthentication( authenticationResult, parsedParams.data.projectRef, - parsedParams.data.envSlug + parsedParams.data.envSlug, + branchNameFromRequest(request) ); // Find the background worker and tasks and files diff --git a/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.$name.ts b/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.$name.ts index 13784b8110..e3081b090e 100644 --- a/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.$name.ts +++ b/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.$name.ts @@ -5,6 +5,7 @@ import { prisma } from "~/db.server"; import { authenticateRequest, authenticatedEnvironmentForAuthentication, + branchNameFromRequest, } from "~/services/apiAuth.server"; import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server"; @@ -30,7 +31,8 @@ export async function action({ params, request }: ActionFunctionArgs) { const environment = await authenticatedEnvironmentForAuthentication( authenticationResult, parsedParams.data.projectRef, - parsedParams.data.slug + parsedParams.data.slug, + branchNameFromRequest(request) ); // Find the environment variable @@ -106,7 +108,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) { const environment = await authenticatedEnvironmentForAuthentication( authenticationResult, parsedParams.data.projectRef, - parsedParams.data.slug + parsedParams.data.slug, + branchNameFromRequest(request) ); // Find the environment variable diff --git a/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.ts b/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.ts index fe2d77cbf8..188290ae8a 100644 --- a/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.ts +++ b/apps/webapp/app/routes/api.v1.projects.$projectRef.envvars.$slug.ts @@ -4,6 +4,7 @@ import { z } from "zod"; import { authenticateRequest, authenticatedEnvironmentForAuthentication, + branchNameFromRequest, } from "~/services/apiAuth.server"; import { EnvironmentVariablesRepository } from "~/v3/environmentVariables/environmentVariablesRepository.server"; @@ -28,7 +29,8 @@ export async function action({ params, request }: ActionFunctionArgs) { const environment = await authenticatedEnvironmentForAuthentication( authenticationResult, parsedParams.data.projectRef, - parsedParams.data.slug + parsedParams.data.slug, + branchNameFromRequest(request) ); const jsonBody = await request.json(); @@ -75,7 +77,8 @@ export async function loader({ params, request }: LoaderFunctionArgs) { const environment = await authenticatedEnvironmentForAuthentication( authenticationResult, parsedParams.data.projectRef, - parsedParams.data.slug + parsedParams.data.slug, + branchNameFromRequest(request) ); const repository = new EnvironmentVariablesRepository(); diff --git a/apps/webapp/app/services/apiAuth.server.ts b/apps/webapp/app/services/apiAuth.server.ts index 989aeef8e9..e5689f9ec6 100644 --- a/apps/webapp/app/services/apiAuth.server.ts +++ b/apps/webapp/app/services/apiAuth.server.ts @@ -497,7 +497,9 @@ export async function authenticatedEnvironmentForAuthentication( throw json({ error: "Project not found" }, { status: 404 }); } - if (!branch) { + const sanitizedBranch = sanitizeBranchName(branch); + + if (!sanitizedBranch) { const environment = await prisma.runtimeEnvironment.findFirst({ where: { projectId: project.id, @@ -526,8 +528,8 @@ export async function authenticatedEnvironmentForAuthentication( const environment = await prisma.runtimeEnvironment.findFirst({ where: { projectId: project.id, - slug: slug, - branchName: sanitizeBranchName(branch), + type: "PREVIEW", + branchName: sanitizedBranch, archivedAt: null, }, include: { @@ -574,7 +576,9 @@ export async function authenticatedEnvironmentForAuthentication( throw json({ error: "Project not found" }, { status: 404 }); } - if (!branch) { + const sanitizedBranch = sanitizeBranchName(branch); + + if (!sanitizedBranch) { const environment = await prisma.runtimeEnvironment.findFirst({ where: { projectId: project.id, @@ -596,8 +600,8 @@ export async function authenticatedEnvironmentForAuthentication( const environment = await prisma.runtimeEnvironment.findFirst({ where: { projectId: project.id, - slug: slug, - branchName: sanitizeBranchName(branch), + type: "PREVIEW", + branchName: sanitizedBranch, archivedAt: null, }, include: { diff --git a/apps/webapp/app/v3/gitBranch.ts b/apps/webapp/app/v3/gitBranch.ts index 109de645b9..06c76f0624 100644 --- a/apps/webapp/app/v3/gitBranch.ts +++ b/apps/webapp/app/v3/gitBranch.ts @@ -29,7 +29,7 @@ export function isValidGitBranchName(branch: string): boolean { return true; } -export function sanitizeBranchName(ref: string): string | null { +export function sanitizeBranchName(ref: string | undefined): string | null { if (!ref) return null; if (ref.startsWith("refs/heads/")) return ref.substring("refs/heads/".length); if (ref.startsWith("refs/remotes/")) return ref.substring("refs/remotes/".length);