Files
MineGeo/shared/account.ts

81 lines
2.3 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 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(),
glowColor: glowColorSchema.optional(),
})
.strict()
.refine(
(value) => value.displayName !== undefined || value.glowColor !== 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 && /^\/pair\?code=[A-F0-9]{8}$/.test(value)) return value;
return "/dashboard";
}