diff --git a/src/lib/import/airtrail.ts b/src/lib/import/airtrail.ts index 160c19e..3704efe 100644 --- a/src/lib/import/airtrail.ts +++ b/src/lib/import/airtrail.ts @@ -36,7 +36,7 @@ const AirTrailFile = z.object({ .string() .min(3, { message: 'Username must be at least 3 characters long' }) .max(20, { message: 'Username must be at most 20 characters long' }) - .regex(/^[a-zA-Z0-9_]+$/, { + .regex(/^\w+$/, { message: 'Username can only contain letters, numbers, and underscores', }), diff --git a/src/lib/import/aita.ts b/src/lib/import/aita.ts index f049f28..2febb24 100644 --- a/src/lib/import/aita.ts +++ b/src/lib/import/aita.ts @@ -18,7 +18,7 @@ const AITA_SEAT_CLASS_MAP: Record = { export const processAITAFile = (input: string, options: PlatformOptions) => { const tripPattern = - /^Ownership\.([\w]+);[^\r\n]*\sflights:\s([\s\S]+?)\shotels:/gm; + /^Ownership\.(\w+);[^\r\n]*\sflights:\s([\s\S]+?)\shotels:/gm; const flightPattern = /^([^;\n\r]*);(\w*);.*?;(\w{2,4};\d{2,4});(\w*);(\w{3});(\w{3});([\d\-T:]+);([\d\-T:]+);([\d\-T:]+);([\d\-T:]+);(.*)/gm; diff --git a/src/lib/import/jetlog.ts b/src/lib/import/jetlog.ts index 4b01529..6f20fa4 100644 --- a/src/lib/import/jetlog.ts +++ b/src/lib/import/jetlog.ts @@ -19,23 +19,27 @@ const JETLOG_FLIGHT_CLASS_MAP: Record = { }; const nullTransformer = (v: string) => (v === '' ? null : v); +const dateRegex = /^\d{4}-\d{2}-\d{2}$/; +const optionalTimePrimitive = z + .string() + .refine((v) => v === '' || v.match(/^\d{2}:\d{2}$/), { + message: 'Invalid time format', + }) + .transform(nullTransformer); +const optionalDatePrimitive = z + .string() + .refine((v) => v === '' || v.match(dateRegex), { + message: 'Invalid date format', + }) + .transform(nullTransformer); const JetLogFlight = z.object({ - date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), + date: z.string().regex(dateRegex), origin: z.string(), destination: z.string(), - departure_time: z - .string() - .regex(/^\d{2}:\d{2}$|/) - .transform(nullTransformer), - arrival_time: z - .string() - .regex(/^\d{2}:\d{2}$|/) - .transform(nullTransformer), - arrival_date: z - .string() - .regex(/^\d{4}-\d{2}-\d{2}$|/) - .transform(nullTransformer), + departure_time: optionalTimePrimitive, + arrival_time: optionalTimePrimitive, + arrival_date: optionalDatePrimitive, seat: z.enum(['window', 'middle', 'aisle', '']).transform(nullTransformer), ticket_class: z.string().transform(nullTransformer), duration: z.string().transform(nullTransformer), diff --git a/src/lib/server/utils/oauth.ts b/src/lib/server/utils/oauth.ts index 228a9ea..fdd5e73 100644 --- a/src/lib/server/utils/oauth.ts +++ b/src/lib/server/utils/oauth.ts @@ -54,7 +54,7 @@ export const getOAuthClient = async () => { const { enabled, clientId, clientSecret, issuerUrl } = config.oauth; if (!enabled || !clientId || !clientSecret || !issuerUrl) { - throw new Error('OAuth is not enabled'); + throw new Error('OAuth is not enabled or configured properly'); } try { diff --git a/src/lib/utils/data/data.ts b/src/lib/utils/data/data.ts index a0ca2b6..a07d2ec 100644 --- a/src/lib/utils/data/data.ts +++ b/src/lib/utils/data/data.ts @@ -217,15 +217,20 @@ export const formatSeat = (f: FlightData) => { const s = f.seats.find((seat) => seat.userId === userId); if (!s) return null; - return s.seat && s.seatNumber && s.seatClass - ? `${t(s.seatClass)} (${s.seat} ${s.seatNumber})` - : s.seat && s.seatNumber - ? `${s.seat} ${s.seatNumber}` - : s.seat && s.seatClass - ? `${t(s.seatClass)} (${s.seat})` - : s.seatClass - ? t(s.seatClass) - : s.seat - ? t(s.seat) - : null; + if (s.seat && s.seatNumber && s.seatClass) { + return `${t(s.seatClass)} (${s.seat} ${s.seatNumber})`; + } + if (s.seat && s.seatNumber) { + return `${s.seat} ${s.seatNumber}`; + } + if (s.seat && s.seatClass) { + return `${t(s.seatClass)} (${s.seat})`; + } + if (s.seatClass) { + return t(s.seatClass); + } + if (s.seat) { + return t(s.seat); + } + return null; }; diff --git a/src/lib/zod/flight.ts b/src/lib/zod/flight.ts index ae3d267..70f5f8d 100644 --- a/src/lib/zod/flight.ts +++ b/src/lib/zod/flight.ts @@ -2,11 +2,25 @@ import { z } from 'zod'; import { FlightReasons, SeatClasses, SeatTypes } from '$lib/db/types'; -// |^$ is for empty string in the case where the user deletes the input -const regex24h = - /^([01]?[0-9]|2[0-3])(?::|\.|)[0-5][0-9](?:\s?(?:am|pm))?$|^$/i; +const regex24h = /^([01]?\d|2[0-3])(?::|\.|)[0-5]\d(?:\s?(?:am|pm))?$/i; const regex12hLike = /^\d{1,2}(?::|\.|)\d{2}\s?(?:am|pm)$/i; -const regex12h = /^([1-9]|1[0-2])(?::|\.|)[0-5][0-9]\s?(?:am|pm)$/i; +const regex12h = /^([1-9]|1[0-2])(?::|\.|)[0-5]\d\s?(?:am|pm)$/i; + +const timePrimitive = z + .string() + .refine((value) => { + // Always allow empty string (to allow the user to delete the input) + if (value === '') return true; + + return regex24h.test(value); + }, 'Invalid 24-hour format') + .refine((value) => { + // Skip 12-hour check if empty or not possibly 12-hour format (caught by the previous refine) + if (value === '' || !regex12hLike.test(value)) return true; + + return regex12h.test(value); + }, 'Invalid 12-hour format') + .nullable(); export const flightAirportsSchema = z.object({ from: z.string().min(1, 'Select an origin'), @@ -19,30 +33,12 @@ export const flightDateTimeSchema = z.object({ .datetime({ offset: true, message: 'Select a departure date' }) .nullable() .refine((value) => value !== null, 'Select a departure date'), - departureTime: z - .string() - .refine((value) => regex24h.test(value), 'Invalid 24-hour format') - .refine((value) => { - if (regex12hLike.test(value)) { - return regex12h.test(value); - } - return true; // If it's not in 12-hour format, just return true (it'll be caught by the previous refine) - }, 'Invalid 12-hour format') - .nullable(), + departureTime: timePrimitive, arrival: z .string() .datetime({ offset: true, message: 'Select an arrival date' }) .nullable(), - arrivalTime: z - .string() - .refine((value) => regex24h.test(value), 'Invalid 24-hour format') - .refine((value) => { - if (regex12hLike.test(value)) { - return regex12h.test(value); - } - return true; // If it's not in 12-hour format, just return true (it'll be caught by the previous refine) - }, 'Invalid 12-hour format') - .nullable(), + arrivalTime: timePrimitive, }); export const flightSeatInformationSchema = z.object({ diff --git a/src/lib/zod/user.ts b/src/lib/zod/user.ts index 3178262..4ce146e 100644 --- a/src/lib/zod/user.ts +++ b/src/lib/zod/user.ts @@ -5,7 +5,7 @@ export const userSchema = z.object({ .string() .min(3, { message: 'Username must be at least 3 characters long' }) .max(20, { message: 'Username must be at most 20 characters long' }) - .regex(/^[a-zA-Z0-9_]+$/, { + .regex(/^\w+$/, { message: 'Username can only contain letters, numbers, and underscores', }), password: z.string().min(8), diff --git a/src/routes/api/flight/save/+server.ts b/src/routes/api/flight/save/+server.ts index ee09656..d531839 100644 --- a/src/routes/api/flight/save/+server.ts +++ b/src/routes/api/flight/save/+server.ts @@ -1,11 +1,5 @@ import type { TZDate } from '@date-fns/tz'; -import { - differenceInSeconds, - format, - formatISO, - isBefore, - isValid, -} from 'date-fns'; +import { differenceInSeconds, format, formatISO, isBefore } from 'date-fns'; import { actionResult, setError, superValidate } from 'sveltekit-superforms'; import { zod } from 'sveltekit-superforms/adapters';