Add profile photo uploads and custom difficulty support

This commit is contained in:
2026-10-13 20:50:54 +05:00
parent 8c3de302e7
commit de812453ec
37 changed files with 1139 additions and 74 deletions

View File

@@ -21,6 +21,11 @@ import { createHash } from "node:crypto";
import { recoveryCookie, verifyRecoveryGrant } from "./password-recovery";
import { requestOrigin } from "./request-origin";
import { issueInitialPassword } from "@/shared/initial-password";
import {
profileAvatarBucket,
profileAvatarMaxBytes,
profileAvatarMimeTypes,
} from "@/shared/profile-appearance";
type Context = {
jsonBody: (r: NextRequest) => Promise<unknown>;
@@ -102,6 +107,41 @@ export async function accountApi(
{ error: "Earn a username glow before choosing its color" },
403,
);
if (
body.avatarPath !== undefined &&
body.avatarPath !== null &&
body.avatarPath !== current.avatar_path
) {
const [ownerId, filename] = body.avatarPath.split("/");
if (
ownerId !== account.id ||
!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.(jpg|png|webp)$/i.test(
filename ?? "",
)
)
return json({ error: "Choose a valid profile photo" }, 400);
const uploaded = await admin()
.storage.from(profileAvatarBucket)
.info(body.avatarPath);
if (uploaded.error || !uploaded.data)
return json(
{ error: "Upload your profile photo again" },
400,
);
if (
!profileAvatarMimeTypes.includes(
uploaded.data.contentType as (typeof profileAvatarMimeTypes)[number],
) ||
!uploaded.data.size ||
uploaded.data.size > profileAvatarMaxBytes
) {
await admin().storage.from(profileAvatarBucket).remove([body.avatarPath]);
return json(
{ error: "Choose an image no larger than 5 MB" },
400,
);
}
}
const profile = required(
await admin()
.from("profiles")
@@ -112,13 +152,25 @@ export async function accountApi(
...(body.glowColor !== undefined
? { glow_color: body.glowColor }
: {}),
...(body.avatarPath !== undefined
? { avatar_path: body.avatarPath }
: {}),
})
.eq("id", account.id)
.select(
"id,display_name,role,name_glow,glow_color,bug_finder,custom_prefix,custom_prefix_icon",
"id,display_name,avatar_path,role,name_glow,glow_color,bug_finder,custom_prefix,custom_prefix_icon",
)
.single(),
);
if (
body.avatarPath !== undefined &&
body.avatarPath !== current.avatar_path
) {
if (current.avatar_path?.startsWith(`${account.id}/`))
await admin()
.storage.from(profileAvatarBucket)
.remove([current.avatar_path]);
}
return json({ profile });
}
return json({ error: "Method not allowed" }, 405);