From 3990b5ba37db3ee4938b6623239bd19d49401b58 Mon Sep 17 00:00:00 2001 From: anime Date: Thu, 2 Jan 2025 18:21:14 +0800 Subject: [PATCH 01/25] =?UTF-8?q?feat(=E6=94=AF=E6=8C=81=E6=9B=B4=E5=8A=A0?= =?UTF-8?q?=E7=AE=80=E5=8D=95=E7=9A=84=E7=9A=84=E7=BC=93=E5=AD=98=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=AF=BB=E5=8F=96=E5=92=8C=E5=86=99=E5=85=A5=E6=96=B9?= =?UTF-8?q?=E5=BC=8F):=20=E5=B0=9D=E8=AF=95=E4=BB=8E=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E4=B8=AD=E8=8E=B7=E5=8F=96=E6=95=B0=E6=8D=AE=EF=BC=8C=E5=A6=82?= =?UTF-8?q?=E6=9E=9C=E6=B2=A1=E6=9C=89=E5=88=99=E5=B0=9D=E8=AF=95=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=95=B0=E6=8D=AE=E5=B9=B6=E5=86=99=E5=85=A5=E7=BC=93?= =?UTF-8?q?=E5=AD=98=EF=BC=8C=E6=9C=80=E7=BB=88=E8=BF=94=E5=9B=9E=E6=89=80?= =?UTF-8?q?=E9=9C=80=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cache/cache_manager.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index f35a1fe1..88dfb8a4 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -2,6 +2,40 @@ import BLOG from '@/blog.config' import FileCache from './local_file_cache' import MemoryCache from './memory_cache' +/** + * 尝试从缓存中获取数据,如果没有则尝试获取数据并写入缓存,最终返回所需数据 + * @param key + * @param getDataFunction + * @param getDataArgs + * @returns {Promise<*|null>} + */ +export async function getOrSetDataWithCache(key, getDataFunction, ...getDataArgs) { + return getOrSetDataWithCustomCache(key, null, getDataFunction, ...getDataArgs) +} + + +/** + * 尝试从缓存中获取数据,如果没有则尝试获取数据并自定义写入缓存,最终返回所需数据 + * @param key + * @param customCacheTime + * @param getDataFunction + * @param getDataArgs + * @returns {Promise<*|null>} + */ +export async function getOrSetDataWithCustomCache(key, customCacheTime, getDataFunction, ...getDataArgs) { + const dataFromCache = await getDataFromCache(key) + if (dataFromCache) { + console.log('[缓存-->>API]:', key) + return dataFromCache + } + const data = await getDataFunction(...getDataArgs) + if (data) { + console.log('[API-->>缓存]:', key) + await setDataToCache(key, data, customCacheTime) + } + return data || null +} + /** * 为减少频繁接口请求,notion数据将被缓存 * @param {*} key From 3e1e4b0c1783abb7f4dc66f60ac846dacb15da03 Mon Sep 17 00:00:00 2001 From: anime Date: Thu, 2 Jan 2025 18:31:57 +0800 Subject: [PATCH 02/25] =?UTF-8?q?perf(SiteData=E7=AB=99=E7=82=B9=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=20=E8=BD=AC=E6=8D=A2=E4=BD=BF=E7=94=A8=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E4=BC=98=E5=8C=96):=20=E5=A4=8D=E6=9D=82=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=BD=AC=E6=8D=A2=E5=87=BD=E6=95=B0=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=BC=93=E5=AD=98=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/db/getSiteData.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/db/getSiteData.js b/lib/db/getSiteData.js index f5e926c6..b898c32b 100755 --- a/lib/db/getSiteData.js +++ b/lib/db/getSiteData.js @@ -12,6 +12,7 @@ import { deepClone } from '@/lib/utils' import { idToUuid } from 'notion-utils' import { siteConfig } from '../config' import { extractLangId, extractLangPrefix, getShortId } from '../utils/pageId' +import { getOrSetDataWithCache } from '@/lib/cache/cache_manager' export { getAllTags } from '../notion/getAllTags' export { getPost } from '../notion/getNotionPost' @@ -66,13 +67,13 @@ export async function getGlobalData({ export async function getSiteDataByPageId({ pageId, from }) { // 获取NOTION原始数据,此接支持mem缓存。 const pageRecordMap = await getPage(pageId, from) - // 将Notion数据按规则转成站点数据 - const data = await converNotionToSiteDate( + return await getOrSetDataWithCache( + `site_data_${pageId}`, + converNotionToSiteDate, pageId, from, deepClone(pageRecordMap) ) - return data } /** @@ -264,11 +265,12 @@ async function converNotionToSiteDate(pageId, from, pageRecordMap) { categoryOptions: getCategoryOptions(schema) }) // 所有标签 - const tagOptions = getAllTags({ - allPages, - tagOptions: getTagOptions(schema), - NOTION_CONFIG - }) + const tagOptions = + getAllTags({ + allPages, + tagOptions: getTagOptions(schema), + NOTION_CONFIG + }) || null // 旧的菜单 const customNav = getCustomNav({ allPages: collectionData.filter( From 2766cb8d3b26dc162fbb19642a425171f6d7abb0 Mon Sep 17 00:00:00 2001 From: anime Date: Thu, 2 Jan 2025 18:36:14 +0800 Subject: [PATCH 03/25] =?UTF-8?q?perf(PostBlocks=20=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=BC=93=E5=AD=98=E4=BC=98=E5=8C=96):=20?= =?UTF-8?q?=E5=A4=8D=E6=9D=82=E7=9A=84=E6=95=B0=E6=8D=AE=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=BD=BF=E7=94=A8=E7=BC=93=E5=AD=98=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/notion/getPostBlocks.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/notion/getPostBlocks.js b/lib/notion/getPostBlocks.js index d5896ddc..0ab2270c 100644 --- a/lib/notion/getPostBlocks.js +++ b/lib/notion/getPostBlocks.js @@ -1,6 +1,9 @@ import BLOG from '@/blog.config' -import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' -import { NotionAPI } from 'notion-client' +import { + getDataFromCache, + getOrSetDataWithCache, + setDataToCache +} from '@/lib/cache/cache_manager' import { deepClone, delay } from '../utils' import getNotionAPI from '@/lib/notion/getNotionAPI' @@ -16,7 +19,13 @@ export async function getPage(id, from = null, slice) { let pageBlock = await getDataFromCache(cacheKey) if (pageBlock) { // console.debug('[API<<--缓存]', `from:${from}`, cacheKey) - return convertNotionBlocksToPost(id, pageBlock, slice) + return await getOrSetDataWithCache( + `page_converted_block_${id}_${slice}`, + convertNotionBlocksToPost, + id, + pageBlock, + slice + ) } // 抓取最新数据 @@ -24,7 +33,13 @@ export async function getPage(id, from = null, slice) { if (pageBlock) { await setDataToCache(cacheKey, pageBlock) - return convertNotionBlocksToPost(id, pageBlock, slice) + return await getOrSetDataWithCache( + `page_converted_block_${id}_${slice}`, + convertNotionBlocksToPost, + id, + pageBlock, + slice + ) } return pageBlock } From b563744e2404ac3103cdc1c6b69d3b8c0fc38126 Mon Sep 17 00:00:00 2001 From: anime Date: Thu, 2 Jan 2025 20:45:18 +0800 Subject: [PATCH 04/25] =?UTF-8?q?perf(=E4=BD=BF=E7=94=A8=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E4=BC=98=E5=8C=96):=20=E5=A4=8D=E6=9D=82=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=BD=AC=E6=8D=A2=E5=87=BD=E6=95=B0=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/db/getSiteData.js | 9 ++++--- lib/notion/getPostBlocks.js | 51 +++++++++++++++---------------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/lib/db/getSiteData.js b/lib/db/getSiteData.js index b898c32b..5dfd9717 100755 --- a/lib/db/getSiteData.js +++ b/lib/db/getSiteData.js @@ -66,13 +66,14 @@ export async function getGlobalData({ */ export async function getSiteDataByPageId({ pageId, from }) { // 获取NOTION原始数据,此接支持mem缓存。 - const pageRecordMap = await getPage(pageId, from) return await getOrSetDataWithCache( `site_data_${pageId}`, - converNotionToSiteDate, + async (pageId, from) => { + const pageRecordMap = await getPage(pageId, from) + return converNotionToSiteDate(pageId, from, deepClone(pageRecordMap)) + }, pageId, - from, - deepClone(pageRecordMap) + from ) } diff --git a/lib/notion/getPostBlocks.js b/lib/notion/getPostBlocks.js index 0ab2270c..9cd03345 100644 --- a/lib/notion/getPostBlocks.js +++ b/lib/notion/getPostBlocks.js @@ -1,9 +1,5 @@ import BLOG from '@/blog.config' -import { - getDataFromCache, - getOrSetDataWithCache, - setDataToCache -} from '@/lib/cache/cache_manager' +import { getDataFromCache, getOrSetDataWithCache, setDataToCache } from '@/lib/cache/cache_manager' import { deepClone, delay } from '../utils' import getNotionAPI from '@/lib/notion/getNotionAPI' @@ -15,33 +11,28 @@ import getNotionAPI from '@/lib/notion/getNotionAPI' * @returns */ export async function getPage(id, from = null, slice) { - const cacheKey = `page_block_${id}` - let pageBlock = await getDataFromCache(cacheKey) - if (pageBlock) { - // console.debug('[API<<--缓存]', `from:${from}`, cacheKey) - return await getOrSetDataWithCache( - `page_converted_block_${id}_${slice}`, - convertNotionBlocksToPost, - id, - pageBlock, - slice - ) - } + return await getOrSetDataWithCache( + `page_content_${id}_${slice}`, + async (id, slice) => { + const cacheKey = `page_block_${id}` + let pageBlock = await getDataFromCache(cacheKey) + if (pageBlock) { + // console.debug('[API<<--缓存]', `from:${from}`, cacheKey) + return convertNotionBlocksToPost(id, pageBlock, slice) + } - // 抓取最新数据 - pageBlock = await getPageWithRetry(id, from) + // 抓取最新数据 + pageBlock = await getPageWithRetry(id, from) - if (pageBlock) { - await setDataToCache(cacheKey, pageBlock) - return await getOrSetDataWithCache( - `page_converted_block_${id}_${slice}`, - convertNotionBlocksToPost, - id, - pageBlock, - slice - ) - } - return pageBlock + if (pageBlock) { + await setDataToCache(cacheKey, pageBlock) + return convertNotionBlocksToPost(id, pageBlock, slice) + } + return pageBlock + }, + id, + slice + ) } /** From 5ae4680e4684998a5a3242ca7dadedefbc1e6221 Mon Sep 17 00:00:00 2001 From: anime Date: Thu, 2 Jan 2025 20:46:35 +0800 Subject: [PATCH 05/25] =?UTF-8?q?refactor(=E4=BF=AE=E5=A4=8D=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=9A=84=E5=87=BD=E6=95=B0=E5=91=BD=E5=90=8D):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/db/getSiteData.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/db/getSiteData.js b/lib/db/getSiteData.js index 5dfd9717..4abdb144 100755 --- a/lib/db/getSiteData.js +++ b/lib/db/getSiteData.js @@ -70,7 +70,7 @@ export async function getSiteDataByPageId({ pageId, from }) { `site_data_${pageId}`, async (pageId, from) => { const pageRecordMap = await getPage(pageId, from) - return converNotionToSiteDate(pageId, from, deepClone(pageRecordMap)) + return convertNotionToSiteDate(pageId, from, deepClone(pageRecordMap)) }, pageId, from @@ -141,7 +141,7 @@ const EmptyData = pageId => { * 这里统一对数据格式化 * @returns {Promise} */ -async function converNotionToSiteDate(pageId, from, pageRecordMap) { +async function convertNotionToSiteDate(pageId, from, pageRecordMap) { if (!pageRecordMap) { console.error('can`t get Notion Data ; Which id is: ', pageId) return {} From ebc5bd15bb79ca8cebec8ca7eeb8d84a384e52bf Mon Sep 17 00:00:00 2001 From: anime Date: Fri, 3 Jan 2025 00:34:25 +0800 Subject: [PATCH 06/25] =?UTF-8?q?feat(=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?UUID=E5=8F=8A=E5=85=B6=E5=8E=BB=E9=99=A4-=E5=BD=A2=E5=BC=8F?= =?UTF-8?q?=E9=87=8D=E5=AE=9A=E5=90=91=E5=88=B0slug):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/redirect.js | 15 +++++++++++++++ middleware.ts | 27 +++++++++++++++++++++++++-- pages/index.js | 3 +++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 lib/redirect.js diff --git a/lib/redirect.js b/lib/redirect.js new file mode 100644 index 00000000..7a05dfee --- /dev/null +++ b/lib/redirect.js @@ -0,0 +1,15 @@ +import fs from 'fs' + +export function generateRedirectJson({ allPages }) { + let uuidSlugMap = {} + allPages.forEach(page => { + if (page.type === 'Post' && page.status === 'Published') { + uuidSlugMap[page.id] = page.slug + } + }) + try { + fs.writeFileSync('./public/redirect.json', JSON.stringify(uuidSlugMap)) + } catch (error) { + console.warn('无法写入文件', error) + } +} diff --git a/middleware.ts b/middleware.ts index dc623d08..c60ee9cd 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,5 +1,7 @@ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' -import { NextResponse } from 'next/server' +import { NextRequest, NextResponse } from 'next/server' +import { checkStrIsNotionId, getLastPartOfUrl } from '@/lib/utils' +import { idToUuid } from 'notion-utils' /** * Clerk 身份验证中间件 @@ -30,8 +32,29 @@ const isTenantAdminRoute = createRouteMatcher([ * @returns */ // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars -const noAuthMiddleware = async (req: any, ev: any) => { +const noAuthMiddleware = async (req: NextRequest, ev: any) => { // 如果没有配置 Clerk 相关环境变量,返回一个默认响应或者继续处理请求 + let redirectJson: Record = {} + try { + const response = await fetch(`${req.nextUrl.origin}/redirect.json`) + if (response.ok) { + redirectJson = (await response.json()) as Record + } + } catch (err) { + console.error('Error fetching static file:', err) + } + let lastPart = getLastPartOfUrl(req.nextUrl.pathname) as string + if (checkStrIsNotionId(lastPart)) { + lastPart = idToUuid(lastPart) + } + if (lastPart && redirectJson[lastPart]) { + const redirectToUrl = req.nextUrl.clone() + redirectToUrl.pathname = '/' + redirectJson[lastPart] + console.log( + `redirect from ${req.nextUrl.pathname} to ${redirectToUrl.pathname}` + ) + return NextResponse.redirect(redirectToUrl) + } return NextResponse.next() } /** diff --git a/pages/index.js b/pages/index.js index 31e2610b..a83d42c8 100644 --- a/pages/index.js +++ b/pages/index.js @@ -5,6 +5,7 @@ import { generateRobotsTxt } from '@/lib/robots.txt' import { generateRss } from '@/lib/rss' import { generateSitemapXml } from '@/lib/sitemap.xml' import { DynamicLayout } from '@/themes/theme' +import { generateRedirectJson } from '@/lib/redirect' /** * 首页布局 @@ -60,6 +61,8 @@ export async function getStaticProps(req) { generateRss(props) // 生成 generateSitemapXml(props) + // 生成重定向 JSON + generateRedirectJson(props) // 生成全文索引 - 仅在 yarn build 时执行 && process.env.npm_lifecycle_event === 'build' From e405ab9dd70c913c4097ab312b83e2cadc048598 Mon Sep 17 00:00:00 2001 From: anime Date: Fri, 3 Jan 2025 00:41:12 +0800 Subject: [PATCH 07/25] =?UTF-8?q?fix(=E4=BF=AE=E5=A4=8D=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=88=B0=E5=80=BC=E7=9A=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- themes/theme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/theme.js b/themes/theme.js index 97fcd64a..3d89f878 100644 --- a/themes/theme.js +++ b/themes/theme.js @@ -6,7 +6,7 @@ import { useRouter } from 'next/router' import { getQueryParam, getQueryVariable, isBrowser } from '../lib/utils' // 在next.config.js中扫描所有主题 -export const { THEMES = [] } = getConfig().publicRuntimeConfig +export const { THEMES = [] } = getConfig()?.publicRuntimeConfig || {} /** * 获取主题配置 From daa47e520321d1471ca445cc686bf73ee77bf0a7 Mon Sep 17 00:00:00 2001 From: anime Date: Fri, 3 Jan 2025 00:48:53 +0800 Subject: [PATCH 08/25] =?UTF-8?q?feat(=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE?= =?UTF-8?q?UUID=E5=8F=8A=E5=85=B6=E5=8E=BB=E9=99=A4-=E5=BD=A2=E5=BC=8F?= =?UTF-8?q?=E9=87=8D=E5=AE=9A=E5=90=91=E5=88=B0slug=E5=8A=9F=E8=83=BD):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog.config.js | 12 +++++------- middleware.ts | 41 ++++++++++++++++++++++------------------- pages/index.js | 6 ++++-- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/blog.config.js b/blog.config.js index 24cf06ae..379e15d3 100644 --- a/blog.config.js +++ b/blog.config.js @@ -271,16 +271,11 @@ const BLOG = { STARRY_SKY: process.env.NEXT_PUBLIC_STARRY_SKY || false, // 开关 // AI 文章摘要生成 - AI_SUMMARY_API: - process.env.AI_SUMMARY_API|| - '', - AI_SUMMARY_KEY: - process.env.AI_SUMMARY_KEY || - '', + AI_SUMMARY_API: process.env.AI_SUMMARY_API || '', + AI_SUMMARY_KEY: process.env.AI_SUMMARY_KEY || '', AI_SUMMARY_CACHE_TIME: process.env.AI_SUMMARY_CACHE_TIME || 1800, // 缓存时间,单位秒 AI_SUMMARY_WORD_LIMIT: process.env.AI_SUMMARY_WORD_LIMIT || 1000, - // ********挂件组件相关******** // AI 文章摘要生成 @see https://docs_s.tianli0.top/ TianliGPT_CSS: @@ -532,6 +527,9 @@ const BLOG = { MAILCHIMP_LIST_ID: process.env.MAILCHIMP_LIST_ID || null, // 开启mailichimp邮件订阅 客户列表ID ,具体使用方法参阅文档 MAILCHIMP_API_KEY: process.env.MAILCHIMP_API_KEY || null, // 开启mailichimp邮件订阅 APIkey + // uuid重定向至 slug + UUID_REDIRECT: process.env.UUID_REDIRECT || false, + // ANIMATE.css 动画 ANIMATE_CSS_URL: process.env.NEXT_PUBLIC_ANIMATE_CSS_URL || diff --git a/middleware.ts b/middleware.ts index c60ee9cd..6c622069 100644 --- a/middleware.ts +++ b/middleware.ts @@ -2,6 +2,7 @@ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' import { NextRequest, NextResponse } from 'next/server' import { checkStrIsNotionId, getLastPartOfUrl } from '@/lib/utils' import { idToUuid } from 'notion-utils' +import { siteConfig } from '@/lib/config' /** * Clerk 身份验证中间件 @@ -34,26 +35,28 @@ const isTenantAdminRoute = createRouteMatcher([ // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars const noAuthMiddleware = async (req: NextRequest, ev: any) => { // 如果没有配置 Clerk 相关环境变量,返回一个默认响应或者继续处理请求 - let redirectJson: Record = {} - try { - const response = await fetch(`${req.nextUrl.origin}/redirect.json`) - if (response.ok) { - redirectJson = (await response.json()) as Record + if (siteConfig('UUID_REDIRECT')) { + let redirectJson: Record = {} + try { + const response = await fetch(`${req.nextUrl.origin}/redirect.json`) + if (response.ok) { + redirectJson = (await response.json()) as Record + } + } catch (err) { + console.error('Error fetching static file:', err) + } + let lastPart = getLastPartOfUrl(req.nextUrl.pathname) as string + if (checkStrIsNotionId(lastPart)) { + lastPart = idToUuid(lastPart) + } + if (lastPart && redirectJson[lastPart]) { + const redirectToUrl = req.nextUrl.clone() + redirectToUrl.pathname = '/' + redirectJson[lastPart] + console.log( + `redirect from ${req.nextUrl.pathname} to ${redirectToUrl.pathname}` + ) + return NextResponse.redirect(redirectToUrl) } - } catch (err) { - console.error('Error fetching static file:', err) - } - let lastPart = getLastPartOfUrl(req.nextUrl.pathname) as string - if (checkStrIsNotionId(lastPart)) { - lastPart = idToUuid(lastPart) - } - if (lastPart && redirectJson[lastPart]) { - const redirectToUrl = req.nextUrl.clone() - redirectToUrl.pathname = '/' + redirectJson[lastPart] - console.log( - `redirect from ${req.nextUrl.pathname} to ${redirectToUrl.pathname}` - ) - return NextResponse.redirect(redirectToUrl) } return NextResponse.next() } diff --git a/pages/index.js b/pages/index.js index a83d42c8..b66b5610 100644 --- a/pages/index.js +++ b/pages/index.js @@ -61,8 +61,10 @@ export async function getStaticProps(req) { generateRss(props) // 生成 generateSitemapXml(props) - // 生成重定向 JSON - generateRedirectJson(props) + if (siteConfig('UUID_REDIRECT', false, props?.NOTION_CONFIG)) { + // 生成重定向 JSON + generateRedirectJson(props) + } // 生成全文索引 - 仅在 yarn build 时执行 && process.env.npm_lifecycle_event === 'build' From 6e85af112ddfaa7a06e7003f419aae269a388104 Mon Sep 17 00:00:00 2001 From: anime Date: Fri, 3 Jan 2025 00:52:36 +0800 Subject: [PATCH 09/25] =?UTF-8?q?fix(=E4=BF=AE=E5=A4=8DUUID=5FREDIRECT=20S?= =?UTF-8?q?iteConfig=E4=BD=BF=E7=94=A8Context=E9=97=AE=E9=A2=98):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/config.js b/lib/config.js index 314e8318..3712d877 100644 --- a/lib/config.js +++ b/lib/config.js @@ -42,6 +42,7 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => { case 'AI_SUMMARY_KEY': case 'AI_SUMMARY_CACHE_TIME': case 'AI_SUMMARY_WORD_LIMIT': + case 'UUID_REDIRECT': // LINK比较特殊, if (key === 'LINK') { if (!extendConfig || Object.keys(extendConfig).length === 0) { From 8a2ec282d8d952fdc748c46a665070efb26fec49 Mon Sep 17 00:00:00 2001 From: anime Date: Fri, 3 Jan 2025 01:08:14 +0800 Subject: [PATCH 10/25] =?UTF-8?q?feat(=E4=B8=BA=E4=BA=86SEO=E9=87=8D?= =?UTF-8?q?=E5=AE=9A=E5=90=91=E7=B1=BB=E5=9E=8B=E8=AE=BE=E7=BD=AE=E4=B8=BA?= =?UTF-8?q?308=E6=B0=B8=E4=B9=85=E9=87=8D=E5=AE=9A=E5=90=91):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware.ts b/middleware.ts index 6c622069..c099e62a 100644 --- a/middleware.ts +++ b/middleware.ts @@ -55,7 +55,7 @@ const noAuthMiddleware = async (req: NextRequest, ev: any) => { console.log( `redirect from ${req.nextUrl.pathname} to ${redirectToUrl.pathname}` ) - return NextResponse.redirect(redirectToUrl) + return NextResponse.redirect(redirectToUrl, 308) } } return NextResponse.next() From 704a4ebeb1b25ec2725909564d435ad4fac5bbf4 Mon Sep 17 00:00:00 2001 From: anime Date: Fri, 3 Jan 2025 02:40:40 +0800 Subject: [PATCH 11/25] =?UTF-8?q?fix(=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8s?= =?UTF-8?q?iteconfig=E6=94=B9=E4=B8=BA=E4=BD=BF=E7=94=A8Blog=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/middleware.ts b/middleware.ts index c099e62a..550c6f8b 100644 --- a/middleware.ts +++ b/middleware.ts @@ -2,7 +2,7 @@ import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' import { NextRequest, NextResponse } from 'next/server' import { checkStrIsNotionId, getLastPartOfUrl } from '@/lib/utils' import { idToUuid } from 'notion-utils' -import { siteConfig } from '@/lib/config' +import BLOG from './blog.config' /** * Clerk 身份验证中间件 @@ -35,7 +35,7 @@ const isTenantAdminRoute = createRouteMatcher([ // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars const noAuthMiddleware = async (req: NextRequest, ev: any) => { // 如果没有配置 Clerk 相关环境变量,返回一个默认响应或者继续处理请求 - if (siteConfig('UUID_REDIRECT')) { + if (BLOG['UUID_REDIRECT']) { let redirectJson: Record = {} try { const response = await fetch(`${req.nextUrl.origin}/redirect.json`) From d6dee96aeb8268a1294fffb6c1397bf250b3a6b1 Mon Sep 17 00:00:00 2001 From: CBE <103420806+Hyphen-H@users.noreply.github.com> Date: Thu, 2 Jan 2025 22:27:39 +0300 Subject: [PATCH 12/25] Update spoiler-text.css --- public/css/spoiler-text.css | 47 ++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/public/css/spoiler-text.css b/public/css/spoiler-text.css index 1dcd045a..ba68870b 100644 --- a/public/css/spoiler-text.css +++ b/public/css/spoiler-text.css @@ -1,15 +1,19 @@ -/* Spoiler text styles */ +/* Spoiler text styles with sharp edges */ .spoiler-text { color: transparent; /* 文字透明 */ - background-color: #808080; /* 背景为黑色 */ - border-color: #808080; - text-decoration-color: #808080; - text-emphasis-color: #808080; - border-radius: 8px; - filter: blur(1px); /* 初始模糊 */ + background-color: #000000; /* 背景为黑色 */ + border-color: #000000; /* 边框颜色 */ + text-decoration-color: #000000; /* 删除线颜色 */ + text-emphasis-color: #000000; /* 强调文字颜色 */ + filter: none; /* 移除模糊效果 */ --hide-transition: 0.3s ease-out; - transition: opacity var(--hide-transition), - filter var(--hide-transition); + transition: + color var(--hide-transition), + background-color var(--hide-transition), + border-color var(--hide-transition), + text-decoration-color var(--hide-transition), + text-emphasis-color var(--hide-transition), + opacity 0.35s cubic-bezier(.25,.46,.45,.94); /* 平滑过渡 */ } .spoiler-text:hover { @@ -19,5 +23,26 @@ text-decoration-color: inherit; text-emphasis-color: inherit; opacity: 1; /* 鼠标悬停时恢复不透明度 */ - filter: blur(0); /* 鼠标悬停时解除模糊 */ -} \ No newline at end of file +} + +/* Spoiler child elements with transition */ +.spoiler-text * { + transition: opacity 0.35s cubic-bezier(.25,.46,.45,.94); /* 子元素透明度平滑过渡 */ +} + +/* Spoiler when not hovered */ +.spoiler-text:not(:hover) { + color: transparent!important; /* 非悬停时文字透明 */ + background-color: #000000!important; /* 非悬停时背景为黑色 */ + border-color: #000000!important; /* 非悬停时边框为黑色 */ +} + +.spoiler-text:not(:hover) * { + opacity: 0!important; /* 非悬停时子元素透明 */ +} + +/* Remove border in non-hover states */ +.spoiler-text:not(:hover), +.spoiler-text:not(:hover) * { + border: none!important; +} From 019f5e470f2359e4ac146b496fb80c0fc7a4940d Mon Sep 17 00:00:00 2001 From: anime Date: Sat, 4 Jan 2025 01:00:49 +0800 Subject: [PATCH 13/25] =?UTF-8?q?feat(=E4=BD=BF=E7=94=A8=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E7=9A=84=20notionAPI):=20=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E5=A4=9A=E6=AC=A1=E5=88=9B=E5=BB=BANotionAPI=EF=BC=8C=E6=9C=89?= =?UTF-8?q?=E6=95=88=E5=88=A9=E7=94=A8=E5=86=85=E7=BD=AE=E7=9A=84concurren?= =?UTF-8?q?cy=E5=B9=B6=E5=8F=91=E8=AF=B7=E6=B1=82=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/notion/getNotionAPI.js | 10 +++++++--- lib/notion/getPageProperties.js | 6 ++---- lib/notion/getPostBlocks.js | 10 +++------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/notion/getNotionAPI.js b/lib/notion/getNotionAPI.js index c2cbb6a6..757534a3 100644 --- a/lib/notion/getNotionAPI.js +++ b/lib/notion/getNotionAPI.js @@ -1,10 +1,14 @@ -import { NotionAPI } from 'notion-client' +import { NotionAPI as NotionLibrary } from 'notion-client' import BLOG from '@/blog.config' -export default function getNotionAPI() { - return new NotionAPI({ +const notionAPI = getNotionAPI() + +function getNotionAPI() { + return new NotionLibrary({ activeUser: BLOG.NOTION_ACTIVE_USER || null, authToken: BLOG.NOTION_TOKEN_V2 || null, userTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone }) } + +export default notionAPI diff --git a/lib/notion/getPageProperties.js b/lib/notion/getPageProperties.js index 5d5e7ff6..571c1b8c 100644 --- a/lib/notion/getPageProperties.js +++ b/lib/notion/getPageProperties.js @@ -1,5 +1,4 @@ import BLOG from '@/blog.config' -import { NotionAPI } from 'notion-client' import { getDateValue, getTextContent } from 'notion-utils' import formatDate from '../utils/formatDate' // import { createHash } from 'crypto' @@ -12,7 +11,7 @@ import { } from '../utils' import { extractLangPrefix } from '../utils/pageId' import { mapImgUrl } from './mapImage' -import getNotionAPI from '@/lib/notion/getNotionAPI' +import notionAPI from '@/lib/notion/getNotionAPI' /** * 获取页面元素成员属性 @@ -57,12 +56,11 @@ export default async function getPageProperties( case 'person': { const rawUsers = val.flat() const users = [] - const api = getNotionAPI() for (let i = 0; i < rawUsers.length; i++) { if (rawUsers[i][0][1]) { const userId = rawUsers[i][0] - const res = await api.getUsers(userId) + const res = await notionAPI.getUsers(userId) const resValue = res?.recordMapWithRoles?.notion_user?.[userId[1]]?.value const user = { diff --git a/lib/notion/getPostBlocks.js b/lib/notion/getPostBlocks.js index d5896ddc..16a36534 100644 --- a/lib/notion/getPostBlocks.js +++ b/lib/notion/getPostBlocks.js @@ -1,8 +1,7 @@ import BLOG from '@/blog.config' import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager' -import { NotionAPI } from 'notion-client' import { deepClone, delay } from '../utils' -import getNotionAPI from '@/lib/notion/getNotionAPI' +import notionAPI from '@/lib/notion/getNotionAPI' /** * 获取文章内容 @@ -43,9 +42,8 @@ export async function getPageWithRetry(id, from, retryAttempts = 3) { retryAttempts < 3 ? `剩余重试次数:${retryAttempts}` : '' ) try { - const api = getNotionAPI() const start = new Date().getTime() - const pageData = await api.getPage(id) + const pageData = await notionAPI.getPage(id) const end = new Date().getTime() console.log('[API<<--响应]', `耗时:${end - start}ms - from:${from}`) return pageData @@ -163,14 +161,12 @@ export const fetchInBatches = async (ids, batchSize = 100) => { ids = [ids] } - const api = getNotionAPI() - let fetchedBlocks = {} for (let i = 0; i < ids.length; i += batchSize) { const batch = ids.slice(i, i + batchSize) console.log('[API-->>请求] Fetching missing blocks', batch, ids.length) const start = new Date().getTime() - const pageChunk = await api.getBlocks(batch) + const pageChunk = await notionAPI.getBlocks(batch) const end = new Date().getTime() console.log( `[API<<--响应] 耗时:${end - start}ms Fetching missing blocks count:${ids.length} ` From 7ff6f4e7fe04127533e9d8fc6214b3ea89e1e1a7 Mon Sep 17 00:00:00 2001 From: Phillweston <2436559745@qq.com> Date: Sat, 4 Jan 2025 14:31:40 +0800 Subject: [PATCH 14/25] Change the spelling of the STARTER_CTA_DESCRIPTION --- themes/starter/components/CTA.js | 2 +- themes/starter/config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/themes/starter/components/CTA.js b/themes/starter/components/CTA.js index 643a0d48..2bf97e76 100644 --- a/themes/starter/components/CTA.js +++ b/themes/starter/components/CTA.js @@ -26,7 +26,7 @@ export const CTA = () => {

- {siteConfig('STARTER_CTA_DESCRIOTN')} + {siteConfig('STARTER_CTA_DESCRIPTION')}

{siteConfig('STARTER_CTA_BUTTON') && ( <> diff --git a/themes/starter/config.js b/themes/starter/config.js index 39ea4773..520e4d92 100644 --- a/themes/starter/config.js +++ b/themes/starter/config.js @@ -364,7 +364,7 @@ const CONFIG = { STARTER_CTA_ENABLE: true, STARTER_CTA_TITLE: '你还在等待什么呢?', STARTER_CTA_TITLE_2: '现在开始吧', - STARTER_CTA_DESCRIOTN: + STARTER_CTA_DESCRIPTION: '访问NotionNext的操作文档,我们提供了详细的教程,帮助你即刻搭建站点', STARTER_CTA_BUTTON: true, // 是否显示按钮 STARTER_CTA_BUTTON_URL: From b93944fe8fb25dcee303c84336874529347d3ab3 Mon Sep 17 00:00:00 2001 From: anime Date: Sat, 4 Jan 2025 14:58:17 +0800 Subject: [PATCH 15/25] =?UTF-8?q?perf(=E4=BC=98=E5=8C=96Vercel=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E7=BC=93=E5=AD=98=E8=A1=8C=E4=B8=BA):=20=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E5=9C=A8Vercel=E7=8E=AF=E5=A2=83=E4=B8=AD=E6=97=A0?= =?UTF-8?q?=E6=84=8F=E4=B9=89=E7=9A=84=E7=BC=93=E5=AD=98=E8=AF=BB=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cache/cache_manager.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index f35a1fe1..9430d684 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -2,13 +2,19 @@ import BLOG from '@/blog.config' import FileCache from './local_file_cache' import MemoryCache from './memory_cache' +// 配置是否开启Vercel环境中的缓存,因为Vercel中现有两种缓存方式在无服务环境下基本都是无意义的,纯粹的浪费资源 +const enableCacheInVercel = + process.env.npm_lifecycle_event === 'build' || + process.env.npm_lifecycle_event === 'export' || + !BLOG['isProd'] + /** * 为减少频繁接口请求,notion数据将被缓存 * @param {*} key * @returns */ export async function getDataFromCache(key, force) { - if (BLOG.ENABLE_CACHE || force) { + if (enableCacheInVercel || BLOG.ENABLE_CACHE || force) { const dataFromCache = await getApi().getCache(key) if (!dataFromCache || JSON.stringify(dataFromCache) === '[]') { return null @@ -21,7 +27,7 @@ export async function getDataFromCache(key, force) { } export async function setDataToCache(key, data, customCacheTime) { - if (!data) { + if (!enableCacheInVercel || !data) { return } // console.trace('[API-->>缓存写入]:', key) From 732c16d717d2e67b09275a01dd5a8600ccd11e81 Mon Sep 17 00:00:00 2001 From: anime Date: Sat, 4 Jan 2025 15:06:13 +0800 Subject: [PATCH 16/25] =?UTF-8?q?feat(=E4=BD=BF=E7=94=A8const=20=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=BC=95=E5=85=A5=E7=BC=93=E5=AD=98=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=B1=BB):=20=E9=81=BF=E5=85=8D=E5=A4=9A=E6=AC=A1=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cache/cache_manager.js | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index 9430d684..5317d798 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -15,7 +15,7 @@ const enableCacheInVercel = */ export async function getDataFromCache(key, force) { if (enableCacheInVercel || BLOG.ENABLE_CACHE || force) { - const dataFromCache = await getApi().getCache(key) + const dataFromCache = await cacheApi.getCache(key) if (!dataFromCache || JSON.stringify(dataFromCache) === '[]') { return null } @@ -31,24 +31,15 @@ export async function setDataToCache(key, data, customCacheTime) { return } // console.trace('[API-->>缓存写入]:', key) - await getApi().setCache(key, data, customCacheTime) + await cacheApi.setCache(key, data, customCacheTime) } export async function delCacheData(key) { if (!JSON.parse(BLOG.ENABLE_CACHE)) { return } - await getApi().delCache(key) + await cacheApi.delCache(key) } -/** - * 缓存实现类 - * @returns - */ -function getApi() { - if (process.env.ENABLE_FILE_CACHE) { - return FileCache - } else { - return MemoryCache - } -} +// 缓存实现类 +const cacheApi = process.env.ENABLE_FILE_CACHE ? FileCache : MemoryCache From c6014ddb864b844d0de4919204b39bac9ddc670f Mon Sep 17 00:00:00 2001 From: qi_xing_jk Date: Mon, 4 Mar 2024 21:12:43 +0800 Subject: [PATCH 17/25] =?UTF-8?q?feat(=E5=A2=9E=E5=BC=BA=E5=88=86=E4=BA=AB?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E7=9A=84tags=E5=8A=9F=E8=83=BD):=20=E7=BB=99?= =?UTF-8?q?Facebook/Twitter/Tumblr=E5=92=8CWorkplace=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=88=86=E4=BA=AB=E6=8C=89=E9=92=AE=E7=9A=84tags=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 1cde3dd8b9cfd2bc781ea03efb542e5f9132a1f8) --- components/ShareButtons.js | 90 ++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/components/ShareButtons.js b/components/ShareButtons.js index 5ea54055..5ad3bae5 100644 --- a/components/ShareButtons.js +++ b/components/ShareButtons.js @@ -5,48 +5,48 @@ import { useRouter } from 'next/router' import { useEffect, useState } from 'react' import { - EmailIcon, - EmailShareButton, - FacebookIcon, - FacebookMessengerIcon, - FacebookMessengerShareButton, - FacebookShareButton, - HatenaIcon, - HatenaShareButton, - InstapaperIcon, - InstapaperShareButton, - LineIcon, - LineShareButton, - LinkedinIcon, - LinkedinShareButton, - LivejournalIcon, - LivejournalShareButton, - MailruIcon, - MailruShareButton, - OKIcon, - OKShareButton, - PinterestIcon, - PinterestShareButton, - PocketIcon, - PocketShareButton, - RedditIcon, - RedditShareButton, - TelegramIcon, - TelegramShareButton, - TumblrIcon, - TumblrShareButton, - TwitterIcon, - TwitterShareButton, - VKIcon, - VKShareButton, - ViberIcon, - ViberShareButton, - WeiboIcon, - WeiboShareButton, - WhatsappIcon, - WhatsappShareButton, - WorkplaceIcon, - WorkplaceShareButton + EmailIcon, + EmailShareButton, + FacebookIcon, + FacebookMessengerIcon, + FacebookMessengerShareButton, + FacebookShareButton, + HatenaIcon, + HatenaShareButton, + InstapaperIcon, + InstapaperShareButton, + LineIcon, + LineShareButton, + LinkedinIcon, + LinkedinShareButton, + LivejournalIcon, + LivejournalShareButton, + MailruIcon, + MailruShareButton, + OKIcon, + OKShareButton, + PinterestIcon, + PinterestShareButton, + PocketIcon, + PocketShareButton, + RedditIcon, + RedditShareButton, + TelegramIcon, + TelegramShareButton, + TumblrIcon, + TumblrShareButton, + TwitterIcon, + TwitterShareButton, + ViberIcon, + ViberShareButton, + VKIcon, + VKShareButton, + WeiboIcon, + WeiboShareButton, + WhatsappIcon, + WhatsappShareButton, + WorkplaceIcon, + WorkplaceShareButton } from 'react-share' const QrCode = dynamic(() => import('@/components/QrCode'), { ssr: false }) @@ -61,6 +61,8 @@ const ShareButtons = ({ post }) => { const [shareUrl, setShareUrl] = useState(siteConfig('LINK') + router.asPath) const title = post?.title || siteConfig('TITLE') const image = post?.pageCover + const tags = post.tags || [] + const hashTags = tags.map(tag => `#${tag}`).join(',') const body = post?.title + ' | ' + title + ' ' + shareUrl + ' ' + post?.summary @@ -95,6 +97,7 @@ const ShareButtons = ({ post }) => { @@ -152,6 +155,7 @@ const ShareButtons = ({ post }) => { key={singleService} url={shareUrl} title={titleWithSiteInfo} + hashtags={tags} className='mx-1'> @@ -229,6 +233,7 @@ const ShareButtons = ({ post }) => { key={singleService} url={shareUrl} title={titleWithSiteInfo} + tags={tags} className='mx-1'> @@ -274,6 +279,7 @@ const ShareButtons = ({ post }) => { key={singleService} url={shareUrl} quote={titleWithSiteInfo} + hashtag={hashTags} className='mx-1'> From 7a2520d488382fbd3ea37a23acda529ef86ab641 Mon Sep 17 00:00:00 2001 From: anime Date: Sat, 4 Jan 2025 20:24:46 +0800 Subject: [PATCH 18/25] =?UTF-8?q?perf(=E4=BD=BF=E7=94=A8switch=20case?= =?UTF-8?q?=E8=80=8C=E9=9D=9E=E8=BF=9E=E7=BB=AD=E7=9A=84if):=20=E8=99=BD?= =?UTF-8?q?=E7=84=B6=E6=B2=A1=E5=95=A5=E5=A4=AA=E5=A4=A7=E7=9A=84=E9=80=9F?= =?UTF-8?q?=E5=BA=A6=E4=BC=98=E5=8C=96=EF=BC=8C=E4=BD=86=E6=98=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7=E6=9B=B4=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/ShareButtons.js | 569 ++++++++++++++++++------------------- 1 file changed, 274 insertions(+), 295 deletions(-) diff --git a/components/ShareButtons.js b/components/ShareButtons.js index 5ad3bae5..29fd37b4 100644 --- a/components/ShareButtons.js +++ b/components/ShareButtons.js @@ -92,303 +92,282 @@ const ShareButtons = ({ post }) => { return ( <> {services.map(singleService => { - if (singleService === 'facebook') { - return ( - - - - ) - } - if (singleService === 'messenger') { - return ( - - - - ) - } - if (singleService === 'line') { - return ( - - - - ) - } - if (singleService === 'reddit') { - return ( - - - - ) - } - if (singleService === 'email') { - return ( - - - - ) - } - if (singleService === 'twitter') { - return ( - - - - ) - } - if (singleService === 'telegram') { - return ( - - - - ) - } - if (singleService === 'whatsapp') { - return ( - - - - ) - } - if (singleService === 'linkedin') { - return ( - - - - ) - } - if (singleService === 'pinterest') { - return ( - - - - ) - } - if (singleService === 'vkshare') { - return ( - - - - ) - } - if (singleService === 'okshare') { - return ( - - - - ) - } - if (singleService === 'tumblr') { - return ( - - - - ) - } - if (singleService === 'livejournal') { - return ( - - - - ) - } - if (singleService === 'mailru') { - return ( - - - - ) - } - if (singleService === 'viber') { - return ( - - - - ) - } - if (singleService === 'workplace') { - return ( - - - - ) - } - if (singleService === 'weibo') { - return ( - - - - ) - } - if (singleService === 'pocket') { - return ( - - - - ) - } - if (singleService === 'instapaper') { - return ( - - - - ) - } - if (singleService === 'hatena') { - return ( - - - - ) - } - if (singleService === 'qq') { - return ( - - ) - } - if (singleService === 'wechat') { - return ( - + ) + case 'wechat': + return ( + - ) +
+
+
+ {qrCodeShow && } +
+ + {locale.COMMON.SCAN_QR_CODE} + +
+
+ + ) + case 'link': + return ( + + ) + default: + return <> } - if (singleService === 'link') { - return ( - - ) - } - return <> })} ) From 442dd87828e0740cb0626aa11c29aa70c2ac404f Mon Sep 17 00:00:00 2001 From: anime Date: Sun, 5 Jan 2025 01:13:04 +0800 Subject: [PATCH 19/25] =?UTF-8?q?build(=E6=B7=BB=E5=8A=A0=20ioredis):=20?= =?UTF-8?q?=E4=BE=9BRedis=E7=BC=93=E5=AD=98=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + yarn.lock | 77 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 85dff78c..0a64f52c 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "algoliasearch": "^4.18.0", "axios": "^1.7.2", "feed": "^4.2.2", + "ioredis": "^5.4.2", "js-md5": "^0.7.3", "lodash.throttle": "^4.1.1", "memory-cache": "^0.2.0", diff --git a/yarn.lock b/yarn.lock index 9ee525b4..74fd3138 100644 --- a/yarn.lock +++ b/yarn.lock @@ -299,6 +299,11 @@ resolved "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://mirrors.cloud.tencent.com/npm/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1238,6 +1243,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +cluster-key-slot@^1.1.0: + version "1.1.2" + resolved "https://mirrors.cloud.tencent.com/npm/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -1443,6 +1453,11 @@ delayed-stream@~1.0.0: resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +denque@^2.1.0: + version "2.1.0" + resolved "https://mirrors.cloud.tencent.com/npm/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -2404,6 +2419,21 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ioredis@^5.4.2: + version "5.4.2" + resolved "https://mirrors.cloud.tencent.com/npm/ioredis/-/ioredis-5.4.2.tgz#ebb6f1a10b825b2c0fb114763d7e82114a0bee6c" + integrity sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" @@ -2770,6 +2800,16 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://mirrors.cloud.tencent.com/npm/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://mirrors.cloud.tencent.com/npm/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -3604,6 +3644,18 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://mirrors.cloud.tencent.com/npm/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://mirrors.cloud.tencent.com/npm/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== + dependencies: + redis-errors "^1.0.0" + reflect.getprototypeof@^1.0.4, reflect.getprototypeof@^1.0.6: version "1.0.8" resolved "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz#c58afb17a4007b4d1118c07b92c23fca422c5d82" @@ -3917,6 +3969,11 @@ stacktrace-js@^2.0.2: stack-generator "^2.0.5" stacktrace-gps "^3.0.4" +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://mirrors.cloud.tencent.com/npm/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + std-env@^3.7.0: version "3.8.0" resolved "https://registry.npmmirror.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" @@ -3927,16 +3984,7 @@ streamsearch@^1.1.0: resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: version "4.2.3" resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4017,14 +4065,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== From ee7bbfc209932491820ad51517c27c5323f2fe81 Mon Sep 17 00:00:00 2001 From: anime Date: Sun, 5 Jan 2025 01:57:19 +0800 Subject: [PATCH 20/25] =?UTF-8?q?feat(=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90?= =?UTF-8?q?Redis=E7=BC=93=E5=AD=98=E5=8A=9F=E8=83=BD):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cache/cache_manager.js | 5 ++++- lib/cache/redis_cache.js | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 lib/cache/redis_cache.js diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index f35a1fe1..4f37cf71 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -1,6 +1,7 @@ import BLOG from '@/blog.config' import FileCache from './local_file_cache' import MemoryCache from './memory_cache' +import RedisCache from './redis_cache' /** * 为减少频繁接口请求,notion数据将被缓存 @@ -40,7 +41,9 @@ export async function delCacheData(key) { * @returns */ function getApi() { - if (process.env.ENABLE_FILE_CACHE) { + if (process.env.REDIS_URL) { + return RedisCache + } else if (process.env.ENABLE_FILE_CACHE) { return FileCache } else { return MemoryCache diff --git a/lib/cache/redis_cache.js b/lib/cache/redis_cache.js new file mode 100644 index 00000000..f4e24cde --- /dev/null +++ b/lib/cache/redis_cache.js @@ -0,0 +1,40 @@ +import Redis from 'ioredis' +import BLOG from '@/blog.config' +import { siteConfig } from '@/lib/config' + +const redisClient = new Redis(process.env.REDIS_URL) +const cacheTime = Math.trunc( + siteConfig('NEXT_REVALIDATE_SECOND', BLOG.NEXT_REVALIDATE_SECOND) * 1.5 +) + +export async function getCache(key) { + try { + const data = await redisClient.get(key) + return data ? JSON.parse(data) : null + } catch (e) { + console.error('redisClient读取失败 ' + e) + } +} + +export async function setCache(key, data, customCacheTime) { + try { + await redisClient.set( + key, + JSON.stringify(data), + 'EX', + customCacheTime || cacheTime + ) + } catch (e) { + console.error('redisClient写入失败 ' + e) + } +} + +export async function delCache(key) { + try { + await redisClient.del(key) + } catch (e) { + console.error('redisClient删除失败 ' + e) + } +} + +export default { getCache, setCache, delCache } From 3ce8e8a6815b8359456f9317dfbdb7f13543d134 Mon Sep 17 00:00:00 2001 From: anime Date: Sun, 5 Jan 2025 02:22:58 +0800 Subject: [PATCH 21/25] =?UTF-8?q?feat(=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE?= =?UTF-8?q?REDIS=5FURL):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog.config.js | 3 +++ lib/cache/cache_manager.js | 2 +- lib/cache/redis_cache.js | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/blog.config.js b/blog.config.js index 24cf06ae..36bfa021 100644 --- a/blog.config.js +++ b/blog.config.js @@ -553,6 +553,9 @@ const BLOG = { DESCRIPTION: process.env.NEXT_PUBLIC_DESCRIPTION || '这是一个由NotionNext生成的站点', // 站点描述,被notion中的页面描述覆盖 + // Redis 缓存数据库地址 + REDIS_URL: process.env.REDIS_URL || '', + // 开发相关 NOTION_ACTIVE_USER: process.env.NOTION_ACTIVE_USER || '', NOTION_TOKEN_V2: process.env.NOTION_TOKEN_V2 || '', // Useful if you prefer not to make your database public diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index 4f37cf71..83040474 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -41,7 +41,7 @@ export async function delCacheData(key) { * @returns */ function getApi() { - if (process.env.REDIS_URL) { + if (BLOG.REDIS_URL) { return RedisCache } else if (process.env.ENABLE_FILE_CACHE) { return FileCache diff --git a/lib/cache/redis_cache.js b/lib/cache/redis_cache.js index f4e24cde..0e9546b6 100644 --- a/lib/cache/redis_cache.js +++ b/lib/cache/redis_cache.js @@ -2,7 +2,7 @@ import Redis from 'ioredis' import BLOG from '@/blog.config' import { siteConfig } from '@/lib/config' -const redisClient = new Redis(process.env.REDIS_URL) +const redisClient = new Redis(BLOG.REDIS_URL) const cacheTime = Math.trunc( siteConfig('NEXT_REVALIDATE_SECOND', BLOG.NEXT_REVALIDATE_SECOND) * 1.5 ) From 93939cedf5bbee1525b227beaa8ef14f07b1686e Mon Sep 17 00:00:00 2001 From: tangly1024 Date: Sun, 5 Jan 2025 15:22:55 +0800 Subject: [PATCH 22/25] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20package.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4.8.1版本号 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 85dff78c..9d685349 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notion-next", - "version": "4.8.0", + "version": "4.8.1", "homepage": "https://github.com/tangly1024/NotionNext.git", "license": "MIT", "repository": { From ff565a1204bfde0c48480e684c0e6d7ba91309c7 Mon Sep 17 00:00:00 2001 From: anime Date: Sun, 5 Jan 2025 15:39:15 +0800 Subject: [PATCH 23/25] =?UTF-8?q?feat(=E5=AF=BC=E5=87=BARedisClient?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=EF=BC=8C=E4=BE=BF=E4=BA=8E=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E7=B1=BB=E5=9E=8B=E6=96=B9=E6=B3=95):?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cache/redis_cache.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cache/redis_cache.js b/lib/cache/redis_cache.js index 0e9546b6..2556a815 100644 --- a/lib/cache/redis_cache.js +++ b/lib/cache/redis_cache.js @@ -2,7 +2,7 @@ import Redis from 'ioredis' import BLOG from '@/blog.config' import { siteConfig } from '@/lib/config' -const redisClient = new Redis(BLOG.REDIS_URL) +export const redisClient = new Redis(BLOG.REDIS_URL) const cacheTime = Math.trunc( siteConfig('NEXT_REVALIDATE_SECOND', BLOG.NEXT_REVALIDATE_SECOND) * 1.5 ) From 497915655c36e30837aaf9efc7de94aadfbdca67 Mon Sep 17 00:00:00 2001 From: tangly1024 Date: Sun, 5 Jan 2025 19:39:04 +0800 Subject: [PATCH 24/25] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog.config.js | 391 +----------------------------------------- conf/dev.config.js | 4 + conf/plugin.config.js | 7 + conf/post.config.js | 35 ++++ 4 files changed, 51 insertions(+), 386 deletions(-) create mode 100644 conf/post.config.js diff --git a/blog.config.js b/blog.config.js index 33a7a53d..c35c446d 100644 --- a/blog.config.js +++ b/blog.config.js @@ -28,14 +28,16 @@ const BLOG = { // 原配置文件过长,且并非所有人都会用到,故此将配置拆分到/conf/目录下, 按需找到对应文件并修改即可 ...require('./conf/comment.config'), // 评论插件 ...require('./conf/contact.config'), // 作者联系方式配置 - ...require('./conf/image.config'), // 网站图片相关配置 ...require('./conf/analytics.config'), // 站点访问统计 + ...require('./conf/post.config'), // 博客列表数量、文章url相关配置 + ...require('./conf/image.config'), // 网站图片相关配置 ...require('./conf/font.config'), // 网站字体 ...require('./conf/right-click-menu'), // 自定义右键菜单相关配置 ...require('./conf/code.config'), // 网站代码块样式 ...require('./conf/animation.config'), // 动效美化效果 ...require('./conf/widget.config'), // 悬浮在网页上的挂件,聊天客服、宠物挂件、音乐播放器等 ...require('./conf/ad.config'), // 广告营收插件 + ...require('./conf/plugin.config'), // 其他第三方插件 algolia全文索引 // 高级用法 ...require('./conf/layout-map.config'), // 路由与布局映射自定义,例如自定义特定路由的页面布局 @@ -49,47 +51,9 @@ const BLOG = { // 自定义菜单 CUSTOM_MENU: process.env.NEXT_PUBLIC_CUSTOM_MENU || true, // 支持Menu类型的菜单,替代了3.12版本前的Page类型 - // 网站全文搜索 - ALGOLIA_APP_ID: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || null, // 在这里查看 https://dashboard.algolia.com/account/api-keys/ - ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/ - ALGOLIA_SEARCH_ONLY_APP_KEY: - process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY - ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库 - // 文章列表相关设置 CAN_COPY: process.env.NEXT_PUBLIC_CAN_COPY || true, // 是否允许复制页面内容 默认允许,如果设置为false、则全栈禁止复制内容。 - // 文章URL前缀 - POST_URL_PREFIX: process.env.NEXT_PUBLIC_POST_URL_PREFIX ?? 'article', - // POST类型文章的默认路径前缀,例如默认POST类型的路径是 /article/[slug] - // 如果此项配置为 '' 空, 则文章将没有前缀路径 - // 支援類似 WP 可自訂文章連結格式的功能:https://wordpress.org/documentation/article/customize-permalinks/,目前只先實作 %year%/%month%/%day% - // 例:如想連結改成前綴 article + 時間戳記,可變更為: 'article/%year%/%month%/%day%' - - // 分享条 - POST_SHARE_BAR_ENABLE: process.env.NEXT_PUBLIC_POST_SHARE_BAR || 'true', //文章底部分享条开关 - POSTS_SHARE_SERVICES: - process.env.NEXT_PUBLIC_POST_SHARE_SERVICES || - 'link,wechat,qq,weibo,email,facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin', // 分享的服務,按顺序显示,逗号隔开 - // 所有支持的分享服务:link(复制链接),wechat(微信),qq,weibo(微博),email(邮件),facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin,vkshare,okshare,tumblr,livejournal,mailru,viber,workplace,pocket,instapaper,hatena - - POST_TITLE_ICON: process.env.NEXT_PUBLIC_POST_TITLE_ICON || true, // 是否显示标题icon - POST_DISABLE_GALLERY_CLICK: - process.env.NEXT_PUBLIC_POST_DISABLE_GALLERY_CLICK || false, // 画册视图禁止点击,方便在友链页面的画册插入链接 - POST_LIST_STYLE: process.env.NEXT_PUBLIC_POST_LIST_STYLE || 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载 - POST_LIST_PREVIEW: process.env.NEXT_PUBLIC_POST_PREVIEW || 'false', // 是否在列表加载文章预览 - POST_PREVIEW_LINES: process.env.NEXT_PUBLIC_POST_POST_PREVIEW_LINES || 12, // 预览博客行数 - POST_RECOMMEND_COUNT: process.env.NEXT_PUBLIC_POST_RECOMMEND_COUNT || 6, // 推荐文章数量 - POSTS_PER_PAGE: process.env.NEXT_PUBLIC_POST_PER_PAGE || 12, // post counts per page - POSTS_SORT_BY: process.env.NEXT_PUBLIC_POST_SORT_BY || 'notion', // 排序方式 'date'按时间,'notion'由notion控制 - POST_WAITING_TIME_FOR_404: - process.env.NEXT_PUBLIC_POST_WAITING_TIME_FOR_404 || '8', // 文章加载超时时间,单位秒;超时后跳转到404页面 - - // 标签相关 - TAG_SORT_BY_COUNT: true, // 标签是否按照文章数量倒序排列,文章多的标签排在前。 - IS_TAG_COLOR_DISTINGUISHED: - process.env.NEXT_PUBLIC_IS_TAG_COLOR_DISTINGUISHED === 'true' || true, // 对于名称相同的tag是否区分tag的颜色 - // 侧栏布局 是否反转(左变右,右变左) 已支持主题: hexo next medium fukasawa example LAYOUT_SIDEBAR_REVERSE: process.env.NEXT_PUBLIC_LAYOUT_SIDEBAR_REVERSE || false, @@ -97,355 +61,10 @@ const BLOG = { // 欢迎语打字效果,Hexo,Matery主题支持, 英文逗号隔开多个欢迎语。 GREETING_WORDS: process.env.NEXT_PUBLIC_GREETING_WORDS || - 'Hi,我是一个程序员, Hi,我是一个打工人,Hi,我是一个干饭人,欢迎来到我的博客🎉' - ALGOLIA_APP_ID: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || null, // 在这里查看 https://dashboard.algolia.com/account/api-keys/ - ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/ - ALGOLIA_SEARCH_ONLY_APP_KEY: - process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY - ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库 - // ALGOLIA_RECREATE_DATA: process.env.ALGOLIA_RECREATE_DATA || process.env.npm_lifecycle_event === 'build', // 为true时重新构建索引数据; 默认在build时会构建 - - PREVIEW_CATEGORY_COUNT: 16, // 首页最多展示的分类数量,0为不限制 - PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制 - - POST_TITLE_ICON: process.env.NEXT_PUBLIC_POST_TITLE_ICON || true, // 是否显示标题icon - POST_DISABLE_GALLERY_CLICK: - process.env.NEXT_PUBLIC_POST_DISABLE_GALLERY_CLICK || false, // 画册视图禁止点击,方便在友链页面的画册插入链接 - - // ********动态特效相关******** - // 鼠标点击烟花特效 - FIREWORKS: process.env.NEXT_PUBLIC_FIREWORKS || false, // 开关 - // 烟花色彩,感谢 https://github.com/Vixcity 提交的色彩 - FIREWORKS_COLOR: [ - '255, 20, 97', - '24, 255, 146', - '90, 135, 255', - '251, 243, 140' - ], - - // 鼠标跟随特效 - MOUSE_FOLLOW: process.env.NEXT_PUBLIC_MOUSE_FOLLOW || false, // 开关 - // 这两个只有在鼠标跟随特效开启时才生效 - // 鼠标类型 1:路劲散点 2:下降散点 3:上升散点 4:边缘向鼠标移动散点 5:跟踪转圈散点 6:路径线条 7:聚集散点 8:聚集网格 9:移动网格 10:上升粒子 11:转圈随机颜色粒子 12:圆锥放射跟随蓝色粒子 - MOUSE_FOLLOW_EFFECT_TYPE: 11, // 1-12 - MOUSE_FOLLOW_EFFECT_COLOR: '#ef672a', // 鼠标点击特效颜色 #xxxxxx 或者 rgba(r,g,b,a) - - // 樱花飘落特效 - SAKURA: process.env.NEXT_PUBLIC_SAKURA || false, // 开关 - // 漂浮线段特效 - NEST: process.env.NEXT_PUBLIC_NEST || false, // 开关 - // 动态彩带特效 - FLUTTERINGRIBBON: process.env.NEXT_PUBLIC_FLUTTERINGRIBBON || false, // 开关 - // 静态彩带特效 - RIBBON: process.env.NEXT_PUBLIC_RIBBON || false, // 开关 - // 星空雨特效 黑夜模式才会生效 - STARRY_SKY: process.env.NEXT_PUBLIC_STARRY_SKY || false, // 开关 - - // AI 文章摘要生成 - AI_SUMMARY_API: process.env.AI_SUMMARY_API || '', - AI_SUMMARY_KEY: process.env.AI_SUMMARY_KEY || '', - AI_SUMMARY_CACHE_TIME: process.env.AI_SUMMARY_CACHE_TIME || 1800, // 缓存时间,单位秒 - AI_SUMMARY_WORD_LIMIT: process.env.AI_SUMMARY_WORD_LIMIT || 1000, - - // ********挂件组件相关******** - // AI 文章摘要生成 @see https://docs_s.tianli0.top/ - TianliGPT_CSS: - process.env.NEXT_PUBLIC_TIANLI_GPT_CSS || - 'https://cdn1.tianli0.top/gh/zhheo/Post-Abstract-AI@0.15.2/tianli_gpt.css', - TianliGPT_JS: - process.env.NEXT_PUBLIC_TIANLI_GPT_JS || - 'https://cdn1.tianli0.top/gh/zhheo/Post-Abstract-AI@0.15.2/tianli_gpt.js', - TianliGPT_KEY: process.env.NEXT_PUBLIC_TIANLI_GPT_KEY || '', - - // Chatbase 是否显示chatbase机器人 https://www.chatbase.co/ - CHATBASE_ID: process.env.NEXT_PUBLIC_CHATBASE_ID || null, - // WebwhizAI 机器人 @see https://github.com/webwhiz-ai/webwhiz - WEB_WHIZ_ENABLED: process.env.NEXT_PUBLIC_WEB_WHIZ_ENABLED || false, // 是否显示 - WEB_WHIZ_BASE_URL: - process.env.NEXT_PUBLIC_WEB_WHIZ_BASE_URL || 'https://api.webwhiz.ai', // 可以自建服务器 - WEB_WHIZ_CHAT_BOT_ID: process.env.NEXT_PUBLIC_WEB_WHIZ_CHAT_BOT_ID || null, // 在后台获取ID - DIFY_CHATBOT_ENABLED: process.env.NEXT_PUBLIC_DIFY_CHATBOT_ENABLED || false, - DIFY_CHATBOT_BASE_URL: process.env.NEXT_PUBLIC_DIFY_CHATBOT_BASE_URL || '', - DIFY_CHATBOT_TOKEN: process.env.NEXT_PUBLIC_DIFY_CHATBOT_TOKEN || '', - // 悬浮挂件 - WIDGET_PET: process.env.NEXT_PUBLIC_WIDGET_PET || true, // 是否显示宠物挂件 - WIDGET_PET_LINK: - process.env.NEXT_PUBLIC_WIDGET_PET_LINK || - 'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 挂件模型地址 @see https://github.com/xiazeyu/live2d-widget-models - WIDGET_PET_SWITCH_THEME: - process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题 - - SPOILER_TEXT_TAG: process.env.NEXT_PUBLIC_SPOILER_TEXT_TAG || '', // Spoiler文本隐藏功能,如Notion中 [sp]希望被spoiler的文字[sp],填入[sp] 即可 - - // 音乐播放插件 - MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件 - MUSIC_PLAYER_VISIBLE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_VISIBLE || true, // 是否在左下角显示播放和切换,如果使用播放器,打开自动播放再隐藏,就会以类似背景音乐的方式播放,无法取消和暂停 - MUSIC_PLAYER_AUTO_PLAY: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_AUTO_PLAY || true, // 是否自动播放,不过自动播放时常不生效(移动设备不支持自动播放) - MUSIC_PLAYER_LRC_TYPE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_LRC_TYPE || '0', // 歌词显示类型,可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url)(前提是有配置歌词路径,对 meting 无效) - MUSIC_PLAYER_CDN_URL: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_CDN_URL || - 'https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.js', - MUSIC_PLAYER_ORDER: process.env.NEXT_PUBLIC_MUSIC_PLAYER_ORDER || 'list', // 默认播放方式,顺序 list,随机 random - MUSIC_PLAYER_AUDIO_LIST: [ - // 示例音乐列表。除了以下配置外,还可配置歌词,具体配置项看此文档 https://aplayer.js.org/#/zh-Hans/ - { - name: '风を共に舞う気持ち', - artist: 'Falcom Sound Team jdk', - url: 'https://music.163.com/song/media/outer/url?id=731419.mp3', - cover: - 'https://p2.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg' - }, - { - name: '王都グランセル', - artist: 'Falcom Sound Team jdk', - url: 'https://music.163.com/song/media/outer/url?id=731355.mp3', - cover: - 'https://p1.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg' - } - ], - MUSIC_PLAYER_METING: process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING || false, // 是否要开启 MetingJS,从平台获取歌单。会覆盖自定义的 MUSIC_PLAYER_AUDIO_LIST,更多配置信息:https://github.com/metowolf/MetingJS - MUSIC_PLAYER_METING_SERVER: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_SERVER || 'netease', // 音乐平台,[netease, tencent, kugou, xiami, baidu] - MUSIC_PLAYER_METING_ID: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_ID || '60198', // 对应歌单的 id - MUSIC_PLAYER_METING_LRC_TYPE: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_LRC_TYPE || '1', // 已废弃!!!可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url) - - // ********挂件组件相关******** - // ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK - - COMMENT_HIDE_SINGLE_TAB: - process.env.NEXT_PUBLIC_COMMENT_HIDE_SINGLE_TAB || false, // Whether hide the tab when there's no tabs. 只有一个评论组件时是否隐藏切换组件的标签页 - - // artalk 评论插件 - COMMENT_ARTALK_SERVER: process.env.NEXT_PUBLIC_COMMENT_ARTALK_SERVER || '', // ArtalkServert后端地址 https://artalk.js.org/guide/deploy.html - COMMENT_ARTALK_JS: - process.env.NEXT_PUBLIC_COMMENT_ARTALK_JS || - 'https://cdnjs.cloudflare.com/ajax/libs/artalk/2.5.5/Artalk.js', // ArtalkServert js cdn - COMMENT_ARTALK_CSS: - process.env.NEXT_PUBLIC_COMMENT_ARTALK_CSS || - 'https://cdnjs.cloudflare.com/ajax/libs/artalk/2.5.5/Artalk.css', // ArtalkServert css cdn - - // twikoo - COMMENT_TWIKOO_ENV_ID: process.env.NEXT_PUBLIC_COMMENT_ENV_ID || '', // TWIKOO后端地址 腾讯云环境填envId;Vercel环境填域名,教程:https://tangly1024.com/article/notionnext-twikoo - COMMENT_TWIKOO_COUNT_ENABLE: - process.env.NEXT_PUBLIC_COMMENT_TWIKOO_COUNT_ENABLE || false, // 博客列表是否显示评论数 - COMMENT_TWIKOO_CDN_URL: - process.env.NEXT_PUBLIC_COMMENT_TWIKOO_CDN_URL || - 'https://cdn.jsdelivr.net/npm/twikoo@1.6.17/dist/twikoo.all.min.js', // twikoo客户端cdn - - // utterance - COMMENT_UTTERRANCES_REPO: - process.env.NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO || '', // 你的代码仓库名, 例如我是 'tangly1024/NotionNext'; 更多文档参考 https://utteranc.es/ - - // giscus @see https://giscus.app/ - COMMENT_GISCUS_REPO: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO || '', // 你的Github仓库名 e.g 'tangly1024/NotionNext' - COMMENT_GISCUS_REPO_ID: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO_ID || '', // 你的Github Repo ID e.g ( 設定完 giscus 即可看到 ) - COMMENT_GISCUS_CATEGORY_ID: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_CATEGORY_ID || '', // 你的Github Discussions 內的 Category ID ( 設定完 giscus 即可看到 ) - COMMENT_GISCUS_MAPPING: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_MAPPING || 'pathname', // 你的Github Discussions 使用哪種方式來標定文章, 預設 'pathname' - COMMENT_GISCUS_REACTIONS_ENABLED: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_REACTIONS_ENABLED || '1', // 你的 Giscus 是否開啟文章表情符號 '1' 開啟 "0" 關閉 預設開啟 - COMMENT_GISCUS_EMIT_METADATA: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_EMIT_METADATA || '0', // 你的 Giscus 是否提取 Metadata '1' 開啟 '0' 關閉 預設關閉 - COMMENT_GISCUS_INPUT_POSITION: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_INPUT_POSITION || 'bottom', // 你的 Giscus 發表留言位置 'bottom' 尾部 'top' 頂部, 預設 'bottom' - COMMENT_GISCUS_LANG: process.env.NEXT_PUBLIC_COMMENT_GISCUS_LANG || 'zh-CN', // 你的 Giscus 語言 e.g 'en', 'zh-TW', 'zh-CN', 預設 'en' - COMMENT_GISCUS_LOADING: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_LOADING || 'lazy', // 你的 Giscus 載入是否漸進式載入, 預設 'lazy' - COMMENT_GISCUS_CROSSORIGIN: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_CROSSORIGIN || 'anonymous', // 你的 Giscus 可以跨網域, 預設 'anonymous' - - COMMENT_CUSDIS_APP_ID: process.env.NEXT_PUBLIC_COMMENT_CUSDIS_APP_ID || '', // data-app-id 36位 see https://cusdis.com/ - COMMENT_CUSDIS_HOST: - process.env.NEXT_PUBLIC_COMMENT_CUSDIS_HOST || 'https://cusdis.com', // data-host, change this if you're using self-hosted version - COMMENT_CUSDIS_SCRIPT_SRC: - process.env.NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC || '/js/cusdis.es.js', // change this if you're using self-hosted version - - // gitalk评论插件 更多参考 https://gitalk.github.io/ - COMMENT_GITALK_REPO: process.env.NEXT_PUBLIC_COMMENT_GITALK_REPO || '', // 你的Github仓库名,例如 'NotionNext' - COMMENT_GITALK_OWNER: process.env.NEXT_PUBLIC_COMMENT_GITALK_OWNER || '', // 你的用户名 e.g tangly1024 - COMMENT_GITALK_ADMIN: process.env.NEXT_PUBLIC_COMMENT_GITALK_ADMIN || '', // 管理员用户名、一般是自己 e.g 'tangly1024' - COMMENT_GITALK_CLIENT_ID: - process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID || '', // e.g 20位ID , 在gitalk后台获取 - COMMENT_GITALK_CLIENT_SECRET: - process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET || '', // e.g 40位ID, 在gitalk后台获取 - COMMENT_GITALK_DISTRACTION_FREE_MODE: false, // 类似facebook的无干扰模式 - COMMENT_GITALK_JS_CDN_URL: - process.env.NEXT_PUBLIC_COMMENT_GITALK_JS_CDN_URL || - 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js', // gitalk客户端 js cdn - COMMENT_GITALK_CSS_CDN_URL: - process.env.NEXT_PUBLIC_COMMENT_GITALK_CSS_CDN_URL || - 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css', // gitalk客户端 css cdn - - COMMENT_GITTER_ROOM: process.env.NEXT_PUBLIC_COMMENT_GITTER_ROOM || '', // gitter聊天室 see https://gitter.im/ 不需要则留空 - COMMENT_DAO_VOICE_ID: process.env.NEXT_PUBLIC_COMMENT_DAO_VOICE_ID || '', // DaoVoice http://dashboard.daovoice.io/get-started - COMMENT_TIDIO_ID: process.env.NEXT_PUBLIC_COMMENT_TIDIO_ID || '', // [tidio_id] -> //code.tidio.co/[tidio_id].js - - COMMENT_VALINE_CDN: - process.env.NEXT_PUBLIC_VALINE_CDN || - 'https://unpkg.com/valine@1.5.1/dist/Valine.min.js', - COMMENT_VALINE_APP_ID: process.env.NEXT_PUBLIC_VALINE_ID || '', // Valine @see https://valine.js.org/quickstart.html 或 https://github.com/stonehank/react-valine#%E8%8E%B7%E5%8F%96app-id-%E5%92%8C-app-key - COMMENT_VALINE_APP_KEY: process.env.NEXT_PUBLIC_VALINE_KEY || '', - COMMENT_VALINE_SERVER_URLS: process.env.NEXT_PUBLIC_VALINE_SERVER_URLS || '', // 该配置适用于国内自定义域名用户, 海外版本会自动检测(无需手动填写) @see https://valine.js.org/configuration.html#serverURLs - COMMENT_VALINE_PLACEHOLDER: - process.env.NEXT_PUBLIC_VALINE_PLACEHOLDER || '抢个沙发吧~', // 可以搭配后台管理评论 https://github.com/DesertsP/Valine-Admin 便于查看评论,以及邮件通知,垃圾评论过滤等功能 - - COMMENT_WALINE_SERVER_URL: process.env.NEXT_PUBLIC_WALINE_SERVER_URL || '', // 请配置完整的Waline评论地址 例如 hhttps://preview-waline.tangly1024.com @see https://waline.js.org/guide/get-started.html - COMMENT_WALINE_RECENT: process.env.NEXT_PUBLIC_WALINE_RECENT || false, // 最新评论 - - // 此评论系统基于WebMention,细节可参考https://webmention.io - // 它是一个基于IndieWeb理念的开放式评论系统,下方COMMENT_WEBMENTION包含的属性皆需配置: - // ENABLE: 是否开启 - // AUTH: Webmention使用的IndieLogin,可使用Twitter或Github个人页面连结 - // HOSTNAME: Webmention绑定之网域,通常即为本站网址 - // TWITTER_USERNAME: 评论显示区域需要的资讯 - // TOKEN: Webmention的API token - COMMENT_WEBMENTION_ENABLE: process.env.NEXT_PUBLIC_WEBMENTION_ENABLE || false, - COMMENT_WEBMENTION_AUTH: process.env.NEXT_PUBLIC_WEBMENTION_AUTH || '', - COMMENT_WEBMENTION_HOSTNAME: - process.env.NEXT_PUBLIC_WEBMENTION_HOSTNAME || '', - COMMENT_WEBMENTION_TWITTER_USERNAME: - process.env.NEXT_PUBLIC_TWITTER_USERNAME || '', - COMMENT_WEBMENTION_TOKEN: process.env.NEXT_PUBLIC_WEBMENTION_TOKEN || '', - - // <---- 评论插件 - - // ----> 站点统计 - ANALYTICS_VERCEL: process.env.NEXT_PUBLIC_ANALYTICS_VERCEL || false, // vercel自带的统计 https://vercel.com/docs/concepts/analytics/quickstart https://github.com/tangly1024/NotionNext/issues/897 - ANALYTICS_BUSUANZI_ENABLE: - process.env.NEXT_PUBLIC_ANALYTICS_BUSUANZI_ENABLE || true, // 展示网站阅读量、访问数 see http://busuanzi.ibruce.info/ - ANALYTICS_BAIDU_ID: process.env.NEXT_PUBLIC_ANALYTICS_BAIDU_ID || '', // e.g 只需要填写百度统计的id,[baidu_id] -> https://hm.baidu.com/hm.js?[baidu_id] - ANALYTICS_CNZZ_ID: process.env.NEXT_PUBLIC_ANALYTICS_CNZZ_ID || '', // 只需要填写站长统计的id, [cnzz_id] -> https://s9.cnzz.com/z_stat.php?id=[cnzz_id]&web_id=[cnzz_id] - ANALYTICS_GOOGLE_ID: process.env.NEXT_PUBLIC_ANALYTICS_GOOGLE_ID || '', // 谷歌Analytics的id e.g: G-XXXXXXXXXX - - // 51la 站点统计 https://www.51.la/ - ANALYTICS_51LA_ID: process.env.NEXT_PUBLIC_ANALYTICS_51LA_ID || '', // id,在51la后台获取 参阅 https://docs.tangly1024.com/article/notion-next-51-la - ANALYTICS_51LA_CK: process.env.NEXT_PUBLIC_ANALYTICS_51LA_CK || '', // ck,在51la后台获取 - - // Matomo 网站统计 - MATOMO_HOST_URL: process.env.NEXT_PUBLIC_MATOMO_HOST_URL || '', // Matomo服务器地址,不带斜杠 - MATOMO_SITE_ID: process.env.NEXT_PUBLIC_MATOMO_SITE_ID || '', // Matomo网站ID - // ACKEE网站访客统计工具 - ANALYTICS_ACKEE_TRACKER: - process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.com/tracker.js' - ANALYTICS_ACKEE_DATA_SERVER: - process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.com , don't end with a slash - ANALYTICS_ACKEE_DOMAIN_ID: - process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '82e51db6-dec2-423a-b7c9-b4ff7ebb3302' - - SEO_GOOGLE_SITE_VERIFICATION: - process.env.NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code - - SEO_BAIDU_SITE_VERIFICATION: - process.env.NEXT_PUBLIC_SEO_BAIDU_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code - - // 微软 Clarity 站点分析 - CLARITY_ID: process.env.NEXT_PUBLIC_CLARITY_ID || null, // 只需要复制Clarity脚本中的ID部分,ID是一个十位的英文数字组合 - - // <---- 站点统计 - - // START---->营收相关 - - // 谷歌广告 - ADSENSE_GOOGLE_ID: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_ID || '', // 谷歌广告ID e.g ca-pub-xxxxxxxxxxxxxxxx - ADSENSE_GOOGLE_TEST: process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_TEST || false, // 谷歌广告ID测试模式,这种模式获取假的测试广告,用于开发 https://www.tangly1024.com/article/local-dev-google-adsense - ADSENSE_GOOGLE_SLOT_IN_ARTICLE: - process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_IN_ARTICLE || '3806269138', // Google AdScene>广告>按单元广告>新建文章内嵌广告 粘贴html代码中的data-ad-slot值 - ADSENSE_GOOGLE_SLOT_FLOW: - process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_FLOW || '1510444138', // Google AdScene>广告>按单元广告>新建信息流广告 - ADSENSE_GOOGLE_SLOT_NATIVE: - process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_NATIVE || '4980048999', // Google AdScene>广告>按单元广告>新建原生广告 - ADSENSE_GOOGLE_SLOT_AUTO: - process.env.NEXT_PUBLIC_ADSENSE_GOOGLE_SLOT_AUTO || '8807314373', // Google AdScene>广告>按单元广告>新建展示广告 (自动广告) - - // 万维广告 - AD_WWADS_ID: process.env.NEXT_PUBLIC_WWAD_ID || null, // https://wwads.cn/ 创建您的万维广告单元ID - AD_WWADS_BLOCK_DETECT: process.env.NEXT_PUBLIC_WWADS_AD_BLOCK_DETECT || false, // 是否开启WWADS广告屏蔽插件检测,开启后会在广告位上以文字提示 @see https://github.com/bytegravity/whitelist-wwads - - // END<----营收相关 - - // 自定义配置notion数据库字段名 - NOTION_PROPERTY_NAME: { - password: process.env.NEXT_PUBLIC_NOTION_PROPERTY_PASSWORD || 'password', - type: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE || 'type', // 文章类型, - type_post: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_POST || 'Post', // 当type文章类型与此值相同时,为博文。 - type_page: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_PAGE || 'Page', // 当type文章类型与此值相同时,为单页。 - type_notice: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_NOTICE || 'Notice', // 当type文章类型与此值相同时,为公告。 - type_menu: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_MENU || 'Menu', // 当type文章类型与此值相同时,为菜单。 - type_sub_menu: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_SUB_MENU || 'SubMenu', // 当type文章类型与此值相同时,为子菜单。 - title: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TITLE || 'title', // 文章标题 - status: process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS || 'status', - status_publish: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_PUBLISH || 'Published', // 当status状态值与此相同时为发布,可以为中文 - status_invisible: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_INVISIBLE || 'Invisible', // 当status状态值与此相同时为隐藏发布,可以为中文 , 除此之外其他页面状态不会显示在博客上 - summary: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SUMMARY || 'summary', - slug: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SLUG || 'slug', - category: process.env.NEXT_PUBLIC_NOTION_PROPERTY_CATEGORY || 'category', - date: process.env.NEXT_PUBLIC_NOTION_PROPERTY_DATE || 'date', - tags: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TAGS || 'tags', - icon: process.env.NEXT_PUBLIC_NOTION_PROPERTY_ICON || 'icon', - ext: process.env.NEXT_PUBLIC_NOTION_PROPERTY_EXT || 'ext' // 扩展字段,存放json-string,用于复杂业务 - }, - - // RSS订阅 - ENABLE_RSS: process.env.NEXT_PUBLIC_ENABLE_RSS || true, // 是否开启RSS订阅功能 - MAILCHIMP_LIST_ID: process.env.MAILCHIMP_LIST_ID || null, // 开启mailichimp邮件订阅 客户列表ID ,具体使用方法参阅文档 - MAILCHIMP_API_KEY: process.env.MAILCHIMP_API_KEY || null, // 开启mailichimp邮件订阅 APIkey + 'Hi,我是一个程序员, Hi,我是一个打工人,Hi,我是一个干饭人,欢迎来到我的博客🎉', // uuid重定向至 slug - UUID_REDIRECT: process.env.UUID_REDIRECT || false, - - // ANIMATE.css 动画 - ANIMATE_CSS_URL: - process.env.NEXT_PUBLIC_ANIMATE_CSS_URL || - 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css', // 动画CDN - - // 网站图片 - IMG_LAZY_LOAD_PLACEHOLDER: - process.env.NEXT_PUBLIC_IMG_LAZY_LOAD_PLACEHOLDER || - 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', // 懒加载占位图片地址,支持base64或url - IMG_URL_TYPE: process.env.NEXT_PUBLIC_IMG_TYPE || 'Notion', // 此配置已失效,请勿使用;AMAZON方案不再支持,仅支持Notion方案。 ['Notion','AMAZON'] 站点图片前缀 默认 Notion:(https://notion.so/images/xx) , AMAZON(https://s3.us-west-2.amazonaws.com/xxx) - IMG_SHADOW: process.env.NEXT_PUBLIC_IMG_SHADOW || false, // 文章图片是否自动添加阴影 - IMG_COMPRESS_WIDTH: process.env.NEXT_PUBLIC_IMG_COMPRESS_WIDTH || 800, // Notion图片压缩宽度 - - // 作废配置 - AVATAR: process.env.NEXT_PUBLIC_AVATAR || '/avatar.svg', // 作者头像,被notion中的ICON覆盖。若无ICON则取public目录下的avatar.png - TITLE: process.env.NEXT_PUBLIC_TITLE || 'NotionNext BLOG', // 站点标题 ,被notion中的页面标题覆盖;此处请勿留空白,否则服务器无法编译 - HOME_BANNER_IMAGE: - process.env.NEXT_PUBLIC_HOME_BANNER_IMAGE || '/bg_image.jpg', // 首页背景大图, 会被notion中的封面图覆盖,若无封面图则会使用代码中的 /public/bg_image.jpg 文件 - DESCRIPTION: - process.env.NEXT_PUBLIC_DESCRIPTION || '这是一个由NotionNext生成的站点', // 站点描述,被notion中的页面描述覆盖 - - // Redis 缓存数据库地址 - REDIS_URL: process.env.REDIS_URL || '', - - // 开发相关 - NOTION_ACTIVE_USER: process.env.NOTION_ACTIVE_USER || '', - NOTION_TOKEN_V2: process.env.NOTION_TOKEN_V2 || '', // Useful if you prefer not to make your database public - DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮 - ENABLE_CACHE: - process.env.ENABLE_CACHE || - process.env.npm_lifecycle_event === 'build' || - process.env.npm_lifecycle_event === 'export', // 在打包过程中默认开启缓存,开发或运行时开启此功能意义不大。 - isProd: process.env.VERCEL_ENV === 'production' || process.env.EXPORT, // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) - BUNDLE_ANALYZER: process.env.ANALYZE === 'true' || false, // 是否展示编译依赖内容与大小 - VERSION: (() => { - try { - // 优先使用环境变量,否则从package.json中获取版本号 - return ( - process.env.NEXT_PUBLIC_VERSION || require('./package.json').version - ) - } catch (error) { - console.warn('Failed to load package.json version:', error) - return '1.0.0' // 缺省版本号 - } - })() + UUID_REDIRECT: process.env.UUID_REDIRECT || false } module.exports = BLOG diff --git a/conf/dev.config.js b/conf/dev.config.js index 2cbdfa93..c34a8c64 100644 --- a/conf/dev.config.js +++ b/conf/dev.config.js @@ -7,6 +7,10 @@ module.exports = { // TAILWINDCSS 配置的自定义颜色,作废 BACKGROUND_LIGHT: '#eeeeee', // use hex value, don't forget '#' e.g #fffefc BACKGROUND_DARK: '#000000', // use hex value, don't forget '#' + + // Redis 缓存数据库地址 + REDIS_URL: process.env.REDIS_URL || '', + ENABLE_CACHE: process.env.ENABLE_CACHE || process.env.npm_lifecycle_event === 'build' || diff --git a/conf/plugin.config.js b/conf/plugin.config.js index 371e75d6..aa9e0016 100644 --- a/conf/plugin.config.js +++ b/conf/plugin.config.js @@ -2,6 +2,13 @@ * 一些插件 */ module.exports = { + // 网站全文搜索 + ALGOLIA_APP_ID: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID || null, // 在这里查看 https://dashboard.algolia.com/account/api-keys/ + ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/ + ALGOLIA_SEARCH_ONLY_APP_KEY: + process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY + ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库 + // AI 文章摘要生成 AI_SUMMARY_API: process.env.AI_SUMMARY_API || '', diff --git a/conf/post.config.js b/conf/post.config.js new file mode 100644 index 00000000..ee89d12f --- /dev/null +++ b/conf/post.config.js @@ -0,0 +1,35 @@ +/** + * 博客列表相关配置 + */ +module.exports = { + // 文章URL前缀 + POST_URL_PREFIX: process.env.NEXT_PUBLIC_POST_URL_PREFIX ?? 'article', + // POST类型文章的默认路径前缀,例如默认POST类型的路径是 /article/[slug] + // 如果此项配置为 '' 空, 则文章将没有前缀路径 + // 支援類似 WP 可自訂文章連結格式的功能:https://wordpress.org/documentation/article/customize-permalinks/,目前只先實作 %year%/%month%/%day% + // 例:如想連結改成前綴 article + 時間戳記,可變更為: 'article/%year%/%month%/%day%' + + // 分享条 + POST_SHARE_BAR_ENABLE: process.env.NEXT_PUBLIC_POST_SHARE_BAR || 'true', //文章底部分享条开关 + POSTS_SHARE_SERVICES: + process.env.NEXT_PUBLIC_POST_SHARE_SERVICES || + 'link,wechat,qq,weibo,email,facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin', // 分享的服務,按顺序显示,逗号隔开 + // 所有支持的分享服务:link(复制链接),wechat(微信),qq,weibo(微博),email(邮件),facebook,twitter,telegram,messenger,line,reddit,whatsapp,linkedin,vkshare,okshare,tumblr,livejournal,mailru,viber,workplace,pocket,instapaper,hatena + + POST_TITLE_ICON: process.env.NEXT_PUBLIC_POST_TITLE_ICON || true, // 是否显示标题icon + POST_DISABLE_GALLERY_CLICK: + process.env.NEXT_PUBLIC_POST_DISABLE_GALLERY_CLICK || false, // 画册视图禁止点击,方便在友链页面的画册插入链接 + POST_LIST_STYLE: process.env.NEXT_PUBLIC_POST_LIST_STYLE || 'page', // ['page','scroll] 文章列表样式:页码分页、单页滚动加载 + POST_LIST_PREVIEW: process.env.NEXT_PUBLIC_POST_PREVIEW || 'false', // 是否在列表加载文章预览 + POST_PREVIEW_LINES: process.env.NEXT_PUBLIC_POST_POST_PREVIEW_LINES || 12, // 预览博客行数 + POST_RECOMMEND_COUNT: process.env.NEXT_PUBLIC_POST_RECOMMEND_COUNT || 6, // 推荐文章数量 + POSTS_PER_PAGE: process.env.NEXT_PUBLIC_POST_PER_PAGE || 12, // post counts per page + POSTS_SORT_BY: process.env.NEXT_PUBLIC_POST_SORT_BY || 'notion', // 排序方式 'date'按时间,'notion'由notion控制 + POST_WAITING_TIME_FOR_404: + process.env.NEXT_PUBLIC_POST_WAITING_TIME_FOR_404 || '8', // 文章加载超时时间,单位秒;超时后跳转到404页面 + + // 标签相关 + TAG_SORT_BY_COUNT: true, // 标签是否按照文章数量倒序排列,文章多的标签排在前。 + IS_TAG_COLOR_DISTINGUISHED: + process.env.NEXT_PUBLIC_IS_TAG_COLOR_DISTINGUISHED === 'true' || true // 对于名称相同的tag是否区分tag的颜色 +} From bb612dede292aa9958423800e7112a20e8b589b4 Mon Sep 17 00:00:00 2001 From: tangly1024 Date: Sun, 5 Jan 2025 19:57:25 +0800 Subject: [PATCH 25/25] =?UTF-8?q?=E7=BC=93=E5=AD=98=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=95=B4=E5=90=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/cache/cache_manager.js | 59 ++++++++++++++++++++++++++++++++++++-- lib/cache/redis_cache.js | 5 ++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/lib/cache/cache_manager.js b/lib/cache/cache_manager.js index 470a0f1a..f0c412ab 100644 --- a/lib/cache/cache_manager.js +++ b/lib/cache/cache_manager.js @@ -3,13 +3,61 @@ import FileCache from './local_file_cache' import MemoryCache from './memory_cache' import RedisCache from './redis_cache' +// 配置是否开启Vercel环境中的缓存,因为Vercel中现有两种缓存方式在无服务环境下基本都是无意义的,纯粹的浪费资源 +const enableCacheInVercel = + process.env.npm_lifecycle_event === 'build' || + process.env.npm_lifecycle_event === 'export' || + !BLOG['isProd'] + +/** + * 尝试从缓存中获取数据,如果没有则尝试获取数据并写入缓存,最终返回所需数据 + * @param key + * @param getDataFunction + * @param getDataArgs + * @returns {Promise<*|null>} + */ +export async function getOrSetDataWithCache( + key, + getDataFunction, + ...getDataArgs +) { + return getOrSetDataWithCustomCache(key, null, getDataFunction, ...getDataArgs) +} + +/** + * 尝试从缓存中获取数据,如果没有则尝试获取数据并自定义写入缓存,最终返回所需数据 + * @param key + * @param customCacheTime + * @param getDataFunction + * @param getDataArgs + * @returns {Promise<*|null>} + */ +export async function getOrSetDataWithCustomCache( + key, + customCacheTime, + getDataFunction, + ...getDataArgs +) { + const dataFromCache = await getDataFromCache(key) + if (dataFromCache) { + console.log('[缓存-->>API]:', key) + return dataFromCache + } + const data = await getDataFunction(...getDataArgs) + if (data) { + console.log('[API-->>缓存]:', key) + await setDataToCache(key, data, customCacheTime) + } + return data || null +} + /** * 为减少频繁接口请求,notion数据将被缓存 * @param {*} key * @returns */ export async function getDataFromCache(key, force) { - if (enableCacheInVercel || BLOG.ENABLE_CACHE || force) { + if (BLOG.ENABLE_CACHE || force) { const dataFromCache = await getApi().getCache(key) if (!dataFromCache || JSON.stringify(dataFromCache) === '[]') { return null @@ -21,6 +69,13 @@ export async function getDataFromCache(key, force) { } } +/** + * 写入缓存 + * @param {*} key + * @param {*} data + * @param {*} customCacheTime + * @returns + */ export async function setDataToCache(key, data, customCacheTime) { if (!enableCacheInVercel || !data) { return @@ -40,7 +95,7 @@ export async function delCacheData(key) { * 缓存实现类 * @returns */ -function getApi() { +export function getApi() { if (BLOG.REDIS_URL) { return RedisCache } else if (process.env.ENABLE_FILE_CACHE) { diff --git a/lib/cache/redis_cache.js b/lib/cache/redis_cache.js index 2556a815..b35f472a 100644 --- a/lib/cache/redis_cache.js +++ b/lib/cache/redis_cache.js @@ -1,8 +1,9 @@ -import Redis from 'ioredis' import BLOG from '@/blog.config' import { siteConfig } from '@/lib/config' +import Redis from 'ioredis' + +export const redisClient = BLOG.REDIS_URL ? new Redis(BLOG.REDIS_URL) : {} -export const redisClient = new Redis(BLOG.REDIS_URL) const cacheTime = Math.trunc( siteConfig('NEXT_REVALIDATE_SECOND', BLOG.NEXT_REVALIDATE_SECOND) * 1.5 )