mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-18 15:09:34 +00:00
Merge branch 'main' into pr/jiashu1024/1622
This commit is contained in:
31
lib/cache/cache_manager.js
vendored
31
lib/cache/cache_manager.js
vendored
@@ -3,15 +3,6 @@ import FileCache from './local_file_cache'
|
||||
import MongoCache from './mongo_db_cache'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
let api
|
||||
if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
|
||||
api = MongoCache
|
||||
} else if (process.env.ENABLE_FILE_CACHE) {
|
||||
api = FileCache
|
||||
} else {
|
||||
api = MemoryCache
|
||||
}
|
||||
|
||||
/**
|
||||
* 为减少频繁接口请求,notion数据将被缓存
|
||||
* @param {*} key
|
||||
@@ -19,11 +10,11 @@ if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
|
||||
*/
|
||||
export async function getDataFromCache(key, force) {
|
||||
if (BLOG.ENABLE_CACHE || force) {
|
||||
const dataFromCache = await api.getCache(key)
|
||||
const dataFromCache = await getApi().getCache(key)
|
||||
if (JSON.stringify(dataFromCache) === '[]') {
|
||||
return null
|
||||
}
|
||||
return api.getCache(key)
|
||||
return getApi().getCache(key)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
@@ -33,12 +24,26 @@ export async function setDataToCache(key, data) {
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
await api.setCache(key, data)
|
||||
await getApi().setCache(key, data)
|
||||
}
|
||||
|
||||
export async function delCacheData(key) {
|
||||
if (!BLOG.ENABLE_CACHE) {
|
||||
return
|
||||
}
|
||||
await api.delCache(key)
|
||||
await getApi().delCache(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存实现类
|
||||
* @returns
|
||||
*/
|
||||
function getApi() {
|
||||
if (process.env.MONGO_DB_URL && process.env.MONGO_DB_NAME) {
|
||||
return MongoCache
|
||||
} else if (process.env.ENABLE_FILE_CACHE) {
|
||||
return FileCache
|
||||
} else {
|
||||
return MemoryCache
|
||||
}
|
||||
}
|
||||
|
||||
10
lib/cache/local_file_cache.js
vendored
10
lib/cache/local_file_cache.js
vendored
@@ -41,7 +41,7 @@ export async function setCache (key, data) {
|
||||
fs.writeFileSync(jsonFile, JSON.stringify(json))
|
||||
}
|
||||
|
||||
export async function delCache (key, data) {
|
||||
export async function delCache (key) {
|
||||
const exist = await fs.existsSync(jsonFile)
|
||||
const json = exist ? JSON.parse(await fs.readFileSync(jsonFile)) : {}
|
||||
delete json.key
|
||||
@@ -49,4 +49,12 @@ export async function delCache (key, data) {
|
||||
fs.writeFileSync(jsonFile, JSON.stringify(json))
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存
|
||||
*/
|
||||
export async function cleanCache() {
|
||||
const json = {}
|
||||
fs.writeFileSync(jsonFile, JSON.stringify(json))
|
||||
}
|
||||
|
||||
export default { getCache, setCache, delCache }
|
||||
|
||||
93
lib/config.js
Normal file
93
lib/config.js
Normal file
@@ -0,0 +1,93 @@
|
||||
'use client'
|
||||
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from './global'
|
||||
import { deepClone } from './utils'
|
||||
|
||||
/**
|
||||
* 读取配置顺序
|
||||
* 1. 优先读取NotionConfig表
|
||||
* 2. 其次读取环境变量
|
||||
* 3. 再读取blog.config.js / 或各个主题的CONFIG文件
|
||||
* @param {*} key ; 参数名
|
||||
* @param {*} defaultVal ; 参数不存在默认返回值
|
||||
* @param {*} extendConfig ; 参考配置对象{key:val},如果notion中找不到优先尝试在这里面查找
|
||||
* @returns
|
||||
*/
|
||||
export const siteConfig = (key, defaultVal = null, extendConfig = null) => {
|
||||
let global = null
|
||||
try {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
global = useGlobal()
|
||||
} catch (error) {}
|
||||
|
||||
// 首先 配置最优先读取NOTION中的表格配置
|
||||
let val = null
|
||||
let siteInfo = null
|
||||
|
||||
if (global) {
|
||||
val = global.NOTION_CONFIG?.[key]
|
||||
siteInfo = global.siteInfo
|
||||
// console.log('当前变量', key, val)
|
||||
}
|
||||
|
||||
if (!val) {
|
||||
// 这里针对部分key做一些兼容处理
|
||||
switch (key) {
|
||||
case 'HOME_BANNER_IMAGE':
|
||||
val = siteInfo?.pageCover // 封面图取Notion的封面
|
||||
break
|
||||
case 'AVATAR':
|
||||
val = siteInfo?.icon // 封面图取Notion的头像
|
||||
break
|
||||
case 'TITLE':
|
||||
val = siteInfo?.title // 标题取Notion中的标题
|
||||
break
|
||||
case 'DESCRIPTION':
|
||||
val = siteInfo?.description // 标题取Notion中的标题
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 其次 有传入的配置参考,则尝试读取
|
||||
if (!val && extendConfig) {
|
||||
val = extendConfig[key]
|
||||
}
|
||||
|
||||
// 其次 NOTION没有找到配置,则会读取blog.config.js文件
|
||||
if (!val) {
|
||||
val = BLOG[key]
|
||||
}
|
||||
|
||||
if (!val) {
|
||||
return defaultVal
|
||||
} else {
|
||||
if (typeof val === 'string') {
|
||||
return val;
|
||||
} else {
|
||||
try {
|
||||
return JSON.parse(val);
|
||||
} catch (error) {
|
||||
// 如果值是一个字符串但不是有效的 JSON 格式,直接返回字符串
|
||||
return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取所有配置
|
||||
* 1. 优先读取NotionConfig表
|
||||
* 2. 其次读取环境变量
|
||||
* 3. 再读取blog.config.js文件
|
||||
* @param {*} key
|
||||
* @returns
|
||||
*/
|
||||
export const siteConfigMap = () => {
|
||||
const val = deepClone(BLOG)
|
||||
for (const key in val) {
|
||||
val[key] = siteConfig(key)
|
||||
// console.log('site', key, val[key], siteConfig(key))
|
||||
}
|
||||
return val
|
||||
}
|
||||
@@ -1,34 +1,65 @@
|
||||
import { generateLocaleDict, initLocale } from './lang'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { THEMES, initDarkMode, saveDarkModeToCookies } from '@/themes/theme'
|
||||
|
||||
import BLOG from '@/blog.config'
|
||||
import { THEMES, initDarkMode } from '@/themes/theme'
|
||||
import NProgress from 'nprogress'
|
||||
import { getQueryVariable, isBrowser } from './utils'
|
||||
|
||||
const GlobalContext = createContext()
|
||||
|
||||
/**
|
||||
* 全局变量Provider,包括语言本地化、样式主题、搜索词
|
||||
* 定义全局变量,包括语言、主题、深色模式、加载状态
|
||||
* @param children
|
||||
* @returns {JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
export function GlobalContextProvider(props) {
|
||||
const { children, siteInfo, categoryOptions, tagOptions } = props
|
||||
const { children, siteInfo, categoryOptions, tagOptions, NOTION_CONFIG } = props
|
||||
const router = useRouter()
|
||||
const [lang, updateLang] = useState(BLOG.LANG) // 默认语言
|
||||
const [locale, updateLocale] = useState(generateLocaleDict(BLOG.LANG)) // 默认语言
|
||||
const [theme, setTheme] = useState(BLOG.THEME) // 默认博客主题
|
||||
const [isDarkMode, updateDarkMode] = useState(BLOG.APPEARANCE === 'dark') // 默认深色模式
|
||||
const [lang, updateLang] = useState(NOTION_CONFIG?.LANG || BLOG.LANG) // 默认语言
|
||||
const [locale, updateLocale] = useState(generateLocaleDict(NOTION_CONFIG?.LANG || BLOG.LANG)) // 默认语言
|
||||
const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || BLOG.THEME) // 默认博客主题
|
||||
const [isDarkMode, updateDarkMode] = useState(NOTION_CONFIG?.APPEARANCE || BLOG.APPEARANCE === 'dark') // 默认深色模式
|
||||
const [onLoading, setOnLoading] = useState(false) // 抓取文章数据
|
||||
|
||||
// 切换主题
|
||||
function switchTheme() {
|
||||
const currentIndex = THEMES.indexOf(theme)
|
||||
const newIndex = currentIndex < THEMES.length - 1 ? currentIndex + 1 : 0
|
||||
const newTheme = THEMES[newIndex]
|
||||
const query = router.query
|
||||
query.theme = newTheme
|
||||
router.push({ pathname: router.pathname, query })
|
||||
return newTheme
|
||||
}
|
||||
|
||||
// 切换深色模式
|
||||
const toggleDarkMode = () => {
|
||||
const newStatus = !isDarkMode
|
||||
saveDarkModeToCookies(newStatus)
|
||||
updateDarkMode(newStatus)
|
||||
const htmlElement = document.getElementsByTagName('html')[0]
|
||||
htmlElement.classList?.remove(newStatus ? 'light' : 'dark')
|
||||
htmlElement.classList?.add(newStatus ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新语言
|
||||
*/
|
||||
function changeLang(lang) {
|
||||
if (lang) {
|
||||
updateLang(lang)
|
||||
updateLocale(generateLocaleDict(lang))
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
initLocale(lang, locale, updateLang, updateLocale)
|
||||
initDarkMode(updateDarkMode)
|
||||
initTheme()
|
||||
initLocale(lang, locale, updateLang, updateLocale)
|
||||
}, [])
|
||||
|
||||
// 加载进度条
|
||||
useEffect(() => {
|
||||
const handleStart = (url) => {
|
||||
NProgress.start()
|
||||
@@ -43,8 +74,7 @@ export function GlobalContextProvider(props) {
|
||||
NProgress.done()
|
||||
setOnLoading(false)
|
||||
}
|
||||
const queryTheme = getQueryVariable('theme') || BLOG.THEME
|
||||
setTheme(queryTheme)
|
||||
|
||||
router.events.on('routeChangeStart', handleStart)
|
||||
router.events.on('routeChangeError', handleStop)
|
||||
router.events.on('routeChangeComplete', handleStop)
|
||||
@@ -55,21 +85,14 @@ export function GlobalContextProvider(props) {
|
||||
}
|
||||
}, [router])
|
||||
|
||||
// 切换主题
|
||||
function switchTheme() {
|
||||
const currentIndex = THEMES.indexOf(theme)
|
||||
const newIndex = currentIndex < THEMES.length - 1 ? currentIndex + 1 : 0
|
||||
const newTheme = THEMES[newIndex]
|
||||
const query = router.query
|
||||
query.theme = newTheme
|
||||
router.push({ pathname: router.pathname, query })
|
||||
return newTheme
|
||||
}
|
||||
|
||||
return (
|
||||
<GlobalContext.Provider value={{
|
||||
NOTION_CONFIG,
|
||||
toggleDarkMode,
|
||||
onLoading,
|
||||
setOnLoading,
|
||||
lang,
|
||||
changeLang,
|
||||
locale,
|
||||
updateLocale,
|
||||
isDarkMode,
|
||||
@@ -86,23 +109,4 @@ export function GlobalContextProvider(props) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换主题时的特殊处理
|
||||
* @param {*} setTheme
|
||||
*/
|
||||
const initTheme = () => {
|
||||
if (isBrowser) {
|
||||
setTimeout(() => {
|
||||
const elements = document.querySelectorAll('[id^="theme-"]')
|
||||
if (elements?.length > 1) {
|
||||
elements[elements.length - 1].scrollIntoView()
|
||||
// 删除前面的元素,只保留最后一个元素
|
||||
for (let i = 0; i < elements.length - 1; i++) {
|
||||
elements[i].parentNode.removeChild(elements[i])
|
||||
}
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
export const useGlobal = () => useContext(GlobalContext)
|
||||
|
||||
21
lib/lang.js
21
lib/lang.js
@@ -12,7 +12,7 @@ import { getQueryVariable, isBrowser, mergeDeep } from './utils'
|
||||
* 在这里配置所有支持的语言
|
||||
* 国家-地区
|
||||
*/
|
||||
const lang = {
|
||||
const LANGS = {
|
||||
'en-US': enUS,
|
||||
'zh-CN': zhCN,
|
||||
'zh-HK': zhHK,
|
||||
@@ -22,7 +22,7 @@ const lang = {
|
||||
'ja-JP': jaJP
|
||||
}
|
||||
|
||||
export default lang
|
||||
export default LANGS
|
||||
|
||||
/**
|
||||
* 获取当前语言字典
|
||||
@@ -30,33 +30,33 @@ export default lang
|
||||
* @returns 不同语言对应字典
|
||||
*/
|
||||
export function generateLocaleDict(langString) {
|
||||
const supportedLocales = Object.keys(lang)
|
||||
const supportedLocales = Object.keys(LANGS)
|
||||
let userLocale
|
||||
|
||||
// 将语言字符串拆分为语言和地区代码,例如将 "zh-CN" 拆分为 "zh" 和 "CN"
|
||||
const [language, region] = langString.split(/[-_]/)
|
||||
const [language, region] = langString?.split(/[-_]/)
|
||||
|
||||
// 优先匹配语言和地区都匹配的情况
|
||||
const specificLocale = `${language}-${region}`
|
||||
if (supportedLocales.includes(specificLocale)) {
|
||||
userLocale = lang[specificLocale]
|
||||
userLocale = LANGS[specificLocale]
|
||||
}
|
||||
|
||||
// 然后尝试匹配只有语言匹配的情况
|
||||
if (!userLocale) {
|
||||
const languageOnlyLocales = supportedLocales.filter(locale => locale.startsWith(language))
|
||||
if (languageOnlyLocales.length > 0) {
|
||||
userLocale = lang[languageOnlyLocales[0]]
|
||||
userLocale = LANGS[languageOnlyLocales[0]]
|
||||
}
|
||||
}
|
||||
|
||||
// 如果还没匹配到,则返回最接近的语言包
|
||||
if (!userLocale) {
|
||||
const fallbackLocale = supportedLocales.find(locale => locale.startsWith('en'))
|
||||
userLocale = lang[fallbackLocale]
|
||||
userLocale = LANGS[fallbackLocale]
|
||||
}
|
||||
|
||||
return mergeDeep({}, lang['en-US'], userLocale)
|
||||
return mergeDeep({}, LANGS['en-US'], userLocale)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,11 +65,12 @@ export function generateLocaleDict(langString) {
|
||||
*/
|
||||
export function initLocale(lang, locale, changeLang, changeLocale) {
|
||||
if (isBrowser) {
|
||||
const queryLang = getQueryVariable('lang') || loadLangFromCookies() || window.navigator.language
|
||||
const queryLang = getQueryVariable('lang') || loadLangFromCookies()
|
||||
let currentLang = lang
|
||||
if (queryLang !== lang) {
|
||||
if (queryLang && queryLang !== 'undefined' && queryLang !== lang) {
|
||||
currentLang = queryLang
|
||||
}
|
||||
|
||||
changeLang(currentLang)
|
||||
saveLangToCookies(currentLang)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
LOCALE: 'en-US',
|
||||
LOCALE: 'English',
|
||||
MENU: {
|
||||
WALK_AROUND: 'Walk Around',
|
||||
CATEGORY: 'Category',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
LOCALE: 'fr-FR',
|
||||
LOCALE: 'français',
|
||||
NAV: {
|
||||
INDEX: 'Accueil',
|
||||
RSS: 'RSS',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
LOCALE: 'ja-JP',
|
||||
LOCALE: '日本語',
|
||||
NAV: {
|
||||
INDEX: 'ホーム',
|
||||
RSS: '購読',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
LOCALE: 'tr-TR',
|
||||
LOCALE: 'Türkçe',
|
||||
NAV: {
|
||||
INDEX: 'Blog',
|
||||
RSS: 'RSS',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
LOCALE: 'zh-CN',
|
||||
LOCALE: '中文(简体)',
|
||||
MENU: {
|
||||
WALK_AROUND: '随便逛逛',
|
||||
CATEGORY: '博客分类',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export default {
|
||||
LOCALE: '中文(繁体香港)',
|
||||
NAV: {
|
||||
INDEX: '網誌',
|
||||
RSS: '訂閱',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export default {
|
||||
LOCALE: 'zh-TW',
|
||||
LOCALE: '中文(繁体台湾)',
|
||||
NAV: {
|
||||
INDEX: '部落格',
|
||||
RSS: '訂閱',
|
||||
|
||||
136
lib/notion/getNotionConfig.js
Normal file
136
lib/notion/getNotionConfig.js
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* 从Notion中读取站点配置;
|
||||
* 在Notion模板中创建一个类型为CONFIG的页面,再添加一个数据库表格,即可用于填写配置
|
||||
* Notion数据库配置优先级最高,将覆盖vercel环境变量以及blog.config.js中的配置
|
||||
* --注意--
|
||||
* 数据库请从模板复制 https://www.notion.so/tanghh/287869a92e3d4d598cf366bd6994755e
|
||||
*
|
||||
*/
|
||||
import { getDateValue, getTextContent } from 'notion-utils'
|
||||
import { getPostBlocks } from './getPostBlocks'
|
||||
import getAllPageIds from './getAllPageIds'
|
||||
|
||||
/**
|
||||
* 从Notion中读取Config配置表
|
||||
* @param {*} allPages
|
||||
* @returns
|
||||
*/
|
||||
export async function getConfigMapFromConfigPage(allPages) {
|
||||
// 默认返回配置文件
|
||||
const notionConfig = {}
|
||||
|
||||
if (!allPages || !Array.isArray(allPages) || allPages.length === 0) {
|
||||
console.warn('[Notion配置] 忽略的配置')
|
||||
return null
|
||||
}
|
||||
const configPage = allPages?.find(post => {
|
||||
return post && post?.type && post?.type === 'CONFIG'
|
||||
})
|
||||
|
||||
if (!configPage) {
|
||||
console.warn('[Notion配置] 未找到配置页面')
|
||||
return null
|
||||
}
|
||||
const configPageId = configPage.id
|
||||
|
||||
// console.log('[Notion配置]请求配置数据 ', configPage.id)
|
||||
const pageRecordMap = await getPostBlocks(configPageId, 'config-table')
|
||||
// console.log('配置中心Page', configPageId, pageRecordMap)
|
||||
const content = pageRecordMap.block[configPageId].value.content
|
||||
|
||||
if (!content) {
|
||||
console.warn('[Notion配置] 未找到配置表格', pageRecordMap.block[configPageId], pageRecordMap.block[configPageId].value)
|
||||
return null
|
||||
}
|
||||
|
||||
// 找到配置文件中的database
|
||||
// for (const contentId of content) {
|
||||
// console.log('内容', contentId, configPageRecordMap.block[contentId].value.type === 'collection_view')
|
||||
// }
|
||||
const configTableId = content?.find(contentId => {
|
||||
return pageRecordMap.block[contentId].value.type === 'collection_view'
|
||||
})
|
||||
|
||||
// eslint-disable-next-line no-constant-condition, no-self-compare
|
||||
if (!configTableId) {
|
||||
console.warn('[Notion配置]未找到配置表格数据', pageRecordMap.block[configPageId], pageRecordMap.block[configPageId].value)
|
||||
return null
|
||||
}
|
||||
|
||||
// 页面查找
|
||||
const databaseRecordMap = pageRecordMap.block[configTableId]
|
||||
const block = pageRecordMap.block || {}
|
||||
const rawMetadata = databaseRecordMap.value
|
||||
// Check Type Page-Database和Inline-Database
|
||||
if (
|
||||
rawMetadata?.type !== 'collection_view_page' && rawMetadata?.type !== 'collection_view'
|
||||
) {
|
||||
console.error(`pageId "${configTableId}" is not a database`)
|
||||
return null
|
||||
}
|
||||
// console.log('表格', databaseRecordMap, block, rawMetadata)
|
||||
const collectionId = rawMetadata?.collection_id
|
||||
const collection = pageRecordMap.collection[collectionId].value
|
||||
const collectionQuery = pageRecordMap.collection_query
|
||||
const collectionView = pageRecordMap.collection_view
|
||||
const schema = collection?.schema
|
||||
const viewIds = rawMetadata?.view_ids
|
||||
const pageIds = getAllPageIds(collectionQuery, collectionId, collectionView, viewIds)
|
||||
if (pageIds?.length === 0) {
|
||||
console.error('[Notion配置]获取到的文章列表为空,请检查notion模板', collectionQuery, collection, collectionView, viewIds, databaseRecordMap)
|
||||
}
|
||||
// 遍历用户的表格
|
||||
for (let i = 0; i < pageIds.length; i++) {
|
||||
const id = pageIds[i]
|
||||
const value = block[id]?.value
|
||||
if (!value) {
|
||||
continue
|
||||
}
|
||||
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
|
||||
const excludeProperties = ['date', 'select', 'multi_select', 'person']
|
||||
const properties = {}
|
||||
for (let i = 0; i < rawProperties.length; i++) {
|
||||
const [key, val] = rawProperties[i]
|
||||
properties.id = id
|
||||
if (schema[key]?.type && !excludeProperties.includes(schema[key].type)) {
|
||||
properties[schema[key].name] = getTextContent(val)
|
||||
} else {
|
||||
switch (schema[key]?.type) {
|
||||
case 'date': {
|
||||
const dateProperty = getDateValue(val)
|
||||
delete dateProperty.type
|
||||
properties[schema[key].name] = dateProperty
|
||||
break
|
||||
}
|
||||
case 'select':
|
||||
case 'multi_select': {
|
||||
const selects = getTextContent(val)
|
||||
if (selects[0]?.length) {
|
||||
properties[schema[key].name] = selects.split(',')
|
||||
}
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (properties) {
|
||||
// 将表格中的字段映射成 英文
|
||||
const config = {
|
||||
enable: properties['启用'] === 'Yes',
|
||||
key: properties['配置名'],
|
||||
value: properties['配置值']
|
||||
}
|
||||
|
||||
// 只导入生效的配置
|
||||
if (config.enable) {
|
||||
// console.log('[Notion配置]', config.key, config.value)
|
||||
notionConfig[config.key] = config.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return notionConfig
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import getAllPageIds from './getAllPageIds'
|
||||
import { getAllTags } from './getAllTags'
|
||||
import getPageProperties from './getPageProperties'
|
||||
import { mapImgUrl, compressImage } from './mapImage'
|
||||
import { getConfigMapFromConfigPage } from './getNotionConfig'
|
||||
|
||||
/**
|
||||
* 获取博客数据
|
||||
@@ -272,6 +273,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
|
||||
// 文章计数
|
||||
let postCount = 0
|
||||
|
||||
// 查找所有的Post和Page
|
||||
const allPages = collectionData.filter(post => {
|
||||
if (post?.type === 'Post' && post.status === 'Published') {
|
||||
@@ -282,6 +284,9 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
(post?.status === 'Invisible' || post?.status === 'Published')
|
||||
})
|
||||
|
||||
// 站点配置优先读取配置表格,否则读取blog.config.js 文件
|
||||
const NOTION_CONFIG = await getConfigMapFromConfigPage(collectionData) || {}
|
||||
|
||||
// Sort by date
|
||||
if (BLOG.POSTS_SORT_BY === 'date') {
|
||||
allPages.sort((a, b) => {
|
||||
@@ -300,6 +305,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
const allNavPages = getNavPages({ allPages })
|
||||
|
||||
return {
|
||||
NOTION_CONFIG,
|
||||
notice,
|
||||
siteInfo,
|
||||
allPages,
|
||||
|
||||
@@ -6,6 +6,15 @@ import formatDate from '../formatDate'
|
||||
import md5 from 'js-md5'
|
||||
import { mapImgUrl } from './mapImage'
|
||||
|
||||
/**
|
||||
* 获取页面元素成员属性
|
||||
* @param {*} id
|
||||
* @param {*} block
|
||||
* @param {*} schema
|
||||
* @param {*} authToken
|
||||
* @param {*} tagOptions
|
||||
* @returns
|
||||
*/
|
||||
export default async function getPageProperties(id, block, schema, authToken, tagOptions) {
|
||||
const rawProperties = Object.entries(block?.[id]?.value?.properties || [])
|
||||
const excludeProperties = ['date', 'select', 'multi_select', 'person']
|
||||
@@ -108,6 +117,7 @@ export default async function getPageProperties(id, block, schema, authToken, ta
|
||||
properties.slug += '.html'
|
||||
}
|
||||
}
|
||||
// 密码字段md5
|
||||
properties.password = properties.password ? md5(properties.slug + properties.password) : ''
|
||||
return properties
|
||||
}
|
||||
|
||||
50
lib/utils.js
50
lib/utils.js
@@ -75,7 +75,6 @@ export function getQueryVariable(key) {
|
||||
}
|
||||
return (false)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 URL 中指定参数的值
|
||||
* @param {string} url
|
||||
@@ -83,8 +82,10 @@ export function getQueryVariable(key) {
|
||||
* @returns {string|null}
|
||||
*/
|
||||
export function getQueryParam(url, param) {
|
||||
const searchParams = new URLSearchParams(url.split('?')[1])
|
||||
return searchParams.get(param)
|
||||
// 移除哈希部分
|
||||
const urlWithoutHash = url.split('#')[0];
|
||||
const searchParams = new URLSearchParams(urlWithoutHash.split('?')[1]);
|
||||
return searchParams.get(param);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,3 +203,46 @@ export const isMobile = () => {
|
||||
|
||||
return isMobile
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描页面上的所有文本节点,将url格式的文本转为可点击链接
|
||||
* @param {*} node
|
||||
*/
|
||||
export const scanAndConvertToLinks = (node) => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
const text = node.textContent;
|
||||
const urlRegex = /https?:\/\/[^\s]+/g;
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
|
||||
const newNode = document.createElement('span');
|
||||
|
||||
while ((match = urlRegex.exec(text)) !== null) {
|
||||
const beforeText = text.substring(lastIndex, match.index);
|
||||
const url = match[0];
|
||||
|
||||
if (beforeText) {
|
||||
newNode.appendChild(document.createTextNode(beforeText));
|
||||
}
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.target = '_blank'
|
||||
link.textContent = url;
|
||||
|
||||
newNode.appendChild(link);
|
||||
|
||||
lastIndex = urlRegex.lastIndex;
|
||||
}
|
||||
|
||||
if (lastIndex < text.length) {
|
||||
newNode.appendChild(document.createTextNode(text.substring(lastIndex)));
|
||||
}
|
||||
|
||||
node.parentNode.replaceChild(newNode, node);
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
for (const childNode of node.childNodes) {
|
||||
scanAndConvertToLinks(childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user