mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-29 06:43:17 -05:00
rename lib -> utils
This commit is contained in:
48
utils/validators/build.ts
Normal file
48
utils/validators/build.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { isAbilityArray } from "utils/lists/abilities";
|
||||
import { clothingGear, headGear, shoesGear } from "utils/lists/gear";
|
||||
import { weaponsWithHero } from "utils/lists/weaponsWithHero";
|
||||
import * as z from "zod";
|
||||
import { abilityEnum, hasNoDuplicates } from "./common";
|
||||
|
||||
export const TITLE_CHARACTER_LIMIT = 100;
|
||||
export const DESCRIPTION_CHARACTER_LIMIT = 1000;
|
||||
|
||||
export const buildSchema = z.object({
|
||||
weapon: z.string().refine((val) => weaponsWithHero.includes(val as any)),
|
||||
title: z.string().max(TITLE_CHARACTER_LIMIT).optional().nullable(),
|
||||
description: z
|
||||
.string()
|
||||
.max(DESCRIPTION_CHARACTER_LIMIT)
|
||||
.optional()
|
||||
.nullable(),
|
||||
modes: z
|
||||
.array(z.enum(["TW", "SZ", "TC", "RM", "CB"]))
|
||||
.min(1)
|
||||
.max(5)
|
||||
.refine(hasNoDuplicates),
|
||||
headAbilities: z
|
||||
.array(abilityEnum)
|
||||
.refine((arr) => isAbilityArray(arr, "HEAD")),
|
||||
clothingAbilities: z
|
||||
.array(abilityEnum)
|
||||
.refine((arr) => isAbilityArray(arr, "CLOTHING")),
|
||||
shoesAbilities: z
|
||||
.array(abilityEnum)
|
||||
.refine((arr) => isAbilityArray(arr, "SHOES")),
|
||||
headGear: z
|
||||
.string()
|
||||
.refine((val) => !val || headGear.includes(val as any))
|
||||
.optional()
|
||||
.nullable(),
|
||||
clothingGear: z
|
||||
.string()
|
||||
.refine((val) => !val || clothingGear.includes(val as any))
|
||||
.optional()
|
||||
.nullable(),
|
||||
shoesGear: z
|
||||
.string()
|
||||
.refine((val) => !val || shoesGear.includes(val as any))
|
||||
.optional()
|
||||
.nullable(),
|
||||
id: z.number().optional(),
|
||||
});
|
||||
32
utils/validators/common.ts
Normal file
32
utils/validators/common.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as z from "zod";
|
||||
|
||||
export const hasNoDuplicates = (arr: any[]) => new Set(arr).size === arr.length;
|
||||
|
||||
export const abilityEnum = z.enum([
|
||||
"ISM",
|
||||
"ISS",
|
||||
"REC",
|
||||
"RSU",
|
||||
"SSU",
|
||||
"SCU",
|
||||
"SS",
|
||||
"SPU",
|
||||
"QR",
|
||||
"QSJ",
|
||||
"BRU",
|
||||
"RES",
|
||||
"BDU",
|
||||
"MPU",
|
||||
"OG",
|
||||
"LDE",
|
||||
"T",
|
||||
"CB",
|
||||
"NS",
|
||||
"H",
|
||||
"TI",
|
||||
"RP",
|
||||
"AD",
|
||||
"SJ",
|
||||
"OS",
|
||||
"DR",
|
||||
]);
|
||||
48
utils/validators/detailedMap.ts
Normal file
48
utils/validators/detailedMap.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { weapons } from "utils/lists/weapons";
|
||||
import * as z from "zod";
|
||||
import { abilityEnum } from "./common";
|
||||
|
||||
const playerSchema = z.object({
|
||||
principal_id: z.string(),
|
||||
name: z.string().min(1).max(10),
|
||||
weapon: z.string().refine((val) => weapons.includes(val as any)),
|
||||
main_abilities: z.array(abilityEnum),
|
||||
sub_abilities: z.array(z.array(abilityEnum)),
|
||||
kills: z.number().int().min(0).max(50),
|
||||
assists: z.number().int().min(0).max(50),
|
||||
deaths: z.number().int().min(0).max(50),
|
||||
specials: z.number().int().min(0).max(50),
|
||||
paint: z.number().int().min(0).max(10000),
|
||||
gear: z.array(z.string()),
|
||||
});
|
||||
|
||||
const teamInfoSchema = z.object({
|
||||
score: z.number().int().min(0).max(100),
|
||||
players: z.array(playerSchema),
|
||||
});
|
||||
|
||||
export const detailedMapSchema = z.array(
|
||||
z.object({
|
||||
stage: z.string(),
|
||||
mode: z.enum(["SZ", "TC", "RM", "CB"]),
|
||||
duration: z.number().int().min(15).max(500),
|
||||
winners: teamInfoSchema,
|
||||
losers: teamInfoSchema,
|
||||
date: z.string().refine((val) => {
|
||||
const d = new Date(Number(val));
|
||||
const timestamp = d.getTime();
|
||||
if (Number.isNaN(timestamp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nd = new Date();
|
||||
nd.setMonth(-6);
|
||||
|
||||
if (d.getTime() < nd.getTime()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}),
|
||||
})
|
||||
);
|
||||
9
utils/validators/fapost.ts
Normal file
9
utils/validators/fapost.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import * as z from "zod";
|
||||
|
||||
export const FA_POST_CONTENT_LIMIT = 2000;
|
||||
|
||||
export const freeAgentPostSchema = z.object({
|
||||
playstyles: z.array(z.enum(["FRONTLINE", "MIDLINE", "BACKLINE"])).min(1),
|
||||
canVC: z.enum(["YES", "NO", "MAYBE"]),
|
||||
content: z.string().max(FA_POST_CONTENT_LIMIT),
|
||||
});
|
||||
58
utils/validators/profile.ts
Normal file
58
utils/validators/profile.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { countries } from "countries-list";
|
||||
import { weaponsWithHero } from "utils/lists/weaponsWithHero";
|
||||
import * as z from "zod";
|
||||
import { hasNoDuplicates } from "./common";
|
||||
|
||||
export const PROFILE_CHARACTER_LIMIT = 7000;
|
||||
|
||||
const profileRootSchema = z.object({
|
||||
bio: z.string().max(PROFILE_CHARACTER_LIMIT).optional().nullable(),
|
||||
country: z
|
||||
.string()
|
||||
.refine((val) => !val || Object.keys(countries).includes(val))
|
||||
.optional()
|
||||
.nullable(),
|
||||
customUrlPath: z
|
||||
.string()
|
||||
.max(32)
|
||||
.refine(
|
||||
(val) => !val || isCustomUrl(val),
|
||||
"Name in the custom URL can't only contain numbers"
|
||||
)
|
||||
.optional()
|
||||
.nullable(),
|
||||
twitchName: z.string().max(25).optional().nullable(),
|
||||
twitterName: z.string().max(15).optional().nullable(),
|
||||
youtubeId: z.string().optional().nullable(),
|
||||
weaponPool: z
|
||||
.array(z.string())
|
||||
.max(5)
|
||||
.refine((arr) => arr.every((val) => weaponsWithHero.includes(val as any)))
|
||||
.refine(hasNoDuplicates),
|
||||
});
|
||||
|
||||
export const profileSchemaFrontend = profileRootSchema.extend({
|
||||
sensMotion: z.string(),
|
||||
sensStick: z.string(),
|
||||
});
|
||||
|
||||
export const profileSchemaBackend = profileRootSchema.extend({
|
||||
sensMotion: z
|
||||
.number()
|
||||
.min(-5)
|
||||
.max(5)
|
||||
.refine((val) => (val * 10) % 5 === 0)
|
||||
.optional()
|
||||
.nullable(),
|
||||
sensStick: z
|
||||
.number()
|
||||
.min(-50)
|
||||
.max(50)
|
||||
.refine((val) => (val * 10) % 5 === 0)
|
||||
.optional()
|
||||
.nullable(),
|
||||
});
|
||||
|
||||
export function isCustomUrl(identifier: string) {
|
||||
return isNaN(Number(identifier));
|
||||
}
|
||||
51
utils/validators/salmonRunRecord.ts
Normal file
51
utils/validators/salmonRunRecord.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import * as z from "zod";
|
||||
|
||||
// https://stackoverflow.com/a/3809435
|
||||
const urlRegex = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/;
|
||||
|
||||
export const salmonRunRecordSchema = z.object({
|
||||
rotationId: z.number(), // check on db level
|
||||
goldenEggCount: z.number().min(10).max(300),
|
||||
category: z.enum([
|
||||
"TOTAL",
|
||||
"TOTAL_NO_NIGHT",
|
||||
"PRINCESS",
|
||||
"NT_NORMAL",
|
||||
"HT_NORMAL",
|
||||
"LT_NORMAL",
|
||||
"NT_RUSH",
|
||||
"HT_RUSH",
|
||||
"NT_FOG",
|
||||
"HT_FOG",
|
||||
"LT_FOG",
|
||||
"NT_GOLDIE",
|
||||
"HT_GOLDIE",
|
||||
"NT_GRILLERS",
|
||||
"HT_GRILLERS",
|
||||
"NT_MOTHERSHIP",
|
||||
"HT_MOTHERSHIP",
|
||||
"LT_MOTHERSHIP",
|
||||
"LT_COHOCK",
|
||||
]),
|
||||
userIds: z.array(z.number()),
|
||||
links: z
|
||||
.string()
|
||||
.refine((val) => {
|
||||
const lines = linesFromTextareaValue(val);
|
||||
if (lines.length === 0 || lines.length > 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, "Include 1-4 links")
|
||||
.refine((val) => {
|
||||
return linesFromTextareaValue(val).every((link) => urlRegex.test(link));
|
||||
}, "One of the links is invalid"),
|
||||
});
|
||||
|
||||
function linesFromTextareaValue(value: string) {
|
||||
return value
|
||||
.trim()
|
||||
.split("\n")
|
||||
.filter((val) => val !== "");
|
||||
}
|
||||
15
utils/validators/suggestion.ts
Normal file
15
utils/validators/suggestion.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import * as z from "zod";
|
||||
|
||||
export const SUGGESTION_DESCRIPTION_LIMIT = 500;
|
||||
|
||||
const suggestionRootSchema = z.object({
|
||||
description: z.string().max(SUGGESTION_DESCRIPTION_LIMIT),
|
||||
});
|
||||
|
||||
export const suggestionFullSchema = suggestionRootSchema.extend({
|
||||
suggestedId: z.number().int(),
|
||||
tier: z.number().int().min(1).max(3),
|
||||
region: z.enum(["NA", "EU"]),
|
||||
});
|
||||
|
||||
export const resuggestionSchema = suggestionRootSchema;
|
||||
14
utils/validators/team.ts
Normal file
14
utils/validators/team.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as z from "zod";
|
||||
|
||||
export const TEAM_BIO_CHARACTER_LIMIT = 7000;
|
||||
export const TEAM_RECRUITING_POST_CHARACTER_LIMIT = 2000;
|
||||
|
||||
export const teamSchema = z.object({
|
||||
twitterName: z.string().max(15).optional().nullable(),
|
||||
bio: z.string().max(TEAM_BIO_CHARACTER_LIMIT).optional().nullable(),
|
||||
recruitingPost: z
|
||||
.string()
|
||||
.max(TEAM_RECRUITING_POST_CHARACTER_LIMIT)
|
||||
.optional()
|
||||
.nullable(),
|
||||
});
|
||||
7
utils/validators/vouch.ts
Normal file
7
utils/validators/vouch.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import * as z from "zod";
|
||||
|
||||
export const vouchSchema = z.object({
|
||||
vouchedId: z.number().int(),
|
||||
tier: z.number().int().min(1).max(3),
|
||||
region: z.enum(["NA", "EU"]),
|
||||
});
|
||||
Reference in New Issue
Block a user