mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-03-28 19:23:15 +00:00
280 lines
8.6 KiB
TypeScript
280 lines
8.6 KiB
TypeScript
import { MiiGender, MiiPlatform } from "@prisma/client";
|
|
import { z } from "zod";
|
|
|
|
// profanity censoring bypasses the regex in some of these but I think it's funny
|
|
|
|
export const querySchema = z
|
|
.string()
|
|
.trim()
|
|
.min(2, { error: "Search query must be at least 2 characters long" })
|
|
.max(64, { error: "Search query cannot be more than 64 characters long" })
|
|
.regex(/^[a-zA-Z0-9-_. ']+$/, {
|
|
error: "Search query can only contain letters, numbers, dashes, underscores, apostrophes, and spaces.",
|
|
});
|
|
|
|
// Miis
|
|
export const nameSchema = z
|
|
.string()
|
|
.trim()
|
|
.min(2, { error: "Name must be at least 2 characters long" })
|
|
.max(64, { error: "Name cannot be more than 64 characters long" })
|
|
.regex(/^[a-zA-Z0-9-_. ']+$/, {
|
|
error: "Name can only contain letters, numbers, dashes, underscores, apostrophes, and spaces.",
|
|
});
|
|
|
|
export const tagsSchema = z
|
|
.array(
|
|
z
|
|
.string()
|
|
.min(2, { error: "Tags must be at least 2 characters long" })
|
|
.max(20, { error: "Tags cannot be more than 20 characters long" })
|
|
.regex(/^[a-z0-9-_]+$/, {
|
|
error: "Tags can only contain lowercase letters, numbers, dashes, and underscores.",
|
|
}),
|
|
)
|
|
.min(1, { error: "There must be at least 1 tag" })
|
|
.max(8, { error: "There cannot be more than 8 tags" });
|
|
|
|
export const idSchema = z.coerce.number({ error: "ID must be a number" }).int({ error: "ID must be an integer" }).positive({ error: "ID must be valid" });
|
|
|
|
export const searchSchema = z.object({
|
|
q: querySchema.optional(),
|
|
sort: z.enum(["likes", "newest", "oldest", "random"], { error: "Sort must be either 'likes', 'newest', 'oldest', or 'random'" }).default("newest"),
|
|
tags: z
|
|
.string()
|
|
.optional()
|
|
.transform((value) =>
|
|
value
|
|
?.split(",")
|
|
.map((tag) => tag.trim())
|
|
.filter((tag) => tag.length > 0),
|
|
),
|
|
exclude: z
|
|
.string()
|
|
.optional()
|
|
.transform((value) =>
|
|
value
|
|
?.split(",")
|
|
.map((tag) => tag.trim())
|
|
.filter((tag) => tag.length > 0),
|
|
),
|
|
platform: z.enum(MiiPlatform, { error: "Platform must be either 'THREE_DS', or 'SWITCH'" }).optional(),
|
|
gender: z.enum(MiiGender, { error: "Gender must be either 'MALE', 'FEMALE', or 'NONBINARY' if on Switch platform" }).optional(),
|
|
allowCopying: z.coerce.boolean({ error: "Allow Copying must be either true or false" }).optional(),
|
|
// todo: incorporate tagsSchema
|
|
// Pages
|
|
limit: z.coerce
|
|
.number({ error: "Limit must be a number" })
|
|
.int({ error: "Limit must be an integer" })
|
|
.min(1, { error: "Limit must be at least 1" })
|
|
.max(100, { error: "Limit cannot be more than 100" })
|
|
.optional(),
|
|
page: z.coerce.number({ error: "Page must be a number" }).int({ error: "Page must be an integer" }).min(1, { error: "Page must be at least 1" }).optional(),
|
|
// Random sort
|
|
seed: z.coerce.number({ error: "Seed must be a number" }).int({ error: "Seed must be an integer" }).optional(),
|
|
});
|
|
|
|
// Account Info
|
|
export const usernameSchema = z
|
|
.string()
|
|
.trim()
|
|
.min(3, "Username must be at least 3 characters long")
|
|
.max(20, "Username cannot be more than 20 characters long")
|
|
.regex(/^[a-zA-Z0-9_]+$/, "Username can only contain letters, numbers, and underscores");
|
|
|
|
export const displayNameSchema = z
|
|
.string()
|
|
.trim()
|
|
.min(2, { error: "Display name must be at least 2 characters long" })
|
|
.max(64, { error: "Display name cannot be more than 64 characters long" })
|
|
.regex(/^[a-zA-Z0-9-_. ']+$/, {
|
|
error: "Display name can only contain letters, numbers, dashes, underscores, apostrophes, and spaces.",
|
|
});
|
|
|
|
const colorSchema = z.number().int().min(0).max(107).optional();
|
|
const geometrySchema = z.number().int().min(-5).max(5).optional();
|
|
|
|
export const switchMiiInstructionsSchema = z
|
|
.object({
|
|
head: z.object({ type: z.number().int().min(1).max(16).optional(), skinColor: z.number().int().min(0).max(121).optional() }).optional(),
|
|
hair: z
|
|
.object({
|
|
setType: z.number().int().min(0).max(25).optional(),
|
|
bangsType: z.number().int().min(0).max(25).optional(),
|
|
backType: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
subColor: colorSchema,
|
|
style: z.number().int().min(0).max(3).optional(),
|
|
isFlipped: z.boolean().optional(),
|
|
})
|
|
.optional(),
|
|
eyebrows: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
rotation: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
eyes: z
|
|
.object({
|
|
eyesType: z.number().int().min(0).max(25).optional(),
|
|
eyelashesTop: z.number().int().min(0).max(6).optional(),
|
|
eyelashesBottom: z.number().int().min(0).max(25).optional(),
|
|
eyelidTop: z.number().int().min(0).max(2).optional(),
|
|
eyelidBottom: z.number().int().min(0).max(25).optional(),
|
|
eyeliner: z.number().int().min(0).max(25).optional(),
|
|
pupil: z.number().int().min(0).max(9).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
rotation: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
nose: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
height: geometrySchema,
|
|
size: geometrySchema,
|
|
})
|
|
.optional(),
|
|
lips: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
rotation: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
hasLipstick: z.boolean().optional(),
|
|
})
|
|
.optional(),
|
|
ears: z
|
|
.object({
|
|
type: z.number().int().min(0).max(4).optional(),
|
|
height: geometrySchema,
|
|
size: geometrySchema,
|
|
})
|
|
.optional(),
|
|
glasses: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
ringColor: colorSchema,
|
|
shadesColor: colorSchema,
|
|
height: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
other: z
|
|
.object({
|
|
wrinkles1: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
wrinkles2: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
beard: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
moustache: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
goatee: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
mole: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
eyeShadow: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
blush: z
|
|
.object({
|
|
type: z.number().int().min(0).max(25).optional(),
|
|
color: colorSchema,
|
|
height: geometrySchema,
|
|
distance: geometrySchema,
|
|
size: geometrySchema,
|
|
stretch: geometrySchema,
|
|
})
|
|
.optional(),
|
|
})
|
|
.optional(),
|
|
height: z.number().int().min(0).max(100).optional(),
|
|
weight: z.number().int().min(0).max(100).optional(),
|
|
datingPreferences: z.array(z.enum(MiiGender)).optional(),
|
|
voice: z
|
|
.object({
|
|
speed: z.number().int().min(0).max(100).optional(),
|
|
pitch: z.number().int().min(0).max(100).optional(),
|
|
depth: z.number().int().min(0).max(100).optional(),
|
|
delivery: z.number().int().min(0).max(100).optional(),
|
|
tone: z.number().int().min(1).max(6).optional(),
|
|
})
|
|
.optional(),
|
|
personality: z
|
|
.object({
|
|
movement: z.number().int().min(1).max(8).optional(),
|
|
speech: z.number().int().min(1).max(8).optional(),
|
|
energy: z.number().int().min(1).max(8).optional(),
|
|
thinking: z.number().int().min(1).max(8).optional(),
|
|
overall: z.number().int().min(1).max(8).optional(),
|
|
})
|
|
.optional(),
|
|
})
|
|
.optional();
|