Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion components/NotionPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,15 @@ const NotionPage = ({ post, className }) => {
return () => clearTimeout(timer)
}, [post])

const cleanBlockMap = cleanBlocksWithWarn(post.blockMap);


return (
<div
id='notion-article'
className={`mx-auto overflow-hidden ${className || ''}`}>
<NotionRenderer
recordMap={post?.blockMap}
recordMap={cleanBlockMap}
mapPageUrl={mapPageUrl}
mapImageUrl={mapImgUrl}
components={{
Expand All @@ -140,6 +143,44 @@ const NotionPage = ({ post, className }) => {
)
}

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,
};
}



/**
* 页面的数据库链接禁止跳转,只能查看
*/
Expand Down
136 changes: 126 additions & 10 deletions lib/db/getSiteData.js
Original file line number Diff line number Diff line change
Expand Up @@ -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数据转站点数据
* 这里统一对数据格式化
Expand All @@ -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' &&
Expand All @@ -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 = []

Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
Loading