From 55af0ed516c5bf8894ab22de5c3d0a954b372111 Mon Sep 17 00:00:00 2001 From: anghunk Date: Wed, 28 Jan 2026 13:35:32 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=A4=B4=E5=83=8F=E7=94=9F=E6=88=90):=20?= =?UTF-8?q?=E5=BD=93=E9=82=AE=E7=AE=B1=E4=B8=BA=E7=A9=BA=E6=97=B6=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=98=B5=E7=A7=B0=E7=94=9F=E6=88=90=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=E5=93=88=E5=B8=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复 getCravatar 函数在邮箱为空时无法生成有效头像的问题。现在当邮箱为空或不存在时,函数会尝试使用昵称作为替代标识符生成 MD5 哈希。如果两者都不可用,则返回默认占位符头像,确保始终返回有效的头像 URL。 --- cwd-api/src/api/admin/listComments.ts | 2 +- cwd-api/src/api/public/getComments.ts | 2 +- cwd-api/src/utils/getAvatar.ts | 46 ++++++++++++++++++++------- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/cwd-api/src/api/admin/listComments.ts b/cwd-api/src/api/admin/listComments.ts index 26e3335..cb1aec1 100644 --- a/cwd-api/src/api/admin/listComments.ts +++ b/cwd-api/src/api/admin/listComments.ts @@ -65,7 +65,7 @@ export const listComments = async (c: Context<{ Bindings: Bindings }>) => { ? row.likes : 0, ua: row.ua, - avatar: await getCravatar(row.email, avatarPrefix || undefined), + avatar: await getCravatar(row.email, row.name, avatarPrefix || undefined), isAdmin: adminEmail && row.email === adminEmail })) ); diff --git a/cwd-api/src/api/public/getComments.ts b/cwd-api/src/api/public/getComments.ts index ce62e44..36fa417 100644 --- a/cwd-api/src/api/public/getComments.ts +++ b/cwd-api/src/api/public/getComments.ts @@ -69,7 +69,7 @@ export const getComments = async (c: Context<{ Bindings: Bindings }>) => { // 2. 批量处理头像并格式化 const allComments = await Promise.all(results.map(async (row: any) => ({ ...row, - avatar: await getCravatar(row.email, avatar_prefix || undefined), + avatar: await getCravatar(row.email, row.name, avatar_prefix || undefined), isAdmin: adminEmail && row.email === adminEmail, replies: [] }))) diff --git a/cwd-api/src/utils/getAvatar.ts b/cwd-api/src/utils/getAvatar.ts index 83b92a8..3e7957c 100644 --- a/cwd-api/src/utils/getAvatar.ts +++ b/cwd-api/src/utils/getAvatar.ts @@ -4,16 +4,38 @@ const DEFAULT_AVATAR_PREFIX = 'https://gravatar.com/avatar'; /** - * 辅助函数:生成 gravatar.com 头像地址 (MD5 算法) - * @param email - 邮箱地址 - * @param prefix - 头像服务前缀,默认为 https://gravatar.com/avatar + * 当缺少邮箱或邮箱为空时使用的占位 hash + * 32 个 0,符合 MD5 长度要求,Gravatar 会回退到默认头像样式 */ -export const getCravatar = async (email: string, prefix?: string): Promise => { - const cleanEmail = email.trim().toLowerCase(); - const msgUint8 = new TextEncoder().encode(cleanEmail); - const hashBuffer = await crypto.subtle.digest('MD5', msgUint8); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); - const avatarPrefix = prefix || DEFAULT_AVATAR_PREFIX; - return `${avatarPrefix}/${hashHex}?s=200&d=retro`; -}; \ No newline at end of file +const DEFAULT_AVATAR_HASH = '00000000000000000000000000000000'; + +/** + * 辅助函数:生成 gravatar.com 头像地址 (MD5 算法) + * 优先使用邮箱;当邮箱不存在或为空时,使用昵称计算 MD5。 + */ +export const getCravatar = async ( + email: string | null | undefined, + name?: string | null | undefined, + prefix?: string +): Promise => { + const avatarPrefix = prefix || DEFAULT_AVATAR_PREFIX; + + const pickIdentifier = (value: string | null | undefined) => { + if (!value || typeof value !== 'string') return null; + const trimmed = value.trim().toLowerCase(); + return trimmed || null; + }; + + const identifier = pickIdentifier(email) || pickIdentifier(name); + + if (!identifier) { + return `${avatarPrefix}/${DEFAULT_AVATAR_HASH}?s=200&d=retro`; + } + + const msgUint8 = new TextEncoder().encode(identifier); + const hashBuffer = await crypto.subtle.digest('MD5', msgUint8); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); + + return `${avatarPrefix}/${hashHex}?s=200&d=retro`; +};