lib/avatar-source.ts
Rama main · réplica de lectura del repositorio original.
/** Prefix distinguishing an uploaded image URL from a Lorelei seed. */
const UPLOADED_AVATAR_PREFIX = "uploaded:";
/** Public bucket containing normalized user avatar images. */
export const USER_AVATAR_BUCKET = "user-avatars";
/** Encodes a same-origin avatar route for legacy avatar-seed fields. */
export function uploadedAvatarSource(path: string): string {
return `${UPLOADED_AVATAR_PREFIX}${path}`;
}
/**
* Extracts a same-origin avatar path and rejects arbitrary external sources.
* Public feedback pseudonyms also reach the avatar renderer as seeds, so merely
* checking for `https:` would turn user-controlled text into a tracking request.
*/
export function uploadedAvatarUrl(source: string | null | undefined): string | null {
if (!source?.startsWith(UPLOADED_AVATAR_PREFIX)) return null;
try {
const value = source.slice(UPLOADED_AVATAR_PREFIX.length);
if (!value.startsWith("/")) return null;
const url = new URL(value, "https://minddy.invalid");
if (!/^\/api\/avatars\/[0-9a-f-]{36}$/i.test(url.pathname)) {
return null;
}
return `${url.pathname}${url.search}`;
} catch {
return null;
}
}
/** External avatars and local upload previews must use image-fetching schemes. */
export function avatarImageUrl(value: string | null | undefined): string | null {
if (!value) return null;
try {
const url = new URL(value);
if (url.protocol === "https:" || url.protocol === "http:") return url.href;
if (url.protocol === "blob:") {
const origin = new URL(url.pathname);
if (origin.protocol === "https:" || origin.protocol === "http:") return url.href;
}
} catch {
// Invalid or relative external sources fall back to the account portrait.
}
return null;
}