@@ -7,33 +7,53 @@ import type {
77import { defineRouteContract } from '@/lib/api/contracts/types'
88import { validateAwsRegion } from '@/lib/core/security/input-validation'
99
10- const ScanSchema = z . object ( {
11- region : z
12- . string ( )
13- . min ( 1 , 'AWS region is required' )
14- . refine ( ( v ) => validateAwsRegion ( v ) . isValid , {
15- message : 'Invalid AWS region format (e.g., us-east-1, eu-west-2)' ,
16- } ) ,
17- accessKeyId : z . string ( ) . min ( 1 , 'AWS access key ID is required' ) ,
18- secretAccessKey : z . string ( ) . min ( 1 , 'AWS secret access key is required' ) ,
19- tableName : z . string ( ) . min ( 1 , 'Table name is required' ) ,
20- filterExpression : z . string ( ) . optional ( ) ,
21- projectionExpression : z . string ( ) . optional ( ) ,
22- expressionAttributeNames : z . record ( z . string ( ) , z . string ( ) ) . optional ( ) ,
23- expressionAttributeValues : z . record ( z . string ( ) , z . unknown ( ) ) . optional ( ) ,
24- limit : z . number ( ) . positive ( ) . optional ( ) ,
25- exclusiveStartKey : z . record ( z . string ( ) , z . unknown ( ) ) . optional ( ) ,
26- indexName : z . string ( ) . optional ( ) ,
27- consistentRead : z
28- . union ( [ z . boolean ( ) , z . string ( ) ] )
29- . optional ( )
30- . transform ( ( val ) => {
31- if ( val === true || val === 'true' ) return true
32- return undefined
33- } ) ,
34- segment : z . number ( ) . int ( ) . min ( 0 ) . max ( 999999 ) . optional ( ) ,
35- totalSegments : z . number ( ) . int ( ) . min ( 1 ) . max ( 1000000 ) . optional ( ) ,
36- } )
10+ const ScanSchema = z
11+ . object ( {
12+ region : z
13+ . string ( )
14+ . min ( 1 , 'AWS region is required' )
15+ . refine ( ( v ) => validateAwsRegion ( v ) . isValid , {
16+ message : 'Invalid AWS region format (e.g., us-east-1, eu-west-2)' ,
17+ } ) ,
18+ accessKeyId : z . string ( ) . min ( 1 , 'AWS access key ID is required' ) ,
19+ secretAccessKey : z . string ( ) . min ( 1 , 'AWS secret access key is required' ) ,
20+ tableName : z . string ( ) . min ( 1 , 'Table name is required' ) ,
21+ filterExpression : z . string ( ) . optional ( ) ,
22+ projectionExpression : z . string ( ) . optional ( ) ,
23+ expressionAttributeNames : z . record ( z . string ( ) , z . string ( ) ) . optional ( ) ,
24+ expressionAttributeValues : z . record ( z . string ( ) , z . unknown ( ) ) . optional ( ) ,
25+ limit : z . number ( ) . positive ( ) . optional ( ) ,
26+ exclusiveStartKey : z . record ( z . string ( ) , z . unknown ( ) ) . optional ( ) ,
27+ indexName : z . string ( ) . optional ( ) ,
28+ consistentRead : z
29+ . union ( [ z . boolean ( ) , z . string ( ) ] )
30+ . optional ( )
31+ . transform ( ( val ) => {
32+ if ( val === true || val === 'true' ) return true
33+ return undefined
34+ } ) ,
35+ segment : z . number ( ) . int ( ) . min ( 0 ) . max ( 999999 ) . optional ( ) ,
36+ totalSegments : z . number ( ) . int ( ) . min ( 1 ) . max ( 1000000 ) . optional ( ) ,
37+ } )
38+ . superRefine ( ( value , ctx ) => {
39+ if ( ( value . segment === undefined ) !== ( value . totalSegments === undefined ) ) {
40+ ctx . addIssue ( {
41+ code : 'custom' ,
42+ path : [ 'segment' ] ,
43+ message : 'Segment and Total Segments must be set together for a parallel scan' ,
44+ } )
45+ } else if (
46+ value . segment !== undefined &&
47+ value . totalSegments !== undefined &&
48+ value . segment >= value . totalSegments
49+ ) {
50+ ctx . addIssue ( {
51+ code : 'custom' ,
52+ path : [ 'segment' ] ,
53+ message : 'Segment must be less than Total Segments' ,
54+ } )
55+ }
56+ } )
3757
3858const ScanResponseSchema = z . object ( {
3959 message : z . string ( ) ,
0 commit comments