mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-18 07:26:48 +00:00
Merge branch 'main' into original-main-fix-DOMException
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from './global'
|
||||
import { deepClone } from './utils'
|
||||
import { deepClone, isUrl } from './utils'
|
||||
|
||||
/**
|
||||
* 读取配置顺序
|
||||
@@ -19,7 +19,7 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
||||
return null
|
||||
}
|
||||
|
||||
// 特殊配置处理;某些配置只在服务端生效;而Global的NOTION_CONFIG仅限前端组件使用,因此需要从extendConfig中读取
|
||||
// 特殊配置处理;以下配置只在服务端生效;而Global的NOTION_CONFIG仅限前端组件使用,因此需要从extendConfig中读取
|
||||
switch (key) {
|
||||
case 'NEXT_REVALIDATE_SECOND':
|
||||
case 'POST_RECOMMEND_COUNT':
|
||||
@@ -32,6 +32,8 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
||||
case 'POST_LIST_STYLE':
|
||||
case 'POST_LIST_PREVIEW':
|
||||
case 'POST_URL_PREFIX_MAPPING_CATEGORY':
|
||||
case 'IS_TAG_COLOR_DISTINGUISHED':
|
||||
case 'TAG_SORT_BY_COUNT':
|
||||
return convertVal(extendConfig[key] || defaultVal || BLOG[key])
|
||||
default:
|
||||
}
|
||||
@@ -53,8 +55,8 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
||||
let siteInfo = null
|
||||
|
||||
if (global) {
|
||||
val = global.NOTION_CONFIG?.[key]
|
||||
siteInfo = global.siteInfo
|
||||
val = global.NOTION_CONFIG?.[key] || global.THEME_CONFIG?.[key]
|
||||
}
|
||||
|
||||
if (!val) {
|
||||
@@ -89,46 +91,62 @@ export const siteConfig = (key, defaultVal = null, extendConfig = {}) => {
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// 从Notion_CONFIG读取的配置通常都是字符串,适当转义
|
||||
return convertVal(val)
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置默认都是string类型;
|
||||
* 识别配置的值是否数字、布尔、[]数组,若是则转成对应类型
|
||||
* 从环境变量和NotionConfig读取的配置都是string类型;
|
||||
* 这里识别出配置的字符值若为否 数字、布尔、[]数组,{}对象,若是则转成对应类型
|
||||
* 使用JSON和eval两个函数
|
||||
* @param {*} val
|
||||
* @returns
|
||||
*/
|
||||
export const convertVal = val => {
|
||||
if (typeof val === 'string') {
|
||||
// 解析布尔
|
||||
if (val === 'true' || val === 'false') {
|
||||
return JSON.parse(val)
|
||||
}
|
||||
|
||||
// 解析数字,parseInt将字符串转换为数字
|
||||
if (/^\d+$/.test(val)) {
|
||||
return parseInt(val)
|
||||
}
|
||||
// 转移 [] , {} 这种json串为json对象
|
||||
try {
|
||||
const parsedJson = JSON.parse(val)
|
||||
// 检查解析后的结果是否是对象或数组
|
||||
if (typeof parsedJson === 'object' && parsedJson !== null) {
|
||||
return parsedJson
|
||||
}
|
||||
} catch (error) {
|
||||
// JSON 解析失败,返回原始字符串值
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(val)
|
||||
} catch (error) {
|
||||
// 如果值是一个字符串但不是有效的 JSON 格式,直接返回字符串
|
||||
// 如果传入参数本身就是obj、数组、boolean 就无需处理
|
||||
if (typeof val !== 'string' || !val) {
|
||||
return val
|
||||
}
|
||||
|
||||
// 解析数字,parseInt将字符串转换为数字
|
||||
if (/^\d+$/.test(val)) {
|
||||
return parseInt(val)
|
||||
}
|
||||
|
||||
// 检测是否url
|
||||
if (isUrl(val)) {
|
||||
return val
|
||||
}
|
||||
// 检测是否url
|
||||
if (val === 'true' || val === 'false') {
|
||||
return JSON.parse(val)
|
||||
}
|
||||
|
||||
// 配置值前可能有污染的空格
|
||||
if (val.indexOf('[') < 0 && val.indexOf('{') < 0) {
|
||||
return val
|
||||
}
|
||||
|
||||
// 转换 [] , {} , true/false 这类字符串为对象
|
||||
try {
|
||||
// 尝试解析json
|
||||
const parsedJson = JSON.parse(val)
|
||||
if (parsedJson !== null) {
|
||||
return parsedJson
|
||||
}
|
||||
} catch (error) {
|
||||
// try {
|
||||
// // 尝试解析对象,对象解析能力不如上一步的json
|
||||
// const evalObj = eval('(' + val + ')')
|
||||
// if (evalObj !== null) {
|
||||
// return evalObj
|
||||
// }
|
||||
// } catch (error) {
|
||||
// // Ojbject 解析失败,返回原始字符串值
|
||||
// return val
|
||||
// }
|
||||
return val
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,18 +5,18 @@ import getAllPageIds from '@/lib/notion/getAllPageIds'
|
||||
import { getAllTags } from '@/lib/notion/getAllTags'
|
||||
import { getConfigMapFromConfigPage } from '@/lib/notion/getNotionConfig'
|
||||
import getPageProperties, {
|
||||
adjustPageProperties
|
||||
adjustPageProperties
|
||||
} from '@/lib/notion/getPageProperties'
|
||||
import { fetchInBatches, getPostBlocks } from '@/lib/notion/getPostBlocks'
|
||||
import { fetchInBatches, getPage } from '@/lib/notion/getPostBlocks'
|
||||
import { compressImage, mapImgUrl } from '@/lib/notion/mapImage'
|
||||
import { deepClone } from '@/lib/utils'
|
||||
import { idToUuid } from 'notion-utils'
|
||||
import { siteConfig } from '../config'
|
||||
import { extractLangId, extractLangPrefix } from '../utils/pageId'
|
||||
import { extractLangId, extractLangPrefix, getShortId } from '../utils/pageId'
|
||||
|
||||
export { getAllTags } from '../notion/getAllTags'
|
||||
export { getPost } from '../notion/getNotionPost'
|
||||
export { getPostBlocks } from '../notion/getPostBlocks'
|
||||
export { getPage as getPostBlocks } from '../notion/getPostBlocks'
|
||||
|
||||
/**
|
||||
* 获取博客数据; 基于Notion实现
|
||||
@@ -77,7 +77,18 @@ export async function getNotionPageData({ pageId, from }) {
|
||||
}
|
||||
|
||||
// 返回给前端的数据做处理
|
||||
const db = deepClone(data)
|
||||
return handleDataBeforeReturn(deepClone(data))
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回给浏览器前端的数据处理
|
||||
* 适当脱敏
|
||||
* 减少体积
|
||||
* 其它处理
|
||||
* @param {*} db
|
||||
*/
|
||||
function handleDataBeforeReturn(db) {
|
||||
// 清理多余数据
|
||||
delete db.block
|
||||
delete db.schema
|
||||
delete db.rawMetadata
|
||||
@@ -91,42 +102,88 @@ export async function getNotionPageData({ pageId, from }) {
|
||||
// 清理多余的块
|
||||
if (db?.notice) {
|
||||
db.notice = cleanBlock(db?.notice)
|
||||
delete db.notice?.id
|
||||
}
|
||||
if (db?.post) {
|
||||
db.post = cleanBlock(db?.post)
|
||||
}
|
||||
|
||||
db.tagOptions = cleanIds(db?.tagOptions)
|
||||
db.categoryOptions = cleanIds(db?.categoryOptions)
|
||||
db.customMenu = cleanIds(db?.customMenu)
|
||||
|
||||
// db.latestPosts = shortenIds(db?.latestPosts)
|
||||
db.allNavPages = shortenIds(db?.allNavPages)
|
||||
// db.allPages = cleanBlocks(db?.allPages)
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理一组数据的id
|
||||
* @param {*} items
|
||||
* @returns
|
||||
*/
|
||||
function shortenIds(items) {
|
||||
if (items && Array.isArray(items)) {
|
||||
return deepClone(
|
||||
items.map(item => {
|
||||
item.short_id = getShortId(item.id)
|
||||
delete item.id
|
||||
return item
|
||||
})
|
||||
)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理一组数据的id
|
||||
* @param {*} items
|
||||
* @returns
|
||||
*/
|
||||
function cleanIds(items) {
|
||||
if (items && Array.isArray(items)) {
|
||||
return deepClone(
|
||||
items.map(item => {
|
||||
delete item.id
|
||||
return item
|
||||
})
|
||||
)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理block数据
|
||||
*/
|
||||
function cleanBlock(post) {
|
||||
function cleanBlock(item) {
|
||||
const post = deepClone(item)
|
||||
const pageBlock = post?.blockMap?.block
|
||||
for (const i in pageBlock) {
|
||||
pageBlock[i] = cleanBlock(pageBlock[i])
|
||||
delete pageBlock[i]?.role
|
||||
delete pageBlock[i]?.value?.version
|
||||
delete pageBlock[i]?.value?.created_by_table
|
||||
delete pageBlock[i]?.value?.created_by_id
|
||||
delete pageBlock[i]?.value?.last_edited_by_table
|
||||
delete pageBlock[i]?.value?.last_edited_by_id
|
||||
delete pageBlock[i]?.value?.space_id
|
||||
delete pageBlock[i]?.value?.version
|
||||
delete pageBlock[i]?.value?.format?.copied_from_pointer
|
||||
delete pageBlock[i]?.value?.format?.block_locked_by
|
||||
delete pageBlock[i]?.value?.parent_table
|
||||
delete pageBlock[i]?.value?.copied_from_pointer
|
||||
delete pageBlock[i]?.value?.copied_from
|
||||
delete pageBlock[i]?.value?.created_by_table
|
||||
delete pageBlock[i]?.value?.created_by_id
|
||||
delete pageBlock[i]?.value?.last_edited_by_table
|
||||
delete pageBlock[i]?.value?.last_edited_by_id
|
||||
delete pageBlock[i]?.value?.permissions
|
||||
delete pageBlock[i]?.value?.alive
|
||||
}
|
||||
|
||||
// delete post?.id
|
||||
// delete post?.blockMap?.collection
|
||||
|
||||
if (pageBlock) {
|
||||
for (const i in pageBlock) {
|
||||
pageBlock[i] = cleanBlock(pageBlock[i])
|
||||
delete pageBlock[i]?.role
|
||||
delete pageBlock[i]?.value?.version
|
||||
delete pageBlock[i]?.value?.created_by_table
|
||||
delete pageBlock[i]?.value?.created_by_id
|
||||
delete pageBlock[i]?.value?.last_edited_by_table
|
||||
delete pageBlock[i]?.value?.last_edited_by_id
|
||||
delete pageBlock[i]?.value?.space_id
|
||||
delete pageBlock[i]?.value?.version
|
||||
delete pageBlock[i]?.value?.format?.copied_from_pointer
|
||||
delete pageBlock[i]?.value?.format?.block_locked_by
|
||||
delete pageBlock[i]?.value?.parent_table
|
||||
delete pageBlock[i]?.value?.copied_from_pointer
|
||||
delete pageBlock[i]?.value?.copied_from
|
||||
delete pageBlock[i]?.value?.created_by_table
|
||||
delete pageBlock[i]?.value?.created_by_id
|
||||
delete pageBlock[i]?.value?.last_edited_by_table
|
||||
delete pageBlock[i]?.value?.last_edited_by_id
|
||||
delete pageBlock[i]?.value?.permissions
|
||||
delete pageBlock[i]?.value?.alive
|
||||
}
|
||||
}
|
||||
return post
|
||||
}
|
||||
|
||||
@@ -290,7 +347,6 @@ export function getNavPages({ allPages }) {
|
||||
return (
|
||||
post &&
|
||||
post?.slug &&
|
||||
!post?.slug?.startsWith('http') &&
|
||||
post?.type === 'Post' &&
|
||||
post?.status === 'Published'
|
||||
)
|
||||
@@ -320,7 +376,7 @@ async function getNotice(post) {
|
||||
return null
|
||||
}
|
||||
|
||||
post.blockMap = await getPostBlocks(post.id, 'data-notice')
|
||||
post.blockMap = await getPage(post.id, 'data-notice')
|
||||
return post
|
||||
}
|
||||
|
||||
@@ -372,7 +428,7 @@ const EmptyData = pageId => {
|
||||
*/
|
||||
async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
console.log('[Fetching Data]', pageId, from)
|
||||
const pageRecordMap = await getPostBlocks(pageId, from)
|
||||
const pageRecordMap = await getPage(pageId, from)
|
||||
if (!pageRecordMap) {
|
||||
console.error('can`t get Notion Data ; Which id is: ', pageId)
|
||||
return {}
|
||||
@@ -455,6 +511,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
adjustPageProperties(element, NOTION_CONFIG)
|
||||
})
|
||||
|
||||
// 站点基础信息
|
||||
const siteInfo = getSiteInfo({ collection, block, pageId })
|
||||
|
||||
// 文章计数
|
||||
@@ -468,7 +525,7 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
return (
|
||||
post &&
|
||||
post?.slug &&
|
||||
!post?.slug?.startsWith('http') &&
|
||||
// !post?.slug?.startsWith('http') &&
|
||||
(post?.status === 'Invisible' || post?.status === 'Published')
|
||||
)
|
||||
})
|
||||
@@ -490,11 +547,17 @@ async function getDataBaseInfoByNotionAPI({ pageId, from }) {
|
||||
)
|
||||
})?.[0]
|
||||
)
|
||||
// 所有分类
|
||||
const categoryOptions = getAllCategories({
|
||||
allPages,
|
||||
categoryOptions: getCategoryOptions(schema)
|
||||
})
|
||||
const tagOptions = getAllTags({ allPages, tagOptions: getTagOptions(schema) })
|
||||
// 所有标签
|
||||
const tagOptions = getAllTags({
|
||||
allPages,
|
||||
tagOptions: getTagOptions(schema),
|
||||
NOTION_CONFIG
|
||||
})
|
||||
// 旧的菜单
|
||||
const customNav = getCustomNav({
|
||||
allPages: collectionData.filter(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
THEMES,
|
||||
getThemeConfig,
|
||||
initDarkMode,
|
||||
saveDarkModeToLocalStorage
|
||||
} from '@/themes/theme'
|
||||
@@ -29,11 +30,14 @@ export function GlobalContextProvider(props) {
|
||||
tagOptions,
|
||||
NOTION_CONFIG
|
||||
} = props
|
||||
|
||||
const [lang, updateLang] = useState(NOTION_CONFIG?.LANG || LANG) // 默认语言
|
||||
const [locale, updateLocale] = useState(
|
||||
generateLocaleDict(NOTION_CONFIG?.LANG || LANG)
|
||||
) // 默认语言
|
||||
const [theme, setTheme] = useState(NOTION_CONFIG?.THEME || THEME) // 默认博客主题
|
||||
const [THEME_CONFIG, SET_THEME_CONFIG] = useState(null) // 主题配置
|
||||
|
||||
const defaultDarkMode = NOTION_CONFIG?.APPEARANCE || APPEARANCE
|
||||
const [isDarkMode, updateDarkMode] = useState(defaultDarkMode === 'dark') // 默认深色模式
|
||||
const [onLoading, setOnLoading] = useState(false) // 抓取文章数据
|
||||
@@ -54,6 +58,12 @@ export function GlobalContextProvider(props) {
|
||||
return newTheme
|
||||
}
|
||||
|
||||
// 抓取主题配置
|
||||
const updateThemeConfig = async theme => {
|
||||
const config = await getThemeConfig(theme)
|
||||
SET_THEME_CONFIG(config)
|
||||
}
|
||||
|
||||
// 切换深色模式
|
||||
const toggleDarkMode = () => {
|
||||
const newStatus = !isDarkMode
|
||||
@@ -79,7 +89,10 @@ export function GlobalContextProvider(props) {
|
||||
useEffect(() => {
|
||||
initDarkMode(updateDarkMode, defaultDarkMode)
|
||||
initLocale(lang, locale, updateLang, updateLocale)
|
||||
redirectUserLang(NOTION_PAGE_ID)
|
||||
// 可以
|
||||
if (NOTION_CONFIG?.REDIRECT_LANG) {
|
||||
redirectUserLang(NOTION_PAGE_ID)
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 加载进度条
|
||||
@@ -96,6 +109,9 @@ export function GlobalContextProvider(props) {
|
||||
setOnLoading(false)
|
||||
}
|
||||
|
||||
const currentTheme = router?.query?.theme || theme
|
||||
updateThemeConfig(currentTheme)
|
||||
|
||||
router.events.on('routeChangeStart', handleStart)
|
||||
router.events.on('routeChangeError', handleStop)
|
||||
router.events.on('routeChangeComplete', handleStop)
|
||||
@@ -111,6 +127,7 @@ export function GlobalContextProvider(props) {
|
||||
value={{
|
||||
fullWidth,
|
||||
NOTION_CONFIG,
|
||||
THEME_CONFIG,
|
||||
toggleDarkMode,
|
||||
onLoading,
|
||||
setOnLoading,
|
||||
|
||||
@@ -14,11 +14,15 @@ export default {
|
||||
INDEX: 'Home',
|
||||
RSS: 'RSS',
|
||||
SEARCH: 'Search',
|
||||
NAVIGATOR: 'NAV',
|
||||
ABOUT: 'About',
|
||||
MAIL: 'E-Mail',
|
||||
ARCHIVE: 'Archive'
|
||||
},
|
||||
COMMON: {
|
||||
THEME: 'Theme',
|
||||
ARTICLE_LIST: 'Article List',
|
||||
RECOMMEND_POSTS: 'Recommend Posts',
|
||||
MORE: 'More',
|
||||
NO_MORE: 'No More',
|
||||
LATEST_POSTS: 'Latest posts',
|
||||
@@ -33,15 +37,21 @@ export default {
|
||||
COPYRIGHT: 'Copyright',
|
||||
AUTHOR: 'Author',
|
||||
URL: 'URL',
|
||||
NOW: 'NOW',
|
||||
RECOMMEND_BADGES: 'Recommend',
|
||||
BLOG: 'Blog',
|
||||
POSTS: 'Posts',
|
||||
ARTICLE: 'Article',
|
||||
VISITORS: 'Visitors',
|
||||
VIEWS: 'Views',
|
||||
COPYRIGHT_NOTICE: 'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!',
|
||||
PAGE_URL_COPIED: 'Page URL copied',
|
||||
COPYRIGHT_NOTICE:
|
||||
'All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!',
|
||||
RESULT_OF_SEARCH: 'Results Found',
|
||||
ARTICLE_DETAIL: 'Article Details',
|
||||
PASSWORD_ERROR: 'Password Error!',
|
||||
ARTICLE_LOCK_TIPS: 'Please Enter the password:',
|
||||
ARTICLE_UNLOCK_TIPS: 'Article Unlocked',
|
||||
NO_RESULTS_FOUND: 'No results found.',
|
||||
SUBMIT: 'Submit',
|
||||
POST_TIME: 'Post on',
|
||||
@@ -55,7 +65,8 @@ export default {
|
||||
START_READING: 'Start Reading',
|
||||
MINUTE: 'min',
|
||||
WORD_COUNT: 'Words',
|
||||
READ_TIME: 'Read Time'
|
||||
READ_TIME: 'Read Time',
|
||||
NEXT_POST: '下一篇'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: 'Prev',
|
||||
|
||||
@@ -29,11 +29,14 @@ export default {
|
||||
ARTICLE: '記事',
|
||||
VISITORS: '人の訪問者',
|
||||
VIEWS: '回の閲覧',
|
||||
COPYRIGHT_NOTICE: 'この記事はCC BY-NC-SA 4.0 ライセンスの下でライセンスされています。転載する場合には出典を明らかにしてください。',
|
||||
COPYRIGHT_NOTICE:
|
||||
'この記事はCC BY-NC-SA 4.0 ライセンスの下でライセンスされています。転載する場合には出典を明らかにしてください。',
|
||||
RESULT_OF_SEARCH: '個の検索結果',
|
||||
ARTICLE_DETAIL: '記事の詳細',
|
||||
PASSWORD_ERROR: 'パスワードが違います!',
|
||||
ARTICLE_LOCK_TIPS: 'この記事はロックされています。アクセスパスワードを入力してください。',
|
||||
ARTICLE_LOCK_TIPS:
|
||||
'この記事はロックされています。アクセスパスワードを入力してください。',
|
||||
ARTICLE_UNLOCK_TIPS: '記事がロック解除されました',
|
||||
SUBMIT: '送信',
|
||||
POST_TIME: '公開日',
|
||||
LAST_EDITED_TIME: '最終更新日',
|
||||
|
||||
@@ -20,6 +20,9 @@ export default {
|
||||
ARCHIVE: '归档'
|
||||
},
|
||||
COMMON: {
|
||||
THEME: 'Theme',
|
||||
ARTICLE_LIST: '文章列表',
|
||||
RECOMMEND_POSTS: '推荐文章',
|
||||
MORE: '更多',
|
||||
NO_MORE: '没有更多了',
|
||||
LATEST_POSTS: '最新发布',
|
||||
@@ -35,16 +38,21 @@ export default {
|
||||
AUTHOR: '作者',
|
||||
URL: '链接',
|
||||
ANALYTICS: '统计',
|
||||
RECOMMEND_BADGES: '荐',
|
||||
BLOG: '博客',
|
||||
NOW: '此刻',
|
||||
POSTS: '篇文章',
|
||||
ARTICLE: '文章',
|
||||
VISITORS: '位访客',
|
||||
VIEWS: '次查看',
|
||||
PAGE_URL_COPIED: '页面地址已复制',
|
||||
COPYRIGHT_NOTICE: '本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。',
|
||||
RESULT_OF_SEARCH: '篇搜索到的结果',
|
||||
NO_RESULTS_FOUND: '没有找到文章',
|
||||
ARTICLE_DETAIL: '文章详情',
|
||||
PASSWORD_ERROR: '密码错误!',
|
||||
ARTICLE_LOCK_TIPS: '文章已上锁,请输入访问密码',
|
||||
ARTICLE_UNLOCK_TIPS: '文章已解锁',
|
||||
SUBMIT: '提交',
|
||||
POST_TIME: '发布于',
|
||||
LAST_EDITED_TIME: '最后更新',
|
||||
@@ -57,7 +65,8 @@ export default {
|
||||
START_READING: '开始阅读',
|
||||
MINUTE: '分钟',
|
||||
WORD_COUNT: '字数',
|
||||
READ_TIME: '阅读时长'
|
||||
READ_TIME: '阅读时长',
|
||||
NEXT_POST: '下一篇'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: '上页',
|
||||
|
||||
@@ -5,7 +5,39 @@ export default {
|
||||
RSS: '訂閱',
|
||||
SEARCH: '搜尋',
|
||||
ABOUT: '關於',
|
||||
MAIL: '電郵'
|
||||
MAIL: '電郵',
|
||||
NAVIGATOR: '導航',
|
||||
ARCHIVE: '封存'
|
||||
},
|
||||
COMMON: {
|
||||
ARTICLE_LIST: '文章列表',
|
||||
MORE: '更多',
|
||||
NO_MORE: '沒有更多了',
|
||||
LATEST_POSTS: '最新文章',
|
||||
TAGS: '標籤',
|
||||
NO_TAG: '無標籤',
|
||||
CATEGORY: '分類',
|
||||
SHARE: '分享',
|
||||
SCAN_QR_CODE: 'QRCode',
|
||||
URL_COPIED: '連結已複製!',
|
||||
TABLE_OF_CONTENTS: '目錄',
|
||||
RELATE_POSTS: '相關文章',
|
||||
COPYRIGHT: '著作權',
|
||||
AUTHOR: '作者',
|
||||
URL: '連結',
|
||||
ANALYTICS: '分析',
|
||||
POSTS: '篇文章',
|
||||
ARTICLE: '文章',
|
||||
VISITORS: '位訪客',
|
||||
VIEWS: '次查看',
|
||||
COPYRIGHT_NOTICE: '本文採用 CC BY-NC-SA 4.0 許可協議,轉載請註明出處。',
|
||||
RESULT_OF_SEARCH: '篇搜尋到的结果',
|
||||
ARTICLE_DETAIL: '完整文章',
|
||||
PASSWORD_ERROR: '密碼錯誤!',
|
||||
ARTICLE_LOCK_TIPS: '文章已上鎖,請輸入訪問密碼',
|
||||
SUBMIT: '提交',
|
||||
POST_TIME: '发布于',
|
||||
LAST_EDITED_TIME: '最后更新'
|
||||
},
|
||||
PAGINATION: {
|
||||
PREV: '上一頁',
|
||||
@@ -13,7 +45,7 @@ export default {
|
||||
},
|
||||
SEARCH: {
|
||||
ARTICLES: '搜尋文章',
|
||||
TAGS: '搜尋標簽'
|
||||
TAGS: '搜尋標籤'
|
||||
},
|
||||
POST: {
|
||||
BACK: '返回',
|
||||
|
||||
@@ -10,6 +10,7 @@ export default {
|
||||
ARCHIVE: '封存'
|
||||
},
|
||||
COMMON: {
|
||||
ARTICLE_LIST: '文章列表',
|
||||
MORE: '更多',
|
||||
NO_MORE: '沒有更多了',
|
||||
LATEST_POSTS: '最新文章',
|
||||
|
||||
@@ -1,33 +1,44 @@
|
||||
import { uuidToId } from 'notion-utils'
|
||||
import { idToUuid } from 'notion-utils'
|
||||
import { checkStrIsNotionId, getLastPartOfUrl, isBrowser } from '../utils'
|
||||
|
||||
/**
|
||||
* 处理页面内连接跳转:
|
||||
* 1. 若是本站域名,则在当前窗口打开、不开新窗口
|
||||
* 2. 若是Notion笔记中的内链,尝试转换成博客中现有的文章地址
|
||||
* 1.若是本站域名,则在当前窗口打开、不开新窗口
|
||||
* 2.url是notion-id,转成站内文章链接
|
||||
*/
|
||||
export const mapPageUrl = allPages => {
|
||||
export const convertInnerUrl = allPages => {
|
||||
if (isBrowser) {
|
||||
const allAnchorTags = document
|
||||
?.getElementById('notion-article')
|
||||
?.getElementsByTagName('a')
|
||||
|
||||
if (!allAnchorTags) {
|
||||
return
|
||||
}
|
||||
const currentURL = window.location.origin + window.location.pathname
|
||||
const allAnchorTags = document.getElementsByTagName('a') // 或者使用 document.querySelectorAll('a') 获取 NodeList
|
||||
// url替换成slug
|
||||
for (const anchorTag of allAnchorTags) {
|
||||
// 检查url
|
||||
if (anchorTag?.href) {
|
||||
// 如果url是一个Notion_id,尝试匹配成博客的文章内链
|
||||
const slug = getLastPartOfUrl(anchorTag.href)
|
||||
if (checkStrIsNotionId(slug)) {
|
||||
const slugPage = allPages?.find(page => uuidToId(page.id) === slug)
|
||||
const slugPage = allPages?.find(page => {
|
||||
return idToUuid(slug).indexOf(page.short_id) === 0
|
||||
})
|
||||
if (slugPage) {
|
||||
anchorTag.href = slugPage?.href
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 链接在当前页面打开
|
||||
for (const anchorTag of allAnchorTags) {
|
||||
if (anchorTag?.target === '_blank') {
|
||||
const hrefWithoutQueryHash = anchorTag.href.split('?')[0].split('#')[0]
|
||||
const hrefWithRelativeHash =
|
||||
currentURL.split('#')[0] + anchorTag.href.split('#')[1]
|
||||
|
||||
currentURL.split('#')[0] || '' + anchorTag.href.split('#')[1] || ''
|
||||
if (
|
||||
currentURL === hrefWithoutQueryHash ||
|
||||
currentURL === hrefWithRelativeHash
|
||||
@@ -4,7 +4,7 @@ import { isIterable } from '../utils'
|
||||
* 获取所有文章的标签
|
||||
* @param allPosts
|
||||
* @param sliceCount 默认截取数量为12,若为0则返回全部
|
||||
* @param tagOptions tags的下拉选项
|
||||
* @param categoryOptions categories的下拉选项
|
||||
* @returns {Promise<{}|*[]>}
|
||||
*/
|
||||
|
||||
@@ -13,8 +13,14 @@ import { isIterable } from '../utils'
|
||||
* @param allPosts
|
||||
* @returns {Promise<{}|*[]>}
|
||||
*/
|
||||
export function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) {
|
||||
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
export function getAllCategories({
|
||||
allPages,
|
||||
categoryOptions,
|
||||
sliceCount = 0
|
||||
}) {
|
||||
const allPosts = allPages?.filter(
|
||||
page => page.type === 'Post' && page.status === 'Published'
|
||||
)
|
||||
if (!allPosts || !categoryOptions) {
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { siteConfig } from '../config'
|
||||
import { isIterable } from '../utils'
|
||||
import BLOG from '@/blog.config'
|
||||
|
||||
/**
|
||||
* 获取所有文章的标签
|
||||
@@ -8,8 +8,15 @@ import BLOG from '@/blog.config'
|
||||
* @param tagOptions tags的下拉选项
|
||||
* @returns {Promise<{}|*[]>}
|
||||
*/
|
||||
export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
||||
const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published')
|
||||
export function getAllTags({
|
||||
allPages,
|
||||
sliceCount = 0,
|
||||
tagOptions,
|
||||
NOTION_CONFIG
|
||||
}) {
|
||||
const allPosts = allPages?.filter(
|
||||
page => page.type === 'Post' && page.status === 'Published'
|
||||
)
|
||||
|
||||
if (!allPosts || !tagOptions) {
|
||||
return []
|
||||
@@ -27,7 +34,12 @@ export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
||||
})
|
||||
|
||||
const list = []
|
||||
const { IS_TAG_COLOR_DISTINGUISHED } = BLOG
|
||||
const IS_TAG_COLOR_DISTINGUISHED = siteConfig(
|
||||
'IS_TAG_COLOR_DISTINGUISHED',
|
||||
false,
|
||||
NOTION_CONFIG
|
||||
)
|
||||
const TAG_SORT_BY_COUNT = siteConfig('TAG_SORT_BY_COUNT', true, NOTION_CONFIG)
|
||||
if (isIterable(tagOptions)) {
|
||||
if (!IS_TAG_COLOR_DISTINGUISHED) {
|
||||
// 如果不区分颜色, 那么不同颜色相同名称的tag当做同一种tag
|
||||
@@ -52,7 +64,10 @@ export function getAllTags({ allPages, sliceCount = 0, tagOptions }) {
|
||||
}
|
||||
|
||||
// 按照数量排序
|
||||
// list.sort((a, b) => b.count - a.count)
|
||||
if (TAG_SORT_BY_COUNT) {
|
||||
list.sort((a, b) => b.count - a.count)
|
||||
}
|
||||
|
||||
if (sliceCount && sliceCount > 0) {
|
||||
return list.slice(0, sliceCount)
|
||||
} else {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { getDateValue, getTextContent } from 'notion-utils'
|
||||
import { deepClone } from '../utils'
|
||||
import getAllPageIds from './getAllPageIds'
|
||||
import { getPostBlocks } from './getPostBlocks'
|
||||
import { getPage } from './getPostBlocks'
|
||||
|
||||
/**
|
||||
* 从Notion中读取Config配置表
|
||||
@@ -41,12 +41,12 @@ export async function getConfigMapFromConfigPage(allPages) {
|
||||
}
|
||||
const configPageId = configPage.id
|
||||
// console.log('[Notion配置]请求配置数据 ', configPage.id)
|
||||
let pageRecordMap = await getPostBlocks(configPageId, 'config-table')
|
||||
let pageRecordMap = await getPage(configPageId, 'config-table')
|
||||
// console.log('配置中心Page', configPageId, pageRecordMap)
|
||||
let content = pageRecordMap.block[configPageId].value.content
|
||||
for (const table of ['Config-Table', 'CONFIG-TABLE']) {
|
||||
if (content) break
|
||||
pageRecordMap = await getPostBlocks(configPageId, table)
|
||||
pageRecordMap = await getPage(configPageId, table)
|
||||
content = pageRecordMap.block[configPageId].value.content
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import BLOG from '@/blog.config'
|
||||
import { idToUuid } from 'notion-utils'
|
||||
import { defaultMapImageUrl } from 'react-notion-x'
|
||||
import formatDate from '../utils/formatDate'
|
||||
import { getPostBlocks } from './getPostBlocks'
|
||||
import { getPage } from './getPostBlocks'
|
||||
|
||||
/**
|
||||
* 根据页面ID获取内容
|
||||
@@ -10,7 +10,7 @@ import { getPostBlocks } from './getPostBlocks'
|
||||
* @returns
|
||||
*/
|
||||
export async function getPost(pageId) {
|
||||
const blockMap = await getPostBlocks(pageId, 'slug')
|
||||
const blockMap = await getPage(pageId, 'slug')
|
||||
if (!blockMap) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ import formatDate from '../utils/formatDate'
|
||||
import md5 from 'js-md5'
|
||||
import { siteConfig } from '../config'
|
||||
import {
|
||||
checkContainHttp,
|
||||
convertUrlStartWithOneSlash,
|
||||
sliceUrlFromHttp
|
||||
checkStartWithHttp,
|
||||
convertUrlStartWithOneSlash,
|
||||
getLastSegmentFromUrl
|
||||
} from '../utils'
|
||||
import { extractLangPrefix } from '../utils/pageId'
|
||||
import { mapImgUrl } from './mapImage'
|
||||
|
||||
/**
|
||||
@@ -94,6 +95,7 @@ export default async function getPageProperties(
|
||||
properties.type = properties.type?.[0] || ''
|
||||
properties.status = properties.status?.[0] || ''
|
||||
properties.category = properties.category?.[0] || ''
|
||||
properties.comment = properties.comment?.[0] || ''
|
||||
|
||||
// 映射值:用户个性化type和status字段的下拉框选项,在此映射回代码的英文标识
|
||||
mapProperties(properties)
|
||||
@@ -185,26 +187,38 @@ export function adjustPageProperties(properties, NOTION_CONFIG) {
|
||||
properties.name = properties.title ?? ''
|
||||
}
|
||||
|
||||
// 开启伪静态路径
|
||||
if (JSON.parse(NOTION_CONFIG?.PSEUDO_STATIC || BLOG.PSEUDO_STATIC)) {
|
||||
if (
|
||||
!properties?.href?.endsWith('.html') &&
|
||||
!properties?.href?.startsWith('http')
|
||||
) {
|
||||
properties.href += '.html'
|
||||
}
|
||||
}
|
||||
|
||||
// 检查处理外链
|
||||
properties.href = checkContainHttp(properties?.href)
|
||||
? sliceUrlFromHttp(properties?.href)
|
||||
: convertUrlStartWithOneSlash(properties?.href)
|
||||
|
||||
// 设置链接在页内或新页面打开
|
||||
if (properties.href?.indexOf('http') === 0) {
|
||||
// http or https 开头的视为外链
|
||||
if (checkStartWithHttp(properties?.href)) {
|
||||
properties.href = properties?.slug
|
||||
properties.target = '_blank'
|
||||
} else {
|
||||
properties.target = '_self'
|
||||
// 伪静态路径右侧拼接.html
|
||||
if (siteConfig('PSEUDO_STATIC', false, NOTION_CONFIG)) {
|
||||
if (
|
||||
!properties?.href?.endsWith('.html') &&
|
||||
properties?.href !== '' &&
|
||||
properties?.href !== '#' &&
|
||||
properties?.href !== '/'
|
||||
) {
|
||||
properties.href += '.html'
|
||||
}
|
||||
}
|
||||
|
||||
// 相对路径转绝对路径:url左侧拼接 /
|
||||
properties.href = convertUrlStartWithOneSlash(properties?.href)
|
||||
}
|
||||
|
||||
// 如果跳转链接是多语言,则在新窗口打开
|
||||
if (BLOG.NOTION_PAGE_ID.indexOf(',') > 0) {
|
||||
const siteIds = BLOG.NOTION_PAGE_ID.split(',')
|
||||
for (let index = 0; index < siteIds.length; index++) {
|
||||
const siteId = siteIds[index]
|
||||
const prefix = extractLangPrefix(siteId)
|
||||
if (getLastSegmentFromUrl(properties.href) === prefix) {
|
||||
properties.target = '_blank'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 密码字段md5
|
||||
@@ -221,6 +235,10 @@ export function adjustPageProperties(properties, NOTION_CONFIG) {
|
||||
* @returns
|
||||
*/
|
||||
function generateCustomizeSlug(postProperties, NOTION_CONFIG) {
|
||||
// 外链不处理
|
||||
if (checkStartWithHttp(postProperties.slug)) {
|
||||
return postProperties.slug
|
||||
}
|
||||
let fullPrefix = ''
|
||||
const allSlugPatterns = siteConfig(
|
||||
'POST_URL_PREFIX',
|
||||
@@ -272,5 +290,10 @@ function generateCustomizeSlug(postProperties, NOTION_CONFIG) {
|
||||
if (fullPrefix.endsWith('/')) {
|
||||
fullPrefix = fullPrefix.substring(0, fullPrefix.length - 1) // 去掉尾部部的"/"
|
||||
}
|
||||
return `${fullPrefix}/${postProperties.slug ?? postProperties.id}`
|
||||
|
||||
if(fullPrefix){
|
||||
return `${fullPrefix}/${postProperties.slug ?? postProperties.id}`
|
||||
}else{
|
||||
return `${postProperties.slug ?? postProperties.id}`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { deepClone, delay } from '../utils'
|
||||
* @param {*} slice
|
||||
* @returns
|
||||
*/
|
||||
export async function getPostBlocks(id, from, slice) {
|
||||
export async function getPage(id, from, slice) {
|
||||
const cacheKey = 'page_block_' + id
|
||||
let pageBlock = await getDataFromCache(cacheKey)
|
||||
if (pageBlock) {
|
||||
@@ -27,28 +27,6 @@ export async function getPostBlocks(id, from, slice) {
|
||||
return pageBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对在getDataBaseInfoByNotionAPI=>getPostBlocks中获取不到的溢出的block-id,用此方法另外抓取
|
||||
* @param {*} id
|
||||
* @param {*} from
|
||||
* @returns
|
||||
*/
|
||||
export async function getSingleBlock(id, from) {
|
||||
const cacheKey = 'single_block_' + id
|
||||
let pageBlock = await getDataFromCache(cacheKey)
|
||||
if (pageBlock) {
|
||||
// console.log('[API<<--缓存]', `from:${from}`, cacheKey)
|
||||
return pageBlock
|
||||
}
|
||||
|
||||
pageBlock = await getPageWithRetry(id, 'single_' + from)
|
||||
|
||||
if (pageBlock) {
|
||||
await setDataToCache(cacheKey, pageBlock)
|
||||
}
|
||||
return pageBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用接口,失败会重试
|
||||
* @param {*} id
|
||||
@@ -103,49 +81,69 @@ export async function getPageWithRetry(id, from, retryAttempts = 3) {
|
||||
function filterPostBlocks(id, blockMap, slice) {
|
||||
const clonePageBlock = deepClone(blockMap)
|
||||
let count = 0
|
||||
const blocksToProcess = Object.keys(clonePageBlock?.block || {})
|
||||
|
||||
// 循环遍历文档的每个block
|
||||
for (const i in clonePageBlock?.block) {
|
||||
const b = clonePageBlock?.block[i]
|
||||
if (slice && slice > 0 && count > slice) {
|
||||
delete clonePageBlock?.block[i]
|
||||
continue
|
||||
for (let i = 0; i < blocksToProcess.length; i++) {
|
||||
const blockId = blocksToProcess[i]
|
||||
const b = clonePageBlock?.block[blockId]
|
||||
|
||||
if (slice && slice > 0 && count > slice) {
|
||||
delete clonePageBlock?.block[blockId]
|
||||
continue
|
||||
}
|
||||
|
||||
// 当BlockId等于PageId时移除
|
||||
if (b?.value?.id === id) {
|
||||
// 此block含有敏感信息
|
||||
delete b?.value?.properties
|
||||
continue
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
if (b?.value?.type === 'sync_block' && b?.value?.children) {
|
||||
const childBlocks = b.value.children
|
||||
// 移除同步块
|
||||
delete clonePageBlock.block[blockId]
|
||||
// 用子块替代同步块
|
||||
childBlocks.forEach((childBlock, index) => {
|
||||
const newBlockId = `${blockId}_child_${index}`
|
||||
clonePageBlock.block[newBlockId] = childBlock
|
||||
blocksToProcess.splice(i + index + 1, 0, newBlockId)
|
||||
})
|
||||
// 重新处理新加入的子块
|
||||
i--
|
||||
continue
|
||||
}
|
||||
|
||||
// 处理 c++、c#、汇编等语言名字映射
|
||||
if (b?.value?.type === 'code') {
|
||||
if (b?.value?.properties?.language?.[0][0] === 'C++') {
|
||||
b.value.properties.language[0][0] = 'cpp'
|
||||
}
|
||||
// 当BlockId等于PageId时移除
|
||||
if (b?.value?.id === id) {
|
||||
// 此block含有敏感信息
|
||||
delete b?.value?.properties
|
||||
continue
|
||||
if (b?.value?.properties?.language?.[0][0] === 'C#') {
|
||||
b.value.properties.language[0][0] = 'csharp'
|
||||
}
|
||||
|
||||
count++
|
||||
// 处理 c++、c#、汇编等语言名字映射
|
||||
if (b?.value?.type === 'code') {
|
||||
if (b?.value?.properties?.language?.[0][0] === 'C++') {
|
||||
b.value.properties.language[0][0] = 'cpp'
|
||||
}
|
||||
if (b?.value?.properties?.language?.[0][0] === 'C#') {
|
||||
b.value.properties.language[0][0] = 'csharp'
|
||||
}
|
||||
if (b?.value?.properties?.language?.[0][0] === 'Assembly') {
|
||||
b.value.properties.language[0][0] = 'asm6502'
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是文件,或嵌入式PDF,需要重新加密签名
|
||||
if (
|
||||
(b?.value?.type === 'file' ||
|
||||
b?.value?.type === 'pdf' ||
|
||||
b?.value?.type === 'video' ||
|
||||
b?.value?.type === 'audio') &&
|
||||
b?.value?.properties?.source?.[0][0] &&
|
||||
b?.value?.properties?.source?.[0][0].indexOf('amazonaws.com') > 0
|
||||
) {
|
||||
const oldUrl = b?.value?.properties?.source?.[0][0]
|
||||
const newUrl = `https://notion.so/signed/${encodeURIComponent(oldUrl)}?table=block&id=${b?.value?.id}`
|
||||
b.value.properties.source[0][0] = newUrl
|
||||
if (b?.value?.properties?.language?.[0][0] === 'Assembly') {
|
||||
b.value.properties.language[0][0] = 'asm6502'
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是文件,或嵌入式PDF,需要重新加密签名
|
||||
if (
|
||||
(b?.value?.type === 'file' ||
|
||||
b?.value?.type === 'pdf' ||
|
||||
b?.value?.type === 'video' ||
|
||||
b?.value?.type === 'audio') &&
|
||||
b?.value?.properties?.source?.[0][0] &&
|
||||
b?.value?.properties?.source?.[0][0].indexOf('amazonaws.com') > 0
|
||||
) {
|
||||
const oldUrl = b?.value?.properties?.source?.[0][0]
|
||||
const newUrl = `https://notion.so/signed/${encodeURIComponent(oldUrl)}?table=block&id=${b?.value?.id}`
|
||||
b.value.properties.source[0][0] = newUrl
|
||||
}
|
||||
}
|
||||
|
||||
// 去掉不用的字段
|
||||
if (id === BLOG.NOTION_PAGE_ID) {
|
||||
@@ -162,6 +160,11 @@ function filterPostBlocks(id, blockMap, slice) {
|
||||
* @returns
|
||||
*/
|
||||
export const fetchInBatches = async (ids, batchSize = 100) => {
|
||||
// 如果 ids 不是数组,则将其转换为数组
|
||||
if (!Array.isArray(ids)) {
|
||||
ids = [ids]
|
||||
}
|
||||
|
||||
const authToken = BLOG.NOTION_ACCESS_TOKEN || null
|
||||
const api = new NotionAPI({
|
||||
authToken,
|
||||
@@ -171,7 +174,7 @@ export const fetchInBatches = async (ids, batchSize = 100) => {
|
||||
let fetchedBlocks = {}
|
||||
for (let i = 0; i < ids.length; i += batchSize) {
|
||||
const batch = ids.slice(i, i + batchSize)
|
||||
console.log('[API-->>请求] Fetching missing blocks', ids.length)
|
||||
console.log('[API-->>请求] Fetching missing blocks', batch, ids.length)
|
||||
const start = new Date().getTime()
|
||||
const pageChunk = await api.getBlocks(batch)
|
||||
const end = new Date().getTime()
|
||||
|
||||
@@ -110,6 +110,8 @@ const compressImage = (image, width, quality = 50, fmt = 'webp') => {
|
||||
return image
|
||||
}
|
||||
|
||||
if (image.includes(".svg")) return image
|
||||
|
||||
if (!width || width === 0) {
|
||||
width = siteConfig('IMAGE_COMPRESS_WIDTH')
|
||||
}
|
||||
|
||||
37
lib/password.js
Normal file
37
lib/password.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { isBrowser } from './utils'
|
||||
|
||||
/**
|
||||
* 获取默认密码
|
||||
* 用户可以通过url中拼接参数,输入密码
|
||||
* 输入过一次的密码会被存储在浏览器中,便于下一次免密访问
|
||||
* 返回的是一组历史密码,便于客户端多次尝试
|
||||
*/
|
||||
export const getPasswordQuery = path => {
|
||||
// 使用 URL 对象解析 URL
|
||||
const url = new URL(path, isBrowser ? window.location.origin : '')
|
||||
|
||||
// 获取查询参数
|
||||
const queryParams = Object.fromEntries(url.searchParams.entries())
|
||||
|
||||
// 请求中带着密码
|
||||
if (queryParams.password) {
|
||||
// 将已输入密码作为默认密码存放在 localStorage,便于下次读取并自动尝试
|
||||
localStorage.setItem('password_default', queryParams.password)
|
||||
}
|
||||
|
||||
// 获取路径部分
|
||||
const cleanedPath = url.pathname
|
||||
|
||||
// 从 localStorage 中获取相关密码
|
||||
const storedPassword = localStorage.getItem('password_' + cleanedPath)
|
||||
const defaultPassword = localStorage.getItem('password_default')
|
||||
|
||||
// 将所有密码存储在一个数组中,并过滤掉无效值
|
||||
const passwords = [
|
||||
queryParams.password,
|
||||
storedPassword,
|
||||
defaultPassword
|
||||
].filter(Boolean)
|
||||
|
||||
return passwords
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import algoliasearch from 'algoliasearch'
|
||||
* 生成全文索引
|
||||
* @param {*} allPages
|
||||
*/
|
||||
const generateAlgoliaSearch = async({ allPages, force = false }) => {
|
||||
const generateAlgoliaSearch = async ({ allPages, force = false }) => {
|
||||
allPages?.forEach(p => {
|
||||
// 判断这篇文章是否需要重新创建索引
|
||||
if (p && !p.password) {
|
||||
@@ -19,7 +19,7 @@ const generateAlgoliaSearch = async({ allPages, force = false }) => {
|
||||
* 上传数据
|
||||
* 根据上次修改文章日期和上次更新索引数据判断是否需要更新algolia索引
|
||||
*/
|
||||
const uploadDataToAlgolia = async(post) => {
|
||||
const uploadDataToAlgolia = async post => {
|
||||
// Connect and authenticate with your Algolia app
|
||||
const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY)
|
||||
|
||||
@@ -61,14 +61,18 @@ const uploadDataToAlgolia = async(post) => {
|
||||
summary: post.summary,
|
||||
lastEditedDate: post.lastEditedDate, // 更新文章时间
|
||||
lastIndexDate: new Date(), // 更新索引时间
|
||||
content: truncate(getPageContentText(post, post.blockMap), 9000) // 索引9000个字节,因为api限制总请求内容上限1万个字节
|
||||
content: truncate(getPageContentText(post, post.blockMap), 8192) // 索引8192个字符,API限制总请求内容上限1万个字节
|
||||
}
|
||||
// console.log('更新Algolia索引', record)
|
||||
index.saveObject(record).wait().then(r => {
|
||||
console.log('Algolia索引更新', r)
|
||||
}).catch(err => {
|
||||
console.log('Algolia异常', err)
|
||||
})
|
||||
index
|
||||
.saveObject(record)
|
||||
.wait()
|
||||
.then(r => {
|
||||
console.log('Algolia索引更新', r)
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('Algolia异常', err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,12 @@ export const memorize = Component => {
|
||||
return memo(MemoizedComponent)
|
||||
}
|
||||
|
||||
// 转换外链
|
||||
/**
|
||||
* 诸如 article/https://test.com 等被错误拼接前缀的slug进行处理
|
||||
* 转换为 https://test.com
|
||||
* @param {*} str
|
||||
* @returns
|
||||
*/
|
||||
export function sliceUrlFromHttp(str) {
|
||||
// 检查字符串是否包含http
|
||||
if (str?.includes('http:') || str?.includes('https:')) {
|
||||
@@ -81,12 +86,25 @@ export function convertUrlStartWithOneSlash(str) {
|
||||
return str
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是一个相对或绝对路径的ur类
|
||||
* @param {*} str
|
||||
* @returns
|
||||
*/
|
||||
export function isUrl(str) {
|
||||
if (!str) {
|
||||
return false
|
||||
}
|
||||
|
||||
return str?.indexOf('/') === 0 || checkStartWithHttp(str)
|
||||
}
|
||||
|
||||
// 检查是否外链
|
||||
export function checkContainHttp(str) {
|
||||
export function checkStartWithHttp(str) {
|
||||
// 检查字符串是否包含http
|
||||
if (str?.includes('http:') || str?.includes('https:')) {
|
||||
if (str?.indexOf('http:') === 0 || str?.indexOf('https:') === 0) {
|
||||
// 如果包含,找到http的位置
|
||||
return str?.indexOf('http') > -1
|
||||
return true
|
||||
} else {
|
||||
// 不包含
|
||||
return false
|
||||
@@ -354,3 +372,19 @@ export const scanAndConvertToLinks = node => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取url最后一个斜杆后面的内容
|
||||
* @param {*} url
|
||||
* @returns
|
||||
*/
|
||||
export function getLastSegmentFromUrl(url) {
|
||||
if (!url) {
|
||||
return ''
|
||||
}
|
||||
// 去掉 URL 中的查询参数部分
|
||||
const trimmedUrl = url.split('?')[0]
|
||||
// 获取最后一个斜杠后面的内容
|
||||
const segments = trimmedUrl.split('/')
|
||||
return segments[segments.length - 1]
|
||||
}
|
||||
|
||||
@@ -30,4 +30,18 @@ function extractLangId(str) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { extractLangPrefix, extractLangId }
|
||||
/**
|
||||
* 列表中用过来区分page只需要端的id足够
|
||||
*/
|
||||
|
||||
function getShortId(uuid) {
|
||||
if (!uuid || uuid.indexOf('-') < 0) {
|
||||
return uuid
|
||||
}
|
||||
// 找到第一个 '-' 的位置
|
||||
const index = uuid.indexOf('-')
|
||||
// 截取从开始到第一个 '-' 之前的部分
|
||||
return uuid.substring(0, index)
|
||||
}
|
||||
|
||||
module.exports = { extractLangPrefix, extractLangId, getShortId }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 文章相关工具
|
||||
*/
|
||||
import { checkContainHttp } from '.'
|
||||
import { checkStartWithHttp } from '.'
|
||||
|
||||
/**
|
||||
* 获取文章的关联推荐文章列表,目前根据标签关联性筛选
|
||||
@@ -50,7 +50,7 @@ export function checkSlugHasNoSlash(row) {
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length === 0 &&
|
||||
!checkContainHttp(slug) &&
|
||||
!checkStartWithHttp(slug) &&
|
||||
row.type.indexOf('Menu') < 0
|
||||
)
|
||||
}
|
||||
@@ -67,7 +67,7 @@ export function checkSlugHasOneSlash(row) {
|
||||
}
|
||||
return (
|
||||
(slug.match(/\//g) || []).length === 1 &&
|
||||
!checkContainHttp(slug) &&
|
||||
!checkStartWithHttp(slug) &&
|
||||
row.type.indexOf('Menu') < 0
|
||||
)
|
||||
}
|
||||
@@ -85,6 +85,6 @@ export function checkSlugHasMorThanTwoSlash(row) {
|
||||
return (
|
||||
(slug.match(/\//g) || []).length >= 2 &&
|
||||
row.type.indexOf('Menu') < 0 &&
|
||||
!checkContainHttp(slug)
|
||||
!checkStartWithHttp(slug)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user