import { cdnUrl } from "@/lib/cdn-images"; import { ensureDiscordUser } from "@/lib/auth/discord-user"; import { getCanonicalUrl, getShareBotSecret } from "@/lib/config/server"; import { getDiscordSenderProfile } from "@/lib/discord/discord"; import { buildBotShareResponse, hasValidShareBotBearer, rejectSuppliedBotProfile, resolveBotSenderProfile, } from "@/lib/share/bot"; import { createShare } from "@/lib/share/create"; import { ShareHttpError, shareErrorResponse } from "@/lib/share/http-error"; import { enforceShareRateLimit } from "@/lib/share/rate-limit"; import { validateOptionalImage, validateShareDescription, validateTextFile, } from "@/lib/share/validation"; export const dynamic = "force-dynamic"; function hasValidBearerToken(request: Request) { const authorization = request.headers.get("authorization"); let expected: string; try { expected = getShareBotSecret(); } catch { throw new ShareHttpError("Share bot API is not configured", 503); } return hasValidShareBotBearer(authorization, expected); } export async function POST(request: Request) { try { if (!hasValidBearerToken(request)) { throw new ShareHttpError("Unauthorized", 401); } const baseUrl = getCanonicalUrl(); await enforceShareRateLimit({ key: "bot:global", limit: 60, windowSeconds: 60 * 60, }); let formData: FormData; try { formData = await request.formData(); } catch { throw new ShareHttpError("Malformed multipart form data", 400); } rejectSuppliedBotProfile(formData); const file = formData.get("file"); const senderDiscordId = formData.get("senderDiscordId"); if (!(file instanceof File)) { throw new ShareHttpError("A .txt file is required", 400); } if ( typeof senderDiscordId !== "string" || !/^\d{15,22}$/.test(senderDiscordId) ) { throw new ShareHttpError("A valid senderDiscordId is required", 400); } const [content, image] = await Promise.all([ validateTextFile(file), validateOptionalImage(formData.get("image")), ]); const description = validateShareDescription(formData.get("description")); const profile = await resolveBotSenderProfile( senderDiscordId, getDiscordSenderProfile, ); await enforceShareRateLimit({ key: `sender:${senderDiscordId}`, limit: 10, windowSeconds: 60 * 60, }); const displayName = profile.globalName?.trim() || profile.username; const user = await ensureDiscordUser({ discordId: senderDiscordId, displayName, avatarUrl: profile.avatarUrl, }); const share = await createShare({ content, description, image, source: "bot", author: { userId: user.id, discordId: senderDiscordId, displayName, avatarUrl: profile.avatarUrl, }, }); return Response.json( buildBotShareResponse({ baseUrl, share, displayName, avatarUrl: profile.avatarUrl, imagePath: share.imageCdnId ? cdnUrl(share.imageCdnId) : null, }), { status: 201 } ); } catch (error) { return shareErrorResponse(error); } }