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.
Files changed (218) hide show
  1. package/.dockerignore +1 -0
  2. package/.env.local +2 -0
  3. package/.eslintrc.json +15 -0
  4. package/.github/stale.yml +16 -0
  5. package/.github/workflows/codeql-analysis.yml +73 -0
  6. package/.github/workflows/docker-ghcr.yaml +59 -0
  7. package/.prettierrc +9 -0
  8. package/.vscode/launch.json +28 -0
  9. package/.vscode/settings.json +6 -0
  10. package/Dockerfile +38 -0
  11. package/LICENSE +21 -0
  12. package/README.md +16 -0
  13. package/blog.config.js +454 -0
  14. package/components/Ackee.js +83 -0
  15. package/components/AdBlockDetect.js +40 -0
  16. package/components/AlgoliaSearchModal.js +250 -0
  17. package/components/Artalk.js +30 -0
  18. package/components/Busuanzi.js +26 -0
  19. package/components/ChatBase.js +19 -0
  20. package/components/Collapse.tsx +134 -0
  21. package/components/Comment.js +161 -0
  22. package/components/CommonHead.tsx +101 -0
  23. package/components/CommonScript.js +125 -0
  24. package/components/CusdisComponent.js +35 -0
  25. package/components/CustomContextMenu.js +221 -0
  26. package/components/DebugPanel.js +134 -0
  27. package/components/DisableCopy.js +21 -0
  28. package/components/Draggable.js +167 -0
  29. package/components/Equation.js +31 -0
  30. package/components/ExternalPlugins.js +75 -0
  31. package/components/ExternalScript.js +29 -0
  32. package/components/FacebookMessenger.js +255 -0
  33. package/components/FacebookPage.js +34 -0
  34. package/components/Fireworks.js +210 -0
  35. package/components/FlipCard.js +56 -0
  36. package/components/FlutteringRibbon.js +322 -0
  37. package/components/FullScreenButton.js +48 -0
  38. package/components/Giscus.js +33 -0
  39. package/components/Gitalk.js +42 -0
  40. package/components/GoogleAdsense.js +111 -0
  41. package/components/Gtag.js +18 -0
  42. package/components/HeroIcons.tsx +321 -0
  43. package/components/KatexReact.js +53 -0
  44. package/components/LazyImage.js +95 -0
  45. package/components/Live2D.js +52 -0
  46. package/components/Loading.js +20 -0
  47. package/components/Mark.js +28 -0
  48. package/components/NProgress.ts +8 -0
  49. package/components/Nest.js +124 -0
  50. package/components/NotionIcon.js +20 -0
  51. package/components/NotionPage.tsx +206 -0
  52. package/components/Player.js +54 -0
  53. package/components/PrismMac.js +236 -0
  54. package/components/QrCode.js +34 -0
  55. package/components/Ribbon.js +98 -0
  56. package/components/Sakura.js +192 -0
  57. package/components/Select.js +40 -0
  58. package/components/ShareBar.js +29 -0
  59. package/components/ShareButtons.js +403 -0
  60. package/components/SideBarDrawer.js +50 -0
  61. package/components/StarrySky.js +130 -0
  62. package/components/Tabs.js +64 -0
  63. package/components/ThemeSwitch.js +67 -0
  64. package/components/Twikoo.js +27 -0
  65. package/components/TwikooCommentCount.js +22 -0
  66. package/components/TwikooCommentCounter.js +78 -0
  67. package/components/TwikooRecentComments.js +11 -0
  68. package/components/Utterances.js +35 -0
  69. package/components/VConsole.js +76 -0
  70. package/components/ValineComponent.js +59 -0
  71. package/components/ValineCount.js +6 -0
  72. package/components/ValinePanel.js +3 -0
  73. package/components/Vercel.tsx +54 -0
  74. package/components/WWAds.js +18 -0
  75. package/components/WalineComponent.js +83 -0
  76. package/components/WebMention.js +173 -0
  77. package/components/Webwhiz.js +17 -0
  78. package/components/WordCount.js +73 -0
  79. package/hooks/useToggleClickOutSide.ts +32 -0
  80. package/hooks/useWindowSize.ts +30 -0
  81. package/lib/algolia.js +108 -0
  82. package/lib/busuanzi.js +99 -0
  83. package/lib/cache/cacheManager.ts +49 -0
  84. package/lib/cache/localFileCache.ts +56 -0
  85. package/lib/cache/memoryMache.ts +20 -0
  86. package/lib/cache/mongoDbCache.ts +70 -0
  87. package/lib/cache/types.ts +5 -0
  88. package/lib/font.js +46 -0
  89. package/lib/global.tsx +129 -0
  90. package/lib/gtag.js +17 -0
  91. package/lib/mailchimp.js +49 -0
  92. package/lib/memorize.js +0 -0
  93. package/lib/mhchem.js +1696 -0
  94. package/lib/notion/getAllCategories.ts +51 -0
  95. package/lib/notion/getAllPageIds.ts +51 -0
  96. package/lib/notion/getAllPosts.js +68 -0
  97. package/lib/notion/getAllTags.ts +43 -0
  98. package/lib/notion/getNotionData.ts +340 -0
  99. package/lib/notion/getPageInfoOfPostPage.ts +58 -0
  100. package/lib/notion/getPageProperties.ts +203 -0
  101. package/lib/notion/getPageTableOfContents.ts +107 -0
  102. package/lib/notion/getPostBlocks.ts +147 -0
  103. package/lib/notion/mapImage.ts +130 -0
  104. package/lib/notion/types.ts +125 -0
  105. package/lib/notion.js +2 -0
  106. package/lib/robots.txt.js +25 -0
  107. package/lib/rss.js +63 -0
  108. package/lib/sitemap.xml.js +67 -0
  109. package/lib/utils.js +212 -0
  110. package/next-env.d.ts +5 -0
  111. package/next-i18next.config.js +7 -0
  112. package/next-sitemap.config.js +11 -0
  113. package/next.config.js +124 -0
  114. package/package.json +92 -0
  115. package/pages/404.tsx +40 -0
  116. package/pages/[prefix]/[slug].tsx +123 -0
  117. package/pages/[prefix]/index.tsx +223 -0
  118. package/pages/_app.js +59 -0
  119. package/pages/_document.js +42 -0
  120. package/pages/api/subscribe.js +22 -0
  121. package/pages/archive/index.tsx +79 -0
  122. package/pages/category/[category]/index.tsx +87 -0
  123. package/pages/category/[category]/page/[page].tsx +103 -0
  124. package/pages/category/index.tsx +43 -0
  125. package/pages/index.tsx +88 -0
  126. package/pages/page/[page].tsx +93 -0
  127. package/pages/search/[keyword]/index.tsx +162 -0
  128. package/pages/search/[keyword]/page/[page].tsx +166 -0
  129. package/pages/search/index.tsx +69 -0
  130. package/pages/sitemap.xml.js +70 -0
  131. package/pages/tag/[tag]/index.tsx +73 -0
  132. package/pages/tag/[tag]/page/[page].tsx +87 -0
  133. package/pages/tag/index.tsx +42 -0
  134. package/postcss.config.js +6 -0
  135. package/public/ads.txt +1 -0
  136. package/public/avatar.png +0 -0
  137. package/public/avatar.svg +11 -0
  138. package/public/bg_image.jpg +0 -0
  139. package/public/css/all.min.css +9 -0
  140. package/public/css/custom.css +8 -0
  141. package/public/css/img-shadow.css +5 -0
  142. package/public/css/prism-mac-style.css +58 -0
  143. package/public/favicon.ico +0 -0
  144. package/public/favicon.svg +9 -0
  145. package/public/js/cusdis.es.js +107 -0
  146. package/public/js/custom.js +1 -0
  147. package/public/locales/en/common.json +44 -0
  148. package/public/locales/en/menu.json +9 -0
  149. package/public/locales/en/nav.json +11 -0
  150. package/public/locales/zh-CN/common.json +44 -0
  151. package/public/locales/zh-CN/menu.json +9 -0
  152. package/public/locales/zh-CN/nav.json +9 -0
  153. package/public/webfonts/fa-brands-400.ttf +0 -0
  154. package/public/webfonts/fa-brands-400.woff2 +0 -0
  155. package/public/webfonts/fa-regular-400.ttf +0 -0
  156. package/public/webfonts/fa-regular-400.woff2 +0 -0
  157. package/public/webfonts/fa-solid-900.ttf +0 -0
  158. package/public/webfonts/fa-solid-900.woff2 +0 -0
  159. package/public/webfonts/fa-v4compatibility.ttf +0 -0
  160. package/public/webfonts/fa-v4compatibility.woff2 +0 -0
  161. package/styles/animate.css +503 -0
  162. package/styles/globals.css +183 -0
  163. package/styles/notion.css +2064 -0
  164. package/styles/nprogress.css +84 -0
  165. package/styles/prism-theme.css +119 -0
  166. package/styles/utility-patterns.css +79 -0
  167. package/tailwind.config.js +37 -0
  168. package/theme/index.ts +6 -0
  169. package/theme/types/@theme-components.d.ts +29 -0
  170. package/theme/useLayout.ts +41 -0
  171. package/theme/utils.ts +108 -0
  172. package/themes/innocent/package.json +7 -0
  173. package/themes/innocent/theme.config.js +1 -0
  174. package/themes/nobelium/components/Announcement.tsx +27 -0
  175. package/themes/nobelium/components/ArticleFooter.tsx +39 -0
  176. package/themes/nobelium/components/ArticleInfo.tsx +58 -0
  177. package/themes/nobelium/components/ArticleLock.tsx +86 -0
  178. package/themes/nobelium/components/BlogArchiveItem.js +41 -0
  179. package/themes/nobelium/components/BlogListBar.js +39 -0
  180. package/themes/nobelium/components/BlogListPage.tsx +67 -0
  181. package/themes/nobelium/components/BlogListScroll.tsx +96 -0
  182. package/themes/nobelium/components/BlogPost.tsx +33 -0
  183. package/themes/nobelium/components/DarkModeButton.tsx +50 -0
  184. package/themes/nobelium/components/ExampleRecentComments.js +35 -0
  185. package/themes/nobelium/components/Footer.tsx +35 -0
  186. package/themes/nobelium/components/JumpToTopButton.tsx +39 -0
  187. package/themes/nobelium/components/LanguageSwitchButton.tsx +58 -0
  188. package/themes/nobelium/components/MenuItemCollapse.tsx +92 -0
  189. package/themes/nobelium/components/MenuItemDrop.tsx +83 -0
  190. package/themes/nobelium/components/Nav/Nav.module.css +50 -0
  191. package/themes/nobelium/components/Nav/Nav.tsx +187 -0
  192. package/themes/nobelium/components/RandomPostButton.tsx +31 -0
  193. package/themes/nobelium/components/SearchButton.tsx +31 -0
  194. package/themes/nobelium/components/SearchInput.tsx +94 -0
  195. package/themes/nobelium/components/SearchNavBar.js +19 -0
  196. package/themes/nobelium/components/SideBar.js +83 -0
  197. package/themes/nobelium/components/SvgIcon.js +29 -0
  198. package/themes/nobelium/components/TagItem.js +13 -0
  199. package/themes/nobelium/components/Tags.tsx +44 -0
  200. package/themes/nobelium/components/Title.js +19 -0
  201. package/themes/nobelium/index.tsx +28 -0
  202. package/themes/nobelium/layout/LayoutBase.tsx +79 -0
  203. package/themes/nobelium/pages/Archive.tsx +30 -0
  204. package/themes/nobelium/pages/Category.tsx +43 -0
  205. package/themes/nobelium/pages/Home.tsx +22 -0
  206. package/themes/nobelium/pages/PageNotFound.tsx +15 -0
  207. package/themes/nobelium/pages/Post.tsx +34 -0
  208. package/themes/nobelium/pages/PostList.tsx +74 -0
  209. package/themes/nobelium/pages/Search.tsx +65 -0
  210. package/themes/nobelium/pages/Tag.tsx +42 -0
  211. package/themes/nobelium/providers/index.tsx +60 -0
  212. package/themes/nobelium/stores/index.tsx +42 -0
  213. package/themes/nobelium/theme.config.ts +17 -0
  214. package/themes/nobelium/types/index.ts +10 -0
  215. package/tsconfig.json +29 -0
  216. package/types/index.ts +1 -0
  217. package/types/page.ts +102 -0
  218. 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
+ }