1
- import { fromZodError } from "zod-validation-error" ;
2
- import type { ActionFunctionArgs } from "@remix-run/server-runtime" ;
3
1
import { json } from "@remix-run/server-runtime" ;
4
- import { TriggerTaskRequestBody } from "@trigger.dev/core/v3" ;
2
+ import { generateJWT as internal_generateJWT , TriggerTaskRequestBody } from "@trigger.dev/core/v3" ;
3
+ import { TaskRun } from "@trigger.dev/database" ;
5
4
import { z } from "zod" ;
6
5
import { env } from "~/env.server" ;
7
- import { authenticateApiRequest } from "~/services/apiAuth.server" ;
6
+ import { AuthenticatedEnvironment } from "~/services/apiAuth.server" ;
8
7
import { logger } from "~/services/logger.server" ;
9
- import { parseRequestJsonAsync } from "~/utils/parseRequestJson .server" ;
8
+ import { createActionApiRoute } from "~/services/routeBuiilders/apiBuilder .server" ;
10
9
import { ServiceValidationError } from "~/v3/services/baseService.server" ;
11
10
import { OutOfEntitlementError , TriggerTaskService } from "~/v3/services/triggerTask.server" ;
12
- import { startActiveSpan } from "~/v3/tracer.server" ;
13
11
14
12
const ParamsSchema = z . object ( {
15
13
taskId : z . string ( ) ,
@@ -20,115 +18,125 @@ export const HeadersSchema = z.object({
20
18
"trigger-version" : z . string ( ) . nullish ( ) ,
21
19
"x-trigger-span-parent-as-link" : z . coerce . number ( ) . nullish ( ) ,
22
20
"x-trigger-worker" : z . string ( ) . nullish ( ) ,
21
+ "x-trigger-client" : z . string ( ) . nullish ( ) ,
23
22
traceparent : z . string ( ) . optional ( ) ,
24
23
tracestate : z . string ( ) . optional ( ) ,
25
24
} ) ;
26
25
27
- export async function action ( { request, params } : ActionFunctionArgs ) {
28
- // Ensure this is a POST request
29
- if ( request . method . toUpperCase ( ) !== "POST" ) {
30
- return { status : 405 , body : "Method Not Allowed" } ;
31
- }
32
-
33
- logger . debug ( "TriggerTask action" , { headers : Object . fromEntries ( request . headers ) } ) ;
34
-
35
- // Next authenticate the request
36
- const authenticationResult = await authenticateApiRequest ( request ) ;
37
-
38
- if ( ! authenticationResult ) {
39
- return json ( { error : "Invalid or Missing API key" } , { status : 401 } ) ;
40
- }
41
-
42
- const contentLength = request . headers . get ( "content-length" ) ;
43
-
44
- if ( ! contentLength || parseInt ( contentLength ) > env . TASK_PAYLOAD_MAXIMUM_SIZE ) {
45
- return json ( { error : "Request body too large" } , { status : 413 } ) ;
46
- }
26
+ const { action, loader } = createActionApiRoute (
27
+ {
28
+ headers : HeadersSchema ,
29
+ params : ParamsSchema ,
30
+ body : TriggerTaskRequestBody ,
31
+ allowJWT : true ,
32
+ maxContentLength : env . TASK_PAYLOAD_MAXIMUM_SIZE ,
33
+ authorization : {
34
+ action : "write" ,
35
+ resource : ( params ) => ( { tasks : params . taskId } ) ,
36
+ superScopes : [ "write:tasks" , "admin" ] ,
37
+ } ,
38
+ corsStrategy : "all" ,
39
+ } ,
40
+ async ( { body, headers, params, authentication } ) => {
41
+ const {
42
+ "idempotency-key" : idempotencyKey ,
43
+ "trigger-version" : triggerVersion ,
44
+ "x-trigger-span-parent-as-link" : spanParentAsLink ,
45
+ traceparent,
46
+ tracestate,
47
+ "x-trigger-worker" : isFromWorker ,
48
+ "x-trigger-client" : triggerClient ,
49
+ } = headers ;
50
+
51
+ const service = new TriggerTaskService ( ) ;
52
+
53
+ try {
54
+ const traceContext =
55
+ traceparent && isFromWorker /// If the request is from a worker, we should pass the trace context
56
+ ? { traceparent, tracestate }
57
+ : undefined ;
58
+
59
+ logger . debug ( "Triggering task" , {
60
+ taskId : params . taskId ,
61
+ idempotencyKey,
62
+ triggerVersion,
63
+ headers,
64
+ options : body . options ,
65
+ isFromWorker,
66
+ traceContext,
67
+ } ) ;
68
+
69
+ const run = await service . call ( params . taskId , authentication . environment , body , {
70
+ idempotencyKey : idempotencyKey ?? undefined ,
71
+ triggerVersion : triggerVersion ?? undefined ,
72
+ traceContext,
73
+ spanParentAsLink : spanParentAsLink === 1 ,
74
+ } ) ;
75
+
76
+ if ( ! run ) {
77
+ return json ( { error : "Task not found" } , { status : 404 } ) ;
78
+ }
47
79
48
- const rawHeaders = Object . fromEntries ( request . headers ) ;
80
+ const $responseHeaders = await responseHeaders (
81
+ run ,
82
+ authentication . environment ,
83
+ triggerClient
84
+ ) ;
49
85
50
- const headers = HeadersSchema . safeParse ( rawHeaders ) ;
86
+ return json (
87
+ {
88
+ id : run . friendlyId ,
89
+ } ,
90
+ {
91
+ headers : $responseHeaders ,
92
+ }
93
+ ) ;
94
+ } catch ( error ) {
95
+ if ( error instanceof ServiceValidationError ) {
96
+ return json ( { error : error . message } , { status : 422 } ) ;
97
+ } else if ( error instanceof OutOfEntitlementError ) {
98
+ return json ( { error : error . message } , { status : 422 } ) ;
99
+ } else if ( error instanceof Error ) {
100
+ return json ( { error : error . message } , { status : 400 } ) ;
101
+ }
51
102
52
- if ( ! headers . success ) {
53
- return json ( { error : "Invalid headers" } , { status : 400 } ) ;
103
+ return json ( { error : "Something went wrong" } , { status : 500 } ) ;
104
+ }
54
105
}
55
-
56
- const {
57
- "idempotency-key" : idempotencyKey ,
58
- "trigger-version" : triggerVersion ,
59
- "x-trigger-span-parent-as-link" : spanParentAsLink ,
60
- traceparent,
61
- tracestate,
62
- "x-trigger-worker" : isFromWorker ,
63
- } = headers . data ;
64
-
65
- const { taskId } = ParamsSchema . parse ( params ) ;
66
-
67
- // Now parse the request body
68
- const anyBody = await parseRequestJsonAsync ( request , { taskId } ) ;
69
-
70
- const body = await startActiveSpan ( "TriggerTaskRequestBody.safeParse()" , async ( span ) => {
71
- return TriggerTaskRequestBody . safeParse ( anyBody ) ;
106
+ ) ;
107
+
108
+ async function responseHeaders (
109
+ run : TaskRun ,
110
+ environment : AuthenticatedEnvironment ,
111
+ triggerClient ?: string | null
112
+ ) : Promise < Record < string , string > > {
113
+ const claimsHeader = JSON . stringify ( {
114
+ sub : run . runtimeEnvironmentId ,
115
+ pub : true ,
72
116
} ) ;
73
117
74
- if ( ! body . success ) {
75
- return json (
76
- { error : fromZodError ( body . error , { prefix : "Invalid trigger call" } ) . toString ( ) } ,
77
- { status : 400 }
78
- ) ;
79
- }
80
-
81
- const service = new TriggerTaskService ( ) ;
82
-
83
- try {
84
- const traceContext =
85
- traceparent && isFromWorker /// If the request is from a worker, we should pass the trace context
86
- ? { traceparent, tracestate }
87
- : undefined ;
88
-
89
- logger . debug ( "Triggering task" , {
90
- taskId,
91
- idempotencyKey,
92
- triggerVersion,
93
- headers : Object . fromEntries ( request . headers ) ,
94
- options : body . data . options ,
95
- isFromWorker,
96
- traceContext,
118
+ if ( triggerClient === "browser" ) {
119
+ const claims = {
120
+ sub : run . runtimeEnvironmentId ,
121
+ pub : true ,
122
+ scopes : [ `read:runs:${ run . friendlyId } ` ] ,
123
+ } ;
124
+
125
+ const jwt = await internal_generateJWT ( {
126
+ secretKey : environment . apiKey ,
127
+ payload : claims ,
128
+ expirationTime : "1h" ,
97
129
} ) ;
98
130
99
- const run = await service . call ( taskId , authenticationResult . environment , body . data , {
100
- idempotencyKey : idempotencyKey ?? undefined ,
101
- triggerVersion : triggerVersion ?? undefined ,
102
- traceContext,
103
- spanParentAsLink : spanParentAsLink === 1 ,
104
- } ) ;
105
-
106
- if ( ! run ) {
107
- return json ( { error : "Task not found" } , { status : 404 } ) ;
108
- }
109
-
110
- return json (
111
- {
112
- id : run . friendlyId ,
113
- } ,
114
- {
115
- headers : {
116
- "x-trigger-jwt-claims" : JSON . stringify ( {
117
- sub : authenticationResult . environment . id ,
118
- pub : true ,
119
- } ) ,
120
- } ,
121
- }
122
- ) ;
123
- } catch ( error ) {
124
- if ( error instanceof ServiceValidationError ) {
125
- return json ( { error : error . message } , { status : 422 } ) ;
126
- } else if ( error instanceof OutOfEntitlementError ) {
127
- return json ( { error : error . message } , { status : 422 } ) ;
128
- } else if ( error instanceof Error ) {
129
- return json ( { error : error . message } , { status : 400 } ) ;
130
- }
131
-
132
- return json ( { error : "Something went wrong" } , { status : 500 } ) ;
131
+ return {
132
+ "x-trigger-jwt-claims" : claimsHeader ,
133
+ "x-trigger-jwt" : jwt ,
134
+ } ;
133
135
}
136
+
137
+ return {
138
+ "x-trigger-jwt-claims" : claimsHeader ,
139
+ } ;
134
140
}
141
+
142
+ export { action , loader } ;
0 commit comments