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`; +};