All checks were successful
Deploy MineGeo to Dokploy / deploy (push) Successful in 3m27s
90 lines
2.8 KiB
TypeScript
90 lines
2.8 KiB
TypeScript
import { z } from "zod";
|
||
import { glowColorSchema } from "./profile-appearance";
|
||
|
||
export const displayNameSchema = z
|
||
.string()
|
||
.trim()
|
||
.min(2, "Use at least 2 characters")
|
||
.max(32, "Use at most 32 characters")
|
||
.refine(
|
||
(value) => !/[\u0000-\u001f\u007f\u202a-\u202e\u2066-\u2069]/u.test(value),
|
||
"Use a single line without hidden control characters",
|
||
);
|
||
export const usernameSchema = z.string().trim().toLowerCase().regex(/^[a-z0-9_]{3,32}$/, "Use 3–32 letters, numbers or underscores.");
|
||
export const newPasswordSchema = z
|
||
.string()
|
||
.min(12, "Use at least 12 characters")
|
||
.refine(
|
||
(value) => new TextEncoder().encode(value).length <= 72,
|
||
"Use at most 72 UTF-8 bytes for your password",
|
||
);
|
||
const email = z.string().trim().email().max(254);
|
||
export const authActionSchema = z.discriminatedUnion("action", [
|
||
z
|
||
.object({
|
||
action: z.literal("login"),
|
||
email,
|
||
password: z.string().min(1).max(1024),
|
||
next: z.string().optional(),
|
||
})
|
||
.strict(),
|
||
z
|
||
.object({
|
||
action: z.literal("signup"),
|
||
email,
|
||
password: newPasswordSchema,
|
||
displayName: displayNameSchema,
|
||
next: z.string().optional(),
|
||
})
|
||
.strict(),
|
||
z.object({ action: z.literal("recover"), email }).strict(),
|
||
]);
|
||
export const profileUpdateSchema = z
|
||
.object({
|
||
displayName: displayNameSchema.optional(),
|
||
username: usernameSchema.optional(),
|
||
glowColor: glowColorSchema.optional(),
|
||
avatarPath: z.string().max(128).nullable().optional(),
|
||
})
|
||
.strict()
|
||
.refine(
|
||
(value) =>
|
||
value.displayName !== undefined ||
|
||
value.username !== undefined ||
|
||
value.glowColor !== undefined ||
|
||
value.avatarPath !== undefined,
|
||
"Choose a profile change",
|
||
);
|
||
export const initialPasswordSchema = z
|
||
.object({ password: newPasswordSchema })
|
||
.strict();
|
||
export const passwordUpdateSchema = z
|
||
.object({
|
||
password: newPasswordSchema,
|
||
currentPassword: z.string().min(1).max(1024).optional(),
|
||
nonce: z
|
||
.string()
|
||
.trim()
|
||
.regex(/^[0-9]{6,10}$/)
|
||
.optional(),
|
||
})
|
||
.strict();
|
||
|
||
/** Only supported product destinations can follow an authentication link. */
|
||
export function authDestination(value: string | null | undefined): string {
|
||
if (
|
||
value === "/settings" ||
|
||
value === "/dashboard" ||
|
||
value === "/servers" ||
|
||
value === "/support" ||
|
||
value === "/auth/reset-password"
|
||
)
|
||
return value;
|
||
if (value && /^\/dashboard\/[0-9a-f-]{36}$/i.test(value)) return value;
|
||
if (value && /^\/support\/[0-9a-f-]{36}$/i.test(value)) return value;
|
||
if (value && /^\/tournaments\?map=[0-9a-f-]{36}$/i.test(value)) return value;
|
||
if (value && /^\/pair\?code=[A-F0-9]{8}$/.test(value)) return value;
|
||
if (value && /^\/moderator-application\/[a-f0-9]{64}$/.test(value)) return value;
|
||
return "/dashboard";
|
||
}
|