@@ -2,7 +2,7 @@ import { type ActionFunctionArgs, json } from "@remix-run/server-runtime";
22import { tryCatch } from "@trigger.dev/core" ;
33import { z } from "zod" ;
44import { prisma } from "~/db.server" ;
5- import { authenticateRequest } from "~/services/apiAuth.server" ;
5+ import { authenticateRequestWithScopedApiKey } from "~/services/apiAuth.server" ;
66import { ArchiveBranchService } from "~/services/archiveBranch.server" ;
77import { logger } from "~/services/logger.server" ;
88import { toBranchableEnvironmentType } from "~/utils/branchableEnvironment" ;
@@ -24,15 +24,25 @@ export async function action({ request, params }: ActionFunctionArgs) {
2424
2525 logger . info ( "Archive branch" , { url : request . url , params } ) ;
2626
27- const authenticationResult = await authenticateRequest ( request , {
27+ const authentication = await authenticateRequestWithScopedApiKey ( request , {
2828 personalAccessToken : true ,
2929 organizationAccessToken : true ,
30- apiKey : false ,
30+ apiKey : {
31+ action : "write" ,
32+ resource : { type : "branches" } ,
33+ allowPreviewParent : true ,
34+ } ,
3135 } ) ;
3236
33- if ( ! authenticationResult ) {
34- return json ( { error : "Invalid or Missing Access Token" } , { status : 401 } ) ;
37+ if ( ! authentication . ok ) {
38+ return json ( { error : authentication . error } , { status : authentication . status } ) ;
3539 }
40+ const authenticationResult = authentication . authentication ;
41+
42+ const apiKeyEnvironment =
43+ authenticationResult . type === "apiKey" && authenticationResult . result . ok
44+ ? authenticationResult . result . environment
45+ : undefined ;
3646
3747 const parsedParams = ParamsSchema . safeParse ( params ) ;
3848
@@ -54,25 +64,44 @@ export async function action({ request, params }: ActionFunctionArgs) {
5464
5565 const { env, branch } = parsed . data ;
5666
67+ // API keys can only archive Preview branches
68+ if (
69+ authenticationResult . type === "apiKey" &&
70+ ( ! apiKeyEnvironment ||
71+ apiKeyEnvironment . type !== "PREVIEW" ||
72+ apiKeyEnvironment . parentEnvironmentId !== null ||
73+ env !== "preview" )
74+ ) {
75+ return json (
76+ { error : "API keys must belong to the parent Preview environment." } ,
77+ { status : 403 }
78+ ) ;
79+ }
80+
5781 const environmentType = toBranchableEnvironmentType ( env ) ;
82+
83+ const organizationFilter =
84+ authenticationResult . type === "organizationAccessToken"
85+ ? { id : authenticationResult . result . organizationId }
86+ : authenticationResult . type === "apiKey"
87+ ? { id : apiKeyEnvironment ! . organizationId }
88+ : {
89+ members : {
90+ some : {
91+ userId : authenticationResult . result . userId ,
92+ } ,
93+ } ,
94+ } ;
95+
5896 const environments = await prisma . runtimeEnvironment . findMany ( {
5997 select : {
6098 id : true ,
6199 archivedAt : true ,
62100 } ,
63101 where : {
64- organization :
65- authenticationResult . type === "organizationAccessToken"
66- ? { id : authenticationResult . result . organizationId }
67- : {
68- members : {
69- some : {
70- userId : authenticationResult . result . userId ,
71- } ,
72- } ,
73- } ,
102+ organization : organizationFilter ,
74103 // Dev branches are per-org-member: only the owner may archive their own.
75- ...( authenticationResult . type !== "organizationAccessToken " &&
104+ ...( authenticationResult . type === "personalAccessToken " &&
76105 environmentType === "DEVELOPMENT"
77106 ? { orgMember : { userId : authenticationResult . result . userId } }
78107 : { } ) ,
@@ -91,7 +120,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
91120 const activeEnvironments = environments . filter ( ( env ) => env . archivedAt === null ) ;
92121
93122 if (
94- authenticationResult . type === "organizationAccessToken " &&
123+ authenticationResult . type !== "personalAccessToken " &&
95124 environmentType === "DEVELOPMENT" &&
96125 activeEnvironments . length > 1
97126 ) {
@@ -110,15 +139,21 @@ export async function action({ request, params }: ActionFunctionArgs) {
110139 return json ( { error : "Branch already archived" } , { status : 400 } ) ;
111140 }
112141
142+ let orgFilter :
143+ | { type : "userMembership" ; userId : string }
144+ | { type : "orgId" ; organizationId : string } ;
145+ if ( authenticationResult . type === "personalAccessToken" ) {
146+ orgFilter = { type : "userMembership" , userId : authenticationResult . result . userId } ;
147+ } else if ( authenticationResult . type === "organizationAccessToken" ) {
148+ orgFilter = { type : "orgId" , organizationId : authenticationResult . result . organizationId } ;
149+ } else {
150+ orgFilter = { type : "orgId" , organizationId : apiKeyEnvironment ! . organizationId } ;
151+ }
152+
113153 const service = new ArchiveBranchService ( ) ;
114- const result = await service . call (
115- authenticationResult . type === "organizationAccessToken"
116- ? { type : "orgId" , organizationId : authenticationResult . result . organizationId }
117- : { type : "userMembership" , userId : authenticationResult . result . userId } ,
118- {
119- environmentId : environment . id ,
120- }
121- ) ;
154+ const result = await service . call ( orgFilter , {
155+ environmentId : environment . id ,
156+ } ) ;
122157
123158 if ( result . success ) {
124159 return json ( result ) ;
0 commit comments