diff --git a/components/NotionPage.js b/components/NotionPage.js index 586e0c0c4a4..0a011968ac4 100644 --- a/components/NotionPage.js +++ b/components/NotionPage.js @@ -116,12 +116,15 @@ const NotionPage = ({ post, className }) => { return () => clearTimeout(timer) }, [post]) + const cleanBlockMap = cleanBlocksWithWarn(post.blockMap); + + return (
{ ) } +function cleanBlocksWithWarn(blockMap) { + const cleanedBlocks = {}; + const removedBlockIds = []; + + for (const [id, block] of Object.entries(blockMap.block || {})) { + if (!block?.value?.id) { + removedBlockIds.push(id); + continue; + } + + const newBlock = { ...block }; + + if (Array.isArray(newBlock.value.content)) { + // 递归清理 content 中无效的 blockId + newBlock.value.content = newBlock.value.content.filter((cid) => { + if (!blockMap.block[cid]?.value?.id) { + removedBlockIds.push(cid); + return false; + } + return true; + }); + } + + cleanedBlocks[id] = newBlock; + } + + if (removedBlockIds.length) { + console.warn('Removed invalid blocks:', removedBlockIds); + } + + return { + ...blockMap, + block: cleanedBlocks, + }; +} + + + /** * 页面的数据库链接禁止跳转,只能查看 */ diff --git a/lib/db/getSiteData.js b/lib/db/getSiteData.js index e2d704e279a..9cb68813ddd 100755 --- a/lib/db/getSiteData.js +++ b/lib/db/getSiteData.js @@ -136,6 +136,106 @@ const EmptyData = pageId => { return empty } +/** + * 可能由于Notion接口升级导致数据格式变化,这里进行统一处理 + * @param {*} block + * @param {*} pageId + * @returns + */ +function normalizeNotionMetadata(block, pageId) { + const rawValue = block?.[pageId]?.value + if (!rawValue) return null + return rawValue.type ? rawValue : rawValue.value ?? null +} + +/** + * 兼容新老 Notion collection 结构 , 新版会用space_id 包裹一层 + * 统一返回真正的 collection.value(包含 schema 的那一层) + */ +function normalizeCollection(collection) { + let current = collection + + // 最多剥 3 层,防止死循环 + for (let i = 0; i < 3; i++) { + if (!current) break + + // 已经是最终形态:有 schema + if (current.schema) { + return current + } + + // 常见包装:{ value: {...}, role } + if (current.value) { + current = current.value + continue + } + + break + } + + return current ?? {} +} + +/** + * 兼容 Notion schema + * 保留原始字段 id 作为 key + */ +/** + * 兼容 Notion schema + * 保留原始字段 id 作为 key + */ +function normalizeSchema(schema = {}) { + const result = {} + + Object.entries(schema).forEach(([key, value]) => { + result[key] = { + ...value, + name: value?.name || '', + type: value?.type || '' + } + }) + + return result +} + + +/** + * ✅ 终极版:兼容 Notion 新老 Page Block 结构 + * 最终一定返回:{ id, type, properties } + */ +function normalizePageBlock(blockItem) { + if (!blockItem) return null + + let current = blockItem + + for (let i = 0; i < 5; i++) { + if (!current) return null + + // 针对 collection 兼容 + if ( + (current.type === 'collection_view_page' || current.type === 'collection_view') && + current.collection_id + ) { + return current + } + + if (current.type && current.properties) { + return current + } + + if (current.value) { + current = current.value + continue + } + + break + } + + return null +} + + + /** * 将Notion数据转站点数据 * 这里统一对数据格式化 @@ -148,7 +248,7 @@ async function convertNotionToSiteData(pageId, from, pageRecordMap) { } pageId = idToUuid(pageId) let block = pageRecordMap.block || {} - const rawMetadata = block[pageId]?.value + const rawMetadata = normalizeNotionMetadata(block, pageId) // Check Type Page-Database和Inline-Database if ( rawMetadata?.type !== 'collection_view_page' && @@ -157,12 +257,17 @@ async function convertNotionToSiteData(pageId, from, pageRecordMap) { console.error(`pageId "${pageId}" is not a database`) return EmptyData(pageId) } - const collection = Object.values(pageRecordMap.collection)[0]?.value || {} const collectionId = rawMetadata?.collection_id + + const rawCollection = + pageRecordMap.collection?.[collectionId] || + pageRecordMap.collection?.[idToUuid(collectionId)] || + {} + + const collection = normalizeCollection(rawCollection) const collectionQuery = pageRecordMap.collection_query const collectionView = pageRecordMap.collection_view - const schema = collection?.schema - + const schema = normalizeSchema(collection?.schema || {}) const viewIds = rawMetadata?.view_ids const collectionData = [] @@ -186,26 +291,37 @@ async function convertNotionToSiteData(pageId, from, pageRecordMap) { // console.log('有效Page数量', pageIds?.length) } - // 抓取主数据库最多抓取1000个blocks,溢出的数block这里统一抓取一遍 + // 1️⃣ 找出需要 fetch 的 block const blockIdsNeedFetch = [] for (let i = 0; i < pageIds.length; i++) { const id = pageIds[i] - const value = block[id]?.value - if (!value) { + const pageBlock = normalizePageBlock(block[id]) + + if (!pageBlock) { blockIdsNeedFetch.push(id) } } + + // 2️⃣ fetch 缺失的 blocks const fetchedBlocks = await fetchInBatches(blockIdsNeedFetch) block = Object.assign({}, block, fetchedBlocks) - // 获取每篇文章基础数据 + // 3️⃣ 只执行一次:生成 collectionData for (let i = 0; i < pageIds.length; i++) { const id = pageIds[i] - const value = block[id]?.value || fetchedBlocks[id]?.value + + const rawBlock = block[id] + const pageBlock = normalizePageBlock(rawBlock) + + if (!pageBlock) { + console.warn('⚠️ 无法解析 page block:', id, rawBlock) + continue + } + const properties = (await getPageProperties( id, - value, + pageBlock, schema, null, getTagOptions(schema) diff --git a/package.json b/package.json index 4607e4cf964..178ccef5f48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notion-next", - "version": "4.9.2", + "version": "4.9.2.1", "homepage": "https://github.com/tangly1024/NotionNext.git", "license": "MIT", "engines": {