From 7ce6ad0c2f77a91c42ffacb6d43619f17f0139fb Mon Sep 17 00:00:00 2001 From: anime Date: Tue, 8 Jul 2025 01:13:09 +0800 Subject: [PATCH 01/19] feat(algolia): add data deletion functionality and execute in index.js - Add `checkDataFromAlgolia` function to identify and delete unused data - Implement `deletePostDataFromAlgolia` helper function - Execute Algolia data check during homepage generation - Handle password-protected and draft pages deletion Fixes 1526 --- lib/plugins/algolia.js | 53 ++++++++++++++++++++++++++++++++++++++++++ pages/index.js | 3 +++ 2 files changed, 56 insertions(+) diff --git a/lib/plugins/algolia.js b/lib/plugins/algolia.js index e6c76422..fecec3a1 100644 --- a/lib/plugins/algolia.js +++ b/lib/plugins/algolia.js @@ -15,6 +15,59 @@ const generateAlgoliaSearch = ({ allPages, force = false }) => { }) } + +/** + * 检查数据是否需要从algolia删除 + * @param {*} props + */ +export const checkDataFromAlgolia = async props => { + const { allPages } = props + const deletions = (allPages || []) + .map(p => { + if (p && (p.password || p.status === 'Draft')) { + return deletePostDataFromAlgolia(p) + } + }) + .filter(Boolean) // 去除 undefined + await Promise.all(deletions) +} + +/** + * 删除数据 + * @param post + */ +const deletePostDataFromAlgolia = async post => { + // Connect and authenticate with your Algolia app + const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY) + + // Create a new index and add a record + const index = client.initIndex(BLOG.ALGOLIA_INDEX) + + if (!post) { + return + } + + // 检查是否有索引 + let existed + try { + existed = await index.getObject(post.id) + } catch (error) { + // 通常是不存在索引 + } + + if (existed) { + await index + .deleteObject(post.id) + .wait() + .then(r => { + console.log('Algolia索引更新', r) + }) + .catch(err => { + console.log('Algolia异常', err) + }) + } +} + /** * 上传数据 * 根据上次修改文章日期和上次更新索引数据判断是否需要更新algolia索引 diff --git a/pages/index.js b/pages/index.js index b66b5610..b4b9937a 100644 --- a/pages/index.js +++ b/pages/index.js @@ -6,6 +6,7 @@ import { generateRss } from '@/lib/rss' import { generateSitemapXml } from '@/lib/sitemap.xml' import { DynamicLayout } from '@/themes/theme' import { generateRedirectJson } from '@/lib/redirect' +import { checkDataFromAlgolia } from '@/lib/plugins/algolia' /** * 首页布局 @@ -61,6 +62,8 @@ export async function getStaticProps(req) { generateRss(props) // 生成 generateSitemapXml(props) + // 检查数据是否需要从algolia删除 + checkDataFromAlgolia(props) if (siteConfig('UUID_REDIRECT', false, props?.NOTION_CONFIG)) { // 生成重定向 JSON generateRedirectJson(props) From 8d200120fc21c23605f6c00f1aaa77c2c0247720 Mon Sep 17 00:00:00 2001 From: anime Date: Tue, 8 Jul 2025 01:14:20 +0800 Subject: [PATCH 02/19] feat(algolia): add data deletion functionality and execute in index.js - Add `checkDataFromAlgolia` function to identify and delete unused data - Implement `deletePostDataFromAlgolia` helper function - Execute Algolia data check during homepage generation - Handle password-protected and draft pages deletion --- lib/plugins/algolia.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/algolia.js b/lib/plugins/algolia.js index fecec3a1..f3485ab7 100644 --- a/lib/plugins/algolia.js +++ b/lib/plugins/algolia.js @@ -60,7 +60,7 @@ const deletePostDataFromAlgolia = async post => { .deleteObject(post.id) .wait() .then(r => { - console.log('Algolia索引更新', r) + console.log('Algolia索引删除成功', r) }) .catch(err => { console.log('Algolia异常', err) From 55c39cbab8a45d90dc402b6b8163adb318ae6128 Mon Sep 17 00:00:00 2001 From: anime Date: Tue, 8 Jul 2025 01:41:50 +0800 Subject: [PATCH 03/19] feat(algolia): implement global client initialization and reuse - Add global Algolia client and index initialization - Initialize client once and reuse across functions - Remove redundant client initialization in individual functions - Add configuration validation during initialization --- lib/plugins/algolia.js | 46 ++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/plugins/algolia.js b/lib/plugins/algolia.js index f3485ab7..439451f1 100644 --- a/lib/plugins/algolia.js +++ b/lib/plugins/algolia.js @@ -2,6 +2,31 @@ import BLOG from '@/blog.config' import { getPageContentText } from '@/pages/search/[keyword]' import algoliasearch from 'algoliasearch' +// 全局初始化 Algolia 客户端和索引 +let algoliaClient +let algoliaIndex + +const initAlgolia = () => { + if (!algoliaClient) { + if ( + !BLOG.ALGOLIA_APP_ID || + !BLOG.ALGOLIA_ADMIN_APP_KEY || + !BLOG.ALGOLIA_INDEX + ) { + throw new Error('Algolia configuration is missing') + } + algoliaClient = algoliasearch( + BLOG.ALGOLIA_APP_ID, + BLOG.ALGOLIA_ADMIN_APP_KEY + ) + algoliaIndex = algoliaClient.initIndex(BLOG.ALGOLIA_INDEX) + } + return { client: algoliaClient, index: algoliaIndex } +} + +// 初始化全局实例 +initAlgolia() + /** * 生成全文索引 * @param {*} allPages @@ -15,7 +40,6 @@ const generateAlgoliaSearch = ({ allPages, force = false }) => { }) } - /** * 检查数据是否需要从algolia删除 * @param {*} props @@ -37,12 +61,6 @@ export const checkDataFromAlgolia = async props => { * @param post */ const deletePostDataFromAlgolia = async post => { - // Connect and authenticate with your Algolia app - const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY) - - // Create a new index and add a record - const index = client.initIndex(BLOG.ALGOLIA_INDEX) - if (!post) { return } @@ -50,13 +68,13 @@ const deletePostDataFromAlgolia = async post => { // 检查是否有索引 let existed try { - existed = await index.getObject(post.id) + existed = await algoliaIndex.getObject(post.id) } catch (error) { // 通常是不存在索引 } if (existed) { - await index + await algoliaIndex .deleteObject(post.id) .wait() .then(r => { @@ -73,12 +91,6 @@ const deletePostDataFromAlgolia = async post => { * 根据上次修改文章日期和上次更新索引数据判断是否需要更新algolia索引 */ const uploadDataToAlgolia = async post => { - // Connect and authenticate with your Algolia app - const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY) - - // Create a new index and add a record - const index = client.initIndex(BLOG.ALGOLIA_INDEX) - if (!post) { return } @@ -87,7 +99,7 @@ const uploadDataToAlgolia = async post => { let existed let needUpdateIndex = false try { - existed = await index.getObject(post.id) + existed = await algoliaIndex.getObject(post.id) } catch (error) { // 通常是不存在索引 } @@ -117,7 +129,7 @@ const uploadDataToAlgolia = async post => { content: truncate(getPageContentText(post, post.blockMap), 8192) // 索引8192个字符,API限制总请求内容上限1万个字节 } // console.log('更新Algolia索引', record) - index + algoliaIndex .saveObject(record) .wait() .then(r => { From 93df743559a95f0bf9599fb2d94f8c4b5d9bd248 Mon Sep 17 00:00:00 2001 From: anime Date: Tue, 8 Jul 2025 15:37:59 +0800 Subject: [PATCH 04/19] feat(algolia): replace error throw with console.error for missing config Change error handling from throwing an exception to logging an error when Algolia configuration is missing. This provides better error reporting without breaking the application flow. --- lib/plugins/algolia.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/algolia.js b/lib/plugins/algolia.js index 439451f1..2421d142 100644 --- a/lib/plugins/algolia.js +++ b/lib/plugins/algolia.js @@ -13,7 +13,7 @@ const initAlgolia = () => { !BLOG.ALGOLIA_ADMIN_APP_KEY || !BLOG.ALGOLIA_INDEX ) { - throw new Error('Algolia configuration is missing') + console.error('Algolia configuration is missing') } algoliaClient = algoliasearch( BLOG.ALGOLIA_APP_ID, From 645edd7eaddf060707ee064ced931b0437848ed4 Mon Sep 17 00:00:00 2001 From: anime Date: Tue, 8 Jul 2025 22:59:08 +0800 Subject: [PATCH 05/19] feat: enhance mailto/tel link handling and streamline imports - Introduced isMailOrTelLink utility function to validate mailto/tel links. - Updated getPageProperties to properly handle mailto/tel links with target=_self, preventing unintended conversion to internal links by avoiding the addition of slashes. Closes #3485 --- lib/notion/getPageProperties.js | 12 ++++++------ lib/utils/index.js | 9 +++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/notion/getPageProperties.js b/lib/notion/getPageProperties.js index 571c1b8c..963607d1 100644 --- a/lib/notion/getPageProperties.js +++ b/lib/notion/getPageProperties.js @@ -4,11 +4,7 @@ import formatDate from '../utils/formatDate' // import { createHash } from 'crypto' import md5 from 'js-md5' import { siteConfig } from '../config' -import { - checkStartWithHttp, - convertUrlStartWithOneSlash, - getLastSegmentFromUrl -} from '../utils' +import { checkStartWithHttp, convertUrlStartWithOneSlash, getLastSegmentFromUrl, isMailOrTelLink } from '../utils' import { extractLangPrefix } from '../utils/pageId' import { mapImgUrl } from './mapImage' import notionAPI from '@/lib/notion/getNotionAPI' @@ -85,8 +81,9 @@ export default async function getPageProperties( const fieldNames = BLOG.NOTION_PROPERTY_NAME if (fieldNames) { Object.keys(fieldNames).forEach(key => { - if (fieldNames[key] && properties[fieldNames[key]]) + if (fieldNames[key] && properties[fieldNames[key]]) { properties[key] = properties[fieldNames[key]] + } }) } @@ -193,6 +190,9 @@ export function adjustPageProperties(properties, NOTION_CONFIG) { if (checkStartWithHttp(properties?.href)) { properties.href = properties?.slug properties.target = '_blank' + } else if (isMailOrTelLink(properties?.href)) { + properties.href = properties?.slug + properties.target = '_self' } else { properties.target = '_self' // 伪静态路径右侧拼接.html diff --git a/lib/utils/index.js b/lib/utils/index.js index e12f2f85..fe49c942 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -104,6 +104,15 @@ export function checkStartWithHttp(str) { } } +/** + * 检查是否是邮件或电话链接 + * @param href + * @returns {boolean} + */ +export function isMailOrTelLink(href) { + return /^(mailto:|tel:)/i.test(href) +} + // 检查一个字符串是否UUID https://ihateregex.io/expr/uuid/ export function checkStrIsUuid(str) { if (!str) { From b16512ac9aff156760a11c40a9e4f0f616d367cd Mon Sep 17 00:00:00 2001 From: anime Date: Tue, 8 Jul 2025 23:04:41 +0800 Subject: [PATCH 06/19] refactor: rename checkStartWithHttp to isHttpLink - Update function name and implementation in utils/index.js - Replace all occurrences in getPageProperties.js, post.js, and BlogPostCard.js - Improve function documentation and use regex for better URL matching --- lib/notion/getPageProperties.js | 6 +++--- lib/utils/index.js | 19 ++++++++----------- lib/utils/post.js | 8 ++++---- themes/nav/components/BlogPostCard.js | 4 ++-- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/lib/notion/getPageProperties.js b/lib/notion/getPageProperties.js index 963607d1..29bb5472 100644 --- a/lib/notion/getPageProperties.js +++ b/lib/notion/getPageProperties.js @@ -4,7 +4,7 @@ import formatDate from '../utils/formatDate' // import { createHash } from 'crypto' import md5 from 'js-md5' import { siteConfig } from '../config' -import { checkStartWithHttp, convertUrlStartWithOneSlash, getLastSegmentFromUrl, isMailOrTelLink } from '../utils' +import { convertUrlStartWithOneSlash, getLastSegmentFromUrl, isHttpLink, isMailOrTelLink } from '../utils' import { extractLangPrefix } from '../utils/pageId' import { mapImgUrl } from './mapImage' import notionAPI from '@/lib/notion/getNotionAPI' @@ -187,7 +187,7 @@ export function adjustPageProperties(properties, NOTION_CONFIG) { } // http or https 开头的视为外链 - if (checkStartWithHttp(properties?.href)) { + if (isHttpLink(properties?.href)) { properties.href = properties?.slug properties.target = '_blank' } else if (isMailOrTelLink(properties?.href)) { @@ -238,7 +238,7 @@ export function adjustPageProperties(properties, NOTION_CONFIG) { */ function generateCustomizeSlug(postProperties, NOTION_CONFIG) { // 外链不处理 - if (checkStartWithHttp(postProperties.slug)) { + if (isHttpLink(postProperties.slug)) { return postProperties.slug } let fullPrefix = '' diff --git a/lib/utils/index.js b/lib/utils/index.js index fe49c942..bfef0f46 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -89,19 +89,16 @@ export function isUrl(str) { return false } - return str?.indexOf('/') === 0 || checkStartWithHttp(str) + return str?.indexOf('/') === 0 || isHttpLink(str) } -// 检查是否外链 -export function checkStartWithHttp(str) { - // 检查字符串是否包含http - if (str?.indexOf('http:') === 0 || str?.indexOf('https:') === 0) { - // 如果包含,找到http的位置 - return true - } else { - // 不包含 - return false - } +/** + * 判断是否是 http(s) 开头的链接(外部网页) + * @param str + * @returns {boolean} + */ +export function isHttpLink(str) { + return typeof str === 'string' && /^https?:\/\//i.test(str) } /** diff --git a/lib/utils/post.js b/lib/utils/post.js index a3ee0f91..5cb769f2 100644 --- a/lib/utils/post.js +++ b/lib/utils/post.js @@ -1,7 +1,7 @@ /** * 文章相关工具 */ -import { checkStartWithHttp } from '.' +import { isHttpLink } from '.' import { getPostBlocks } from '@/lib/db/getSiteData' import { getPageTableOfContents } from '@/lib/notion/getPageTableOfContents' import { siteConfig } from '@/lib/config' @@ -59,7 +59,7 @@ export function checkSlugHasNoSlash(row) { } return ( (slug.match(/\//g) || []).length === 0 && - !checkStartWithHttp(slug) && + !isHttpLink(slug) && row.type.indexOf('Menu') < 0 ) } @@ -76,7 +76,7 @@ export function checkSlugHasOneSlash(row) { } return ( (slug.match(/\//g) || []).length === 1 && - !checkStartWithHttp(slug) && + !isHttpLink(slug) && row.type.indexOf('Menu') < 0 ) } @@ -94,7 +94,7 @@ export function checkSlugHasMorThanTwoSlash(row) { return ( (slug.match(/\//g) || []).length >= 2 && row.type.indexOf('Menu') < 0 && - !checkStartWithHttp(slug) + !isHttpLink(slug) ) } diff --git a/themes/nav/components/BlogPostCard.js b/themes/nav/components/BlogPostCard.js index 0f92a30a..cdff4d55 100755 --- a/themes/nav/components/BlogPostCard.js +++ b/themes/nav/components/BlogPostCard.js @@ -1,5 +1,5 @@ import { siteConfig } from '@/lib/config' -import { checkStartWithHttp } from '@/lib/utils' +import { isHttpLink } from '@/lib/utils' import Link from 'next/link' import { useRouter } from 'next/router' import NotionIcon from './NotionIcon' @@ -23,7 +23,7 @@ const BlogPostCard = ({ post, className }) => { return (
Date: Tue, 8 Jul 2025 23:06:07 +0800 Subject: [PATCH 07/19] refactor(utils): rename and improve isUrl to isUrlLikePath - Rename isUrl function to isUrlLikePath for better clarity - Simplify function logic and add type checking - Update all references to use the new function name - Improve function documentation and parameter typing --- lib/config.js | 4 ++-- lib/utils/index.js | 15 ++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/config.js b/lib/config.js index 54b043a7..81ff88e3 100644 --- a/lib/config.js +++ b/lib/config.js @@ -2,7 +2,7 @@ import BLOG from '@/blog.config' import { useGlobal } from './global' -import { deepClone, isUrl } from './utils' +import { deepClone, isUrlLikePath } from './utils' /** * 读取配置顺序 @@ -146,7 +146,7 @@ export const convertVal = val => { } // 检测是否为 URL - if (isUrl(val)) { + if (isUrlLikePath(val)) { return val } diff --git a/lib/utils/index.js b/lib/utils/index.js index bfef0f46..b0e0c77e 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -80,16 +80,13 @@ export function convertUrlStartWithOneSlash(str) { } /** - * 是否是一个相对或绝对路径的ur类 - * @param {*} str - * @returns + * 判断是否是一个“URL 样式”的路径(内部或外部) + * 内部如 /about,外部如 http://xxx.com + * @param str + * @returns {boolean} */ -export function isUrl(str) { - if (!str) { - return false - } - - return str?.indexOf('/') === 0 || isHttpLink(str) +export function isUrlLikePath(str) { + return typeof str === 'string' && (str.startsWith('/') || isHttpLink(str)) } /** From c6b3d0a10838ccb723552ec955499bbbc888bef8 Mon Sep 17 00:00:00 2001 From: yucui Date: Fri, 11 Jul 2025 13:38:58 +0800 Subject: [PATCH 08/19] =?UTF-8?q?Feat=20typography=20=E5=8D=9A=E5=AE=A2?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E6=A0=87=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- themes/typography/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/typography/config.js b/themes/typography/config.js index c496beb4..1f7d0957 100644 --- a/themes/typography/config.js +++ b/themes/typography/config.js @@ -1,7 +1,7 @@ const CONFIG = { // 博客標題 雙語言 TYPOGRAPHY_BLOG_NAME: process.env.NEXT_PUBLIC_TYPOGRAPHY_BLOG_NAME || '活字印刷', - TYPOGRAPHY_BLOG_NAME_EN: process.env.NEXT_PUBLIC_TYPOGRAPHY_BLOG_NAME || 'Typography', + TYPOGRAPHY_BLOG_NAME_EN: process.env.NEXT_PUBLIC_TYPOGRAPHY_BLOG_NAME_EN || process.env.NEXT_PUBLIC_TYPOGRAPHY_BLOG_NAME || 'Typography', TYPOGRAPHY_POST_AD_ENABLE: process.env.NEXT_PUBLIC_TYPOGRAPHY_POST_AD_ENABLE || false, // 文章列表是否插入广告 From b2c9ed50148e19898609a83f922f01d2583ee154 Mon Sep 17 00:00:00 2001 From: tangly1024 Date: Fri, 11 Jul 2025 17:29:38 +0800 Subject: [PATCH 09/19] =?UTF-8?q?starter=E4=B8=BB=E9=A2=98=E6=96=B0?= =?UTF-8?q?=E5=A2=9Elite=E6=A8=A1=E5=BC=8F=EF=BC=8C=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E9=A1=B5=E5=A4=B4=E9=A1=B5=E8=84=9A=E4=BE=BF=E4=BA=8E=E5=B5=8C?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/global.js | 5 ++++ themes/starter/index.js | 59 ++++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/lib/global.js b/lib/global.js index b5d76d83..5039b9f2 100644 --- a/lib/global.js +++ b/lib/global.js @@ -31,6 +31,7 @@ export function GlobalContextProvider(props) { ) // 默认语言 const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || THEME) // 默认博客主题 const [THEME_CONFIG, SET_THEME_CONFIG] = useState(null) // 主题配置 + const [isLiteMode,setLiteMode] = useState(false) const defaultDarkMode = NOTION_CONFIG?.APPEARANCE || APPEARANCE const [isDarkMode, updateDarkMode] = useState(defaultDarkMode === 'dark') // 默认深色模式 @@ -107,6 +108,9 @@ export function GlobalContextProvider(props) { const newUrl = `${url}${url.includes('?') ? '&' : '?'}theme=${themeStr}` router.push(newUrl) } + if (router.query.lite && router.query.lite==='true') { + setLiteMode(true) + } if (!onLoading) { setOnLoading(true) } @@ -134,6 +138,7 @@ export function GlobalContextProvider(props) { return ( { - const { children } = props + const { children } = props + // 极简模式,会隐藏掉页头页脚等组件,便于嵌入网页等功能 + const { isLiteMode } = useGlobal() + const router = useRouter() - // 加载wow动画 - useEffect(() => { - loadWowJS() - }, []) + // 加载wow动画 + useEffect(() => { + loadWowJS() + }, []) - return ( -
-