55 lines
2.8 KiB
TypeScript
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);
|
|
}
|