From b946a08ba0d3632b8185cd46bcaafc78a04470fc Mon Sep 17 00:00:00 2001 From: anime Date: Sat, 12 Apr 2025 03:24:03 +0800 Subject: [PATCH 1/7] feat(simplify ENABLE_RSS checks): remove useless JSON.parse calls --- themes/commerce/components/SocialButton.js | 58 ++++++++++++++------- themes/heo/components/SocialButton.js | 2 +- themes/matery/components/SocialButton.js | 60 ++++++++++++++-------- themes/medium/components/SocialButton.js | 58 ++++++++++++++------- themes/nav/components/SocialButton.js | 48 +++++++++++------ themes/plog/components/SocialButton.js | 58 ++++++++++++++------- themes/starter/components/SocialButton.js | 58 ++++++++++++++------- 7 files changed, 230 insertions(+), 112 deletions(-) diff --git a/themes/commerce/components/SocialButton.js b/themes/commerce/components/SocialButton.js index 24823924..3572010a 100644 --- a/themes/commerce/components/SocialButton.js +++ b/themes/commerce/components/SocialButton.js @@ -1,77 +1,97 @@ import { siteConfig } from '@/lib/config' +import { useEffect, useRef } from 'react' /** * 社交联系方式按钮组 * @returns {JSX.Element} * @constructor */ const SocialButton = () => { + const CONTACT_GITHUB = siteConfig('CONTACT_GITHUB') + const CONTACT_TWITTER = siteConfig('CONTACT_TWITTER') + const CONTACT_TELEGRAM = siteConfig('CONTACT_TELEGRAM') + const CONTACT_LINKEDIN = siteConfig('CONTACT_LINKEDIN') + const CONTACT_WEIBO = siteConfig('CONTACT_WEIBO') + const CONTACT_INSTAGRAM = siteConfig('CONTACT_INSTAGRAM') + const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL') + const ENABLE_RSS = siteConfig('ENABLE_RSS') + const CONTACT_BILIBILI = siteConfig('CONTACT_BILIBILI') + const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') + + const emailIcon = useRef(null) + useEffect(() => { + if (CONTACT_EMAIL && emailIcon.current) { + emailIcon.current.href = + 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) + } + }, [CONTACT_EMAIL]) return (
- {siteConfig('CONTACT_GITHUB') && ( + {CONTACT_GITHUB && ( + href={CONTACT_GITHUB}> )} - {siteConfig('CONTACT_TWITTER') && ( + {CONTACT_TWITTER && ( + href={CONTACT_TWITTER}> )} - {siteConfig('CONTACT_TELEGRAM') && ( + {CONTACT_TELEGRAM && ( )} - {siteConfig('CONTACT_LINKEDIN') && ( + {CONTACT_LINKEDIN && ( )} - {siteConfig('CONTACT_WEIBO') && ( + {CONTACT_WEIBO && ( + href={CONTACT_WEIBO}> )} - {siteConfig('CONTACT_INSTAGRAM') && ( + {CONTACT_INSTAGRAM && ( + href={CONTACT_INSTAGRAM}> )} - {siteConfig('CONTACT_EMAIL') && ( + {CONTACT_EMAIL && ( + href={CONTACT_EMAIL}> )} - {JSON.parse(siteConfig('ENABLE_RSS')) && ( + {ENABLE_RSS && ( { )} - {siteConfig('CONTACT_BILIBILI') && ( + {CONTACT_BILIBILI && ( + href={CONTACT_BILIBILI}> )} - {siteConfig('CONTACT_YOUTUBE') && ( + {CONTACT_YOUTUBE && ( + href={CONTACT_YOUTUBE}> )} diff --git a/themes/heo/components/SocialButton.js b/themes/heo/components/SocialButton.js index f746bc0b..2b18b467 100644 --- a/themes/heo/components/SocialButton.js +++ b/themes/heo/components/SocialButton.js @@ -82,7 +82,7 @@ const SocialButton = () => { )} - {JSON.parse(ENABLE_RSS) && ( + {ENABLE_RSS && ( { setShow(!show) } + const CONTACT_GITHUB = siteConfig('CONTACT_GITHUB') + const CONTACT_TWITTER = siteConfig('CONTACT_TWITTER') + const CONTACT_TELEGRAM = siteConfig('CONTACT_TELEGRAM') + const CONTACT_LINKEDIN = siteConfig('CONTACT_LINKEDIN') + const CONTACT_WEIBO = siteConfig('CONTACT_WEIBO') + const CONTACT_INSTAGRAM = siteConfig('CONTACT_INSTAGRAM') + const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL') + const ENABLE_RSS = siteConfig('ENABLE_RSS') + const CONTACT_BILIBILI = siteConfig('CONTACT_BILIBILI') + const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') + + const emailIcon = useRef(null) + useEffect(() => { + if (CONTACT_EMAIL && emailIcon.current) { + emailIcon.current.href = + 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) + } + }, [CONTACT_EMAIL]) + return (
{!show && ( @@ -22,70 +41,71 @@ const SocialButton = () => { )} {show && ( <> - {siteConfig('CONTACT_GITHUB') && ( + {CONTACT_GITHUB && ( + href={CONTACT_GITHUB}> )} - {siteConfig('CONTACT_TWITTER') && ( + {CONTACT_TWITTER && ( + href={CONTACT_TWITTER}> )} - {siteConfig('CONTACT_TELEGRAM') && ( + {CONTACT_TELEGRAM && ( )} - {siteConfig('CONTACT_LINKEDIN') && ( + {CONTACT_LINKEDIN && ( )} - {siteConfig('CONTACT_WEIBO') && ( + {CONTACT_WEIBO && ( + href={CONTACT_WEIBO}> )} - {siteConfig('CONTACT_INSTAGRAM') && ( + {CONTACT_INSTAGRAM && ( + href={CONTACT_INSTAGRAM}> )} - {siteConfig('CONTACT_EMAIL') && ( + {CONTACT_EMAIL && ( + href={CONTACT_EMAIL}> )} - {JSON.parse(siteConfig('ENABLE_RSS')) && ( + {ENABLE_RSS && ( { )} - {siteConfig('CONTACT_BILIBILI') && ( + {CONTACT_BILIBILI && ( + href={CONTACT_BILIBILI}> )} - {siteConfig('CONTACT_YOUTUBE') && ( + {CONTACT_YOUTUBE && ( + href={CONTACT_YOUTUBE}> )} diff --git a/themes/medium/components/SocialButton.js b/themes/medium/components/SocialButton.js index 95b8b412..40ae1ca4 100644 --- a/themes/medium/components/SocialButton.js +++ b/themes/medium/components/SocialButton.js @@ -1,4 +1,5 @@ import { siteConfig } from '@/lib/config' +import { useEffect, useRef } from 'react' /** * 社交联系方式按钮组 @@ -6,72 +7,91 @@ import { siteConfig } from '@/lib/config' * @constructor */ const SocialButton = () => { + const CONTACT_GITHUB = siteConfig('CONTACT_GITHUB') + const CONTACT_TWITTER = siteConfig('CONTACT_TWITTER') + const CONTACT_TELEGRAM = siteConfig('CONTACT_TELEGRAM') + const CONTACT_LINKEDIN = siteConfig('CONTACT_LINKEDIN') + const CONTACT_WEIBO = siteConfig('CONTACT_WEIBO') + const CONTACT_INSTAGRAM = siteConfig('CONTACT_INSTAGRAM') + const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL') + const ENABLE_RSS = siteConfig('ENABLE_RSS') + const CONTACT_BILIBILI = siteConfig('CONTACT_BILIBILI') + const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') + + const emailIcon = useRef(null) + useEffect(() => { + if (CONTACT_EMAIL && emailIcon.current) { + emailIcon.current.href = + 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) + } + }, [CONTACT_EMAIL]) return (
- {siteConfig('CONTACT_GITHUB') && ( + {CONTACT_GITHUB && ( + href={CONTACT_GITHUB}> )} - {siteConfig('CONTACT_TWITTER') && ( + {CONTACT_TWITTER && ( + href={CONTACT_TWITTER}> )} - {siteConfig('CONTACT_TELEGRAM') && ( + {CONTACT_TELEGRAM && ( )} - {siteConfig('CONTACT_LINKEDIN') && ( + {CONTACT_LINKEDIN && ( )} - {siteConfig('CONTACT_WEIBO') && ( + {CONTACT_WEIBO && ( + href={CONTACT_WEIBO}> )} - {siteConfig('CONTACT_INSTAGRAM') && ( + {CONTACT_INSTAGRAM && ( + href={CONTACT_INSTAGRAM}> )} - {siteConfig('CONTACT_EMAIL') && ( + {CONTACT_EMAIL && ( + href={CONTACT_EMAIL}> )} - {JSON.parse(siteConfig('ENABLE_RSS')) && ( + {ENABLE_RSS && ( { )} - {siteConfig('CONTACT_BILIBILI') && ( + {CONTACT_BILIBILI && ( + href={CONTACT_BILIBILI}> )} - {siteConfig('CONTACT_YOUTUBE') && ( + {CONTACT_YOUTUBE && ( + href={CONTACT_YOUTUBE}> )} diff --git a/themes/nav/components/SocialButton.js b/themes/nav/components/SocialButton.js index 74fdad37..64aa4cc1 100755 --- a/themes/nav/components/SocialButton.js +++ b/themes/nav/components/SocialButton.js @@ -1,4 +1,5 @@ import { siteConfig } from '@/lib/config' +import { useEffect, useRef } from 'react' /** * 社交联系方式按钮组 @@ -6,72 +7,89 @@ import { siteConfig } from '@/lib/config' * @constructor */ const SocialButton = () => { + const CONTACT_GITHUB = siteConfig('CONTACT_GITHUB') + const CONTACT_TWITTER = siteConfig('CONTACT_TWITTER') + const CONTACT_TELEGRAM = siteConfig('CONTACT_TELEGRAM') + const CONTACT_LINKEDIN = siteConfig('CONTACT_LINKEDIN') + const CONTACT_WEIBO = siteConfig('CONTACT_WEIBO') + const CONTACT_INSTAGRAM = siteConfig('CONTACT_INSTAGRAM') + const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL') + const ENABLE_RSS = siteConfig('ENABLE_RSS') + + const emailIcon = useRef(null) + useEffect(() => { + if (CONTACT_EMAIL && emailIcon.current) { + emailIcon.current.href = + 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) + } + }, [CONTACT_EMAIL]) return (
- {siteConfig('CONTACT_GITHUB') && ( + {CONTACT_GITHUB && ( + href={CONTACT_GITHUB}> )} - {siteConfig('CONTACT_TWITTER') && ( + {CONTACT_TWITTER && ( + href={CONTACT_TWITTER}> )} - {siteConfig('CONTACT_TELEGRAM') && ( + {CONTACT_TELEGRAM && ( )} - {siteConfig('CONTACT_LINKEDIN') && ( + {CONTACT_LINKEDIN && ( )} - {siteConfig('CONTACT_WEIBO') && ( + {CONTACT_WEIBO && ( + href={CONTACT_WEIBO}> )} - {siteConfig('CONTACT_INSTAGRAM') && ( + {CONTACT_INSTAGRAM && ( + href={CONTACT_INSTAGRAM}> )} - {siteConfig('CONTACT_EMAIL') && ( + {CONTACT_EMAIL && ( + href={CONTACT_EMAIL}> )} - {JSON.parse(siteConfig('ENABLE_RSS')) && ( + {ENABLE_RSS && ( { + const CONTACT_GITHUB = siteConfig('CONTACT_GITHUB') + const CONTACT_TWITTER = siteConfig('CONTACT_TWITTER') + const CONTACT_TELEGRAM = siteConfig('CONTACT_TELEGRAM') + const CONTACT_LINKEDIN = siteConfig('CONTACT_LINKEDIN') + const CONTACT_WEIBO = siteConfig('CONTACT_WEIBO') + const CONTACT_INSTAGRAM = siteConfig('CONTACT_INSTAGRAM') + const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL') + const ENABLE_RSS = siteConfig('ENABLE_RSS') + const CONTACT_BILIBILI = siteConfig('CONTACT_BILIBILI') + const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') + + const emailIcon = useRef(null) + useEffect(() => { + if (CONTACT_EMAIL && emailIcon.current) { + emailIcon.current.href = + 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) + } + }, [CONTACT_EMAIL]) return (
- {siteConfig('CONTACT_GITHUB') && ( + {CONTACT_GITHUB && ( + href={CONTACT_GITHUB}> )} - {siteConfig('CONTACT_TWITTER') && ( + {CONTACT_TWITTER && ( + href={CONTACT_TWITTER}> )} - {siteConfig('CONTACT_TELEGRAM') && ( + {CONTACT_TELEGRAM && ( )} - {siteConfig('CONTACT_LINKEDIN') && ( + {CONTACT_LINKEDIN && ( )} - {siteConfig('CONTACT_WEIBO') && ( + {CONTACT_WEIBO && ( + href={CONTACT_WEIBO}> )} - {siteConfig('CONTACT_INSTAGRAM') && ( + {CONTACT_INSTAGRAM && ( + href={CONTACT_INSTAGRAM}> )} - {siteConfig('CONTACT_EMAIL') && ( + {CONTACT_EMAIL && ( + href={CONTACT_EMAIL}> )} - {JSON.parse(siteConfig('ENABLE_RSS')) && ( + {ENABLE_RSS && ( { )} - {siteConfig('CONTACT_BILIBILI') && ( + {CONTACT_BILIBILI && ( + href={CONTACT_BILIBILI}> )} - {siteConfig('CONTACT_YOUTUBE') && ( + {CONTACT_YOUTUBE && ( + href={CONTACT_YOUTUBE}> )} diff --git a/themes/starter/components/SocialButton.js b/themes/starter/components/SocialButton.js index d11f3423..a90d533b 100644 --- a/themes/starter/components/SocialButton.js +++ b/themes/starter/components/SocialButton.js @@ -1,4 +1,5 @@ import { siteConfig } from '@/lib/config' +import { useEffect, useRef } from 'react' /** * 社交联系方式按钮组 @@ -6,73 +7,92 @@ import { siteConfig } from '@/lib/config' * @constructor */ const SocialButton = () => { + const CONTACT_GITHUB = siteConfig('CONTACT_GITHUB') + const CONTACT_TWITTER = siteConfig('CONTACT_TWITTER') + const CONTACT_TELEGRAM = siteConfig('CONTACT_TELEGRAM') + const CONTACT_LINKEDIN = siteConfig('CONTACT_LINKEDIN') + const CONTACT_WEIBO = siteConfig('CONTACT_WEIBO') + const CONTACT_INSTAGRAM = siteConfig('CONTACT_INSTAGRAM') + const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL') + const ENABLE_RSS = siteConfig('ENABLE_RSS') + const CONTACT_BILIBILI = siteConfig('CONTACT_BILIBILI') + const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') + + const emailIcon = useRef(null) + useEffect(() => { + if (CONTACT_EMAIL && emailIcon.current) { + emailIcon.current.href = + 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) + } + }, [CONTACT_EMAIL]) return (
- {siteConfig('CONTACT_GITHUB') && ( + {CONTACT_GITHUB && ( + href={CONTACT_GITHUB}> )} - {siteConfig('CONTACT_TWITTER') && ( + {CONTACT_TWITTER && ( + href={CONTACT_TWITTER}> )} - {siteConfig('CONTACT_TELEGRAM') && ( + {CONTACT_TELEGRAM && ( )} - {siteConfig('CONTACT_LINKEDIN') && ( + {CONTACT_LINKEDIN && ( )} - {siteConfig('CONTACT_WEIBO') && ( + {CONTACT_WEIBO && ( + href={CONTACT_WEIBO}> )} - {siteConfig('CONTACT_INSTAGRAM') && ( + {CONTACT_INSTAGRAM && ( + href={CONTACT_INSTAGRAM}> )} - {siteConfig('CONTACT_EMAIL') && ( + {CONTACT_EMAIL && ( + href={CONTACT_EMAIL}> )} - {JSON.parse(siteConfig('ENABLE_RSS')) && ( + {ENABLE_RSS && ( { )} - {siteConfig('CONTACT_BILIBILI') && ( + {CONTACT_BILIBILI && ( + href={CONTACT_BILIBILI}> )} - {siteConfig('CONTACT_YOUTUBE') && ( + {CONTACT_YOUTUBE && ( + href={CONTACT_YOUTUBE}> )} From a01d15d883b454e122013eb747342e77c43eeb48 Mon Sep 17 00:00:00 2001 From: anime Date: Sat, 12 Apr 2025 02:50:09 +0800 Subject: [PATCH 2/7] feat(email encrypt): implement base64 encoding for contact email config, only supports HEO theme --- conf/contact.config.js | 7 ++++++- lib/notion/getNotionConfig.js | 11 ++++++++--- lib/plugins/mailEncrypt.js | 12 ++++++++++++ themes/heo/components/SocialButton.js | 13 +++++++++---- 4 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 lib/plugins/mailEncrypt.js diff --git a/conf/contact.config.js b/conf/contact.config.js index d2a53274..2784e360 100644 --- a/conf/contact.config.js +++ b/conf/contact.config.js @@ -3,7 +3,12 @@ */ module.exports = { // 社交链接,不需要可留空白,例如 CONTACT_WEIBO:'' - CONTACT_EMAIL: process.env.NEXT_PUBLIC_CONTACT_EMAIL || '', // 邮箱地址 例如mail@tangly1024.com + CONTACT_EMAIL: + (process.env.NEXT_PUBLIC_CONTACT_EMAIL && + btoa( + unescape(encodeURIComponent(process.env.NEXT_PUBLIC_CONTACT_EMAIL)) + )) || + '', // 邮箱地址 例如mail@tangly1024.com CONTACT_WEIBO: process.env.NEXT_PUBLIC_CONTACT_WEIBO || '', // 你的微博个人主页 CONTACT_TWITTER: process.env.NEXT_PUBLIC_CONTACT_TWITTER || '', // 你的twitter个人主页 CONTACT_GITHUB: process.env.NEXT_PUBLIC_CONTACT_GITHUB || '', // 你的github个人主页 例如 https://github.com/tangly1024 diff --git a/lib/notion/getNotionConfig.js b/lib/notion/getNotionConfig.js index 029c7b96..3e111fcd 100644 --- a/lib/notion/getNotionConfig.js +++ b/lib/notion/getNotionConfig.js @@ -157,9 +157,14 @@ export async function getConfigMapFromConfigPage(allPages) { // 只导入生效的配置 if (config.enable) { // console.log('[Notion配置]', config.key, config.value) - notionConfig[config.key] = - parseTextToJson(config.value) || config.value || null - // 配置不能是undefined,至少是null + if (config.key === 'CONTACT_EMAIL') { + notionConfig[config.key] = + (config.value && btoa(unescape(encodeURIComponent(config.value)))) || null + } else { + notionConfig[config.key] = + parseTextToJson(config.value) || config.value || null + // 配置不能是undefined,至少是null + } } } } diff --git a/lib/plugins/mailEncrypt.js b/lib/plugins/mailEncrypt.js new file mode 100644 index 00000000..091c39b3 --- /dev/null +++ b/lib/plugins/mailEncrypt.js @@ -0,0 +1,12 @@ +export const handleEmailClick = (e, emailIcon, CONTACT_EMAIL) => { + if (CONTACT_EMAIL && emailIcon && !emailIcon.current.href) { + e.preventDefault() + try { + const email = decodeURIComponent(escape(atob(CONTACT_EMAIL))) + emailIcon.current.href = `mailto:${email}` + emailIcon.current.click() + } catch (error) { + console.error('解密邮箱失败:', error) + } + } +} diff --git a/themes/heo/components/SocialButton.js b/themes/heo/components/SocialButton.js index 2b18b467..d6f86562 100644 --- a/themes/heo/components/SocialButton.js +++ b/themes/heo/components/SocialButton.js @@ -1,4 +1,6 @@ import { siteConfig } from '@/lib/config' +import { useRef } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -16,6 +18,9 @@ const SocialButton = () => { const ENABLE_RSS = siteConfig('ENABLE_RSS') const CONTACT_BILIBILI = siteConfig('CONTACT_BILIBILI') const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') + + const emailIcon = useRef(null) + return (
@@ -75,10 +80,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} From 9973d6bf7381b2f90b4e3d01421b209e59f33657 Mon Sep 17 00:00:00 2001 From: anime Date: Sat, 12 Apr 2025 03:22:21 +0800 Subject: [PATCH 3/7] feat(email encrypt): implement base64 encoding for contact email config Closes #2438 Closes #3319 --- themes/commerce/components/SocialButton.js | 20 ++++++++------------ themes/fukasawa/components/SocialButton.js | 14 +++++++++----- themes/gitbook/components/SocialButton.js | 12 ++++++++---- themes/hexo/components/SocialButton.js | 14 +++++++++----- themes/magzine/components/SocialButton.js | 14 +++++++++----- themes/matery/components/SocialButton.js | 19 +++++++------------ themes/medium/components/SocialButton.js | 19 +++++++------------ themes/nav/components/SocialButton.js | 19 +++++++------------ themes/plog/components/SocialButton.js | 19 +++++++------------ themes/starter/components/SocialButton.js | 19 +++++++------------ 10 files changed, 78 insertions(+), 91 deletions(-) diff --git a/themes/commerce/components/SocialButton.js b/themes/commerce/components/SocialButton.js index 3572010a..adf18d7f 100644 --- a/themes/commerce/components/SocialButton.js +++ b/themes/commerce/components/SocialButton.js @@ -1,5 +1,7 @@ import { siteConfig } from '@/lib/config' -import { useEffect, useRef } from 'react' +import { useRef } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' + /** * 社交联系方式按钮组 * @returns {JSX.Element} @@ -18,12 +20,7 @@ const SocialButton = () => { const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') const emailIcon = useRef(null) - useEffect(() => { - if (CONTACT_EMAIL && emailIcon.current) { - emailIcon.current.href = - 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) - } - }, [CONTACT_EMAIL]) + return (
@@ -83,11 +80,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/fukasawa/components/SocialButton.js b/themes/fukasawa/components/SocialButton.js index a2bfa2f8..d8d3c421 100644 --- a/themes/fukasawa/components/SocialButton.js +++ b/themes/fukasawa/components/SocialButton.js @@ -1,6 +1,7 @@ import QrCode from '@/components/QrCode' import { siteConfig } from '@/lib/config' -import { useState } from 'react' +import { useRef, useState } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -32,6 +33,9 @@ const SocialButton = () => { setQrCodeShow(false) } + const emailIcon = useRef(null) + + return (
@@ -91,10 +95,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/gitbook/components/SocialButton.js b/themes/gitbook/components/SocialButton.js index a2bfa2f8..80078ae7 100644 --- a/themes/gitbook/components/SocialButton.js +++ b/themes/gitbook/components/SocialButton.js @@ -1,6 +1,7 @@ import QrCode from '@/components/QrCode' import { siteConfig } from '@/lib/config' import { useState } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -32,6 +33,9 @@ const SocialButton = () => { setQrCodeShow(false) } + const emailIcon = useRef(null) + + return (
@@ -91,10 +95,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/hexo/components/SocialButton.js b/themes/hexo/components/SocialButton.js index 3e220cf7..e7dbf127 100644 --- a/themes/hexo/components/SocialButton.js +++ b/themes/hexo/components/SocialButton.js @@ -1,6 +1,7 @@ import QrCode from '@/components/QrCode' import { siteConfig } from '@/lib/config' -import { useState } from 'react' +import { useRef, useState } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -32,6 +33,9 @@ const SocialButton = () => { const closePopover = () => { setQrCodeShow(false) } + + const emailIcon = useRef(null) + return (
@@ -91,10 +95,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/magzine/components/SocialButton.js b/themes/magzine/components/SocialButton.js index 5b3a3954..aa790cdf 100644 --- a/themes/magzine/components/SocialButton.js +++ b/themes/magzine/components/SocialButton.js @@ -1,6 +1,7 @@ import QrCode from '@/components/QrCode' import { siteConfig } from '@/lib/config' -import { useState } from 'react' +import { useRef, useState } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -31,6 +32,9 @@ const SocialButton = () => { setQrCodeShow(false) } + const emailIcon = useRef(null) + + return (
@@ -90,10 +94,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/matery/components/SocialButton.js b/themes/matery/components/SocialButton.js index 678ad4fd..74e18597 100644 --- a/themes/matery/components/SocialButton.js +++ b/themes/matery/components/SocialButton.js @@ -1,5 +1,6 @@ import { siteConfig } from '@/lib/config' -import { useEffect, useRef, useState } from 'react' +import { useRef, useState } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 可折叠的组件 @@ -24,12 +25,7 @@ const SocialButton = () => { const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') const emailIcon = useRef(null) - useEffect(() => { - if (CONTACT_EMAIL && emailIcon.current) { - emailIcon.current.href = - 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) - } - }, [CONTACT_EMAIL]) + return (
@@ -97,11 +93,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/medium/components/SocialButton.js b/themes/medium/components/SocialButton.js index 40ae1ca4..66899971 100644 --- a/themes/medium/components/SocialButton.js +++ b/themes/medium/components/SocialButton.js @@ -1,5 +1,6 @@ import { siteConfig } from '@/lib/config' -import { useEffect, useRef } from 'react' +import { useRef } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -19,12 +20,7 @@ const SocialButton = () => { const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') const emailIcon = useRef(null) - useEffect(() => { - if (CONTACT_EMAIL && emailIcon.current) { - emailIcon.current.href = - 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) - } - }, [CONTACT_EMAIL]) + return (
{CONTACT_GITHUB && ( @@ -83,11 +79,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/nav/components/SocialButton.js b/themes/nav/components/SocialButton.js index 64aa4cc1..e6992904 100755 --- a/themes/nav/components/SocialButton.js +++ b/themes/nav/components/SocialButton.js @@ -1,5 +1,6 @@ import { siteConfig } from '@/lib/config' -import { useEffect, useRef } from 'react' +import { useRef } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -17,12 +18,7 @@ const SocialButton = () => { const ENABLE_RSS = siteConfig('ENABLE_RSS') const emailIcon = useRef(null) - useEffect(() => { - if (CONTACT_EMAIL && emailIcon.current) { - emailIcon.current.href = - 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) - } - }, [CONTACT_EMAIL]) + return (
{CONTACT_GITHUB && ( @@ -81,11 +77,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/plog/components/SocialButton.js b/themes/plog/components/SocialButton.js index c48909bd..7d6aa1a8 100644 --- a/themes/plog/components/SocialButton.js +++ b/themes/plog/components/SocialButton.js @@ -1,5 +1,6 @@ import { siteConfig } from '@/lib/config' -import { useEffect, useRef } from 'react' +import { useRef } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -19,12 +20,7 @@ const SocialButton = () => { const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') const emailIcon = useRef(null) - useEffect(() => { - if (CONTACT_EMAIL && emailIcon.current) { - emailIcon.current.href = - 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) - } - }, [CONTACT_EMAIL]) + return (
@@ -84,11 +80,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} diff --git a/themes/starter/components/SocialButton.js b/themes/starter/components/SocialButton.js index a90d533b..2cac8bf6 100644 --- a/themes/starter/components/SocialButton.js +++ b/themes/starter/components/SocialButton.js @@ -1,5 +1,6 @@ import { siteConfig } from '@/lib/config' -import { useEffect, useRef } from 'react' +import { useRef } from 'react' +import { handleEmailClick } from '@/lib/plugins/mailEncrypt' /** * 社交联系方式按钮组 @@ -19,12 +20,7 @@ const SocialButton = () => { const CONTACT_YOUTUBE = siteConfig('CONTACT_YOUTUBE') const emailIcon = useRef(null) - useEffect(() => { - if (CONTACT_EMAIL && emailIcon.current) { - emailIcon.current.href = - 'mailto:' + decodeURIComponent(escape(atob(CONTACT_EMAIL))) - } - }, [CONTACT_EMAIL]) + return (
@@ -84,11 +80,10 @@ const SocialButton = () => { )} {CONTACT_EMAIL && ( + onClick={e => handleEmailClick(e, emailIcon, CONTACT_EMAIL)} + title='email' + className='cursor-pointer' + ref={emailIcon}> )} From bdd109381a4c73f02ce9c30f02d0d7d31d2bda52 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 7 Jul 2025 17:32:26 +0800 Subject: [PATCH 4/7] feat(mailEncrypt): add email encryption/decryption functions - refactor email handling logic in handleEmailClick - add new encryptEmail and decryptEmail utility functions - improve error handling for decryption process - simplify the email click handler implementation --- lib/notion/getNotionConfig.js | 3 ++- lib/plugins/mailEncrypt.js | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/notion/getNotionConfig.js b/lib/notion/getNotionConfig.js index 3e111fcd..d32b46fa 100644 --- a/lib/notion/getNotionConfig.js +++ b/lib/notion/getNotionConfig.js @@ -10,6 +10,7 @@ import { getDateValue, getTextContent } from 'notion-utils' import { deepClone } from '../utils' import getAllPageIds from './getAllPageIds' import { getPage } from './getPostBlocks' +import { encryptEmail } from '@/lib/plugins/mailEncrypt' /** * 从Notion中读取Config配置表 @@ -159,7 +160,7 @@ export async function getConfigMapFromConfigPage(allPages) { // console.log('[Notion配置]', config.key, config.value) if (config.key === 'CONTACT_EMAIL') { notionConfig[config.key] = - (config.value && btoa(unescape(encodeURIComponent(config.value)))) || null + (config.value && encryptEmail(config.value)) || null } else { notionConfig[config.key] = parseTextToJson(config.value) || config.value || null diff --git a/lib/plugins/mailEncrypt.js b/lib/plugins/mailEncrypt.js index 091c39b3..a7c6c2bc 100644 --- a/lib/plugins/mailEncrypt.js +++ b/lib/plugins/mailEncrypt.js @@ -1,12 +1,21 @@ export const handleEmailClick = (e, emailIcon, CONTACT_EMAIL) => { if (CONTACT_EMAIL && emailIcon && !emailIcon.current.href) { e.preventDefault() - try { - const email = decodeURIComponent(escape(atob(CONTACT_EMAIL))) - emailIcon.current.href = `mailto:${email}` - emailIcon.current.click() - } catch (error) { - console.error('解密邮箱失败:', error) - } + const email = decryptEmail(CONTACT_EMAIL) + emailIcon.current.href = `mailto:${email}` + emailIcon.current.click() + } +} + +export const encryptEmail = email => { + return btoa(unescape(encodeURIComponent(email))) +} + +export const decryptEmail = encryptedEmail => { + try { + return decodeURIComponent(escape(atob(encryptedEmail))) + } catch (error) { + console.error('解密邮箱失败:', error) + return encryptedEmail } } From 143bae3d8d81a5ea31a9e13239ae2b61518d44e0 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 7 Jul 2025 17:45:39 +0800 Subject: [PATCH 5/7] feat(email encrypt): implement base64 encoding for contact email config --- themes/commerce/components/Footer.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/themes/commerce/components/Footer.js b/themes/commerce/components/Footer.js index 3411773c..aff2cec4 100644 --- a/themes/commerce/components/Footer.js +++ b/themes/commerce/components/Footer.js @@ -4,6 +4,8 @@ import CopyRightDate from '@/components/CopyRightDate' import { siteConfig } from '@/lib/config' import Link from 'next/link' import CONFIG from '../config' +import { decryptEmail, handleEmailClick } from '@/lib/plugins/mailEncrypt' +import { useRef } from 'react' /** * 页脚 @@ -18,6 +20,10 @@ const Footer = props => { parseInt(since) < currentYear ? since + '-' + currentYear : currentYear const { categoryOptions, customMenu } = props + const CONTACT_EMAIL = siteConfig('CONTACT_EMAIL') + + const emailIcon = useRef(null) + return (
{
From e9f31ba1c398e021beb2059da7fed9fa511bcc7a Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 7 Jul 2025 17:55:10 +0800 Subject: [PATCH 6/7] feat(email encrypt): implement base64 encoding for contact email config --- lib/rss.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/rss.js b/lib/rss.js index 1ec3daa7..04c667ce 100644 --- a/lib/rss.js +++ b/lib/rss.js @@ -4,6 +4,7 @@ import { getPostBlocks } from '@/lib/db/getSiteData' import { Feed } from 'feed' import fs from 'fs' import ReactDOMServer from 'react-dom/server' +import { decryptEmail } from '@/lib/plugins/mailEncrypt' /** * 生成RSS内容 @@ -37,7 +38,9 @@ export async function generateRss(props) { const AUTHOR = NOTION_CONFIG?.AUTHOR || BLOG.AUTHOR const LANG = NOTION_CONFIG?.LANG || BLOG.LANG const SUB_PATH = NOTION_CONFIG?.SUB_PATH || BLOG.SUB_PATH - const CONTACT_EMAIL = NOTION_CONFIG?.CONTACT_EMAIL || BLOG.CONTACT_EMAIL + const CONTACT_EMAIL = decryptEmail( + NOTION_CONFIG?.CONTACT_EMAIL || BLOG.CONTACT_EMAIL + ) // 检查 feed 文件是否在10分钟内更新过 if (isFeedRecentlyUpdated('./public/rss/feed.xml', 10)) { From 27b3c5e324224109c4282f308f009f6ddf9c8ad1 Mon Sep 17 00:00:00 2001 From: anime Date: Mon, 7 Jul 2025 18:33:51 +0800 Subject: [PATCH 7/7] feat(email encrypt): implement CanvasEmail component for obfuscation of display email text - Add new CanvasEmail component that renders email on canvas - Replace plain email text with CanvasEmail in Footer --- components/CanvasEmail.js | 95 ++++++++++++++++++++++++++++ themes/commerce/components/Footer.js | 3 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 components/CanvasEmail.js diff --git a/components/CanvasEmail.js b/components/CanvasEmail.js new file mode 100644 index 00000000..1de23fee --- /dev/null +++ b/components/CanvasEmail.js @@ -0,0 +1,95 @@ +import { useEffect, useRef, useState } from 'react' + +const CanvasEmail = ({ email, className = '' }) => { + const canvasRef = useRef(null) + const textRef = useRef(null) + const [isCopied, setIsCopied] = useState(false) + + useEffect(() => { + if (!textRef.current || !canvasRef.current) return + + const canvas = canvasRef.current + const ctx = canvas.getContext('2d') + const textElement = textRef.current + + // Get computed styles from the hidden text element + const style = window.getComputedStyle(textElement) + const font = style.font + const color = style.color + + // Set canvas font and measure text + ctx.font = font + const metrics = ctx.measureText(email) + const fontSize = parseInt(style.fontSize) + const lineHeight = fontSize * 1.2 + + // Set canvas dimensions + const scale = window.devicePixelRatio || 1 + canvas.width = metrics.width * scale + canvas.height = lineHeight * scale + canvas.style.width = `${metrics.width}px` + canvas.style.height = `${lineHeight}px` + + // Redraw with high DPI support + ctx.scale(scale, scale) + ctx.font = font + ctx.fillStyle = color + ctx.textBaseline = 'top' // Changed to 'top' for better vertical alignment + ctx.fillText(email, 0, 0) + + // Handle copy to clipboard + const handleCopy = e => { + e.preventDefault() + navigator.clipboard.writeText(email).then(() => { + setIsCopied(true) + setTimeout(() => setIsCopied(false), 2000) + }) + } + + canvas.style.cursor = 'pointer' + canvas.addEventListener('click', handleCopy) + return () => canvas.removeEventListener('click', handleCopy) + }, [email]) + + return ( + + {/* Hidden span for measuring text metrics */} + + + {/* Canvas that displays the text */} + + + ) +} + +export default CanvasEmail diff --git a/themes/commerce/components/Footer.js b/themes/commerce/components/Footer.js index aff2cec4..0db4f1f7 100644 --- a/themes/commerce/components/Footer.js +++ b/themes/commerce/components/Footer.js @@ -6,6 +6,7 @@ import Link from 'next/link' import CONFIG from '../config' import { decryptEmail, handleEmailClick } from '@/lib/plugins/mailEncrypt' import { useRef } from 'react' +import CanvasEmail from '@/components/CanvasEmail' /** * 页脚 @@ -143,7 +144,7 @@ const Footer = props => { className='cursor-pointer' ref={emailIcon}> {' '} - {decryptEmail(CONTACT_EMAIL)} + )}