- GET /api/tags — public, filterable by ?pool=craft|cooperative, active only, sorted by label - POST /api/tags/suggest — auth-required, creates TagSuggestion doc - Add tagSuggestionSchema and communityConnectionsUpdateSchema to schemas.js - Extend memberProfileUpdateSchema with craftTags, craftTagsPrivacy, communityConnectionsPrivacy
370 lines
12 KiB
JavaScript
370 lines
12 KiB
JavaScript
import * as z from 'zod'
|
|
|
|
const privacyEnum = z.enum(['public', 'members', 'private'])
|
|
|
|
export const emailSchema = z.object({
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
export const memberCreateSchema = z.object({
|
|
email: z.string().trim().toLowerCase().email(),
|
|
name: z.string().min(1).max(200),
|
|
circle: z.enum(['community', 'founder', 'practitioner']),
|
|
contributionTier: z.enum(['0', '5', '15', '30', '50'])
|
|
})
|
|
|
|
export const memberProfileUpdateSchema = z.object({
|
|
pronouns: z.string().max(100).optional(),
|
|
timeZone: z.string().max(100).optional(),
|
|
avatar: z.string().max(500).optional(),
|
|
studio: z.string().max(200).optional(),
|
|
bio: z.string().max(5000).optional(),
|
|
location: z.string().max(200).optional(),
|
|
socialLinks: z.object({
|
|
mastodon: z.string().max(300).optional(),
|
|
linkedin: z.string().max(300).optional(),
|
|
website: z.string().max(300).optional(),
|
|
other: z.string().max(300).optional()
|
|
}).optional(),
|
|
offering: z.object({
|
|
text: z.string().max(2000).optional(),
|
|
tags: z.array(z.string().max(100)).max(20).optional()
|
|
}).optional(),
|
|
lookingFor: z.object({
|
|
text: z.string().max(2000).optional(),
|
|
tags: z.array(z.string().max(100)).max(20).optional()
|
|
}).optional(),
|
|
showInDirectory: z.boolean().optional(),
|
|
notifications: z.object({
|
|
events: z.boolean().optional(),
|
|
updates: z.boolean().optional(),
|
|
peerRequests: z.boolean().optional()
|
|
}).optional(),
|
|
pronounsPrivacy: privacyEnum.optional(),
|
|
timeZonePrivacy: privacyEnum.optional(),
|
|
avatarPrivacy: privacyEnum.optional(),
|
|
studioPrivacy: privacyEnum.optional(),
|
|
bioPrivacy: privacyEnum.optional(),
|
|
locationPrivacy: privacyEnum.optional(),
|
|
socialLinksPrivacy: privacyEnum.optional(),
|
|
offeringPrivacy: privacyEnum.optional(),
|
|
lookingForPrivacy: privacyEnum.optional(),
|
|
craftTags: z.array(z.string().max(100)).max(16).optional(),
|
|
craftTagsPrivacy: privacyEnum.optional(),
|
|
communityConnectionsPrivacy: privacyEnum.optional()
|
|
})
|
|
|
|
export const eventRegistrationSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
dietary: z.boolean().optional()
|
|
})
|
|
|
|
export const paymentVerifySchema = z.object({
|
|
cardToken: z.string().min(1),
|
|
customerId: z.union([z.string(), z.number()]).transform(String)
|
|
})
|
|
|
|
// --- Helcim schemas ---
|
|
|
|
export const helcimCreatePlanSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
amount: z.union([z.string().min(1), z.number().positive()]),
|
|
frequency: z.string().min(1).max(50),
|
|
currency: z.string().max(10).optional()
|
|
})
|
|
|
|
export const helcimCustomerSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
circle: z.enum(['community', 'founder', 'practitioner']).optional(),
|
|
contributionTier: z.enum(['0', '5', '15', '30', '50']).optional()
|
|
})
|
|
|
|
export const helcimInitializePaymentSchema = z.object({
|
|
amount: z.number().min(0).optional(),
|
|
customerCode: z.string().max(200).optional(),
|
|
metadata: z.object({
|
|
type: z.string().max(100).optional(),
|
|
eventTitle: z.string().max(500).optional(),
|
|
eventId: z.string().max(200).optional()
|
|
}).optional()
|
|
})
|
|
|
|
export const helcimSubscriptionSchema = z.object({
|
|
customerId: z.union([z.string().min(1), z.number()]),
|
|
contributionTier: z.enum(['0', '5', '15', '30', '50']),
|
|
customerCode: z.union([z.string().min(1).max(200), z.number()]).transform(String),
|
|
cardToken: z.string().max(500).optional().nullable()
|
|
})
|
|
|
|
export const helcimUpdateBillingSchema = z.object({
|
|
customerId: z.union([z.string().min(1), z.number()]),
|
|
billingAddress: z.object({
|
|
name: z.string().max(200).optional(),
|
|
street: z.string().min(1).max(500),
|
|
city: z.string().min(1).max(200),
|
|
province: z.string().max(200).optional(),
|
|
state: z.string().max(200).optional(),
|
|
country: z.string().min(1).max(100),
|
|
postalCode: z.string().min(1).max(20)
|
|
})
|
|
})
|
|
|
|
// --- Event ticket/registration schemas ---
|
|
|
|
export const ticketPurchaseSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
transactionId: z.string().max(500).optional()
|
|
})
|
|
|
|
export const ticketReserveSchema = z.object({
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
export const ticketEligibilitySchema = z.object({
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
export const waitlistSchema = z.object({
|
|
name: z.string().max(200).optional(),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
membershipLevel: z.string().max(100).optional()
|
|
})
|
|
|
|
export const waitlistDeleteSchema = z.object({
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
export const cancelRegistrationSchema = z.object({
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
export const checkRegistrationSchema = z.object({
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
export const guestRegisterSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
export const eventPaymentSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
paymentToken: z.string().min(1).max(500)
|
|
})
|
|
|
|
// --- Member schemas ---
|
|
|
|
export const updateContributionSchema = z.object({
|
|
contributionTier: z.enum(['0', '5', '15', '30', '50'])
|
|
})
|
|
|
|
export const updateCircleSchema = z.object({
|
|
circle: z.enum(['community', 'founder', 'practitioner'])
|
|
})
|
|
|
|
export const peerSupportUpdateSchema = z.object({
|
|
enabled: z.boolean().optional(),
|
|
skillTopics: z.array(z.string().max(200)).max(20).optional(),
|
|
supportTopics: z.array(z.string().max(200)).max(20).optional(),
|
|
availability: z.string().max(500).optional(),
|
|
personalMessage: z.string().max(2000).optional(),
|
|
slackUsername: z.string().max(200).optional()
|
|
})
|
|
|
|
// --- Series ticket schemas ---
|
|
|
|
export const seriesTicketPurchaseSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
paymentId: z.string().max(500).optional(),
|
|
ticketType: z.enum(['member', 'public', 'guest']).optional(),
|
|
})
|
|
|
|
export const seriesTicketEligibilitySchema = z.object({
|
|
email: z.string().trim().toLowerCase().email()
|
|
})
|
|
|
|
// --- Admin schemas ---
|
|
|
|
export const adminEventCreateSchema = z.object({
|
|
title: z.string().min(1).max(500),
|
|
description: z.string().min(1).max(50000),
|
|
startDate: z.string().min(1),
|
|
endDate: z.string().min(1),
|
|
location: z.string().max(500).optional(),
|
|
maxAttendees: z.number().int().positive().optional(),
|
|
membersOnly: z.boolean().optional(),
|
|
registrationDeadline: z.string().optional(),
|
|
pricing: z.object({
|
|
paymentRequired: z.boolean().optional(),
|
|
isFree: z.boolean().optional()
|
|
}).optional(),
|
|
tickets: z.object({
|
|
enabled: z.boolean().optional(),
|
|
public: z.object({
|
|
available: z.boolean().optional(),
|
|
name: z.string().max(200).optional(),
|
|
description: z.string().max(2000).optional(),
|
|
price: z.number().min(0).optional(),
|
|
quantity: z.number().int().positive().optional(),
|
|
earlyBirdPrice: z.number().min(0).optional(),
|
|
earlyBirdDeadline: z.string().optional()
|
|
}).optional()
|
|
}).optional(),
|
|
image: z.string().url().optional(),
|
|
category: z.string().max(100).optional(),
|
|
tags: z.array(z.string().max(100)).max(20).optional(),
|
|
series: z.string().optional()
|
|
})
|
|
|
|
export const adminEventUpdateSchema = z.object({
|
|
title: z.string().min(1).max(500),
|
|
description: z.string().min(1).max(50000),
|
|
startDate: z.string().min(1),
|
|
endDate: z.string().min(1),
|
|
location: z.string().max(500).optional(),
|
|
maxAttendees: z.number().int().positive().optional().nullable(),
|
|
membersOnly: z.boolean().optional(),
|
|
registrationDeadline: z.string().optional().nullable(),
|
|
pricing: z.object({
|
|
paymentRequired: z.boolean().optional(),
|
|
isFree: z.boolean().optional(),
|
|
publicPrice: z.number().min(0).optional()
|
|
}).optional(),
|
|
tickets: z.object({
|
|
enabled: z.boolean().optional(),
|
|
public: z.object({
|
|
available: z.boolean().optional(),
|
|
name: z.string().max(200).optional(),
|
|
description: z.string().max(2000).optional(),
|
|
price: z.number().min(0).optional(),
|
|
quantity: z.number().int().positive().optional().nullable(),
|
|
sold: z.number().int().min(0).optional(),
|
|
earlyBirdPrice: z.number().min(0).optional().nullable(),
|
|
earlyBirdDeadline: z.string().optional().nullable()
|
|
}).optional()
|
|
}).optional(),
|
|
image: z.string().url().optional().nullable(),
|
|
category: z.string().max(100).optional(),
|
|
tags: z.array(z.string().max(100)).max(20).optional(),
|
|
series: z.any().optional(),
|
|
slug: z.string().max(500).optional()
|
|
}).passthrough()
|
|
|
|
export const adminSeriesCreateSchema = z.object({
|
|
id: z.string().min(1).max(200),
|
|
title: z.string().min(1).max(500),
|
|
description: z.string().min(1).max(50000),
|
|
type: z.string().max(100).optional(),
|
|
totalEvents: z.number().int().positive().optional().nullable()
|
|
})
|
|
|
|
export const adminSeriesUpdateSchema = z.object({
|
|
id: z.string().min(1).max(200),
|
|
title: z.string().min(1).max(500),
|
|
description: z.string().max(50000).optional(),
|
|
type: z.string().max(100).optional(),
|
|
totalEvents: z.number().int().positive().optional().nullable()
|
|
})
|
|
|
|
export const adminSeriesItemUpdateSchema = z.object({
|
|
title: z.string().min(1).max(500).optional(),
|
|
description: z.string().max(50000).optional(),
|
|
type: z.string().max(100).optional(),
|
|
totalEvents: z.number().int().positive().optional().nullable(),
|
|
isActive: z.boolean().optional()
|
|
})
|
|
|
|
export const adminSeriesTicketsSchema = z.object({
|
|
id: z.string().min(1).max(200),
|
|
tickets: z.object({
|
|
enabled: z.boolean().optional(),
|
|
requiresSeriesTicket: z.boolean().optional(),
|
|
allowIndividualEventTickets: z.boolean().optional(),
|
|
currency: z.string().max(10).optional(),
|
|
member: z.object({
|
|
available: z.boolean().optional(),
|
|
isFree: z.boolean().optional(),
|
|
price: z.number().min(0).optional(),
|
|
name: z.string().max(200).optional(),
|
|
description: z.string().max(2000).optional(),
|
|
circleOverrides: z.record(z.any()).optional()
|
|
}).optional(),
|
|
public: z.object({
|
|
available: z.boolean().optional(),
|
|
name: z.string().max(200).optional(),
|
|
description: z.string().max(2000).optional(),
|
|
price: z.number().min(0).optional(),
|
|
quantity: z.number().int().positive().optional().nullable(),
|
|
sold: z.number().int().min(0).optional(),
|
|
reserved: z.number().int().min(0).optional(),
|
|
earlyBirdPrice: z.number().min(0).optional().nullable(),
|
|
earlyBirdDeadline: z.string().optional().nullable()
|
|
}).optional(),
|
|
capacity: z.object({
|
|
total: z.number().int().positive().optional().nullable(),
|
|
reserved: z.number().int().min(0).optional()
|
|
}).optional(),
|
|
waitlist: z.object({
|
|
enabled: z.boolean().optional(),
|
|
maxSize: z.number().int().positive().optional().nullable(),
|
|
entries: z.array(z.any()).optional()
|
|
}).optional()
|
|
})
|
|
})
|
|
|
|
export const adminMemberCreateSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
circle: z.enum(['community', 'founder', 'practitioner']),
|
|
contributionTier: z.enum(['0', '5', '15', '30', '50'])
|
|
})
|
|
|
|
export const adminMemberUpdateSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
circle: z.enum(['community', 'founder', 'practitioner']),
|
|
contributionTier: z.enum(['0', '5', '15', '30', '50']),
|
|
status: z.enum(['pending_payment', 'active', 'suspended', 'cancelled'])
|
|
})
|
|
|
|
export const adminRoleUpdateSchema = z.object({
|
|
role: z.enum(['admin', 'member'])
|
|
})
|
|
|
|
export const bulkMemberImportSchema = z.object({
|
|
members: z.array(z.object({
|
|
name: z.string().min(1).max(200),
|
|
email: z.string().trim().toLowerCase().email(),
|
|
circle: z.enum(['community', 'founder', 'practitioner']),
|
|
contributionTier: z.enum(['0', '5', '15', '30', '50'])
|
|
})).min(1).max(100)
|
|
})
|
|
|
|
export const memberInviteSchema = z.object({
|
|
memberIds: z.array(z.string().min(1)).min(1).max(100),
|
|
emailTemplate: z.string().min(1).max(10000)
|
|
})
|
|
|
|
// --- Tag schemas ---
|
|
|
|
export const tagSuggestionSchema = z.object({
|
|
label: z.string().min(1).max(100),
|
|
pool: z.enum(['craft', 'cooperative'])
|
|
})
|
|
|
|
export const communityConnectionsUpdateSchema = z.object({
|
|
topics: z.array(z.object({
|
|
tagSlug: z.string().min(1).max(100),
|
|
state: z.enum(['help', 'interested', 'seeking'])
|
|
})).max(20).optional(),
|
|
offerPeerSupport: z.boolean().optional(),
|
|
availability: z.string().max(500).optional(),
|
|
slackHandle: z.string().max(200).optional(),
|
|
personalMessage: z.string().max(2000).optional(),
|
|
details: z.string().max(300).optional()
|
|
})
|