1- import type { Actions } from '@sveltejs/kit' ;
21import type { PageServerLoad } from './$types'
32import { redirect } from '@sveltejs/kit' ;
43import { type CampaignResult , prefixesToCampaignResults } from '$lib/utils/groupCampaigns' ;
54import { findLeafPrefixes } from '$lib/server/s3' ;
65import { logger } from '$lib/server/logger' ;
76
7+ import { z } from 'zod' ;
8+ import { fail } from '@sveltejs/kit' ;
9+
10+ // Define the validation schema with Zod
11+ const filterSchema = z . object ( {
12+ year : z . string ( )
13+ . regex ( / ^ \d { 4 } $ / , { message : "Year must be a 4-digit number" } )
14+ . optional ( )
15+ . or ( z . literal ( '' ) ) , // Allow empty string
16+
17+ month : z . preprocess (
18+ // If the input is an empty string, convert it to `undefined`.
19+ // Otherwise, pass it through unchanged.
20+ ( val ) => ( val === "" ? undefined : val ) ,
21+ // Now, validate the processed value.
22+ z . coerce . number ( { invalid_type_error : "Month must be a number" } )
23+ . int ( )
24+ . min ( 1 , { message : "Month must be between 1 and 12" } )
25+ . max ( 12 , { message : "Month must be between 1 and 12" } )
26+ . optional ( )
27+ ) ,
28+
29+ day : z . preprocess (
30+ // Same preprocessing for the day field.
31+ ( val ) => ( val === "" ? undefined : val ) ,
32+ // Validate the processed day value.
33+ z . coerce . number ( { invalid_type_error : "Day must be a number" } )
34+ . int ( )
35+ . min ( 1 , { message : "Day must be between 1 and 31" } )
36+ . max ( 31 , { message : "Day must be between 1 and 31" } )
37+ . optional ( )
38+ ) ,
39+
40+ number : z . string ( )
41+ . regex ( / ^ \d { 1 , 2 } $ / , { message : "Number must be a 1 or 2-digit number" } )
42+ . optional ( )
43+ . or ( z . literal ( '' ) ) ,
44+ } )
45+ // Your dependency rules are still correct and important!
46+ . superRefine ( ( data , ctx ) => {
47+ if ( data . month && ! data . year ) {
48+ ctx . addIssue ( {
49+ code : z . ZodIssueCode . custom ,
50+ path : [ 'year' ] ,
51+ message : 'Year is required to specify a month' ,
52+ } ) ;
53+ }
54+ if ( data . day && ! data . month ) {
55+ ctx . addIssue ( {
56+ code : z . ZodIssueCode . custom ,
57+ path : [ 'month' ] ,
58+ message : 'Month is required to specify a day' ,
59+ } ) ;
60+ }
61+ if ( data . number && ! data . day ) {
62+ ctx . addIssue ( {
63+ code : z . ZodIssueCode . custom ,
64+ path : [ 'day' ] ,
65+ message : 'Day is required to specify a number' ,
66+ } ) ;
67+ }
68+ } ) ;
69+
870export const load : PageServerLoad = async ( { locals, url } ) => {
971 try {
10- const prefix = url . searchParams . get ( 'prefix' ) || 'batch/' ;
72+ const year = url . searchParams . get ( 'year' ) || undefined ;
73+ const rawMonth = url . searchParams . get ( 'month' ) || undefined ;
74+ const rawDay = url . searchParams . get ( 'day' ) || undefined ;
75+ const number = url . searchParams . get ( 'number' ) || undefined ;
76+ const month = rawMonth ? rawMonth . padStart ( 2 , '0' ) : undefined ;
77+ const day = rawDay ? rawDay . padStart ( 2 , '0' ) : undefined ;
78+ const pathParts = [ 'batch' , year , month , day , number ] . filter ( part => part ) ;
79+ const prefix = pathParts . join ( '/' ) ;
80+ logger . info ( { prefix} , `Search campaigns with prefix ${ prefix } ` )
1181
1282 const { prefixes, count } = await findLeafPrefixes ( prefix , 5 ) ;
1383 logger . info ( { prefixes, count} , `got campaign prefixes for start prefix ${ prefix } ` )
@@ -24,16 +94,35 @@ export const load: PageServerLoad = async ({ locals, url }) => {
2494 }
2595} ;
2696
27- export const actions : Actions = {
28- filter : async ( { request, url } ) => {
29- const formData = await request . formData ( ) ;
30- const prefix = formData . get ( 'prefix' ) || 'batch/' ;
97+ export const actions = {
98+ filter : async ( { request, url } ) => {
99+ const formData = await request . formData ( ) ;
100+ const data = Object . fromEntries ( formData ) ;
101+
102+ const result = filterSchema . safeParse ( data ) ;
103+
104+ if ( ! result . success ) {
105+ // The `fail` function sends back a 400 status code
106+ // and the validation errors, along with the original data.
107+ return fail ( 400 , {
108+ data : data ,
109+ errors : result . error . flatten ( ) . fieldErrors ,
110+ } ) ;
111+ }
31112
32- // Create a URL object based on the current page's URL
33- const targetUrl = new URL ( url . origin + url . pathname ) ;
34- targetUrl . searchParams . set ( 'prefix' , prefix ) ;
113+ logger . info ( 'Validation successful! Applying filter with:' , result . data ) ;
35114
36- // Use status 303 (See Other) for the redirect status code
115+ const targetUrl = new URL ( url . origin + url . pathname )
116+ logger . debug ( result . data , 'result.data' ) ;
117+
118+ for ( const [ key , value ] of Object . entries ( result . data ) ) {
119+ if ( value ) {
120+ targetUrl . searchParams . set ( key , value as string ) ;
121+ }
122+ }
123+ logger . info ( `Redirecting to ${ targetUrl } ` ) ;
124+
125+ // Use status 303
37126 throw redirect ( 303 , targetUrl ) ;
38- }
127+ }
39128} ;
0 commit comments