changemaker.lite/api/src/modules/ticketed-events/ticketed-events.schemas.ts
bunker-admin 26ec925d9b CCP restore/tunnel/upgrade + upgrade.sh release-mode fixes + volunteer dashboard polish
- 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
2026-04-12 11:09:46 -06:00

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),
});