Initial commit: Kinship Directory application
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import type { Person } from "./types";
|
||||
|
||||
export const clean = (value: unknown, max = 120) => String(value || "").trim().slice(0, max);
|
||||
export const normalizeEmail = (value: unknown) => clean(value, 254).toLowerCase();
|
||||
|
||||
export function isSameOrigin(request: Request): boolean {
|
||||
const origin = request.headers.get("origin");
|
||||
if (!origin) return true;
|
||||
try {
|
||||
const originUrl = new URL(origin);
|
||||
const host = request.headers.get("x-forwarded-host") || request.headers.get("host");
|
||||
const protocol = request.headers.get("x-forwarded-proto");
|
||||
return originUrl.host === host && (!protocol || originUrl.protocol === `${protocol}:`);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function isSecureRequest(request: Request): boolean {
|
||||
const protocol = request.headers.get("x-forwarded-proto");
|
||||
return protocol ? protocol === "https" : new URL(request.url).protocol === "https:";
|
||||
}
|
||||
|
||||
export function isUnder18(birthday: string): boolean {
|
||||
const slashDate = birthday.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
|
||||
const date = slashDate
|
||||
? new Date(Number(slashDate[3]), Number(slashDate[1]) - 1, Number(slashDate[2]))
|
||||
: new Date(`${birthday}T00:00:00`);
|
||||
if (Number.isNaN(date.getTime())) return false;
|
||||
const today = new Date();
|
||||
let age = today.getFullYear() - date.getFullYear();
|
||||
const beforeBirthday = today.getMonth() < date.getMonth() ||
|
||||
(today.getMonth() === date.getMonth() && today.getDate() < date.getDate());
|
||||
if (beforeBirthday) age--;
|
||||
return age >= 0 && age < 18;
|
||||
}
|
||||
|
||||
function isValidBirthday(birthday: string): boolean {
|
||||
if (!birthday) return true;
|
||||
const monthDay = birthday.match(/^(\d{1,2})\/(\d{1,2})(?:\/(\d{4}))?$/);
|
||||
if (monthDay) {
|
||||
const month = Number(monthDay[1]); const day = Number(monthDay[2]); const year = Number(monthDay[3] || 2000);
|
||||
const date = new Date(year, month - 1, day);
|
||||
return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
|
||||
}
|
||||
return /^\d{4}-\d{2}-\d{2}$/.test(birthday) && !Number.isNaN(new Date(`${birthday}T00:00:00`).getTime());
|
||||
}
|
||||
|
||||
export function parsePeople(value: unknown, children = false): Person[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value.slice(0, 20).map((raw, index) => {
|
||||
const person = raw as Record<string, unknown>;
|
||||
const birthday = clean(person.birthday, 10);
|
||||
if (!isValidBirthday(birthday)) throw new Error(`Person ${index + 1} has an invalid birthday.`);
|
||||
const hasBirthYear = /^\d{4}-/.test(birthday) || /^\d{1,2}\/\d{1,2}\/\d{4}$/.test(birthday);
|
||||
if (children && hasBirthYear && !isUnder18(birthday)) throw new Error(`Child ${index + 1} must be under 18.`);
|
||||
return {
|
||||
id: clean(person.id, 50) || crypto.randomUUID(),
|
||||
firstName: clean(person.firstName, 60),
|
||||
lastName: clean(person.lastName, 60),
|
||||
email: normalizeEmail(person.email) || undefined,
|
||||
phone: clean(person.phone, 30) || undefined,
|
||||
birthday: birthday || undefined,
|
||||
photoId: clean(person.photoId, 50) || undefined,
|
||||
};
|
||||
}).filter((person) => person.firstName && person.lastName);
|
||||
}
|
||||
Reference in New Issue
Block a user