@@ -3,7 +3,11 @@ import { tryCatch, UpsertBranchRequestBody } from "@trigger.dev/core/v3";
33import { DEFAULT_DEV_BRANCH , isDefaultDevBranch } from "@trigger.dev/core/v3/utils/gitBranch" ;
44import { z } from "zod" ;
55import { prisma } from "~/db.server" ;
6- import { authenticateRequest } from "~/services/apiAuth.server" ;
6+ import {
7+ authenticateApiKeyWithScope ,
8+ authenticateRequest ,
9+ type AuthenticationResult ,
10+ } from "~/services/apiAuth.server" ;
711import { logger } from "~/services/logger.server" ;
812import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server" ;
913import { UpsertBranchService } from "~/services/upsertBranch.server" ;
@@ -21,15 +25,35 @@ export async function action({ request, params }: ActionFunctionArgs) {
2125
2226 logger . info ( "project upsert branch" , { url : request . url } ) ;
2327
24- const authenticationResult = await authenticateRequest ( request , {
28+ const userOrOrganizationAuthentication = await authenticateRequest ( request , {
2529 personalAccessToken : true ,
2630 organizationAccessToken : true ,
2731 apiKey : false ,
2832 } ) ;
29- if ( ! authenticationResult ) {
30- return json ( { error : "Invalid or Missing Access Token" } , { status : 401 } ) ;
33+
34+ let authenticationResult : AuthenticationResult ;
35+ if ( userOrOrganizationAuthentication ) {
36+ authenticationResult = userOrOrganizationAuthentication ;
37+ } else {
38+ const apiKeyAuthentication = await authenticateApiKeyWithScope ( request , {
39+ action : "write" ,
40+ resource : { type : "branches" } ,
41+ allowPreviewParent : true ,
42+ } ) ;
43+ if ( ! apiKeyAuthentication . ok ) {
44+ return json ( { error : apiKeyAuthentication . error } , { status : apiKeyAuthentication . status } ) ;
45+ }
46+ authenticationResult = {
47+ type : "apiKey" ,
48+ result : apiKeyAuthentication . authentication ,
49+ } ;
3150 }
3251
52+ const apiKeyEnvironment =
53+ authenticationResult . type === "apiKey" && authenticationResult . result . ok
54+ ? authenticationResult . result . environment
55+ : undefined ;
56+
3357 const parsedParams = ParamsSchema . safeParse ( params ) ;
3458
3559 if ( ! parsedParams . success ) {
@@ -38,24 +62,32 @@ export async function action({ request, params }: ActionFunctionArgs) {
3862
3963 const { projectRef } = parsedParams . data ;
4064
41- const project = await prisma . project . findFirst ( {
42- select : {
43- id : true ,
44- } ,
45- where : {
46- externalRef : projectRef ,
47- organization :
48- authenticationResult . type === "organizationAccessToken"
49- ? { id : authenticationResult . result . organizationId }
50- : {
51- members : {
52- some : {
53- userId : authenticationResult . result . userId ,
65+ let project : { id : string } | null | undefined ;
66+ if ( authenticationResult . type === "apiKey" ) {
67+ project =
68+ apiKeyEnvironment ?. project . externalRef === projectRef
69+ ? { id : apiKeyEnvironment . project . id }
70+ : undefined ;
71+ } else {
72+ project = await prisma . project . findFirst ( {
73+ select : {
74+ id : true ,
75+ } ,
76+ where : {
77+ externalRef : projectRef ,
78+ organization :
79+ authenticationResult . type === "organizationAccessToken"
80+ ? { id : authenticationResult . result . organizationId }
81+ : {
82+ members : {
83+ some : {
84+ userId : authenticationResult . result . userId ,
85+ } ,
5486 } ,
5587 } ,
56- } ,
57- } ,
58- } ) ;
88+ } ,
89+ } ) ;
90+ }
5991 if ( ! project ) {
6092 return json ( { error : "Project not found" } , { status : 404 } ) ;
6193 }
@@ -72,38 +104,64 @@ export async function action({ request, params }: ActionFunctionArgs) {
72104
73105 const { branch, env, git } = parsed . data ;
74106
75- if ( env === "development" && authenticationResult . type === "organizationAccessToken ") {
107+ if ( env === "development" && authenticationResult . type !== "personalAccessToken ") {
76108 return json (
77- { error : "Cannot create dev branches with organization access tokens." } ,
109+ {
110+ error :
111+ authenticationResult . type === "apiKey"
112+ ? "API keys can only create Preview branches."
113+ : "Cannot create dev branches with organization access tokens." ,
114+ } ,
78115 { status : 400 }
79116 ) ;
80117 }
81118
119+ if (
120+ authenticationResult . type === "apiKey" &&
121+ ( ! apiKeyEnvironment ||
122+ apiKeyEnvironment . type !== "PREVIEW" ||
123+ apiKeyEnvironment . parentEnvironmentId !== null )
124+ ) {
125+ return json (
126+ { error : "API keys must belong to the parent Preview environment." } ,
127+ { status : 403 }
128+ ) ;
129+ }
130+
82131 if ( env === "development" && isDefaultDevBranch ( branch ) ) {
83132 return json (
84133 { error : `Cannot create dev branch with name '${ DEFAULT_DEV_BRANCH } '.` } ,
85134 { status : 400 }
86135 ) ;
87136 }
88137
89- const service = new UpsertBranchService ( ) ;
90- const result = await service . call (
91- authenticationResult . type === "organizationAccessToken"
92- ? { type : "orgId" , organizationId : authenticationResult . result . organizationId }
93- : { type : "userMembership" , userId : authenticationResult . result . userId } ,
94- {
95- env ,
96- branchName : branch ,
97- projectId : project . id ,
98- git ,
138+ let orgFilter :
139+ | { type : "userMembership" ; userId : string }
140+ | { type : "orgId" ; organizationId : string } ;
141+ if ( authenticationResult . type === "personalAccessToken" ) {
142+ orgFilter = { type : "userMembership" , userId : authenticationResult . result . userId } ;
143+ } else if ( authenticationResult . type === "organizationAccessToken" ) {
144+ orgFilter = { type : "orgId" , organizationId : authenticationResult . result . organizationId } ;
145+ } else {
146+ if ( ! apiKeyEnvironment ) {
147+ return json ( { error : "Invalid API key" } , { status : 401 } ) ;
99148 }
100- ) ;
149+ orgFilter = { type : "orgId" , organizationId : apiKeyEnvironment . organizationId } ;
150+ }
151+
152+ const service = new UpsertBranchService ( ) ;
153+ const result = await service . call ( orgFilter , {
154+ env,
155+ branchName : branch ,
156+ projectId : project . id ,
157+ git,
158+ } ) ;
101159
102160 if ( ! result . success ) {
103161 return json ( { error : result . error } , { status : 400 } ) ;
104162 }
105163
106- return json ( result . branch ) ;
164+ return json ( { id : result . branch . id } ) ;
107165}
108166
109167export async function loader ( { request, params } : LoaderFunctionArgs ) {
0 commit comments