mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 15:09:22 +00:00
297 lines
8.7 KiB
JavaScript
297 lines
8.7 KiB
JavaScript
import BLOG from '@/blog.config'
|
||
import { getDataFromCache, setDataToCache } from '@/lib/cache/cache_manager'
|
||
import { getPostBlocks } from '@/lib/notion/getPostBlocks'
|
||
import { idToUuid } from 'notion-utils'
|
||
import { defaultMapImageUrl } from 'react-notion-x'
|
||
import { deepClone, isIterable } from '../utils'
|
||
import getAllPageIds from './getAllPageIds'
|
||
import { getAllTags } from './getAllTags'
|
||
import getPageProperties from './getPageProperties'
|
||
|
||
/**
|
||
* 获取博客数据
|
||
* @param {*} pageId
|
||
* @param {*} from
|
||
* @param latestPostCount 截取最新文章数量
|
||
* @param categoryCount
|
||
* @param tagsCount 截取标签数量
|
||
* @param pageType 过滤的文章类型,数组格式 ['Page','Post']
|
||
* @returns
|
||
*
|
||
*/
|
||
export async function getGlobalNotionData({
|
||
pageId = BLOG.NOTION_PAGE_ID,
|
||
from,
|
||
pageType = ['Post']
|
||
}) {
|
||
// 获取Notion数据
|
||
const notionPageData = deepClone(await getNotionPageData({ pageId, from }))
|
||
notionPageData.siteInfo = getBlogInfo({ collection: notionPageData?.collection, block: notionPageData?.block })
|
||
|
||
delete notionPageData.block
|
||
delete notionPageData.collection
|
||
delete notionPageData.collectionQuery
|
||
delete notionPageData.schema
|
||
delete notionPageData.rawMetadata
|
||
delete notionPageData.pageIds
|
||
delete notionPageData.tagOptions
|
||
delete notionPageData.categoryOptions
|
||
return notionPageData
|
||
}
|
||
|
||
/**
|
||
* 获取最新文章 根据最后修改时间倒序排列
|
||
* @param {*}} param0
|
||
* @returns
|
||
*/
|
||
function getLatestPosts({ allPosts, from, latestPostCount }) {
|
||
const latestPosts = Object.create(allPosts).sort((a, b) => {
|
||
const dateA = new Date(a?.lastEditedTime || a?.createdTime || a?.date?.start_date)
|
||
const dateB = new Date(b?.lastEditedTime || b?.createdTime || b?.date?.start_date)
|
||
return dateB - dateA
|
||
})
|
||
return latestPosts.slice(0, latestPostCount)
|
||
}
|
||
|
||
/**
|
||
* 获取指定notion的collection数据
|
||
* @param pageId
|
||
* @param from 请求来源
|
||
* @returns {Promise<JSX.Element|*|*[]>}
|
||
*/
|
||
export async function getNotionPageData({ pageId, from }) {
|
||
// 尝试从缓存获取
|
||
const cacheKey = 'page_block_' + pageId
|
||
const data = await getDataFromCache(cacheKey)
|
||
if (data && data.pageIds?.length > 0) {
|
||
console.log('[命中缓存]:', `from:${from}`, `root-page-id:${pageId}`)
|
||
return data
|
||
}
|
||
const pageRecordMap = await getPageRecordMapByNotionAPI({ pageId, from })
|
||
// 存入缓存
|
||
if (pageRecordMap) {
|
||
await setDataToCache(cacheKey, pageRecordMap)
|
||
}
|
||
return pageRecordMap
|
||
}
|
||
|
||
/**
|
||
* 获取用户自定义单页菜单
|
||
* @param notionPageData
|
||
* @returns {Promise<[]|*[]>}
|
||
*/
|
||
function getCustomNav({ allPages }) {
|
||
const customNav = []
|
||
if (allPages && allPages.length > 0) {
|
||
allPages.forEach(p => {
|
||
if (p?.status?.[0] === 'Published') {
|
||
if (p?.slug?.indexOf('http') === 0) {
|
||
customNav.push({ icon: p.icon || null, name: p.title, to: p.slug, show: true })
|
||
} else {
|
||
customNav.push({ icon: p.icon || null, name: p.title, to: '/' + p.slug, show: true })
|
||
}
|
||
}
|
||
})
|
||
}
|
||
return customNav
|
||
}
|
||
|
||
/**
|
||
* 获取标签选项
|
||
* @param schema
|
||
* @returns {undefined}
|
||
*/
|
||
function getTagOptions(schema) {
|
||
if (!schema) return {}
|
||
const tagSchema = Object.values(schema).find(e => e.name === 'tags')
|
||
return tagSchema?.options || []
|
||
}
|
||
|
||
/**
|
||
* 获取分类选项
|
||
* @param schema
|
||
* @returns {{}|*|*[]}
|
||
*/
|
||
function getCategoryOptions(schema) {
|
||
if (!schema) return {}
|
||
const categorySchema = Object.values(schema).find(e => e.name === 'category')
|
||
return categorySchema?.options || []
|
||
}
|
||
|
||
/**
|
||
* 获取所有文章的分类
|
||
* @param allPosts
|
||
* @returns {Promise<{}|*[]>}
|
||
*/
|
||
function getAllCategories({ allPosts, categoryOptions, sliceCount = 0 }) {
|
||
if (!allPosts || !categoryOptions) {
|
||
return []
|
||
}
|
||
// 计数
|
||
let categories = allPosts.map(p => p.category)
|
||
categories = [...categories.flat()]
|
||
const categoryObj = {}
|
||
categories.forEach(category => {
|
||
if (category in categoryObj) {
|
||
categoryObj[category]++
|
||
} else {
|
||
categoryObj[category] = 1
|
||
}
|
||
})
|
||
const list = []
|
||
if (isIterable(categoryOptions)) {
|
||
for (const c of categoryOptions) {
|
||
const count = categoryObj[c.value]
|
||
if (count) {
|
||
list.push({ id: c.id, name: c.value, color: c.color, count })
|
||
}
|
||
}
|
||
}
|
||
|
||
// 按照数量排序
|
||
// list.sort((a, b) => b.count - a.count)
|
||
if (sliceCount && sliceCount > 0) {
|
||
return list.slice(0, sliceCount)
|
||
} else {
|
||
return list
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 站点信息
|
||
* @param notionPageData
|
||
* @param from
|
||
* @returns {Promise<{title,description,pageCover,icon}>}
|
||
*/
|
||
function getBlogInfo({ collection, block }) {
|
||
const title = collection?.name?.[0][0] || BLOG.TITLE
|
||
const description = collection?.description ? Object.assign(collection).description[0][0] : BLOG.DESCRIPTION
|
||
const pageCover = collection?.cover ? (mapImgUrl(collection?.cover, block[idToUuid(BLOG.NOTION_PAGE_ID)]?.value)) : BLOG.HOME_BANNER_IMAGE
|
||
const icon = collection?.icon ? (mapCollectionImg(collection?.icon, collection)) : BLOG.AVATAR
|
||
return { title, description, pageCover, icon }
|
||
}
|
||
|
||
/**
|
||
* Notion图片映射
|
||
* @param pageCover
|
||
* @returns {string}
|
||
*/
|
||
const mapImgUrl = (img, value) => {
|
||
if (img) {
|
||
if (img.startsWith('/')) return 'https://www.notion.so' + img
|
||
if (img.startsWith('http')) return defaultMapImageUrl(img, value)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* collection 图片映射
|
||
* @param {*} img
|
||
* @param {*} value
|
||
* @returns
|
||
*/
|
||
const mapCollectionImg = (img, value) => {
|
||
if (img) {
|
||
if (img.startsWith('/')) return 'https://www.notion.so' + img
|
||
if (img.startsWith('http')) {
|
||
return 'https://www.notion.so/image/' + encodeURIComponent(img) + '?table=collection&id=' + value.id
|
||
}
|
||
// 判断是否含有emoji表情
|
||
const emojiPattern = /\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g
|
||
if (emojiPattern.test(img)) {
|
||
console.error('请不要使用emoji作为站点图标', img)
|
||
return BLOG.AVATAR
|
||
}
|
||
|
||
console.error('非法的站点图标', img)
|
||
return BLOG.AVATAR
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 调用NotionAPI获取Page数据
|
||
* @returns {Promise<JSX.Element|null|*>}
|
||
*/
|
||
async function getPageRecordMapByNotionAPI({ pageId, from }) {
|
||
const pageRecordMap = await getPostBlocks(pageId, from)
|
||
if (!pageRecordMap) {
|
||
return []
|
||
}
|
||
pageId = idToUuid(pageId)
|
||
const block = pageRecordMap.block
|
||
const rawMetadata = block[pageId]?.value
|
||
// Check Type Page-Database和Inline-Database
|
||
if (
|
||
rawMetadata?.type !== 'collection_view_page' &&
|
||
rawMetadata?.type !== 'collection_view'
|
||
) {
|
||
console.warn(`pageId "${pageId}" is not a database`)
|
||
return null
|
||
}
|
||
|
||
const collection = Object.values(pageRecordMap.collection)[0]?.value
|
||
const collectionId = rawMetadata?.collection_id
|
||
const collectionQuery = pageRecordMap.collection_query
|
||
const collectionView = pageRecordMap.collection_view
|
||
const schema = collection?.schema
|
||
const tagOptions = getTagOptions(schema)
|
||
const categoryOptions = getCategoryOptions(schema)
|
||
const viewIds = rawMetadata?.view_ids
|
||
const collectionData = []
|
||
const pageIds = getAllPageIds(collectionQuery, collectionId, collectionView, viewIds)
|
||
if (pageIds?.length === 0) {
|
||
console.error('获取到的文章列表为空,请检查notion模板', collectionQuery, collection, collectionView, viewIds, pageRecordMap)
|
||
}
|
||
for (let i = 0; i < pageIds.length; i++) {
|
||
const id = pageIds[i]
|
||
const value = block[id]?.value
|
||
if (!value) {
|
||
continue
|
||
}
|
||
const properties = (await getPageProperties(id, block, schema, tagOptions)) || null
|
||
collectionData.push(properties)
|
||
}
|
||
|
||
const allPages = collectionData.filter(post => {
|
||
return post.title && ['Page'].indexOf(post?.type?.[0]) > -1 && (post?.status?.[0] === 'Published' || post?.status?.[0] === 'Invisible')
|
||
})
|
||
const allPosts = collectionData.filter(post => {
|
||
return post.title && ['Post'].indexOf(post?.type?.[0]) > -1 && post?.status?.[0] === 'Published'
|
||
})
|
||
|
||
// Sort by date
|
||
if (BLOG.POSTS_SORT_BY === 'date') {
|
||
allPosts.sort((a, b) => {
|
||
const dateA = new Date(a?.date?.start_date || a.createdTime)
|
||
const dateB = new Date(b?.date?.start_date || b.createdTime)
|
||
return dateB - dateA
|
||
})
|
||
}
|
||
|
||
const customNav = getCustomNav({ allPages })
|
||
const postCount = allPosts?.length || 0
|
||
const categories = getAllCategories({ allPosts, categoryOptions, sliceCount: BLOG.PREVIEW_CATEGORY_COUNT })
|
||
const tags = getAllTags({ allPosts, tagOptions, sliceCount: BLOG.PREVIEW_TAG_COUNT })
|
||
const latestPosts = getLatestPosts({ allPosts, from, latestPostCount: 5 })
|
||
|
||
return {
|
||
allPages,
|
||
allPosts,
|
||
collection,
|
||
collectionQuery,
|
||
collectionId,
|
||
collectionView,
|
||
viewIds,
|
||
block,
|
||
schema,
|
||
tagOptions,
|
||
categoryOptions,
|
||
rawMetadata,
|
||
customNav,
|
||
postCount,
|
||
pageIds,
|
||
categories,
|
||
tags,
|
||
latestPosts
|
||
}
|
||
}
|