83 lines
2.0 KiB
TypeScript
83 lines
2.0 KiB
TypeScript
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<BotSenderProfile | null>,
|
|
) {
|
|
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(),
|
|
},
|
|
};
|
|
}
|