Merge branch 'main' into release/4.8.1

This commit is contained in:
tangly1024
2025-01-05 20:00:11 +08:00
20 changed files with 633 additions and 419 deletions

View File

@@ -1,6 +1,55 @@
import BLOG from '@/blog.config'
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数据将被缓存
@@ -20,8 +69,15 @@ export async function getDataFromCache(key, force) {
}
}
/**
* 写入缓存
* @param {*} key
* @param {*} data
* @param {*} customCacheTime
* @returns
*/
export async function setDataToCache(key, data, customCacheTime) {
if (!data) {
if (!enableCacheInVercel || !data) {
return
}
// console.trace('[API-->>缓存写入]:', key)
@@ -39,8 +95,10 @@ export async function delCacheData(key) {
* 缓存实现类
* @returns
*/
function getApi() {
if (process.env.ENABLE_FILE_CACHE) {
export function getApi() {
if (BLOG.REDIS_URL) {
return RedisCache
} else if (process.env.ENABLE_FILE_CACHE) {
return FileCache
} else {
return MemoryCache

41
lib/cache/redis_cache.js vendored Normal file
View File

@@ -0,0 +1,41 @@
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) : {}
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 }

View File

@@ -43,6 +43,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) {

View File

@@ -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'
@@ -65,14 +66,15 @@ 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}`,
async (pageId, from) => {
const pageRecordMap = await getPage(pageId, from)
return convertNotionToSiteDate(pageId, from, deepClone(pageRecordMap))
},
pageId,
from,
deepClone(pageRecordMap)
from
)
return data
}
/**
@@ -139,7 +141,7 @@ const EmptyData = pageId => {
* 这里统一对数据格式化
* @returns {Promise<JSX.Element|null|*>}
*/
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 {}
@@ -273,11 +275,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(

View File

@@ -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

View File

@@ -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 = {

View File

@@ -1,8 +1,7 @@
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'
import notionAPI from '@/lib/notion/getNotionAPI'
/**
* 获取文章内容
@@ -12,21 +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 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 convertNotionBlocksToPost(id, pageBlock, slice)
}
return pageBlock
if (pageBlock) {
await setDataToCache(cacheKey, pageBlock)
return convertNotionBlocksToPost(id, pageBlock, slice)
}
return pageBlock
},
id,
slice
)
}
/**
@@ -43,9 +49,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 +168,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} `

15
lib/redirect.js Normal file
View File

@@ -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)
}
}