diff --git a/.env.local b/.env.local index 3eba4322..c7c37481 100644 --- a/.env.local +++ b/.env.local @@ -1,2 +1,2 @@ # 环境变量 @see https://www.nextjs.cn/docs/basic-features/environment-variables -NEXT_PUBLIC_VERSION=4.0.6 \ No newline at end of file +NEXT_PUBLIC_VERSION=4.0.8 \ No newline at end of file diff --git a/blog.config.js b/blog.config.js index ce766dc0..d1a0d961 100644 --- a/blog.config.js +++ b/blog.config.js @@ -1,11 +1,11 @@ -// 注: process.env.XX是Vercel的环境变量,配置方式见:https://docs.tangly1024.com/zh/features/personality +// 注: process.env.XX是Vercel的环境变量,配置方式见:https://docs.tangly1024.com/article/how-to-config-notion-next#c4768010ae7d44609b744e79e2f9959a const BLOG = { // Important page_id!!!Duplicate Template from https://www.notion.so/tanghh/02ab3b8678004aa69e9e415905ef32a5 NOTION_PAGE_ID: - process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5', + process.env.NOTION_PAGE_ID || '02ab3b8678004aa69e9e415905ef32a5', PSEUDO_STATIC: process.env.NEXT_PUBLIC_PSEUDO_STATIC || false, // 伪静态路径,开启后所有文章URL都以 .html 结尾。 NEXT_REVALIDATE_SECOND: process.env.NEXT_PUBLIC_REVALIDATE_SECOND || 5, // 更新内容缓存间隔 单位(秒);即每个页面有5秒的纯静态期、此期间无论多少次访问都不会抓取notion数据;调大该值有助于节省Vercel资源、同时提升访问速率,但也会使文章更新有延迟。 - THEME: process.env.NEXT_PUBLIC_THEME || 'hexo', // 主题, 支持 ['next','hexo',"fukasawa','medium','example','matery','gitbook','simple'] @see https://preview.tangly1024.com + THEME: process.env.NEXT_PUBLIC_THEME || 'hexo', // 当前主题,在themes文件夹下可找到所有支持的主题;主题名称就是文件夹名,例如 example,fukasawa,gitbook,heo,hexo,landing,matery,medium,next,nobelium,plog,simple THEME_SWITCH: process.env.NEXT_PUBLIC_THEME_SWITCH || false, // 是否显示切换主题按钮 LANG: process.env.NEXT_PUBLIC_LANG || 'zh-CN', // e.g 'zh-CN','en-US' see /lib/lang.js for more. SINCE: 2021, // e.g if leave this empty, current year will be used. @@ -21,6 +21,7 @@ const BLOG = { BIO: process.env.NEXT_PUBLIC_BIO || '一个普通的干饭人🍚', // 作者简介 LINK: process.env.NEXT_PUBLIC_LINK || 'https://tangly1024.com', // 网站地址 KEYWORDS: process.env.NEXT_PUBLIC_KEYWORD || 'Notion, 博客', // 网站关键词 英文逗号隔开 + // 社交链接,不需要可留空白,例如 CONTACT_WEIBO:'' CONTACT_EMAIL: process.env.NEXT_PUBLIC_CONTACT_EMAIL || '', // 邮箱地址 例如mail@tangly1024.com CONTACT_WEIBO: process.env.NEXT_PUBLIC_CONTACT_WEIBO || '', // 你的微博个人主页 @@ -41,7 +42,7 @@ const BLOG = { FONT_STYLE: process.env.NEXT_PUBLIC_FONT_STYLE || 'font-sans', // ['font-serif','font-sans'] 两种可选,分别是衬线和无衬线: 参考 https://www.jianshu.com/p/55e410bd2115 // 字体CSS 例如 https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css FONT_URL: [ - // 'https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css' + // 'https://npm.elemecdn.com/lxgw-wenkai-webfont@1.6.0/style.css', 'https://fonts.googleapis.com/css?family=Bitter&display=swap', 'https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300&display=swap', 'https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300&display=swap' @@ -144,7 +145,7 @@ const BLOG = { ALGOLIA_ADMIN_APP_KEY: process.env.ALGOLIA_ADMIN_APP_KEY || null, // 管理后台的KEY,不要暴露在代码中,在这里查看 https://dashboard.algolia.com/account/api-keys/ ALGOLIA_SEARCH_ONLY_APP_KEY: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_APP_KEY || null, // 客户端搜索用的KEY ALGOLIA_INDEX: process.env.NEXT_PUBLIC_ALGOLIA_INDEX || null, // 在Algolia中创建一个index用作数据库 - ALGOLIA_RECREATE_DATA: process.env.ALGOLIA_RECREATE_DATA || process.env.npm_lifecycle_event === 'build', // 为true时重新构建索引数据; 默认在build时会构建 + // ALGOLIA_RECREATE_DATA: process.env.ALGOLIA_RECREATE_DATA || process.env.npm_lifecycle_event === 'build', // 为true时重新构建索引数据; 默认在build时会构建 PREVIEW_CATEGORY_COUNT: 16, // 首页最多展示的分类数量,0为不限制 PREVIEW_TAG_COUNT: 16, // 首页最多展示的标签数量,0为不限制 @@ -179,19 +180,19 @@ const BLOG = { // 悬浮挂件 WIDGET_PET: process.env.NEXT_PUBLIC_WIDGET_PET || true, // 是否显示宠物挂件 WIDGET_PET_LINK: - process.env.NEXT_PUBLIC_WIDGET_PET_LINK || - 'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 挂件模型地址 @see https://github.com/xiazeyu/live2d-widget-models + process.env.NEXT_PUBLIC_WIDGET_PET_LINK || + 'https://cdn.jsdelivr.net/npm/live2d-widget-model-wanko@1.0.5/assets/wanko.model.json', // 挂件模型地址 @see https://github.com/xiazeyu/live2d-widget-models WIDGET_PET_SWITCH_THEME: process.env.NEXT_PUBLIC_WIDGET_PET_SWITCH_THEME || true, // 点击宠物挂件切换博客主题 // 音乐播放插件 MUSIC_PLAYER: process.env.NEXT_PUBLIC_MUSIC_PLAYER || false, // 是否使用音乐播放插件 MUSIC_PLAYER_VISIBLE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_VISIBLE || true, // 是否在左下角显示播放和切换,如果使用播放器,打开自动播放再隐藏,就会以类似背景音乐的方式播放,无法取消和暂停 MUSIC_PLAYER_AUTO_PLAY: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_AUTO_PLAY || true, // 是否自动播放,不过自动播放时常不生效(移动设备不支持自动播放) + process.env.NEXT_PUBLIC_MUSIC_PLAYER_AUTO_PLAY || true, // 是否自动播放,不过自动播放时常不生效(移动设备不支持自动播放) MUSIC_PLAYER_LRC_TYPE: process.env.NEXT_PUBLIC_MUSIC_PLAYER_LRC_TYPE || '0', // 歌词显示类型,可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url)(前提是有配置歌词路径,对 meting 无效) MUSIC_PLAYER_CDN_URL: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_CDN_URL || - 'https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.js', + process.env.NEXT_PUBLIC_MUSIC_PLAYER_CDN_URL || + 'https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.js', MUSIC_PLAYER_ORDER: process.env.NEXT_PUBLIC_MUSIC_PLAYER_ORDER || 'list', // 默认播放方式,顺序 list,随机 random MUSIC_PLAYER_AUDIO_LIST: [ // 示例音乐列表。除了以下配置外,还可配置歌词,具体配置项看此文档 https://aplayer.js.org/#/zh-Hans/ @@ -200,23 +201,23 @@ const BLOG = { artist: 'Falcom Sound Team jdk', url: 'https://music.163.com/song/media/outer/url?id=731419.mp3', cover: - 'https://p2.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg' + 'https://p2.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg' }, { name: '王都グランセル', artist: 'Falcom Sound Team jdk', url: 'https://music.163.com/song/media/outer/url?id=731355.mp3', cover: - 'https://p1.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg' + 'https://p1.music.126.net/kn6ugISTonvqJh3LHLaPtQ==/599233837187278.jpg' } ], MUSIC_PLAYER_METING: process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING || false, // 是否要开启 MetingJS,从平台获取歌单。会覆盖自定义的 MUSIC_PLAYER_AUDIO_LIST,更多配置信息:https://github.com/metowolf/MetingJS MUSIC_PLAYER_METING_SERVER: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_SERVER || 'netease', // 音乐平台,[netease, tencent, kugou, xiami, baidu] + process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_SERVER || 'netease', // 音乐平台,[netease, tencent, kugou, xiami, baidu] MUSIC_PLAYER_METING_ID: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_ID || '60198', // 对应歌单的 id + process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_ID || '60198', // 对应歌单的 id MUSIC_PLAYER_METING_LRC_TYPE: - process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_LRC_TYPE || '1', // 可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url) + process.env.NEXT_PUBLIC_MUSIC_PLAYER_METING_LRC_TYPE || '1', // 可选值: 3 | 1 | 0(0:禁用 lrc 歌词,1:lrc 格式的字符串,3:lrc 文件 url) // ********挂件组件相关******** // ----> 评论互动 可同时开启多个支持 WALINE VALINE GISCUS CUSDIS UTTERRANCES GITALK @@ -228,42 +229,42 @@ const BLOG = { // utterance COMMENT_UTTERRANCES_REPO: - process.env.NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO || '', // 你的代码仓库名, 例如我是 'tangly1024/NotionNext'; 更多文档参考 https://utteranc.es/ + process.env.NEXT_PUBLIC_COMMENT_UTTERRANCES_REPO || '', // 你的代码仓库名, 例如我是 'tangly1024/NotionNext'; 更多文档参考 https://utteranc.es/ // giscus @see https://giscus.app/ COMMENT_GISCUS_REPO: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO || '', // 你的Github仓库名 e.g 'tangly1024/NotionNext' COMMENT_GISCUS_REPO_ID: process.env.NEXT_PUBLIC_COMMENT_GISCUS_REPO_ID || '', // 你的Github Repo ID e.g ( 設定完 giscus 即可看到 ) COMMENT_GISCUS_CATEGORY_ID: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_CATEGORY_ID || '', // 你的Github Discussions 內的 Category ID ( 設定完 giscus 即可看到 ) + process.env.NEXT_PUBLIC_COMMENT_GISCUS_CATEGORY_ID || '', // 你的Github Discussions 內的 Category ID ( 設定完 giscus 即可看到 ) COMMENT_GISCUS_MAPPING: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_MAPPING || 'pathname', // 你的Github Discussions 使用哪種方式來標定文章, 預設 'pathname' + process.env.NEXT_PUBLIC_COMMENT_GISCUS_MAPPING || 'pathname', // 你的Github Discussions 使用哪種方式來標定文章, 預設 'pathname' COMMENT_GISCUS_REACTIONS_ENABLED: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_REACTIONS_ENABLED || '1', // 你的 Giscus 是否開啟文章表情符號 '1' 開啟 "0" 關閉 預設開啟 + process.env.NEXT_PUBLIC_COMMENT_GISCUS_REACTIONS_ENABLED || '1', // 你的 Giscus 是否開啟文章表情符號 '1' 開啟 "0" 關閉 預設開啟 COMMENT_GISCUS_EMIT_METADATA: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_EMIT_METADATA || '0', // 你的 Giscus 是否提取 Metadata '1' 開啟 '0' 關閉 預設關閉 + process.env.NEXT_PUBLIC_COMMENT_GISCUS_EMIT_METADATA || '0', // 你的 Giscus 是否提取 Metadata '1' 開啟 '0' 關閉 預設關閉 COMMENT_GISCUS_INPUT_POSITION: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_INPUT_POSITION || 'bottom', // 你的 Giscus 發表留言位置 'bottom' 尾部 'top' 頂部, 預設 'bottom' + process.env.NEXT_PUBLIC_COMMENT_GISCUS_INPUT_POSITION || 'bottom', // 你的 Giscus 發表留言位置 'bottom' 尾部 'top' 頂部, 預設 'bottom' COMMENT_GISCUS_LANG: process.env.NEXT_PUBLIC_COMMENT_GISCUS_LANG || 'zh-CN', // 你的 Giscus 語言 e.g 'en', 'zh-TW', 'zh-CN', 預設 'en' COMMENT_GISCUS_LOADING: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_LOADING || 'lazy', // 你的 Giscus 載入是否漸進式載入, 預設 'lazy' + process.env.NEXT_PUBLIC_COMMENT_GISCUS_LOADING || 'lazy', // 你的 Giscus 載入是否漸進式載入, 預設 'lazy' COMMENT_GISCUS_CROSSORIGIN: - process.env.NEXT_PUBLIC_COMMENT_GISCUS_CROSSORIGIN || 'anonymous', // 你的 Giscus 可以跨網域, 預設 'anonymous' + process.env.NEXT_PUBLIC_COMMENT_GISCUS_CROSSORIGIN || 'anonymous', // 你的 Giscus 可以跨網域, 預設 'anonymous' COMMENT_CUSDIS_APP_ID: process.env.NEXT_PUBLIC_COMMENT_CUSDIS_APP_ID || '', // data-app-id 36位 see https://cusdis.com/ COMMENT_CUSDIS_HOST: - process.env.NEXT_PUBLIC_COMMENT_CUSDIS_HOST || 'https://cusdis.com', // data-host, change this if you're using self-hosted version + process.env.NEXT_PUBLIC_COMMENT_CUSDIS_HOST || 'https://cusdis.com', // data-host, change this if you're using self-hosted version COMMENT_CUSDIS_SCRIPT_SRC: - process.env.NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC || - 'https://cusdis.com/js/cusdis.es.js', // change this if you're using self-hosted version + process.env.NEXT_PUBLIC_COMMENT_CUSDIS_SCRIPT_SRC || + 'https://cusdis.com/js/cusdis.es.js', // change this if you're using self-hosted version // gitalk评论插件 更多参考 https://gitalk.github.io/ COMMENT_GITALK_REPO: process.env.NEXT_PUBLIC_COMMENT_GITALK_REPO || '', // 你的Github仓库名,例如 'NotionNext' COMMENT_GITALK_OWNER: process.env.NEXT_PUBLIC_COMMENT_GITALK_OWNER || '', // 你的用户名 e.g tangly1024 COMMENT_GITALK_ADMIN: process.env.NEXT_PUBLIC_COMMENT_GITALK_ADMIN || '', // 管理员用户名、一般是自己 e.g 'tangly1024' COMMENT_GITALK_CLIENT_ID: - process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID || '', // e.g 20位ID , 在gitalk后台获取 + process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_ID || '', // e.g 20位ID , 在gitalk后台获取 COMMENT_GITALK_CLIENT_SECRET: - process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET || '', // e.g 40位ID, 在gitalk后台获取 + process.env.NEXT_PUBLIC_COMMENT_GITALK_CLIENT_SECRET || '', // e.g 40位ID, 在gitalk后台获取 COMMENT_GITALK_DISTRACTION_FREE_MODE: false, // 类似facebook的无干扰模式 COMMENT_GITALK_JS_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_GITALK_JS_CDN_URL || 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js', // gitalk客户端 js cdn COMMENT_GITALK_CSS_CDN_URL: process.env.NEXT_PUBLIC_COMMENT_GITALK_CSS_CDN_URL || 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css', // gitalk客户端 css cdn @@ -277,7 +278,7 @@ const BLOG = { COMMENT_VALINE_APP_KEY: process.env.NEXT_PUBLIC_VALINE_KEY || '', COMMENT_VALINE_SERVER_URLS: process.env.NEXT_PUBLIC_VALINE_SERVER_URLS || '', // 该配置适用于国内自定义域名用户, 海外版本会自动检测(无需手动填写) @see https://valine.js.org/configuration.html#serverURLs COMMENT_VALINE_PLACEHOLDER: - process.env.NEXT_PUBLIC_VALINE_PLACEHOLDER || '抢个沙发吧~', // 可以搭配后台管理评论 https://github.com/DesertsP/Valine-Admin 便于查看评论,以及邮件通知,垃圾评论过滤等功能 + process.env.NEXT_PUBLIC_VALINE_PLACEHOLDER || '抢个沙发吧~', // 可以搭配后台管理评论 https://github.com/DesertsP/Valine-Admin 便于查看评论,以及邮件通知,垃圾评论过滤等功能 COMMENT_WALINE_SERVER_URL: process.env.NEXT_PUBLIC_WALINE_SERVER_URL || '', // 请配置完整的Waline评论地址 例如 hhttps://preview-waline.tangly1024.com @see https://waline.js.org/guide/get-started.html COMMENT_WALINE_RECENT: process.env.NEXT_PUBLIC_WALINE_RECENT || false, // 最新评论 @@ -307,17 +308,17 @@ const BLOG = { ANALYTICS_GOOGLE_ID: process.env.NEXT_PUBLIC_ANALYTICS_GOOGLE_ID || '', // 谷歌Analytics的id e.g: G-XXXXXXXXXX ANALYTICS_ACKEE_TRACKER: - process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.net/tracker.js' + process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_TRACKER || '', // e.g 'https://ackee.tangly1024.net/tracker.js' ANALYTICS_ACKEE_DATA_SERVER: - process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.net , don't end with a slash + process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DATA_SERVER || '', // e.g https://ackee.tangly1024.net , don't end with a slash ANALYTICS_ACKEE_DOMAIN_ID: - process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b' + process.env.NEXT_PUBLIC_ANALYTICS_ACKEE_DOMAIN_ID || '', // e.g '0e2257a8-54d4-4847-91a1-0311ea48cc7b' SEO_GOOGLE_SITE_VERIFICATION: - process.env.NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code + process.env.NEXT_PUBLIC_SEO_GOOGLE_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code SEO_BAIDU_SITE_VERIFICATION: - process.env.NEXT_PUBLIC_SEO_BAIDU_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code + process.env.NEXT_PUBLIC_SEO_BAIDU_SITE_VERIFICATION || '', // Remove the value or replace it with your own google site verification code // <---- 站点统计 @@ -336,16 +337,16 @@ const BLOG = { type_post: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_POST || 'Post', // 当type文章类型与此值相同时,为博文。 type_page: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_PAGE || 'Page', // 当type文章类型与此值相同时,为单页。 type_notice: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_NOTICE || 'Notice', // 当type文章类型与此值相同时,为公告。 + process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_NOTICE || 'Notice', // 当type文章类型与此值相同时,为公告。 type_menu: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_MENU || 'Menu', // 当type文章类型与此值相同时,为菜单。 type_sub_menu: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_SUB_MENU || 'SubMenu', // 当type文章类型与此值相同时,为子菜单。 + process.env.NEXT_PUBLIC_NOTION_PROPERTY_TYPE_SUB_MENU || 'SubMenu', // 当type文章类型与此值相同时,为子菜单。 title: process.env.NEXT_PUBLIC_NOTION_PROPERTY_TITLE || 'title', // 文章标题 status: process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS || 'status', status_publish: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_PUBLISH || 'Published', // 当status状态值与此相同时为发布,可以为中文 + process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_PUBLISH || 'Published', // 当status状态值与此相同时为发布,可以为中文 status_invisible: - process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_INVISIBLE || 'Invisible', // 当status状态值与此相同时为隐藏发布,可以为中文 , 除此之外其他页面状态不会显示在博客上 + process.env.NEXT_PUBLIC_NOTION_PROPERTY_STATUS_INVISIBLE || 'Invisible', // 当status状态值与此相同时为隐藏发布,可以为中文 , 除此之外其他页面状态不会显示在博客上 summary: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SUMMARY || 'summary', slug: process.env.NEXT_PUBLIC_NOTION_PROPERTY_SLUG || 'slug', category: process.env.NEXT_PUBLIC_NOTION_PROPERTY_CATEGORY || 'category', @@ -363,9 +364,9 @@ const BLOG = { AVATAR: process.env.NEXT_PUBLIC_AVATAR || '/avatar.svg', // 作者头像,被notion中的ICON覆盖。若无ICON则取public目录下的avatar.png TITLE: process.env.NEXT_PUBLIC_TITLE || 'NotionNext BLOG', // 站点标题 ,被notion中的页面标题覆盖;此处请勿留空白,否则服务器无法编译 HOME_BANNER_IMAGE: - process.env.NEXT_PUBLIC_HOME_BANNER_IMAGE || '/bg_image.jpg', // 首页背景大图, 会被notion中的封面图覆盖,若无封面图则会使用代码中的 /public/bg_image.jpg 文件 + process.env.NEXT_PUBLIC_HOME_BANNER_IMAGE || '/bg_image.jpg', // 首页背景大图, 会被notion中的封面图覆盖,若无封面图则会使用代码中的 /public/bg_image.jpg 文件 DESCRIPTION: - process.env.NEXT_PUBLIC_DESCRIPTION || '这是一个由NotionNext生成的站点', // 站点描述,被notion中的页面描述覆盖 + process.env.NEXT_PUBLIC_DESCRIPTION || '这是一个由NotionNext生成的站点', // 站点描述,被notion中的页面描述覆盖 // 网站图片 IMG_LAZY_LOAD_PLACEHOLDER: process.env.NEXT_PUBLIC_IMG_LAZY_LOAD_PLACEHOLDER || 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==', // 懒加载占位图片地址,支持base64或url @@ -375,7 +376,7 @@ const BLOG = { // 开发相关 NOTION_ACCESS_TOKEN: process.env.NOTION_ACCESS_TOKEN || '', // Useful if you prefer not to make your database public DEBUG: process.env.NEXT_PUBLIC_DEBUG || false, // 是否显示调试按钮 - ENABLE_CACHE: process.env.ENABLE_CACHE || false, // 开启缓存会将Notion数据缓存在内存中,通常在开发调试中使用,正式部署开启此功能意义不大。 + ENABLE_CACHE: process.env.ENABLE_CACHE || process.env.npm_lifecycle_event === 'build', // 缓存在开发调试和打包过程中选择性开启,正式部署开启此功能意义不大。 isProd: process.env.VERCEL_ENV === 'production', // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) isProd: process.env.VERCEL_ENV === 'production' // distinguish between development and production environment (ref: https://vercel.com/docs/environment-variables#system-environment-variables) VERSION: process.env.NEXT_PUBLIC_VERSION // 版本号 } diff --git a/components/AlgoliaSearchModal.js b/components/AlgoliaSearchModal.js index 32d0401d..b927fee2 100644 --- a/components/AlgoliaSearchModal.js +++ b/components/AlgoliaSearchModal.js @@ -1,7 +1,10 @@ -import { useState, useImperativeHandle } from 'react' +import { useState, useImperativeHandle, useRef } from 'react' import BLOG from '@/blog.config' import algoliasearch from 'algoliasearch' import replaceSearchResult from '@/components/Mark' +import Link from 'next/link' +import { useGlobal } from '@/lib/global' +import throttle from 'lodash/throttle' /** * 结合 Algolia 实现的弹出式搜索框 @@ -11,6 +14,12 @@ import replaceSearchResult from '@/components/Mark' export default function AlgoliaSearchModal({ cRef }) { const [searchResults, setSearchResults] = useState([]) const [isModalOpen, setIsModalOpen] = useState(false) + const [page, setPage] = useState(0) + const [keyword, setKeyword] = useState(null) + const [totalPage, setTotalPage] = useState(0) + const [totalHit, setTotalHit] = useState(0) + const [useTime, setUseTime] = useState(0) + /** * 对外暴露方法 */ @@ -22,66 +31,108 @@ export default function AlgoliaSearchModal({ cRef }) { } }) - if (!BLOG.ALGOLIA_APP_ID) { - return <> - } - const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_SEARCH_ONLY_APP_KEY) const index = client.initIndex(BLOG.ALGOLIA_INDEX) - const handleSearch = async (query) => { + /** + * 搜索 + * @param {*} query + */ + const handleSearch = async (query, page) => { + setKeyword(query) + setPage(page) + setSearchResults([]) + setUseTime(0) + setTotalPage(0) + setTotalHit(0) + if (!query || query === '') { + return + } + try { - const res = await index.search(query) - console.log(res) - const { hits } = res + const res = await index.search(query, { page, hitsPerPage: 10 }) + const { hits, nbHits, nbPages, processingTimeMS } = res + setUseTime(processingTimeMS) + setTotalPage(nbPages) + setTotalHit(nbHits) setSearchResults(hits) + const doms = document.getElementById('search-wrapper').getElementsByClassName('replace') - replaceSearchResult({ - doms, - search: query, - target: { - element: 'span', - className: 'text-blue-600 border-b border-dashed' - } - }) + + setTimeout(() => { + replaceSearchResult({ + doms, + search: query, + target: { + element: 'span', + className: 'text-blue-600 border-b border-dashed' + } + }) + }, 150) } catch (error) { console.error('Algolia search error:', error) } } + const throttledHandleSearch = useRef(throttle(handleSearch, 300)) // 设置节流延迟时间 + + // 修改input的onChange事件处理函数 + const handleInputChange = (e) => { + const query = e.target.value + throttledHandleSearch.current(query, 0) + } + + /** + * 切换页码 + * @param {*} page + */ + const switchPage = (page) => { + throttledHandleSearch.current(keyword, page) + } + + /** + * 关闭弹窗 + */ const closeModal = () => { setIsModalOpen(false) } + if (!BLOG.ALGOLIA_APP_ID) { + return <> + } + return ( -
- {/* 内容 */} +
+ + {/* 模态框 */}
搜索
-
+
- handleSearch(e.target.value)} - className="bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md" /> + handleInputChange(e)} + className="text-black dark:text-gray-200 bg-gray-50 dark:bg-gray-600 outline-blue-500 w-full px-4 my-2 py-1 mb-4 border rounded-md" /> {/* 标签组 */} -
- +
+
-
Algolia 提供搜索服务
+ +
{totalHit > 0 &&
共搜索到 {totalHit} 条结果,用时 {useTime} 毫秒
}
+
Algolia 提供搜索服务
{/* 遮罩 */} @@ -90,3 +141,59 @@ export default function AlgoliaSearchModal({ cRef }) {
) } + +/** + * 标签组 + */ +function TagGroups(props) { + const { tagOptions } = useGlobal() + // 获取tagOptions数组前十个 + const firstTenTags = tagOptions?.slice(0, 10) + + return
+ { + firstTenTags?.map((tag, index) => { + return +
+
{tag.name}
{tag.count ? {tag.count} : <>} +
+ + + }) + } +
+} + +/** + * 分页 + * @param {*} param0 + */ +function Pagination(props) { + const { totalPage, page, switchPage } = props + if (totalPage <= 0) { + return <> + } + const pagesElement = [] + + for (let i = 0; i < totalPage; i++) { + const selected = page === i + pagesElement.push(getPageElement(i, selected, switchPage)) + } + return
+ {pagesElement.map(p => p)} +
+} + +/** + * 获取分页按钮 + * @param {*} i + * @param {*} selected + */ +function getPageElement(i, selected, switchPage) { + return
switchPage(i)} className={`${selected ? 'font-bold text-white bg-blue-600 rounded' : 'hover:text-blue-600 hover:font-bold'} text-center cursor-pointer w-6 h-6 `}> + {i + 1} +
+} diff --git a/components/CommonHead.js b/components/CommonHead.js index 32b71fad..24bfae69 100644 --- a/components/CommonHead.js +++ b/components/CommonHead.js @@ -58,7 +58,7 @@ const CommonHead = ({ meta, children }) => { <> diff --git a/components/DebugPanel.js b/components/DebugPanel.js index 053962b3..026b5ee0 100644 --- a/components/DebugPanel.js +++ b/components/DebugPanel.js @@ -32,7 +32,6 @@ const DebugPanel = () => { } function handleUpdateDebugTheme(newTheme) { - console.log('切换主题', newTheme) const query = { ...router.query, theme: newTheme } router.push({ pathname: router.pathname, query }) } diff --git a/components/Gitalk.js b/components/Gitalk.js index 166eb8c7..4df0c65d 100644 --- a/components/Gitalk.js +++ b/components/Gitalk.js @@ -16,10 +16,8 @@ const Gitalk = ({ frontMatter }) => { // distractionFreeMode: JSON.parse(BLOG.COMMENT_GITALK_DISTRACTION_FREE_MODE) // }} /> const loadGitalk = async() => { - const css = await loadExternalResource(BLOG.COMMENT_GITALK_CSS_CDN_URL, 'css') - const js = await loadExternalResource(BLOG.COMMENT_GITALK_JS_CDN_URL, 'js') - - console.log('gitalk 加载成功', css, js) + await loadExternalResource(BLOG.COMMENT_GITALK_CSS_CDN_URL, 'css') + await loadExternalResource(BLOG.COMMENT_GITALK_JS_CDN_URL, 'js') const Gitalk = window.Gitalk const gitalk = new Gitalk({ diff --git a/components/GoogleAdsense.js b/components/GoogleAdsense.js index 9cdaa406..22fca879 100644 --- a/components/GoogleAdsense.js +++ b/components/GoogleAdsense.js @@ -17,7 +17,6 @@ export default function GoogleAdsense() { setTimeout(() => { const ads = document.getElementsByClassName('adsbygoogle') const adsbygoogle = window.adsbygoogle - console.log('google-ads', adsbygoogle) if (ads.length > 0) { for (let i = 0; i <= ads.length; i++) { try { diff --git a/components/Mark.js b/components/Mark.js index 1defca32..ec2a7901 100644 --- a/components/Mark.js +++ b/components/Mark.js @@ -9,10 +9,7 @@ export default async function replaceSearchResult({ doms, search, target }) { } try { - const url = await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js', 'js') - console.log('markjs 加载成功', url, window.Mark) - console.log('------', doms) - + await loadExternalResource('https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js', 'js') const Mark = window.Mark if (doms instanceof HTMLCollection) { for (const container of doms) { diff --git a/components/PrismMac.js b/components/PrismMac.js index b37ba503..172c6ceb 100644 --- a/components/PrismMac.js +++ b/components/PrismMac.js @@ -140,8 +140,8 @@ const renderMermaid = async() => { } if (needLoad) { loadExternalResource(BLOG.MERMAID_CDN, 'js').then(url => { - // console.log('mermaid加载成功', url, mermaid) const mermaid = window.mermaid + console.log('mermaid加载成功', url, mermaid) mermaid.contentLoaded() }) } @@ -216,7 +216,6 @@ const fixCodeLineStyle = () => { setTimeout(() => { const preCodes = document.querySelectorAll('pre.notion-code') for (const preCode of preCodes) { - // console.log('code', preCode) Prism.plugins.lineNumbers.resize(preCode) } }, 10) diff --git a/components/TwikooCommentCounter.js b/components/TwikooCommentCounter.js index b3b54ca3..87c16aaf 100644 --- a/components/TwikooCommentCounter.js +++ b/components/TwikooCommentCounter.js @@ -27,7 +27,6 @@ const TwikooCommentCounter = (props) => { urls: posts?.map(post => post.slug), // 不包含协议、域名、参数的文章路径列表,必传参数 includeReply: true // 评论数是否包括回复,默认:false }).then(function (res) { - // console.log('查询', res) commentsData = res updateCommentCount() }).catch(function (err) { diff --git a/components/ValineComponent.js b/components/ValineComponent.js index 359cff6d..3a7464b3 100644 --- a/components/ValineComponent.js +++ b/components/ValineComponent.js @@ -5,8 +5,8 @@ import { useEffect } from 'react' const ValineComponent = ({ path }) => { const loadValine = async () => { try { - const url = await loadExternalResource(BLOG.COMMENT_VALINE_CDN, 'js') - console.log('valine 加载成功', url) + await loadExternalResource(BLOG.COMMENT_VALINE_CDN, 'js') + // console.log('valine 加载成功', url) const Valine = window.Valine // eslint-disable-next-line no-unused-vars const valine = new Valine({ @@ -21,7 +21,6 @@ const ValineComponent = ({ path }) => { serverURLs: BLOG.COMMENT_VALINE_SERVER_URLS, visitor: true }) - console.log('初始化valine成功') } catch (error) { console.error('twikoo 加载失败', error) } diff --git a/lib/algolia.js b/lib/algolia.js index b7439438..d2d9cb37 100644 --- a/lib/algolia.js +++ b/lib/algolia.js @@ -17,26 +17,88 @@ const generateAlgoliaSearch = async({ allPages, force = false }) => { /** * 上传数据 + * 根据上次修改文章日期和上次更新索引数据判断是否需要更新algolia索引 */ -const uploadDataToAlgolia = (post) => { +const uploadDataToAlgolia = async(post) => { // Connect and authenticate with your Algolia app const client = algoliasearch(BLOG.ALGOLIA_APP_ID, BLOG.ALGOLIA_ADMIN_APP_KEY) // Create a new index and add a record const index = client.initIndex(BLOG.ALGOLIA_INDEX) - const record = { - objectID: post.id, - title: post.title, - category: post.category, - tags: post.tags, - pageCover: post.pageCover, - slug: post.slug, - summary: post.summary, - content: getPageContentText(post, post.blockMap) + + if (!post) { + return } - index.saveObject(record).wait().then(r => { - console.log('Algolia索引', r, record) - }) + + // 检查是否有索引 + let existed + let needUpdateIndex = false + try { + existed = await index.getObject(post.id) + } catch (error) { + // 通常是不存在索引 + } + + if (!existed || !existed?.lastEditedDate || !existed?.lastIndexDate) { + needUpdateIndex = true + } else { + const lastEditedDate = new Date(existed.lastEditedDate) + const lastIndexDate = new Date(existed.lastIndexDate) + if (lastEditedDate.getTime() > lastIndexDate.getTime()) { + needUpdateIndex = true + } + } + + // 如果需要更新搜索 + if (needUpdateIndex) { + const record = { + objectID: post.id, + title: post.title, + category: post.category, + tags: post.tags, + pageCover: post.pageCover, + slug: post.slug, + summary: post.summary, + lastEditedDate: post.lastEditedDate, // 更新文章时间 + lastIndexDate: new Date(), // 更新索引时间 + content: truncate(getPageContentText(post, post.blockMap), 9000) // 索引9000个字节,因为api限制总请求内容上限1万个字节 + } + // console.log('更新Algolia索引', record) + index.saveObject(record).wait().then(r => { + console.log('Algolia索引更新', r) + }).catch(err => { + console.log('Algolia异常', err) + }) + } +} + +/** + * 限制内容字节数 + * @param {*} str + * @param {*} maxBytes + * @returns + */ +function truncate(str, maxBytes) { + let count = 0 + let result = '' + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i) + if (code <= 0x7f) { + count += 1 + } else if (code <= 0x7ff) { + count += 2 + } else if (code <= 0xffff) { + count += 3 + } else { + count += 4 + } + if (count <= maxBytes) { + result += str[i] + } else { + break + } + } + return result } export { uploadDataToAlgolia, generateAlgoliaSearch } diff --git a/lib/global.js b/lib/global.js index 9cafd304..04d0176e 100644 --- a/lib/global.js +++ b/lib/global.js @@ -14,7 +14,8 @@ const GlobalContext = createContext() * @returns {JSX.Element} * @constructor */ -export function GlobalContextProvider({ children }) { +export function GlobalContextProvider(props) { + const { children, siteInfo, categoryOptions, tagOptions } = props const router = useRouter() const [lang, updateLang] = useState(BLOG.LANG) // 默认语言 const [locale, updateLocale] = useState(generateLocaleDict(BLOG.LANG)) // 默认语言 @@ -24,7 +25,7 @@ export function GlobalContextProvider({ children }) { useEffect(() => { initLocale(lang, locale, updateLang, updateLocale) - initDarkMode(isDarkMode, updateDarkMode) + initDarkMode(updateDarkMode) initTheme() }, []) @@ -75,7 +76,10 @@ export function GlobalContextProvider({ children }) { updateDarkMode, theme, setTheme, - switchTheme + switchTheme, + siteInfo, + categoryOptions, + tagOptions }}> {children} diff --git a/lib/notion/getAllCategories.js b/lib/notion/getAllCategories.js index 7cfd4588..97c8ab3a 100644 --- a/lib/notion/getAllCategories.js +++ b/lib/notion/getAllCategories.js @@ -14,7 +14,7 @@ import { isIterable } from '../utils' * @returns {Promise<{}|*[]>} */ export function getAllCategories({ allPages, categoryOptions, sliceCount = 0 }) { - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published') if (!allPosts || !categoryOptions) { return [] } diff --git a/lib/notion/getAllPageIds.js b/lib/notion/getAllPageIds.js index e8f6dd8f..4bb12fe0 100644 --- a/lib/notion/getAllPageIds.js +++ b/lib/notion/getAllPageIds.js @@ -4,7 +4,15 @@ export default function getAllPageIds (collectionQuery, collectionId, collection return [] } let pageIds = [] - if (collectionQuery && Object.values(collectionQuery).length > 0) { + // 优先按照第一个视图排序 + if (viewIds && viewIds.length > 0) { + const ids = collectionView[viewIds[0]].value.page_sort + // console.log('PageIds: 从viewId获取', viewIds) + for (const id of ids) { + pageIds.push(id) + } + // 否则按照数据库原始排序 + } else if (collectionQuery && Object.values(collectionQuery).length > 0) { const pageSet = new Set() Object.values(collectionQuery[collectionId]).forEach(view => { view?.blockIds?.forEach(id => pageSet.add(id)) // group视图 @@ -12,12 +20,6 @@ export default function getAllPageIds (collectionQuery, collectionId, collection }) pageIds = [...pageSet] // console.log('PageIds: 从collectionQuery获取', collectionQuery, pageIds.length) - } else if (viewIds && viewIds.length > 0) { - const ids = collectionView[viewIds[0]].value.page_sort - // console.log('PageIds: 从viewId获取', viewIds) - for (const id of ids) { - pageIds.push(id) - } } return pageIds } diff --git a/lib/notion/getAllTags.js b/lib/notion/getAllTags.js index 968b8ca3..dfc92ac7 100644 --- a/lib/notion/getAllTags.js +++ b/lib/notion/getAllTags.js @@ -8,7 +8,7 @@ import { isIterable } from '../utils' * @returns {Promise<{}|*[]>} */ export function getAllTags({ allPages, sliceCount = 0, tagOptions }) { - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published') if (!allPosts || !tagOptions) { return [] diff --git a/lib/notion/getNotion.js b/lib/notion/getNotion.js index f73100c2..3da84ee8 100644 --- a/lib/notion/getNotion.js +++ b/lib/notion/getNotion.js @@ -25,7 +25,7 @@ export async function getNotion(pageId) { title: postInfo?.properties?.title?.[0], status: 'Published', createdTime: formatDate(new Date(postInfo.created_time).toString(), BLOG.LANG), - lastEditedTime: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG), + lastEditedDay: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG), fullWidth: false, page_cover: getPageCover(postInfo), date: { start_date: formatDate(new Date(postInfo?.last_edited_time).toString(), BLOG.LANG) }, diff --git a/lib/notion/getNotionData.js b/lib/notion/getNotionData.js index 393e3c63..20f47be3 100644 --- a/lib/notion/getNotionData.js +++ b/lib/notion/getNotionData.js @@ -25,7 +25,8 @@ export async function getGlobalData({ from }) { // 从notion获取 - const db = deepClone(await getNotionPageData({ pageId, from })) + const data = await getNotionPageData({ pageId, from }) + const db = deepClone(data) // 不返回的敏感数据 delete db.block delete db.schema @@ -48,8 +49,8 @@ function getLatestPosts({ allPages, from, latestPostCount }) { const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published') const latestPosts = Object.create(allPosts).sort((a, b) => { - const dateA = new Date(a?.lastEditedTime || a?.publishDate) - const dateB = new Date(b?.lastEditedTime || b?.publishDate) + const dateA = new Date(a?.lastEditedDay || a?.publishDate) + const dateB = new Date(b?.lastEditedDay || b?.publishDate) return dateB - dateA }) return latestPosts.slice(0, latestPostCount) @@ -173,31 +174,17 @@ function getSiteInfo({ collection, block }) { } /** - * 获取导航pages - * 转为gitbook这类文档主题设计,精减的标题和内容 + * 获取导航用的精减文章列表 + * gitbook主题用到,只保留文章的标题分类标签分类信息,精减掉摘要密码日期等数据 * 导航页面的条件,必须是Posts * @param {*} param0 */ export function getNavPages({ allPages }) { - const allNavPages = allPages.filter(post => { + const allNavPages = allPages?.filter(post => { return post && post?.slug && (!post?.slug?.startsWith('http')) && post?.type === 'Post' && post?.status === 'Published' }) - const result = allNavPages.map(item => ({ id: item.id, title: item.title || '', category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug })) - const groupedArray = result.reduce((groups, item) => { - const categoryName = item?.category ? item?.category : '' // 将category转换为字符串 - const lastGroup = groups[groups.length - 1] // 获取最后一个分组 - - if (!lastGroup || lastGroup?.category !== categoryName) { // 如果当前元素的category与上一个元素不同,则创建新分组 - groups.push({ category: categoryName, items: [] }) - } - - groups[groups.length - 1].items.push(item) // 将元素加入对应的分组 - - return groups - }, []) - - return groupedArray + return allNavPages.map(item => ({ id: item.id, title: item.title || '', pageCoverThumbnail: item.pageCoverThumbnail || '', category: item.category || null, tags: item.tags || null, summary: item.summary || null, slug: item.slug, lastEditedDate: item.lastEditedDate })) } /** @@ -217,7 +204,7 @@ const EmptyData = (pageId) => { const empty = { notice: null, siteInfo: getSiteInfo({}), - allPages: [{ id: 1, title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`, summary: '访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next', status: 'Published', type: 'Post', slug: '13a171332816461db29d50e9f575b00d', date: { start_date: '2023-04-24', lastEditedTime: '2023-04-24', tagItems: [] } }], + allPages: [{ id: 1, title: `无法获取Notion数据,请检查Notion_ID: \n 当前 ${pageId}`, summary: '访问文档获取帮助→ https://tangly1024.com/article/vercel-deploy-notion-next', status: 'Published', type: 'Post', slug: '13a171332816461db29d50e9f575b00d', date: { start_date: '2023-04-24', lastEditedDay: '2023-04-24', tagItems: [] } }], allNavPages: [], collection: [], collectionQuery: {}, diff --git a/lib/notion/getPageProperties.js b/lib/notion/getPageProperties.js index dea5c2b1..b9bdbe4d 100644 --- a/lib/notion/getPageProperties.js +++ b/lib/notion/getPageProperties.js @@ -78,8 +78,9 @@ export default async function getPageProperties(id, block, schema, authToken, ta mapProperties(properties) properties.publishDate = new Date(properties?.date?.start_date || value.created_time).getTime() - properties.publishTime = formatDate(properties.publishDate, BLOG.LANG) - properties.lastEditedTime = formatDate(new Date(value?.last_edited_time), BLOG.LANG) + properties.publishDay = formatDate(properties.publishDate, BLOG.LANG) + properties.lastEditedDate = new Date(value?.last_edited_time) + properties.lastEditedDay = formatDate(new Date(value?.last_edited_time), BLOG.LANG) properties.fullWidth = value.format?.page_full_width ?? false properties.pageIcon = mapImgUrl(block[id].value?.format?.page_icon, block[id].value) ?? '' properties.pageCover = mapImgUrl(block[id].value?.format?.page_cover, block[id].value) ?? '' @@ -141,14 +142,14 @@ function generateCustomizeUrl(postProperties) { let fullPrefix = '' const allSlugPatterns = BLOG.POST_URL_PREFIX.split('/') allSlugPatterns.forEach((pattern, idx) => { - if (pattern === '%year%' && postProperties?.publishTime) { - const formatPostCreatedDate = new Date(postProperties?.publishTime) + if (pattern === '%year%' && postProperties?.publishDay) { + const formatPostCreatedDate = new Date(postProperties?.publishDay) fullPrefix += formatPostCreatedDate.getUTCFullYear() - } else if (pattern === '%month%' && postProperties?.publishTime) { - const formatPostCreatedDate = new Date(postProperties?.publishTime) + } else if (pattern === '%month%' && postProperties?.publishDay) { + const formatPostCreatedDate = new Date(postProperties?.publishDay) fullPrefix += String(formatPostCreatedDate.getUTCMonth() + 1).padStart(2, 0) - } else if (pattern === '%day%' && postProperties?.publishTime) { - const formatPostCreatedDate = new Date(postProperties?.publishTime) + } else if (pattern === '%day%' && postProperties?.publishDay) { + const formatPostCreatedDate = new Date(postProperties?.publishDay) fullPrefix += String(formatPostCreatedDate.getUTCDate()).padStart(2, 0) } else if (pattern === '%slug%') { fullPrefix += (postProperties.slug ?? postProperties.id) diff --git a/lib/rss.js b/lib/rss.js index d40b940b..fff81f7c 100644 --- a/lib/rss.js +++ b/lib/rss.js @@ -46,7 +46,7 @@ export async function generateRss(posts) { link: `${BLOG.LINK}/${post.slug}`, description: post.summary, content: await createFeedContent(post), - date: new Date(post?.publishTime) + date: new Date(post?.publishDay) }) } diff --git a/lib/sitemap.xml.js b/lib/sitemap.xml.js index 6c8bd040..c520c0ff 100644 --- a/lib/sitemap.xml.js +++ b/lib/sitemap.xml.js @@ -26,7 +26,7 @@ export async function generateSitemapXml({ allPages }) { const slugWithoutLeadingSlash = post?.slug?.startsWith('/') ? post?.slug?.slice(1) : post.slug urls.push({ loc: `${BLOG.LINK}/${slugWithoutLeadingSlash}`, - lastmod: new Date(post?.publishTime).toISOString().split('T')[0], + lastmod: new Date(post?.publishDay).toISOString().split('T')[0], changefreq: 'daily' }) }) diff --git a/lib/utils.js b/lib/utils.js index d5ba09b5..e13b192f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -117,7 +117,13 @@ export function deepClone(obj) { if (obj && typeof obj === 'object') { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { - newObj[key] = (obj && typeof obj[key] === 'object') ? deepClone(obj[key]) : obj[key] + if (obj[key] instanceof Date) { // 判断属性值是否为 Date 对象 + newObj[key] = new Date(obj[key].getTime()).toISOString() // 直接拷贝引用 + } else if (obj[key] && typeof obj[key] === 'object') { + newObj[key] = deepClone(obj[key]) + } else { + newObj[key] = obj[key] + } } } } diff --git a/package.json b/package.json index b9f1bb5c..c1baace5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notion-next", - "version": "4.0.6", + "version": "4.0.8", "homepage": "https://github.com/tangly1024/NotionNext.git", "license": "MIT", "repository": { diff --git a/pages/[prefix]/[slug].js b/pages/[prefix]/[slug].js index cf249228..2beae09c 100644 --- a/pages/[prefix]/[slug].js +++ b/pages/[prefix]/[slug].js @@ -4,6 +4,7 @@ import { getGlobalData } from '@/lib/notion/getNotionData' import { idToUuid } from 'notion-utils' import { getNotion } from '@/lib/notion/getNotion' import Slug, { getRecommendPost } from '.' +import { uploadDataToAlgolia } from '@/lib/algolia' /** * 根据notion的slug访问页面 @@ -25,7 +26,7 @@ export async function getStaticPaths() { const from = 'slug-paths' const { allPages } = await getGlobalData({ from }) return { - paths: allPages?.filter(row => row.slug.indexOf('/') > 0).map(row => ({ params: { prefix: row.slug.split('/')[0], slug: row.slug.split('/')[1] } })), + paths: allPages?.filter(row => row.slug.indexOf('/') > 0 && row.type.indexOf('Menu') < 0).map(row => ({ params: { prefix: row.slug.split('/')[0], slug: row.slug.split('/')[1] } })), fallback: true } } @@ -63,9 +64,13 @@ export async function getStaticProps({ params: { prefix, slug } }) { if (!props?.posts?.blockMap) { props.post.blockMap = await getPostBlocks(props.post.id, from) } + // 生成全文索引 && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) + if (BLOG.ALGOLIA_APP_ID) { + uploadDataToAlgolia(props?.post) + } // 推荐关联文章处理 - const allPosts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const allPosts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published') if (allPosts && allPosts.length > 0) { const index = allPosts.indexOf(props.post) props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0] diff --git a/pages/[prefix]/index.js b/pages/[prefix]/index.js index 8c10b063..5f6ac5c8 100644 --- a/pages/[prefix]/index.js +++ b/pages/[prefix]/index.js @@ -90,7 +90,7 @@ export async function getStaticPaths() { const from = 'slug-paths' const { allPages } = await getGlobalData({ from }) return { - paths: allPages?.filter(row => row.slug.indexOf('/') < 0).map(row => ({ params: { prefix: row.slug } })), + paths: allPages?.filter(row => row.slug.indexOf('/') < 0 && row.type.indexOf('Menu') < 0).map(row => ({ params: { prefix: row.slug } })), fallback: true } } @@ -129,12 +129,13 @@ export async function getStaticProps({ params: { prefix } }) { props.post.blockMap = await getPostBlocks(props.post.id, from) } - if (BLOG.ALGOLIA_APP_ID && BLOG.ALGOLIA_APP_KEY) { + // 生成全文索引 && process.env.npm_lifecycle_event === 'build' && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA) + if (BLOG.ALGOLIA_APP_ID) { uploadDataToAlgolia(props?.post) } // 推荐关联文章处理 - const allPosts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const allPosts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published') if (allPosts && allPosts.length > 0) { const index = allPosts.indexOf(props.post) props.prev = allPosts.slice(index - 1, index)[0] ?? allPosts.slice(-1)[0] diff --git a/pages/_app.js b/pages/_app.js index a6a11399..ed0c8f76 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -17,7 +17,6 @@ import dynamic from 'next/dynamic' // 自定义样式css和js引入 import ExternalScript from '@/components/ExternalScript' - // 各种扩展插件 动画等 const ExternalPlugins = dynamic(() => import('@/components/ExternalPlugins')) @@ -27,7 +26,7 @@ const MyApp = ({ Component, pageProps }) => { }, []) return ( - + diff --git a/pages/archive/index.js b/pages/archive/index.js index 917fee76..1ef1f216 100644 --- a/pages/archive/index.js +++ b/pages/archive/index.js @@ -44,7 +44,7 @@ const ArchiveIndex = props => { export async function getStaticProps() { const props = await getGlobalData({ from: 'archive-index' }) // 处理分页 - props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') + props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published') delete props.allPages const postsSortByDate = Object.create(props.posts) diff --git a/pages/category/[category]/index.js b/pages/category/[category]/index.js index 55300733..03cd4fbd 100644 --- a/pages/category/[category]/index.js +++ b/pages/category/[category]/index.js @@ -37,7 +37,7 @@ export async function getStaticProps({ params: { category } }) { let props = await getGlobalData({ from }) // 过滤状态 - props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') + props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published') // 处理过滤 props.posts = props.posts.filter(post => post && post.category && post.category.includes(category)) // 处理文章页数 diff --git a/pages/category/[category]/page/[page].js b/pages/category/[category]/page/[page].js index 2f860c25..9174e117 100644 --- a/pages/category/[category]/page/[page].js +++ b/pages/category/[category]/page/[page].js @@ -37,7 +37,7 @@ export async function getStaticProps({ params: { category, page } }) { let props = await getGlobalData({ from }) // 过滤状态类型 - props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category)) + props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category)) // 处理文章页数 props.postCount = props.posts.length // 处理分页 @@ -61,7 +61,7 @@ export async function getStaticPaths() { categoryOptions?.forEach(category => { // 过滤状态类型 - const categoryPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category.name)) + const categoryPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post.category && post.category.includes(category.name)) // 处理文章页数 const postCount = categoryPosts.length const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) diff --git a/pages/index.js b/pages/index.js index 404fab19..328ece1e 100644 --- a/pages/index.js +++ b/pages/index.js @@ -3,17 +3,14 @@ import { getPostBlocks } from '@/lib/notion' import { getGlobalData } from '@/lib/notion/getNotionData' import { generateRss } from '@/lib/rss' import { generateRobotsTxt } from '@/lib/robots.txt' - import { useRouter } from 'next/router' import { getLayoutByTheme } from '@/themes/theme' -import { generateAlgoliaSearch } from '@/lib/algolia' /** * 首页布局 * @param {*} props * @returns */ - const Index = props => { // 根据页面路径加载不同Layout文件 const Layout = getLayoutByTheme(useRouter()) @@ -29,7 +26,7 @@ export async function getStaticProps() { const props = await getGlobalData({ from }) const { siteInfo } = props - props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published') + props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published') const meta = { title: `${siteInfo?.title} | ${siteInfo?.description}`, @@ -64,9 +61,6 @@ export async function getStaticProps() { } // 生成全文索引 - 仅在 yarn build 时执行 && process.env.npm_lifecycle_event === 'build' - if (BLOG.ALGOLIA_APP_ID && JSON.parse(BLOG.ALGOLIA_RECREATE_DATA)) { - generateAlgoliaSearch({ allPages: props.allPages }) - } delete props.allPages diff --git a/pages/page/[page].js b/pages/page/[page].js index 74b7f25b..02b847a8 100644 --- a/pages/page/[page].js +++ b/pages/page/[page].js @@ -45,7 +45,7 @@ export async function getStaticProps({ params: { page } }) { const from = `page-${page}` const props = await getGlobalData({ from }) const { allPages } = props - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published') // 处理分页 props.posts = allPosts.slice(BLOG.POSTS_PER_PAGE * (page - 1), BLOG.POSTS_PER_PAGE * page) props.page = page diff --git a/pages/search/[keyword]/index.js b/pages/search/[keyword]/index.js index 93fa6826..042bbf85 100644 --- a/pages/search/[keyword]/index.js +++ b/pages/search/[keyword]/index.js @@ -36,7 +36,7 @@ export async function getStaticProps({ params: { keyword } }) { pageType: ['Post'] }) const { allPages } = props - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published') props.posts = await filterByMemCache(allPosts, keyword) props.postCount = props.posts.length // 处理分页 diff --git a/pages/search/[keyword]/page/[page].js b/pages/search/[keyword]/page/[page].js index 11a682f7..20240f19 100644 --- a/pages/search/[keyword]/page/[page].js +++ b/pages/search/[keyword]/page/[page].js @@ -36,7 +36,7 @@ export async function getStaticProps({ params: { keyword, page } }) { pageType: ['Post'] }) const { allPages } = props - const allPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + const allPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published') props.posts = await filterByMemCache(allPosts, keyword) props.postCount = props.posts.length // 处理分页 diff --git a/pages/search/index.js b/pages/search/index.js index ba09db8e..22061cbd 100644 --- a/pages/search/index.js +++ b/pages/search/index.js @@ -55,7 +55,7 @@ export async function getStaticProps() { pageType: ['Post'] }) const { allPages } = props - props.posts = allPages.filter(page => page.type === 'Post' && page.status === 'Published') + props.posts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published') return { props, revalidate: parseInt(BLOG.NEXT_REVALIDATE_SECOND) diff --git a/pages/sitemap.xml.js b/pages/sitemap.xml.js index 58011385..58c9a846 100644 --- a/pages/sitemap.xml.js +++ b/pages/sitemap.xml.js @@ -42,7 +42,7 @@ export const getServerSideProps = async (ctx) => { const slugWithoutLeadingSlash = post?.slug.startsWith('/') ? post?.slug?.slice(1) : post.slug return { loc: `${BLOG.LINK}/${slugWithoutLeadingSlash}`, - lastmod: new Date(post?.publishTime).toISOString().split('T')[0], + lastmod: new Date(post?.publishDay).toISOString().split('T')[0], changefreq: 'daily', priority: '0.7' } diff --git a/pages/tag/[tag]/index.js b/pages/tag/[tag]/index.js index db2aaed8..bd9f7529 100644 --- a/pages/tag/[tag]/index.js +++ b/pages/tag/[tag]/index.js @@ -33,7 +33,7 @@ export async function getStaticProps({ params: { tag } }) { const props = await getGlobalData({ from }) // 过滤状态 - props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag)) + props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag)) // 处理文章页数 props.postCount = props.posts.length diff --git a/pages/tag/[tag]/page/[page].js b/pages/tag/[tag]/page/[page].js index bf383870..2eaef518 100644 --- a/pages/tag/[tag]/page/[page].js +++ b/pages/tag/[tag]/page/[page].js @@ -27,7 +27,7 @@ export async function getStaticProps({ params: { tag, page } }) { const from = 'tag-page-props' const props = await getGlobalData({ from }) // 过滤状态、标签 - props.posts = props.allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag)) + props.posts = props.allPages?.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag)) // 处理文章数 props.postCount = props.posts.length // 处理分页 @@ -48,7 +48,7 @@ export async function getStaticPaths() { const paths = [] tagOptions?.forEach(tag => { // 过滤状态类型 - const tagPosts = allPages.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag.name)) + const tagPosts = allPages?.filter(page => page.type === 'Post' && page.status === 'Published').filter(post => post && post?.tags && post?.tags.includes(tag.name)) // 处理文章页数 const postCount = tagPosts.length const totalPages = Math.ceil(postCount / BLOG.POSTS_PER_PAGE) diff --git a/themes/example/components/ArticleInfo.js b/themes/example/components/ArticleInfo.js index cd29e978..825fc774 100644 --- a/themes/example/components/ArticleInfo.js +++ b/themes/example/components/ArticleInfo.js @@ -29,12 +29,12 @@ export const ArticleInfo = (props) => { passHref className="pl-1 mr-2 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 border-b dark:border-gray-500 border-dashed"> - {post?.publishTime} + {post?.publishDay} | - {locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedTime} + {locale.COMMON.LAST_EDITED_TIME}: {post?.lastEditedDay} | diff --git a/themes/example/components/BlogListGroupByDate.js b/themes/example/components/BlogListGroupByDate.js index 7759eaca..ac5f9cbd 100644 --- a/themes/example/components/BlogListGroupByDate.js +++ b/themes/example/components/BlogListGroupByDate.js @@ -19,9 +19,9 @@ export default function BlogListGroupByDate({ archiveTitle, archivePosts }) { key={post.id} className="border-l-2 p-1 text-xs md:text-base items-center hover:scale-x-105 hover:border-gray-500 dark:hover:border-gray-300 dark:border-gray-400 transform duration-500" > -
+
- {post?.publishTime} + {post?.publishDay} {' '}   diff --git a/themes/example/index.js b/themes/example/index.js index d14bc570..0772f7fd 100644 --- a/themes/example/index.js +++ b/themes/example/index.js @@ -2,7 +2,6 @@ import BLOG from '@/blog.config' import CONFIG from './config' -import CommonHead from '@/components/CommonHead' import { useEffect } from 'react' import { Header } from './components/Header' import { Nav } from './components/Nav' @@ -27,6 +26,7 @@ import TagItem from './components/TagItem' import { useRouter } from 'next/router' import { Transition } from '@headlessui/react' import { Style } from './style' +import CommonHead from '@/components/CommonHead' /** * 基础布局框架 @@ -36,7 +36,7 @@ import { Style } from './style' * @constructor */ const LayoutBase = props => { - const { children, meta, slotTop } = props + const { children, slotTop, meta } = props const { onLoading } = useGlobal() // 增加一个状态以触发 Transition 组件的动画 @@ -49,8 +49,10 @@ const LayoutBase = props => { return (
- {/* 网页SEO信息 */} - + + {/* SEO信息 */} + +