Skip to content

Commit 57ac5e4

Browse files
authored
Merge branch 'main' into feat/mail-encypt
2 parents 27b3c5e + 3b92d29 commit 57ac5e4

File tree

390 files changed

+4508
-1516
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

390 files changed

+4508
-1516
lines changed

blog.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,4 @@ const BLOG = {
6868
UUID_REDIRECT: process.env.UUID_REDIRECT || false
6969
}
7070

71-
module.exports = BLOG
71+
module.exports = BLOG

components/AlgoliaSearchModal.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { siteConfig } from '@/lib/config'
33
import { useGlobal } from '@/lib/global'
44
import algoliasearch from 'algoliasearch'
55
import throttle from 'lodash/throttle'
6-
import Link from 'next/link'
6+
import SmartLink from '@/components/SmartLink'
77
import { useRouter } from 'next/router'
88
import {
99
Fragment,
@@ -104,7 +104,8 @@ export default function AlgoliaSearchModal({ cRef }) {
104104
// 跳转Search结果
105105
const onJumpSearchResult = () => {
106106
if (searchResults.length > 0) {
107-
window.location.href = `${siteConfig('SUB_PATH', '')}/${searchResults[activeIndex].slug}`
107+
const searchResult = searchResults[activeIndex]
108+
window.location.href = `${siteConfig('SUB_PATH', '')}/${searchResult.slug || searchResult.objectID}`
108109
}
109110
}
110111

@@ -246,12 +247,12 @@ export default function AlgoliaSearchModal({ cRef }) {
246247
id='search-wrapper'
247248
className={`${
248249
isModalOpen ? 'opacity-100' : 'invisible opacity-0 pointer-events-none'
249-
} z-30 fixed h-screen w-screen left-0 top-0 sm:mt-12 flex items-start justify-center mt-0`}>
250+
} z-30 fixed h-screen w-screen left-0 top-0 sm:mt-[10vh] flex items-start justify-center mt-0`}>
250251
{/* 模态框 */}
251252
<div
252253
className={`${
253254
isModalOpen ? 'opacity-100' : 'invisible opacity-0 translate-y-10'
254-
} flex flex-col justify-between w-full min-h-[10rem] h-full md:h-fit max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}>
255+
} max-h-[80vh] flex flex-col justify-between w-full min-h-[10rem] h-full md:h-fit max-w-xl dark:bg-hexo-black-gray dark:border-gray-800 bg-white dark:bg- p-5 rounded-lg z-50 shadow border hover:border-blue-600 duration-300 transition-all `}>
255256
<div className='flex justify-between items-center'>
256257
<div className='text-2xl text-blue-600 dark:text-yellow-600 font-bold'>
257258
搜索
@@ -356,7 +357,7 @@ function TagGroups() {
356357
<div id='tags-group' className='dark:border-gray-700 space-y-2'>
357358
{firstTenTags?.map((tag, index) => {
358359
return (
359-
<Link
360+
<SmartLink
360361
passHref
361362
key={index}
362363
href={`/tag/${encodeURIComponent(tag.name)}`}
@@ -372,7 +373,7 @@ function TagGroups() {
372373
<></>
373374
)}
374375
</div>
375-
</Link>
376+
</SmartLink>
376377
)
377378
})}
378379
</div>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { siteConfig } from '@/lib/config'
2+
3+
/**
4+
* 文章过期提醒组件
5+
* 当文章超过指定天数时显示提醒
6+
* @param {Object} props - 组件属性
7+
* @param {Object} props.post - 文章数据
8+
* @param {number} [props.daysThreshold=90] - 过期阈值(天)
9+
* @returns {JSX.Element|null}
10+
*/
11+
export default function ArticleExpirationNotice({
12+
post,
13+
daysThreshold = siteConfig('ARTICLE_EXPIRATION_DAYS', 90)
14+
}) {
15+
const articleExpirationEnabled = siteConfig(
16+
'ARTICLE_EXPIRATION_ENABLED',
17+
false
18+
)
19+
if (!articleExpirationEnabled || !post?.lastEditedDay) {
20+
return null
21+
}
22+
23+
const postDate = new Date(post.lastEditedDay)
24+
const today = new Date()
25+
const diffTime = Math.abs(today - postDate)
26+
const daysOld = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
27+
const isVisible = daysOld >= daysThreshold
28+
29+
if (!isVisible) {
30+
return null
31+
}
32+
33+
// 使用 %%DAYS%% 作为占位符
34+
const articleExpirationMessage = siteConfig(
35+
'ARTICLE_EXPIRATION_MESSAGE',
36+
'这篇文章发布于 %%DAYS%% 天前,内容可能已过时,请谨慎参考。'
37+
)
38+
const articleExpirationMessageParts =
39+
articleExpirationMessage.split('%%DAYS%%')
40+
41+
// 直接返回 JSX 内容
42+
return (
43+
<div
44+
className={
45+
'p-4 rounded-lg border border-blue-300 bg-blue-50 dark:bg-blue-900/20 text-gray-800 dark:text-gray-200 shadow-sm'
46+
}>
47+
<div className='flex items-start'>
48+
<i className='fas fa-exclamation-triangle text-blue-500 dark:text-blue-400 mt-0.5 mr-2 flex-shrink-0' />
49+
<div className='ml-1'>
50+
<div className='text-blue-600 dark:text-blue-400 font-medium'>
51+
{siteConfig('ARTICLE_EXPIRATION_TITLE', '温馨提醒')}
52+
</div>
53+
<div className='flex items-center mt-1 text-sm text-gray-700 dark:text-gray-300'>
54+
<i className='far fa-clock text-red-500 dark:text-red-400 mr-1' />
55+
<span>
56+
{(() => {
57+
return (
58+
<>
59+
{articleExpirationMessageParts[0]}
60+
<span className='text-red-500 dark:text-red-400 font-bold'>
61+
{daysOld}
62+
</span>
63+
{articleExpirationMessageParts[1]}
64+
</>
65+
)
66+
})()}
67+
</span>
68+
</div>
69+
</div>
70+
</div>
71+
</div>
72+
)
73+
}

components/CustomContextMenu.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import useWindowSize from '@/hooks/useWindowSize'
22
import { siteConfig } from '@/lib/config'
33
import { useGlobal } from '@/lib/global'
44
import { THEMES, saveDarkModeToLocalStorage } from '@/themes/theme'
5-
import Link from 'next/link'
5+
import SmartLink from '@/components/SmartLink'
66
import { useRouter } from 'next/router'
77
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
88

@@ -198,23 +198,23 @@ export default function CustomContextMenu(props) {
198198
)}
199199

200200
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_CATEGORY && (
201-
<Link
201+
<SmartLink
202202
href='/category'
203203
title={locale.MENU.CATEGORY}
204204
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
205205
<i className='fa-solid fa-square-minus mr-2' />
206206
<div className='whitespace-nowrap'>{locale.MENU.CATEGORY}</div>
207-
</Link>
207+
</SmartLink>
208208
)}
209209

210210
{CUSTOM_RIGHT_CLICK_CONTEXT_MENU_TAG && (
211-
<Link
211+
<SmartLink
212212
href='/tag'
213213
title={locale.MENU.TAGS}
214214
className='w-full px-2 h-10 flex justify-start items-center flex-nowrap cursor-pointer hover:bg-blue-600 hover:text-white rounded-lg duration-200 transition-all'>
215215
<i className='fa-solid fa-tag mr-2' />
216216
<div className='whitespace-nowrap'>{locale.MENU.TAGS}</div>
217-
</Link>
217+
</SmartLink>
218218
)}
219219
</div>
220220

components/NotByAI.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { useGlobal } from '@/lib/global'
2+
3+
const LANGS = {
4+
'en-US': 'en',
5+
'zh-CN': 'zh',
6+
'zh-HK': 'zh-HK',
7+
'zh-TW': 'zh-TW',
8+
'fr-FR': 'fr',
9+
'tr-TR': 'tr',
10+
'ja-JP': 'ja'
11+
}
12+
13+
/**
14+
* 获取当前not-by-ai文件路径
15+
* 如果匹配到完整的“国家-地区”语言,则显示国家的语言
16+
* @returns string
17+
*/
18+
export function generateNotByAiPath(langString) {
19+
const supportedLocales = Object.keys(LANGS)
20+
let userLocale
21+
22+
// 将语言字符串拆分为语言和地区代码,例如将 "zh-CN" 拆分为 "zh" 和 "CN"
23+
const [language, region] = langString?.split(/[-_]/)
24+
25+
// 优先匹配语言和地区都匹配的情况
26+
const specificLocale = `${language}-${region}`
27+
if (supportedLocales.includes(specificLocale)) {
28+
userLocale = LANGS[specificLocale]
29+
}
30+
31+
// 然后尝试匹配只有语言匹配的情况
32+
if (!userLocale) {
33+
const languageOnlyLocales = supportedLocales.filter(locale =>
34+
locale.startsWith(language)
35+
)
36+
if (languageOnlyLocales.length > 0) {
37+
userLocale = LANGS[languageOnlyLocales[0]]
38+
}
39+
}
40+
41+
// 如果还没匹配到,则返回最接近的
42+
if (!userLocale) {
43+
const fallbackLocale = supportedLocales.find(locale =>
44+
locale.startsWith('en')
45+
)
46+
userLocale = LANGS[fallbackLocale]
47+
}
48+
49+
return userLocale ?? 'zh'
50+
}
51+
52+
/**
53+
* 版权声明
54+
* @returns
55+
*/
56+
export default function NotByAI() {
57+
const { lang, isDarkMode } = useGlobal()
58+
59+
return (
60+
<img
61+
className='transform hover:scale-110 duration-150'
62+
src={`/svg/not-by-ai/${generateNotByAiPath(lang)}/Written-By-Human-Not-By-AI-Badge-${isDarkMode ? 'black' : 'white'}.svg`}
63+
alt='not-by-ai'
64+
/>
65+
)
66+
}

components/Player.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const Player = () => {
6464
<link
6565
rel='stylesheet'
6666
type='text/css'
67-
href='https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.css'
67+
href='https://cdn.jsdelivr.net/npm/aplayer@1.10.0/dist/APlayer.min.css'
6868
/>
6969
{meting ? (
7070
<meting-js

components/SmartLink.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import Link from 'next/link'
2+
import { siteConfig } from '@/lib/config'
3+
4+
// 过滤 <a> 标签不能识别的 props
5+
const filterDOMProps = props => {
6+
const { passHref, legacyBehavior, ...rest } = props
7+
return rest
8+
}
9+
10+
const SmartLink = ({ href, children, ...rest }) => {
11+
const LINK = siteConfig('LINK')
12+
13+
// 获取 URL 字符串用于判断是否是外链
14+
let urlString = ''
15+
16+
if (typeof href === 'string') {
17+
urlString = href
18+
} else if (
19+
typeof href === 'object' &&
20+
href !== null &&
21+
typeof href.pathname === 'string'
22+
) {
23+
urlString = href.pathname
24+
}
25+
26+
const isExternal = urlString.startsWith('http') && !urlString.startsWith(LINK)
27+
28+
if (isExternal) {
29+
// 对于外部链接,必须是 string 类型
30+
const externalUrl =
31+
typeof href === 'string' ? href : new URL(href.pathname, LINK).toString()
32+
33+
return (
34+
<a
35+
href={externalUrl}
36+
target='_blank'
37+
rel='noopener noreferrer'
38+
{...filterDOMProps(rest)}>
39+
{children}
40+
</a>
41+
)
42+
}
43+
44+
// 内部链接(可为对象形式)
45+
return (
46+
<Link href={href} {...rest}>
47+
{children}
48+
</Link>
49+
)
50+
}
51+
52+
export default SmartLink

components/ui/dashboard/DashboardButton.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { siteConfig } from '@/lib/config'
2-
import Link from 'next/link'
2+
import SmartLink from '@/components/SmartLink'
33
import { useRouter } from 'next/router'
44
/**
55
* 跳转仪表盘的按钮
@@ -24,7 +24,7 @@ export default function DashboardButton({ className }) {
2424
<button
2525
type='button'
2626
className={`${className || ''} text-white bg-gray-800 hover:bg-gray-900 hover:ring-4 hover:ring-gray-300 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-5 py-2 me-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700`}>
27-
<Link href='/dashboard'>仪表盘</Link>
27+
<SmartLink href='/dashboard'>仪表盘</SmartLink>
2828
</button>
2929
)
3030
}

components/ui/dashboard/DashboardHeader.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import LazyImage from '@/components/LazyImage'
22
import { useGlobal } from '@/lib/global'
33
import formatDate from '@/lib/utils/formatDate'
4-
import Link from 'next/link'
4+
import SmartLink from '@/components/SmartLink'
55
import DashboardSignOutButton from './DashboardSignOutButton'
66

77
/**
@@ -25,11 +25,11 @@ export default function DashboardHeader() {
2525
<div class='font-medium dark:text-white'>
2626
<div className='flex items-center gap-x-2'>
2727
<span>{user?.fullName}</span>
28-
<Link href='/dashboard/membership'>
28+
<SmartLink href='/dashboard/membership'>
2929
<span class='bg-gray-100 text-gray-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-gray-300'>
3030
普通用户
3131
</span>
32-
</Link>
32+
</SmartLink>
3333
</div>
3434
<div className='text-sm text-gray-500 gap-x-2 flex dark:text-gray-400'>
3535
<span>{user?.username}</span>

components/ui/dashboard/DashboardItemAffliate.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Link from 'next/link'
1+
import SmartLink from '@/components/SmartLink'
22

33
/**
44
* 联盟行销
@@ -140,11 +140,11 @@ export default function DashboardItemAffliate() {
140140
for='remember'
141141
className='ms-2 text-sm font-medium text-gray-900 dark:text-gray-300'>
142142
我以阅读并同意{' '}
143-
<Link
143+
<SmartLink
144144
href='/terms-of-use'
145145
className='text-blue-600 hover:underline dark:text-blue-500'>
146146
服务协议
147-
</Link>
147+
</SmartLink>
148148
.
149149
</label>
150150
</div>

0 commit comments

Comments
 (0)