import { timingSafeEqual } from "node:crypto"; import { ShareHttpError } from "@/lib/share/http-error"; export type BotSenderProfile = { id: string; username: string; globalName: string | null; avatarUrl: string | null; }; const FORBIDDEN_PROFILE_FIELDS = [ "displayName", "avatarUrl", "authorName", "authorAvatarUrl", ] as const; export function hasValidShareBotBearer( authorization: string | null, expected: string, ) { if (!authorization?.startsWith("Bearer ")) return false; const suppliedBytes = Buffer.from(authorization.slice("Bearer ".length)); const expectedBytes = Buffer.from(expected); return suppliedBytes.length === expectedBytes.length && timingSafeEqual(suppliedBytes, expectedBytes); } export function rejectSuppliedBotProfile(formData: FormData) { for (const field of FORBIDDEN_PROFILE_FIELDS) { if (formData.has(field)) { throw new ShareHttpError( `Profile field ${field} must not be supplied`, 400, ); } } } export async function resolveBotSenderProfile( discordId: string, lookup: (discordId: string) => Promise, ) { let profile: BotSenderProfile | null; try { profile = await lookup(discordId); } catch { throw new ShareHttpError("Discord profile lookup is unavailable", 503); } if (!profile) { throw new ShareHttpError( "Discord sender was not found in the configured guild", 422, ); } return profile; } export function buildBotShareResponse({ baseUrl, share, displayName, avatarUrl, imagePath, }: { baseUrl: string; share: { id: string; createdAt: Date }; displayName: string; avatarUrl: string | null; imagePath: string | null; }) { return { success: true as const, share: { id: share.id, url: `${baseUrl}/share/${share.id}`, author: { displayName, avatarUrl }, imageUrl: imagePath ? `${baseUrl}${imagePath}` : null, createdAt: share.createdAt.toISOString(), }, }; }