nnbb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +1 -0
- package/.env.local +2 -0
- package/.eslintrc.json +15 -0
- package/.github/stale.yml +16 -0
- package/.github/workflows/codeql-analysis.yml +73 -0
- package/.github/workflows/docker-ghcr.yaml +59 -0
- package/.prettierrc +9 -0
- package/.vscode/launch.json +28 -0
- package/.vscode/settings.json +6 -0
- package/Dockerfile +38 -0
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/blog.config.js +454 -0
- package/components/Ackee.js +83 -0
- package/components/AdBlockDetect.js +40 -0
- package/components/AlgoliaSearchModal.js +250 -0
- package/components/Artalk.js +30 -0
- package/components/Busuanzi.js +26 -0
- package/components/ChatBase.js +19 -0
- package/components/Collapse.tsx +134 -0
- package/components/Comment.js +161 -0
- package/components/CommonHead.tsx +101 -0
- package/components/CommonScript.js +125 -0
- package/components/CusdisComponent.js +35 -0
- package/components/CustomContextMenu.js +221 -0
- package/components/DebugPanel.js +134 -0
- package/components/DisableCopy.js +21 -0
- package/components/Draggable.js +167 -0
- package/components/Equation.js +31 -0
- package/components/ExternalPlugins.js +75 -0
- package/components/ExternalScript.js +29 -0
- package/components/FacebookMessenger.js +255 -0
- package/components/FacebookPage.js +34 -0
- package/components/Fireworks.js +210 -0
- package/components/FlipCard.js +56 -0
- package/components/FlutteringRibbon.js +322 -0
- package/components/FullScreenButton.js +48 -0
- package/components/Giscus.js +33 -0
- package/components/Gitalk.js +42 -0
- package/components/GoogleAdsense.js +111 -0
- package/components/Gtag.js +18 -0
- package/components/HeroIcons.tsx +321 -0
- package/components/KatexReact.js +53 -0
- package/components/LazyImage.js +95 -0
- package/components/Live2D.js +52 -0
- package/components/Loading.js +20 -0
- package/components/Mark.js +28 -0
- package/components/NProgress.ts +8 -0
- package/components/Nest.js +124 -0
- package/components/NotionIcon.js +20 -0
- package/components/NotionPage.tsx +206 -0
- package/components/Player.js +54 -0
- package/components/PrismMac.js +236 -0
- package/components/QrCode.js +34 -0
- package/components/Ribbon.js +98 -0
- package/components/Sakura.js +192 -0
- package/components/Select.js +40 -0
- package/components/ShareBar.js +29 -0
- package/components/ShareButtons.js +403 -0
- package/components/SideBarDrawer.js +50 -0
- package/components/StarrySky.js +130 -0
- package/components/Tabs.js +64 -0
- package/components/ThemeSwitch.js +67 -0
- package/components/Twikoo.js +27 -0
- package/components/TwikooCommentCount.js +22 -0
- package/components/TwikooCommentCounter.js +78 -0
- package/components/TwikooRecentComments.js +11 -0
- package/components/Utterances.js +35 -0
- package/components/VConsole.js +76 -0
- package/components/ValineComponent.js +59 -0
- package/components/ValineCount.js +6 -0
- package/components/ValinePanel.js +3 -0
- package/components/Vercel.tsx +54 -0
- package/components/WWAds.js +18 -0
- package/components/WalineComponent.js +83 -0
- package/components/WebMention.js +173 -0
- package/components/Webwhiz.js +17 -0
- package/components/WordCount.js +73 -0
- package/hooks/useToggleClickOutSide.ts +32 -0
- package/hooks/useWindowSize.ts +30 -0
- package/lib/algolia.js +108 -0
- package/lib/busuanzi.js +99 -0
- package/lib/cache/cacheManager.ts +49 -0
- package/lib/cache/localFileCache.ts +56 -0
- package/lib/cache/memoryMache.ts +20 -0
- package/lib/cache/mongoDbCache.ts +70 -0
- package/lib/cache/types.ts +5 -0
- package/lib/font.js +46 -0
- package/lib/global.tsx +129 -0
- package/lib/gtag.js +17 -0
- package/lib/mailchimp.js +49 -0
- package/lib/memorize.js +0 -0
- package/lib/mhchem.js +1696 -0
- package/lib/notion/getAllCategories.ts +51 -0
- package/lib/notion/getAllPageIds.ts +51 -0
- package/lib/notion/getAllPosts.js +68 -0
- package/lib/notion/getAllTags.ts +43 -0
- package/lib/notion/getNotionData.ts +340 -0
- package/lib/notion/getPageInfoOfPostPage.ts +58 -0
- package/lib/notion/getPageProperties.ts +203 -0
- package/lib/notion/getPageTableOfContents.ts +107 -0
- package/lib/notion/getPostBlocks.ts +147 -0
- package/lib/notion/mapImage.ts +130 -0
- package/lib/notion/types.ts +125 -0
- package/lib/notion.js +2 -0
- package/lib/robots.txt.js +25 -0
- package/lib/rss.js +63 -0
- package/lib/sitemap.xml.js +67 -0
- package/lib/utils.js +212 -0
- package/next-env.d.ts +5 -0
- package/next-i18next.config.js +7 -0
- package/next-sitemap.config.js +11 -0
- package/next.config.js +124 -0
- package/package.json +92 -0
- package/pages/404.tsx +40 -0
- package/pages/[prefix]/[slug].tsx +123 -0
- package/pages/[prefix]/index.tsx +223 -0
- package/pages/_app.js +59 -0
- package/pages/_document.js +42 -0
- package/pages/api/subscribe.js +22 -0
- package/pages/archive/index.tsx +79 -0
- package/pages/category/[category]/index.tsx +87 -0
- package/pages/category/[category]/page/[page].tsx +103 -0
- package/pages/category/index.tsx +43 -0
- package/pages/index.tsx +88 -0
- package/pages/page/[page].tsx +93 -0
- package/pages/search/[keyword]/index.tsx +162 -0
- package/pages/search/[keyword]/page/[page].tsx +166 -0
- package/pages/search/index.tsx +69 -0
- package/pages/sitemap.xml.js +70 -0
- package/pages/tag/[tag]/index.tsx +73 -0
- package/pages/tag/[tag]/page/[page].tsx +87 -0
- package/pages/tag/index.tsx +42 -0
- package/postcss.config.js +6 -0
- package/public/ads.txt +1 -0
- package/public/avatar.png +0 -0
- package/public/avatar.svg +11 -0
- package/public/bg_image.jpg +0 -0
- package/public/css/all.min.css +9 -0
- package/public/css/custom.css +8 -0
- package/public/css/img-shadow.css +5 -0
- package/public/css/prism-mac-style.css +58 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +9 -0
- package/public/js/cusdis.es.js +107 -0
- package/public/js/custom.js +1 -0
- package/public/locales/en/common.json +44 -0
- package/public/locales/en/menu.json +9 -0
- package/public/locales/en/nav.json +11 -0
- package/public/locales/zh-CN/common.json +44 -0
- package/public/locales/zh-CN/menu.json +9 -0
- package/public/locales/zh-CN/nav.json +9 -0
- package/public/webfonts/fa-brands-400.ttf +0 -0
- package/public/webfonts/fa-brands-400.woff2 +0 -0
- package/public/webfonts/fa-regular-400.ttf +0 -0
- package/public/webfonts/fa-regular-400.woff2 +0 -0
- package/public/webfonts/fa-solid-900.ttf +0 -0
- package/public/webfonts/fa-solid-900.woff2 +0 -0
- package/public/webfonts/fa-v4compatibility.ttf +0 -0
- package/public/webfonts/fa-v4compatibility.woff2 +0 -0
- package/styles/animate.css +503 -0
- package/styles/globals.css +183 -0
- package/styles/notion.css +2064 -0
- package/styles/nprogress.css +84 -0
- package/styles/prism-theme.css +119 -0
- package/styles/utility-patterns.css +79 -0
- package/tailwind.config.js +37 -0
- package/theme/index.ts +6 -0
- package/theme/types/@theme-components.d.ts +29 -0
- package/theme/useLayout.ts +41 -0
- package/theme/utils.ts +108 -0
- package/themes/innocent/package.json +7 -0
- package/themes/innocent/theme.config.js +1 -0
- package/themes/nobelium/components/Announcement.tsx +27 -0
- package/themes/nobelium/components/ArticleFooter.tsx +39 -0
- package/themes/nobelium/components/ArticleInfo.tsx +58 -0
- package/themes/nobelium/components/ArticleLock.tsx +86 -0
- package/themes/nobelium/components/BlogArchiveItem.js +41 -0
- package/themes/nobelium/components/BlogListBar.js +39 -0
- package/themes/nobelium/components/BlogListPage.tsx +67 -0
- package/themes/nobelium/components/BlogListScroll.tsx +96 -0
- package/themes/nobelium/components/BlogPost.tsx +33 -0
- package/themes/nobelium/components/DarkModeButton.tsx +50 -0
- package/themes/nobelium/components/ExampleRecentComments.js +35 -0
- package/themes/nobelium/components/Footer.tsx +35 -0
- package/themes/nobelium/components/JumpToTopButton.tsx +39 -0
- package/themes/nobelium/components/LanguageSwitchButton.tsx +58 -0
- package/themes/nobelium/components/MenuItemCollapse.tsx +92 -0
- package/themes/nobelium/components/MenuItemDrop.tsx +83 -0
- package/themes/nobelium/components/Nav/Nav.module.css +50 -0
- package/themes/nobelium/components/Nav/Nav.tsx +187 -0
- package/themes/nobelium/components/RandomPostButton.tsx +31 -0
- package/themes/nobelium/components/SearchButton.tsx +31 -0
- package/themes/nobelium/components/SearchInput.tsx +94 -0
- package/themes/nobelium/components/SearchNavBar.js +19 -0
- package/themes/nobelium/components/SideBar.js +83 -0
- package/themes/nobelium/components/SvgIcon.js +29 -0
- package/themes/nobelium/components/TagItem.js +13 -0
- package/themes/nobelium/components/Tags.tsx +44 -0
- package/themes/nobelium/components/Title.js +19 -0
- package/themes/nobelium/index.tsx +28 -0
- package/themes/nobelium/layout/LayoutBase.tsx +79 -0
- package/themes/nobelium/pages/Archive.tsx +30 -0
- package/themes/nobelium/pages/Category.tsx +43 -0
- package/themes/nobelium/pages/Home.tsx +22 -0
- package/themes/nobelium/pages/PageNotFound.tsx +15 -0
- package/themes/nobelium/pages/Post.tsx +34 -0
- package/themes/nobelium/pages/PostList.tsx +74 -0
- package/themes/nobelium/pages/Search.tsx +65 -0
- package/themes/nobelium/pages/Tag.tsx +42 -0
- package/themes/nobelium/providers/index.tsx +60 -0
- package/themes/nobelium/stores/index.tsx +42 -0
- package/themes/nobelium/theme.config.ts +17 -0
- package/themes/nobelium/types/index.ts +10 -0
- package/tsconfig.json +29 -0
- package/types/index.ts +1 -0
- package/types/page.ts +102 -0
- package/vercel.json +5 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
import type { CategoryInfo, PageInfo, SelectOption } from './types';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* 获取所有文章的标签
|
5
|
+
* @param allPosts
|
6
|
+
* @param sliceCount 默认截取数量为12,若为0则返回全部
|
7
|
+
* @param tagOptions tags的下拉选项
|
8
|
+
* @returns {CategoryInfo[]}
|
9
|
+
*/
|
10
|
+
|
11
|
+
/**
|
12
|
+
* 获取所有文章的分类
|
13
|
+
* @param allPosts
|
14
|
+
* @returns {Promise<{}|*[]>}
|
15
|
+
*/
|
16
|
+
export function getAllCategories(
|
17
|
+
publishedPosts: PageInfo[],
|
18
|
+
categoryOptions: SelectOption[],
|
19
|
+
sliceCount = 0,
|
20
|
+
): CategoryInfo[] {
|
21
|
+
// 计数
|
22
|
+
const countMap: { [key: string]: number } = {};
|
23
|
+
publishedPosts.forEach(({ category }) => {
|
24
|
+
if (!category) return;
|
25
|
+
if (category in countMap) {
|
26
|
+
countMap[category]++;
|
27
|
+
} else {
|
28
|
+
countMap[category] = 1;
|
29
|
+
}
|
30
|
+
});
|
31
|
+
const list: CategoryInfo[] = [];
|
32
|
+
for (const categoryOption of categoryOptions) {
|
33
|
+
const count = countMap[categoryOption.value];
|
34
|
+
if (count) {
|
35
|
+
list.push({
|
36
|
+
id: categoryOption.id,
|
37
|
+
name: categoryOption.value,
|
38
|
+
color: categoryOption.color,
|
39
|
+
count,
|
40
|
+
});
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
// 按照数量排序
|
45
|
+
// list.sort((a, b) => b.count - a.count)
|
46
|
+
if (sliceCount && sliceCount > 0) {
|
47
|
+
return list.slice(0, sliceCount);
|
48
|
+
} else {
|
49
|
+
return list;
|
50
|
+
}
|
51
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import type { CollectionQueryResult, CollectionViewMap } from './types';
|
2
|
+
|
3
|
+
type Query = {
|
4
|
+
[collectionId: string]: {
|
5
|
+
[collectionViewId: string]: CollectionQueryResult;
|
6
|
+
};
|
7
|
+
};
|
8
|
+
|
9
|
+
export default function getAllPageIds(
|
10
|
+
collectionQuery: Query,
|
11
|
+
collectionId: string | null,
|
12
|
+
collectionView: CollectionViewMap,
|
13
|
+
viewIds: string[],
|
14
|
+
) {
|
15
|
+
if (!collectionQuery && !collectionView) {
|
16
|
+
return [];
|
17
|
+
}
|
18
|
+
// 优先按照第一个视图排序
|
19
|
+
let pageIds: string[] = [];
|
20
|
+
try {
|
21
|
+
if (viewIds && viewIds.length > 0 && collectionId) {
|
22
|
+
const ids =
|
23
|
+
collectionQuery[collectionId][viewIds[0]]?.collection_group_results
|
24
|
+
?.blockIds || [];
|
25
|
+
for (const id of ids) {
|
26
|
+
pageIds.push(id);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
} catch (error) {
|
30
|
+
console.log(error);
|
31
|
+
}
|
32
|
+
|
33
|
+
// 否则按照数据库原始排序
|
34
|
+
if (
|
35
|
+
pageIds.length === 0 &&
|
36
|
+
collectionQuery &&
|
37
|
+
Object.values(collectionQuery).length > 0 &&
|
38
|
+
collectionId
|
39
|
+
) {
|
40
|
+
const pageSet = new Set<string>();
|
41
|
+
Object.values(collectionQuery[collectionId]).forEach((view) => {
|
42
|
+
view?.blockIds?.forEach((id) => pageSet.add(id)); // group视图
|
43
|
+
view?.collection_group_results?.blockIds?.forEach((id) =>
|
44
|
+
pageSet.add(id),
|
45
|
+
); // table视图
|
46
|
+
});
|
47
|
+
pageIds = [...pageSet];
|
48
|
+
// console.log('PageIds: 从collectionQuery获取', collectionQuery, pageIds.length)
|
49
|
+
}
|
50
|
+
return pageIds;
|
51
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import BLOG from '@/blog.config';
|
2
|
+
import getAllPageIds from './getAllPageIds';
|
3
|
+
import getPageProperties from './getPageProperties';
|
4
|
+
import { getNotionPageData } from '@/lib/notion/getNotionData';
|
5
|
+
import { delCacheData } from '@/lib/cache/cache_manager';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* 获取所有文章列表
|
9
|
+
* @param notionPageData
|
10
|
+
* @param from
|
11
|
+
* @param pageType 页面类型数组 ['Post','Page']
|
12
|
+
* @returns {Promise<*[]>}
|
13
|
+
*/
|
14
|
+
export async function getAllPosts({ notionPageData, from, pageType }) {
|
15
|
+
if (!notionPageData) {
|
16
|
+
notionPageData = await getNotionPageData({ from });
|
17
|
+
}
|
18
|
+
if (!notionPageData) {
|
19
|
+
return [];
|
20
|
+
}
|
21
|
+
|
22
|
+
const {
|
23
|
+
block,
|
24
|
+
schema,
|
25
|
+
collectionQuery,
|
26
|
+
collectionId,
|
27
|
+
collectionView,
|
28
|
+
viewIds,
|
29
|
+
} = notionPageData;
|
30
|
+
const data = [];
|
31
|
+
const pageIds = getAllPageIds(
|
32
|
+
collectionQuery,
|
33
|
+
collectionId,
|
34
|
+
collectionView,
|
35
|
+
viewIds,
|
36
|
+
);
|
37
|
+
for (let i = 0; i < pageIds.length; i++) {
|
38
|
+
const id = pageIds[i];
|
39
|
+
const value = block[id]?.value;
|
40
|
+
if (!value) {
|
41
|
+
continue;
|
42
|
+
}
|
43
|
+
const properties = (await getPageProperties(id, block, schema)) || null;
|
44
|
+
data.push(properties);
|
45
|
+
}
|
46
|
+
|
47
|
+
// remove all the the items doesn't meet requirements
|
48
|
+
const posts = data.filter((post) => {
|
49
|
+
return (
|
50
|
+
post.title &&
|
51
|
+
post?.status?.[0] === 'Published' &&
|
52
|
+
pageType.indexOf(post?.type?.[0]) > -1
|
53
|
+
);
|
54
|
+
});
|
55
|
+
|
56
|
+
if (!posts || posts.length === 0) {
|
57
|
+
const cacheKey = 'page_block_' + BLOG.NOTION_PAGE_ID;
|
58
|
+
await delCacheData(cacheKey);
|
59
|
+
}
|
60
|
+
|
61
|
+
// Sort by date
|
62
|
+
if (BLOG.POSTS_SORT_BY === 'date') {
|
63
|
+
posts.sort((a, b) => {
|
64
|
+
return b?.publishDate - a?.publishDate;
|
65
|
+
});
|
66
|
+
}
|
67
|
+
return posts;
|
68
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import { isIterable } from '../utils';
|
2
|
+
import type { PageInfo, SelectOption, TagInfo } from './types';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* 获取所有文章的标签
|
6
|
+
* @param allPosts
|
7
|
+
* @param sliceCount 默认截取数量为12,若为0则返回全部
|
8
|
+
* @param tagOptions tags的下拉选项
|
9
|
+
* @returns {Promise<{}|*[]>}
|
10
|
+
*/
|
11
|
+
export function getAllTags(
|
12
|
+
publishedPosts: PageInfo[],
|
13
|
+
tagOptions: SelectOption[],
|
14
|
+
sliceCount = 0,
|
15
|
+
): TagInfo[] {
|
16
|
+
// 计数
|
17
|
+
const tags = publishedPosts?.map((p) => p.tags).flat();
|
18
|
+
const countMap: { [key: string]: number } = {};
|
19
|
+
tags.forEach((tag) => {
|
20
|
+
if (tag in countMap) {
|
21
|
+
countMap[tag]++;
|
22
|
+
} else {
|
23
|
+
countMap[tag] = 1;
|
24
|
+
}
|
25
|
+
});
|
26
|
+
const list: TagInfo[] = [];
|
27
|
+
if (isIterable(tagOptions)) {
|
28
|
+
tagOptions.forEach((tag) => {
|
29
|
+
const count = countMap[tag.value];
|
30
|
+
if (count) {
|
31
|
+
list.push({ id: tag.id, name: tag.value, color: tag.color, count });
|
32
|
+
}
|
33
|
+
});
|
34
|
+
}
|
35
|
+
|
36
|
+
// 按照数量排序
|
37
|
+
// list.sort((a, b) => b.count - a.count)
|
38
|
+
if (sliceCount && sliceCount > 0) {
|
39
|
+
return list.slice(0, sliceCount);
|
40
|
+
} else {
|
41
|
+
return list;
|
42
|
+
}
|
43
|
+
}
|
@@ -0,0 +1,340 @@
|
|
1
|
+
import BLOG from '@/blog.config';
|
2
|
+
import { getDataFromCache, setDataToCache } from '@/lib/cache/cacheManager';
|
3
|
+
import { getPostBlocks } from '@/lib/notion/getPostBlocks';
|
4
|
+
import { idToUuid } from 'notion-utils';
|
5
|
+
import { getAllCategories } from './getAllCategories';
|
6
|
+
import getAllPageIds from './getAllPageIds';
|
7
|
+
import { getAllTags } from './getAllTags';
|
8
|
+
import getPageProperties from './getPageProperties';
|
9
|
+
import { mapImgUrl, compressImage } from './mapImage';
|
10
|
+
import dayjs from 'dayjs';
|
11
|
+
|
12
|
+
import type {
|
13
|
+
CustomNav,
|
14
|
+
PageInfo,
|
15
|
+
CollectionPropertySchemaMap,
|
16
|
+
SelectOption,
|
17
|
+
SiteInfo,
|
18
|
+
DataBaseInfo,
|
19
|
+
PatchedCollection,
|
20
|
+
} from './types';
|
21
|
+
|
22
|
+
/**
|
23
|
+
* 获取博客数据
|
24
|
+
* @param {*} pageId
|
25
|
+
* @param {*} from
|
26
|
+
* @returns
|
27
|
+
*
|
28
|
+
*/
|
29
|
+
export async function getGlobalData(from: string) {
|
30
|
+
// 从notion获取
|
31
|
+
const data = await getNotionPageData(idToUuid(BLOG.NOTION_PAGE_ID), from);
|
32
|
+
return data;
|
33
|
+
// const db = cloneDeep(data);
|
34
|
+
// 不返回的敏感数据
|
35
|
+
// delete db.block;
|
36
|
+
// delete db.schema;
|
37
|
+
// delete db.rawMetadata;
|
38
|
+
// delete db.pageIds;
|
39
|
+
// delete db.viewIds;
|
40
|
+
// delete db.collection;
|
41
|
+
// delete db.collectionQuery;
|
42
|
+
// delete db.collectionId;
|
43
|
+
// delete db.collectionView;
|
44
|
+
// return db;
|
45
|
+
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* 获取最新文章 根据最后修改时间倒序排列
|
49
|
+
* @param {*}} param0
|
50
|
+
* @returns
|
51
|
+
*/
|
52
|
+
function getLatestPosts(
|
53
|
+
allPages: PageInfo[],
|
54
|
+
latestPostCount: number,
|
55
|
+
): PageInfo[] {
|
56
|
+
const allPosts = allPages.filter(
|
57
|
+
(page) => page.type === 'Post' && page.status === 'Published',
|
58
|
+
);
|
59
|
+
|
60
|
+
const latestPosts = Object.create(allPosts).sort(
|
61
|
+
(a: PageInfo, b: PageInfo) => {
|
62
|
+
const dateA = dayjs(a?.lastEditedDate || a?.publishDate);
|
63
|
+
const dateB = dayjs(b?.lastEditedDate || b?.publishDate);
|
64
|
+
return dateB.isAfter(dateA) ? 1 : -1;
|
65
|
+
},
|
66
|
+
);
|
67
|
+
return latestPosts.slice(0, latestPostCount);
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
* 获取指定notion的collection数据
|
72
|
+
* @param pageId
|
73
|
+
* @param from 请求来源
|
74
|
+
* @returns {Promise<JSX.Element|*|*[]>}
|
75
|
+
*/
|
76
|
+
export async function getNotionPageData(
|
77
|
+
pageId: string,
|
78
|
+
from: string,
|
79
|
+
): Promise<DataBaseInfo> {
|
80
|
+
// 尝试从缓存获取
|
81
|
+
const cacheKey = 'page_block_' + pageId;
|
82
|
+
const data = await getDataFromCache<DataBaseInfo>(cacheKey);
|
83
|
+
if (data && data.pageIds?.length > 0) {
|
84
|
+
console.log('[缓存]:', `from:${from}`, `root-page-id:${pageId}`);
|
85
|
+
return data;
|
86
|
+
}
|
87
|
+
const dataBaseInfo = await getDataBaseInfoByNotionAPI(pageId, from);
|
88
|
+
// 存入缓存
|
89
|
+
if (dataBaseInfo) {
|
90
|
+
await setDataToCache(cacheKey, dataBaseInfo);
|
91
|
+
}
|
92
|
+
return dataBaseInfo;
|
93
|
+
}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* 获取用户自定义单页菜单
|
97
|
+
* @param notionPageData
|
98
|
+
* @returns {Promise<[]|*[]>}
|
99
|
+
*/
|
100
|
+
function getCustomNav(allPages: PageInfo[]) {
|
101
|
+
const customNav: CustomNav[] = [];
|
102
|
+
if (allPages && allPages.length > 0) {
|
103
|
+
allPages.forEach((p: PageInfo) => {
|
104
|
+
if (p?.slug?.indexOf('http') === 0) {
|
105
|
+
customNav.push({
|
106
|
+
icon: p.icon || '',
|
107
|
+
name: p.title,
|
108
|
+
to: p.slug,
|
109
|
+
target: '_blank',
|
110
|
+
show: true,
|
111
|
+
});
|
112
|
+
} else {
|
113
|
+
customNav.push({
|
114
|
+
icon: p.icon || '',
|
115
|
+
name: p.title,
|
116
|
+
to: '/' + p.slug,
|
117
|
+
target: '_self',
|
118
|
+
show: true,
|
119
|
+
});
|
120
|
+
}
|
121
|
+
});
|
122
|
+
}
|
123
|
+
return customNav;
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* 获取标签选项
|
128
|
+
* @param schemaMap
|
129
|
+
* @returns {undefined}
|
130
|
+
*/
|
131
|
+
function getTagOptions(schemaMap: CollectionPropertySchemaMap): SelectOption[] {
|
132
|
+
if (!schemaMap) return [];
|
133
|
+
const tagSchema = Object.values(schemaMap).find(
|
134
|
+
(e) => e.name === BLOG.NOTION_PROPERTY_NAME.tags,
|
135
|
+
);
|
136
|
+
return tagSchema?.options || [];
|
137
|
+
}
|
138
|
+
|
139
|
+
/**
|
140
|
+
* 获取分类选项
|
141
|
+
* @param schemaMap
|
142
|
+
* @returns {{}|*|*[]}
|
143
|
+
*/
|
144
|
+
function getCategoryOptions(
|
145
|
+
schemaMap: CollectionPropertySchemaMap,
|
146
|
+
): SelectOption[] {
|
147
|
+
if (!schemaMap) return [];
|
148
|
+
const categorySchema = Object.values(schemaMap).find(
|
149
|
+
(e) => e.name === BLOG.NOTION_PROPERTY_NAME.category,
|
150
|
+
);
|
151
|
+
return categorySchema?.options || [];
|
152
|
+
}
|
153
|
+
|
154
|
+
/**
|
155
|
+
* 站点信息
|
156
|
+
* @param notionPageData
|
157
|
+
* @param from
|
158
|
+
* @returns {Promise<{title,description,pageCover,icon}>}
|
159
|
+
*/
|
160
|
+
function getSiteInfo(collection: PatchedCollection): SiteInfo {
|
161
|
+
const title = collection.name[0][0] || BLOG.TITLE;
|
162
|
+
const description = collection.description
|
163
|
+
? Object.assign(collection).description[0][0]
|
164
|
+
: BLOG.DESCRIPTION;
|
165
|
+
const pageCover = collection.cover
|
166
|
+
? mapImgUrl(collection.cover, collection, 'collection')
|
167
|
+
: BLOG.HOME_BANNER_IMAGE;
|
168
|
+
let icon = collection?.icon
|
169
|
+
? mapImgUrl(collection?.icon, collection, 'collection')
|
170
|
+
: BLOG.AVATAR;
|
171
|
+
|
172
|
+
// 用户头像压缩一下
|
173
|
+
icon = compressImage(icon);
|
174
|
+
|
175
|
+
// 站点图标不能是emoji情
|
176
|
+
const emojiPattern = /\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g;
|
177
|
+
if (!icon || emojiPattern.test(icon)) {
|
178
|
+
icon = BLOG.AVATAR;
|
179
|
+
}
|
180
|
+
return { title, description, pageCover, icon };
|
181
|
+
}
|
182
|
+
|
183
|
+
/**
|
184
|
+
* 获取公告
|
185
|
+
*/
|
186
|
+
async function getNotice(post: PageInfo) {
|
187
|
+
if (!post) {
|
188
|
+
return null;
|
189
|
+
}
|
190
|
+
|
191
|
+
post.blockMap = await getPostBlocks(post.id, 'data-notice');
|
192
|
+
return post;
|
193
|
+
}
|
194
|
+
|
195
|
+
/**
|
196
|
+
* 调用NotionAPI获取Page数据
|
197
|
+
* @returns {Promise<JSX.Element|null|*>}
|
198
|
+
*/
|
199
|
+
async function getDataBaseInfoByNotionAPI(
|
200
|
+
pageId: string,
|
201
|
+
from: string,
|
202
|
+
): Promise<DataBaseInfo> {
|
203
|
+
const pageRecordMap = await getPostBlocks(pageId, from);
|
204
|
+
if (!pageRecordMap) {
|
205
|
+
console.error('can`t get Notion Data ; Which id is: ', pageId);
|
206
|
+
throw Error('can`t get Notion Data');
|
207
|
+
// return {};
|
208
|
+
}
|
209
|
+
const blockMap = pageRecordMap.block || {};
|
210
|
+
const rawBlockData = blockMap[pageId].value;
|
211
|
+
// Check Type Page-Database和Inline-Database
|
212
|
+
if (
|
213
|
+
rawBlockData.type !== 'collection_view_page' &&
|
214
|
+
rawBlockData.type !== 'collection_view'
|
215
|
+
) {
|
216
|
+
console.error(`pageId "${pageId}" is not a database`);
|
217
|
+
throw Error(`pageId "${pageId}" is not a database`);
|
218
|
+
// return EmptyData(pageUuid);
|
219
|
+
}
|
220
|
+
const collection = Object.values(pageRecordMap.collection)[0].value || {};
|
221
|
+
const siteInfo = getSiteInfo(collection as PatchedCollection);
|
222
|
+
|
223
|
+
const collectionId = rawBlockData.collection_id || null;
|
224
|
+
const viewIds = rawBlockData.view_ids;
|
225
|
+
|
226
|
+
const collectionQuery = pageRecordMap.collection_query;
|
227
|
+
const collectionView = pageRecordMap.collection_view;
|
228
|
+
|
229
|
+
const schemaMap = collection.schema;
|
230
|
+
const tagOptions = getTagOptions(schemaMap);
|
231
|
+
const categoryOptions = getCategoryOptions(schemaMap);
|
232
|
+
|
233
|
+
const pageIds = getAllPageIds(
|
234
|
+
collectionQuery,
|
235
|
+
collectionId,
|
236
|
+
collectionView,
|
237
|
+
viewIds,
|
238
|
+
);
|
239
|
+
if (pageIds.length === 0) {
|
240
|
+
console.error(
|
241
|
+
'获取到的文章列表为空,请检查notion模板',
|
242
|
+
collectionQuery,
|
243
|
+
collection,
|
244
|
+
collectionView,
|
245
|
+
viewIds,
|
246
|
+
pageRecordMap,
|
247
|
+
);
|
248
|
+
}
|
249
|
+
|
250
|
+
const pageProperties: PageInfo[] = (
|
251
|
+
await Promise.all(
|
252
|
+
pageIds.map(async (pageId) => {
|
253
|
+
if (blockMap[pageId]?.value) {
|
254
|
+
try {
|
255
|
+
const properties = await getPageProperties(
|
256
|
+
pageId,
|
257
|
+
blockMap,
|
258
|
+
schemaMap,
|
259
|
+
);
|
260
|
+
return properties || null;
|
261
|
+
} catch (error) {
|
262
|
+
console.error(
|
263
|
+
`Error getting properties for page ${pageId}:`,
|
264
|
+
error,
|
265
|
+
);
|
266
|
+
return null;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
return null;
|
270
|
+
}),
|
271
|
+
)
|
272
|
+
).filter(Boolean) as PageInfo[];
|
273
|
+
|
274
|
+
// 查找所有的Post和Page
|
275
|
+
const allPages: PageInfo[] = [];
|
276
|
+
const publishedPosts: PageInfo[] = [];
|
277
|
+
|
278
|
+
pageProperties.forEach((page) => {
|
279
|
+
if (page.type === 'Post' && page.status === 'Published') {
|
280
|
+
publishedPosts.push(page);
|
281
|
+
}
|
282
|
+
if (
|
283
|
+
page &&
|
284
|
+
page.slug &&
|
285
|
+
!page.slug?.startsWith('http') &&
|
286
|
+
(page.status === 'Invisible' || page.status === 'Published')
|
287
|
+
) {
|
288
|
+
allPages.push(page);
|
289
|
+
}
|
290
|
+
});
|
291
|
+
|
292
|
+
// Sort by date
|
293
|
+
if (BLOG.POSTS_SORT_BY === 'date') {
|
294
|
+
allPages.sort((a, b) => {
|
295
|
+
return dayjs(b.publishDate).isAfter(a.publishDate) ? 1 : -1;
|
296
|
+
});
|
297
|
+
}
|
298
|
+
|
299
|
+
const notice = await getNotice(
|
300
|
+
pageProperties.filter(
|
301
|
+
(post) =>
|
302
|
+
post &&
|
303
|
+
post.type &&
|
304
|
+
post.type === 'Notice' &&
|
305
|
+
post.status &&
|
306
|
+
post.status === 'Published',
|
307
|
+
)[0],
|
308
|
+
);
|
309
|
+
const categories = getAllCategories(publishedPosts, categoryOptions);
|
310
|
+
const tags = getAllTags(publishedPosts, tagOptions);
|
311
|
+
// 旧的菜单
|
312
|
+
const customNav = getCustomNav(
|
313
|
+
pageProperties.filter(
|
314
|
+
(post) => post?.type === 'Page' && post.status === 'Published',
|
315
|
+
),
|
316
|
+
);
|
317
|
+
|
318
|
+
const latestPosts = getLatestPosts(allPages, 6);
|
319
|
+
|
320
|
+
return {
|
321
|
+
notice,
|
322
|
+
siteInfo,
|
323
|
+
allPages,
|
324
|
+
collection,
|
325
|
+
collectionQuery,
|
326
|
+
collectionId,
|
327
|
+
collectionView,
|
328
|
+
viewIds,
|
329
|
+
block: blockMap,
|
330
|
+
schema: schemaMap,
|
331
|
+
tagOptions: tags,
|
332
|
+
categoryOptions: categories,
|
333
|
+
rawMetadata: rawBlockData,
|
334
|
+
customNav,
|
335
|
+
postCount: publishedPosts.length,
|
336
|
+
publishedPosts,
|
337
|
+
pageIds,
|
338
|
+
latestPosts,
|
339
|
+
};
|
340
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import BLOG from '@/blog.config';
|
2
|
+
import { idToUuid } from 'notion-utils';
|
3
|
+
import { getPostBlocks } from './getPostBlocks';
|
4
|
+
import { defaultMapImageUrl } from 'react-notion-x';
|
5
|
+
import dayjs from 'dayjs';
|
6
|
+
|
7
|
+
import {
|
8
|
+
Block,
|
9
|
+
PageInfo,
|
10
|
+
PagePropertiesType,
|
11
|
+
PagePropertiesStatus,
|
12
|
+
} from './types';
|
13
|
+
|
14
|
+
/**
|
15
|
+
* formate from postBlock to pageInfo
|
16
|
+
* @param {*} pageId
|
17
|
+
* @returns
|
18
|
+
*/
|
19
|
+
export async function getPageInfoOfPostPage(pageId: string, from: string) {
|
20
|
+
const blockMap = await getPostBlocks(pageId, from);
|
21
|
+
if (!blockMap) {
|
22
|
+
return;
|
23
|
+
}
|
24
|
+
|
25
|
+
const postInfo = blockMap?.block?.[idToUuid(pageId)].value;
|
26
|
+
|
27
|
+
return {
|
28
|
+
id: pageId,
|
29
|
+
type: PagePropertiesType.Post,
|
30
|
+
category: '',
|
31
|
+
tags: [],
|
32
|
+
title: postInfo?.properties?.title?.[0],
|
33
|
+
status: PagePropertiesStatus.Published,
|
34
|
+
pageCover: getPageCover(postInfo),
|
35
|
+
date: {
|
36
|
+
type: 'date',
|
37
|
+
start_date: dayjs(postInfo.last_edited_time).format('yyyy-MM-dd'),
|
38
|
+
},
|
39
|
+
blockMap,
|
40
|
+
publishDate: dayjs(postInfo.created_time).valueOf(),
|
41
|
+
lastEditedDate: dayjs(postInfo.last_edited_time).valueOf(),
|
42
|
+
pageIcon: '',
|
43
|
+
pageCoverThumbnail: '',
|
44
|
+
content: [],
|
45
|
+
icon: '',
|
46
|
+
summary: '',
|
47
|
+
slug: '',
|
48
|
+
} as PageInfo;
|
49
|
+
}
|
50
|
+
|
51
|
+
function getPageCover(postInfo: Block) {
|
52
|
+
const pageCover = postInfo.format?.page_cover;
|
53
|
+
if (pageCover) {
|
54
|
+
if (pageCover.startsWith('/')) return BLOG.NOTION_HOST + pageCover;
|
55
|
+
if (pageCover.startsWith('http'))
|
56
|
+
return defaultMapImageUrl(pageCover, postInfo);
|
57
|
+
}
|
58
|
+
}
|