- Set up pnpm workspace with 4 packages: shared, server, client-mobile, client-screen - Implement Redis atomic voting with Lua scripts (HINCRBY + distributed lock) - Add optimistic UI with IndexedDB queue for offline resilience - Configure Socket.io with auto-reconnection (infinite retries) - Separate mobile (Vant) and big screen (Pixi.js) dependencies Tech stack: - Frontend Mobile: Vue 3 + Vant + Socket.io-client - Frontend Screen: Vue 3 + Pixi.js + GSAP - Backend: Express + Socket.io + Redis + Prisma/MySQL Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
65 lines
2.3 KiB
TypeScript
65 lines
2.3 KiB
TypeScript
import { z } from 'zod';
|
|
import { VOTE_CATEGORIES, PRIZE_LEVELS } from '../types/socket.types';
|
|
|
|
// ============================================================================
|
|
// Vote Validation Schemas
|
|
// ============================================================================
|
|
|
|
export const voteSubmitSchema = z.object({
|
|
candidateId: z.string().min(1).max(64),
|
|
category: z.enum(VOTE_CATEGORIES as unknown as [string, ...string[]]),
|
|
clientTimestamp: z.number().positive(),
|
|
localId: z.string().uuid(),
|
|
});
|
|
|
|
export const voteBatchSchema = z.object({
|
|
votes: z.array(voteSubmitSchema).min(1).max(7),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Draw Validation Schemas
|
|
// ============================================================================
|
|
|
|
export const drawFiltersSchema = z.object({
|
|
excludeWinners: z.boolean().optional(),
|
|
zodiacFilter: z.string().optional(),
|
|
ageRange: z
|
|
.object({
|
|
min: z.number().min(18).max(100).optional(),
|
|
max: z.number().min(18).max(100).optional(),
|
|
})
|
|
.optional(),
|
|
departments: z.array(z.string()).optional(),
|
|
});
|
|
|
|
export const drawStartSchema = z.object({
|
|
prizeLevel: z.enum(PRIZE_LEVELS as unknown as [string, ...string[]]),
|
|
filters: drawFiltersSchema.optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Connection Validation Schemas
|
|
// ============================================================================
|
|
|
|
export const joinPayloadSchema = z.object({
|
|
userId: z.string().min(1).max(64),
|
|
userName: z.string().min(1).max(100),
|
|
role: z.enum(['user', 'admin', 'screen']),
|
|
sessionToken: z.string().optional(),
|
|
});
|
|
|
|
export const syncRequestSchema = z.object({
|
|
lastEventId: z.string().optional(),
|
|
lastTimestamp: z.number().optional(),
|
|
});
|
|
|
|
// ============================================================================
|
|
// Type Exports
|
|
// ============================================================================
|
|
|
|
export type VoteSubmitInput = z.infer<typeof voteSubmitSchema>;
|
|
export type VoteBatchInput = z.infer<typeof voteBatchSchema>;
|
|
export type DrawStartInput = z.infer<typeof drawStartSchema>;
|
|
export type JoinPayloadInput = z.infer<typeof joinPayloadSchema>;
|
|
export type SyncRequestInput = z.infer<typeof syncRequestSchema>;
|