Files
minecraft-account-manager/apps/web/src/lib/admission-settings.ts
T
dmg c131465ff5
CI / validate (push) Successful in 6m15s
Release / release (push) Successful in 7m49s
feat(admission): add scheduled group access
2026-08-02 14:04:48 -04:00

55 lines
2.8 KiB
TypeScript

export interface AdmissionMessages {
registrationMessage: string;
groupAccessDeniedMessage: string;
vpnDeniedMessage: string;
scheduledAccessDeniedMessage: string;
}
export type AdmissionDenialReason =
| "not_registered"
| "group_access_disabled"
| "schedule_disallowed"
| "anonymized_network_disallowed";
export const DEFAULT_ADMISSION_MESSAGES: AdmissionMessages = {
registrationMessage: "Please register your Minecraft account before joining.",
groupAccessDeniedMessage: "Your account group does not currently have server access. Contact a host if you believe this is a mistake.",
vpnDeniedMessage: "VPN, proxy, and Tor connections are not allowed. Contact a host to request an exception.",
scheduledAccessDeniedMessage: "Your group is only allowed access from {next_start} to {next_end}.",
} as const;
export function admissionDenialMessage(reason: AdmissionDenialReason, messages: AdmissionMessages) {
if (reason === "not_registered") return messages.registrationMessage;
if (reason === "group_access_disabled") return messages.groupAccessDeniedMessage;
if (reason === "schedule_disallowed") return messages.scheduledAccessDeniedMessage;
return messages.vpnDeniedMessage;
}
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
const TEMPLATE_VARIABLE = /\{([a-z_]+)\}/g;
const COMMON_VARIABLES = new Set(["player", "group"]);
const SCHEDULE_VARIABLES = new Set(["player", "group", "next_start", "next_end"]);
type MessageName = keyof AdmissionMessages;
function messageValue(formData: FormData, name: MessageName, allowedVariables: Set<string>) {
const value = String(formData.get(name) ?? "").trim();
if (value.length < 10 || value.length > 500 || CONTROL_CHARACTERS.test(value)) return null;
const withoutVariables = value.replace(TEMPLATE_VARIABLE, (match, variable: string) =>
allowedVariables.has(variable) ? "" : match);
return /[{}]/.test(withoutVariables) ? null : value;
}
export function parseAdmissionMessages(formData: FormData) {
const registrationMessage = messageValue(formData, "registrationMessage", COMMON_VARIABLES);
const groupAccessDeniedMessage = messageValue(formData, "groupAccessDeniedMessage", COMMON_VARIABLES);
const vpnDeniedMessage = messageValue(formData, "vpnDeniedMessage", COMMON_VARIABLES);
const scheduledAccessDeniedMessage = messageValue(formData, "scheduledAccessDeniedMessage", SCHEDULE_VARIABLES);
if (!registrationMessage || !groupAccessDeniedMessage || !vpnDeniedMessage || !scheduledAccessDeniedMessage) return null;
return { registrationMessage, groupAccessDeniedMessage, vpnDeniedMessage, scheduledAccessDeniedMessage };
}
export function renderAdmissionMessage(template: string, variables: Record<string, string>) {
return template.replace(TEMPLATE_VARIABLE, (match, variable: string) => variables[variable] ?? match);
}