@@ -3,8 +3,13 @@ import type { ActionFunction } from "@remix-run/node";
33import { json } from "@remix-run/node" ;
44import { assertExhaustive } from "@trigger.dev/core/utils" ;
55import { z } from "zod" ;
6+ import { prisma } from "~/db.server" ;
67import { redirectWithErrorMessage , redirectWithSuccessMessage } from "~/models/message.server" ;
78import { logger } from "~/services/logger.server" ;
9+ import { requireUserId } from "~/services/session.server" ;
10+ import { sanitizeRedirectPath } from "~/utils" ;
11+ import { runStore } from "~/v3/runStore.server" ;
12+ import { findBatchRunIdForUser } from "~/v3/services/batchRunAccess.server" ;
813import { ResumeBatchRunService } from "~/v3/services/resumeBatchRun.server" ;
914
1015export const checkCompletionSchema = z . object ( {
@@ -16,6 +21,8 @@ const ParamSchema = z.object({
1621} ) ;
1722
1823export const action : ActionFunction = async ( { request, params } ) => {
24+ // Require a logged-in user; org membership is checked below before resuming.
25+ const userId = await requireUserId ( request ) ;
1926 const { batchId } = ParamSchema . parse ( params ) ;
2027
2128 const formData = await request . formData ( ) ;
@@ -25,9 +32,22 @@ export const action: ActionFunction = async ({ request, params }) => {
2532 return json ( submission . reply ( ) ) ;
2633 }
2734
35+ // Keep the post-action redirect same-origin.
36+ const safeRedirectUrl = sanitizeRedirectPath ( submission . value . redirectUrl ) ;
37+
38+ // Only act on a batch in an org the caller belongs to. Accepts either the
39+ // friendlyId or the internal id; both forms stay org-scoped.
40+ const ownedBatchRunId = await findBatchRunIdForUser ( prisma , runStore , batchId , userId ) ;
41+
42+ if ( ! ownedBatchRunId ) {
43+ return redirectWithErrorMessage ( safeRedirectUrl , request , "Batch not found" ) ;
44+ }
45+
2846 try {
2947 const resumeBatchRunService = new ResumeBatchRunService ( ) ;
30- const resumeResult = await resumeBatchRunService . call ( batchId ) ;
48+ // Resume by the resolved internal id: the service looks up strictly by
49+ // `{ id }`, so passing a friendlyId param would resolve to nothing.
50+ const resumeResult = await resumeBatchRunService . call ( ownedBatchRunId ) ;
3151
3252 let message : string | undefined ;
3353
@@ -52,7 +72,7 @@ export const action: ActionFunction = async ({ request, params }) => {
5272 }
5373 }
5474
55- return redirectWithSuccessMessage ( submission . value . redirectUrl , request , message ) ;
75+ return redirectWithSuccessMessage ( safeRedirectUrl , request , message ) ;
5676 } catch ( error ) {
5777 if ( error instanceof Error ) {
5878 logger . error ( "Failed to check batch completion" , {
@@ -62,10 +82,10 @@ export const action: ActionFunction = async ({ request, params }) => {
6282 stack : error . stack ,
6383 } ,
6484 } ) ;
65- return redirectWithErrorMessage ( submission . value . redirectUrl , request , error . message ) ;
85+ return redirectWithErrorMessage ( safeRedirectUrl , request , error . message ) ;
6686 } else {
6787 logger . error ( "Failed to check batch completion" , { error } ) ;
68- return redirectWithErrorMessage ( submission . value . redirectUrl , request , "Unknown error" ) ;
88+ return redirectWithErrorMessage ( safeRedirectUrl , request , "Unknown error" ) ;
6989 }
7090 }
7191} ;
0 commit comments