Keep a guest community logo on review and on the published rule.
Sign-in was clearing the staged file before it uploaded, and review only read the server URL, so a chosen logo disappeared after the upload step.
This commit is contained in:
1 parent
2348a9e94f
commit
8212423ec3
19 files changed
+780
-61
No files matched your search
+35
-11
@@ -31,6 +31,17 @@ function isPurpose(x: string): x is CreateFlowUploadPurpose {
|
||||
return x === "communityAvatar" || x === "customMethodAttachment";
|
||||
}
|
||||
|
||||
function clientIp(request: NextRequest): string {
|
||||
return (
|
||||
request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ??
|
||||
request.headers.get("x-real-ip") ??
|
||||
"unknown"
|
||||
);
|
||||
}
|
||||
|
||||
/** Same spacing as a signed-in upload, keyed by IP so a guest cannot fill the disk. */
|
||||
const ANONYMOUS_COMMUNITY_AVATAR_MIN_INTERVAL_MS = 5_000;
|
||||
|
||||
function messageForValidationReason(
|
||||
reason: CreateFlowUploadValidationReason,
|
||||
): string {
|
||||
@@ -54,7 +65,19 @@ export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
}
|
||||
|
||||
const user = await getSessionUser();
|
||||
if (!user) {
|
||||
|
||||
let formData: FormData | null = null;
|
||||
try {
|
||||
formData = await request.formData();
|
||||
} catch {
|
||||
formData = null;
|
||||
}
|
||||
|
||||
const purposeRaw = formData?.get("purpose");
|
||||
// A guest publishes before they have a session. Only the community logo
|
||||
// can be stored without one; attachments stay signed-in. A body that
|
||||
// cannot be parsed is not a logo upload.
|
||||
if (!user && purposeRaw !== "communityAvatar") {
|
||||
return unauthorized();
|
||||
}
|
||||
|
||||
@@ -64,15 +87,7 @@ export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
);
|
||||
}
|
||||
|
||||
const rl = rateLimitKey(`upload:${user.id}`, 5_000);
|
||||
if (rl.ok === false) {
|
||||
return rateLimited(rl.retryAfterMs);
|
||||
}
|
||||
|
||||
let formData: FormData;
|
||||
try {
|
||||
formData = await request.formData();
|
||||
} catch {
|
||||
if (!formData) {
|
||||
return errorJson(
|
||||
"payload_too_large",
|
||||
"Upload body is too large or malformed.",
|
||||
@@ -80,9 +95,18 @@ export const POST = apiRoute("uploads.post", async (request: NextRequest) => {
|
||||
);
|
||||
}
|
||||
|
||||
const purposeRaw = formData.get("purpose");
|
||||
const file = asUploadedBlob(formData.get("file"));
|
||||
|
||||
const rl = user
|
||||
? rateLimitKey(`upload:${user.id}`, 5_000)
|
||||
: rateLimitKey(
|
||||
`upload-anon:${clientIp(request)}`,
|
||||
ANONYMOUS_COMMUNITY_AVATAR_MIN_INTERVAL_MS,
|
||||
);
|
||||
if (rl.ok === false) {
|
||||
return rateLimited(rl.retryAfterMs);
|
||||
}
|
||||
|
||||
if (typeof purposeRaw !== "string" || !isPurpose(purposeRaw)) {
|
||||
return errorJson(
|
||||
"validation_error",
|
||||
|
||||
Reference in new issue
Block a user