- Add instance restore model, routes, and agent backup/restore endpoints - Add Pangolin tunnel service (subdomain prefix, teardown action, CCP client) - Add slug mutex for concurrent operation safety in agent - Expand upgrade service with remote driver orchestration - Fix upgrade.sh to properly handle release-mode installs (no git operations) - Add CCP registration flags to config.sh (--ccp-url, --ccp-invite-code, --ccp-agent-url) - Auto-detect JVB advertise IP in non-interactive mode - Polish volunteer dashboard ActionStepsList with highlighted step component - Add ticketed event description field + volunteer dashboard query refinements Bunker Admin
119 lines
4.8 KiB
TypeScript
119 lines
4.8 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
export const createEventSchema = z.object({
|
|
title: z.string().min(1).max(200),
|
|
description: z.string().max(2000).optional(),
|
|
richDescription: z.string().max(50000).optional(),
|
|
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
startTime: z.string().regex(/^\d{2}:\d{2}$/),
|
|
endTime: z.string().regex(/^\d{2}:\d{2}$/),
|
|
doorsOpenTime: z.string().regex(/^\d{2}:\d{2}$/).optional(),
|
|
eventFormat: z.enum(['IN_PERSON', 'ONLINE', 'HYBRID']).default('IN_PERSON'),
|
|
venueName: z.string().max(200).optional(),
|
|
venueAddress: z.string().max(500).optional(),
|
|
latitude: z.number().min(-90).max(90).optional(),
|
|
longitude: z.number().min(-180).max(180).optional(),
|
|
visibility: z.enum(['PUBLIC', 'UNLISTED', 'PRIVATE']).optional(),
|
|
coverImageUrl: z.string().url().optional(),
|
|
maxAttendees: z.number().int().positive().optional(),
|
|
organizerName: z.string().max(200).optional(),
|
|
organizerEmail: z.string().email().optional(),
|
|
tiers: z.array(z.object({
|
|
name: z.string().min(1).max(100),
|
|
description: z.string().max(500).optional(),
|
|
tierType: z.enum(['PAID', 'FREE', 'DONATION']),
|
|
priceCAD: z.number().int().min(0).default(0),
|
|
minDonationCAD: z.number().int().min(0).optional(),
|
|
maxQuantity: z.number().int().positive().optional(),
|
|
maxPerOrder: z.number().int().min(1).max(100).default(10),
|
|
salesStartAt: z.string().datetime().optional(),
|
|
salesEndAt: z.string().datetime().optional(),
|
|
sortOrder: z.number().int().default(0),
|
|
})).optional(),
|
|
});
|
|
|
|
export const updateEventSchema = z.object({
|
|
title: z.string().min(1).max(200).optional(),
|
|
description: z.string().max(2000).nullable().optional(),
|
|
richDescription: z.string().max(50000).nullable().optional(),
|
|
date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
startTime: z.string().regex(/^\d{2}:\d{2}$/).optional(),
|
|
endTime: z.string().regex(/^\d{2}:\d{2}$/).optional(),
|
|
doorsOpenTime: z.string().regex(/^\d{2}:\d{2}$/).nullable().optional(),
|
|
eventFormat: z.enum(['IN_PERSON', 'ONLINE', 'HYBRID']).optional(),
|
|
venueName: z.string().max(200).nullable().optional(),
|
|
venueAddress: z.string().max(500).nullable().optional(),
|
|
latitude: z.number().min(-90).max(90).nullable().optional(),
|
|
longitude: z.number().min(-180).max(180).nullable().optional(),
|
|
visibility: z.enum(['PUBLIC', 'UNLISTED', 'PRIVATE']).optional(),
|
|
coverImageUrl: z.string().url().nullable().optional(),
|
|
maxAttendees: z.number().int().positive().nullable().optional(),
|
|
organizerName: z.string().max(200).nullable().optional(),
|
|
organizerEmail: z.string().email().nullable().optional(),
|
|
featured: z.boolean().optional(),
|
|
});
|
|
|
|
export const createTierSchema = z.object({
|
|
name: z.string().min(1).max(100),
|
|
description: z.string().max(500).optional(),
|
|
tierType: z.enum(['PAID', 'FREE', 'DONATION']),
|
|
priceCAD: z.number().int().min(0).default(0),
|
|
minDonationCAD: z.number().int().min(0).optional(),
|
|
maxQuantity: z.number().int().positive().optional(),
|
|
maxPerOrder: z.number().int().min(1).max(100).default(10),
|
|
salesStartAt: z.string().datetime().optional(),
|
|
salesEndAt: z.string().datetime().optional(),
|
|
sortOrder: z.number().int().default(0),
|
|
});
|
|
|
|
export const updateTierSchema = z.object({
|
|
name: z.string().min(1).max(100).optional(),
|
|
description: z.string().max(500).nullable().optional(),
|
|
tierType: z.enum(['PAID', 'FREE', 'DONATION']).optional(),
|
|
priceCAD: z.number().int().min(0).optional(),
|
|
minDonationCAD: z.number().int().min(0).nullable().optional(),
|
|
maxQuantity: z.number().int().positive().nullable().optional(),
|
|
maxPerOrder: z.number().int().min(1).max(100).optional(),
|
|
salesStartAt: z.string().datetime().nullable().optional(),
|
|
salesEndAt: z.string().datetime().nullable().optional(),
|
|
sortOrder: z.number().int().optional(),
|
|
isActive: z.boolean().optional(),
|
|
});
|
|
|
|
export const checkoutSchema = z.object({
|
|
tierId: z.string().min(1),
|
|
quantity: z.number().int().min(1).max(100).default(1),
|
|
buyerEmail: z.string().email(),
|
|
buyerName: z.string().max(200).optional(),
|
|
});
|
|
|
|
export const registerFreeSchema = z.object({
|
|
tierId: z.string().min(1),
|
|
quantity: z.number().int().min(1).max(10).default(1),
|
|
holderEmail: z.string().email(),
|
|
holderName: z.string().max(200).optional(),
|
|
});
|
|
|
|
export const validateTokenSchema = z.object({
|
|
token: z.string().min(1),
|
|
});
|
|
|
|
export const confirmCheckinSchema = z.object({
|
|
token: z.string().min(1),
|
|
notes: z.string().max(500).optional(),
|
|
});
|
|
|
|
export const manualCheckinSchema = z.object({
|
|
eventId: z.string().min(1),
|
|
ticketCode: z.string().optional(),
|
|
holderEmail: z.string().email().optional(),
|
|
notes: z.string().max(500).optional(),
|
|
}).refine(
|
|
data => data.ticketCode || data.holderEmail,
|
|
{ message: 'Either ticketCode or holderEmail is required' },
|
|
);
|
|
|
|
export const meetingAccessSchema = z.object({
|
|
ticketCode: z.string().min(1),
|
|
});
|