From ed2ef30d220d38a524d790a021302651729edf8e Mon Sep 17 00:00:00 2001 From: biss Date: Thu, 18 Jun 2026 21:59:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astro.config.mjs | 3 +- public/js/search/typesense-search.js | 39 +++----- public/js/shuoshuo.js | 14 +-- public/js/shuoshuoshouye.js | 24 +++-- site.config.mjs | 129 +++++++++++++++++++++++++++ src/components/ArticleSidebar.astro | 7 +- src/components/HomeSidebar.astro | 17 ++-- src/components/PageHeaderCard.astro | 4 +- src/components/TwikooComments.astro | 5 +- src/env.d.ts | 7 ++ src/layouts/BaseLayout.astro | 82 ++++++++--------- src/lib/external-link-whitelist.ts | 13 +-- src/lib/pagination.ts | 4 +- src/pages/about.astro | 7 +- src/pages/cc.astro | 3 +- src/pages/cookie.astro | 5 +- src/pages/index.astro | 3 +- src/pages/link.astro | 20 ++--- src/pages/page/[page].astro | 3 +- src/pages/posts/[slug].astro | 4 +- src/pages/privacy.astro | 5 +- src/pages/rss.xml.js | 5 +- src/pages/shuoshuo.astro | 4 +- 23 files changed, 270 insertions(+), 137 deletions(-) create mode 100644 site.config.mjs diff --git a/astro.config.mjs b/astro.config.mjs index 972300e..ecb4d5a 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -4,9 +4,10 @@ import { unified } from '@astrojs/markdown-remark'; import rehypeMathjax from 'rehype-mathjax'; import remarkMath from 'remark-math'; import remarkHexoLinkCards from './src/lib/remarkHexoLinkCards.mjs'; +import { siteConfig } from './site.config.mjs'; export default defineConfig({ - site: 'https://blog.biss.click', + site: siteConfig.site.url, output: 'static', integrations: [sitemap()], markdown: { diff --git a/public/js/search/typesense-search.js b/public/js/search/typesense-search.js index 58c896c..896d4ca 100644 --- a/public/js/search/typesense-search.js +++ b/public/js/search/typesense-search.js @@ -4,28 +4,11 @@ // ============================================================================ // 配置区域 - 请根据实际情况修改 // ============================================================================ - const CONFIG = { - apiKey: "2uxQzk7eD2GBCljdSmIiuU3ause7UK9n", // ⚠️ 建议使用 Search-Only API Key - server: { - host: "typesense.biss.click", - port: "443", - protocol: "https" - }, - indexName: "blogs", - searchParams: { - query_by: "title,content", - highlight_full_fields: "title,content", - per_page: 8, - num_typos: 1, - typo_tokens_threshold: 1, - prefix: true - }, - ui: { - maxRetries: 10, - retryDelay: 100, - animationDuration: 300 - } - }; + const CONFIG = window.SITE_CONFIG?.search; + if (!CONFIG) { + console.warn('Typesense 搜索配置未加载'); + return; + } // ============================================================================ // 状态管理 @@ -34,7 +17,7 @@ let isInitialized = false; let isSearchOpen = false; let initRetryCount = 0; - const MAX_INIT_RETRIES = 30; // 最多重试30次 (3秒) + const MAX_INIT_RETRIES = CONFIG.ui.maxInitRetries || 30; // 最多重试30次 (3秒) // ============================================================================ // 错误提示函数 @@ -326,7 +309,7 @@ setTimeout(function() { mask.style.display = 'none'; document.body.style.overflow = ''; - }, CONFIG.ui.animationDuration); + }, CONFIG.ui.animationDuration || 300); } function focusSearchInput(retryCount) { @@ -335,10 +318,10 @@ if (input) { input.focus(); input.select(); - } else if (retryCount < CONFIG.ui.maxRetries) { + } else if (retryCount < (CONFIG.ui.maxRetries || 10)) { setTimeout(function() { focusSearchInput(retryCount + 1); - }, CONFIG.ui.retryDelay); + }, CONFIG.ui.retryDelay || 100); } } @@ -443,7 +426,7 @@ searchInstance.addWidgets([ instantsearch.widgets.searchBox({ container: '#searchbox', - placeholder: '输入关键词寻找故事...', + placeholder: CONFIG.ui.placeholder || '输入关键词寻找故事...', autofocus: true, showReset: true, showSubmit: false, @@ -542,4 +525,4 @@ getInstance: function() { return searchInstance; } }; -})(); \ No newline at end of file +})(); diff --git a/public/js/shuoshuo.js b/public/js/shuoshuo.js index 86b761d..252d7b4 100644 --- a/public/js/shuoshuo.js +++ b/public/js/shuoshuo.js @@ -1,6 +1,7 @@ function renderTalks() { const talkContainer = document.querySelector('#talk'); if (!talkContainer) return; + const config = window.SITE_CONFIG?.talks || {}; talkContainer.innerHTML = ''; const generateIconSVG = () => { return ``; @@ -76,10 +77,11 @@ function renderTalks() { }; const fetchAndRenderTalks = () => { - const url = 'https://mm.biss.click/api/echo/page'; - const cacheKey = 'talksCache'; - const cacheTimeKey = 'talksCacheTime'; - const cacheDuration = 30 * 60 * 1000; + const url = config.apiUrl; + if (!url) return; + const cacheKey = config.cacheKey || 'talksCache'; + const cacheTimeKey = config.cacheTimeKey || 'talksCacheTime'; + const cacheDuration = config.cacheDurationMs || 30 * 60 * 1000; const cachedData = localStorage.getItem(cacheKey); const cachedTime = localStorage.getItem(cacheTimeKey); const now = Date.now(); @@ -90,7 +92,7 @@ function renderTalks() { fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ page: 1, pageSize: 30 }) + body: JSON.stringify({ page: 1, pageSize: config.pageSize || 30 }) }) .then(res => res.json()) .then(data => { @@ -263,7 +265,7 @@ function renderTalks() { return { content, user: item.username || '匿名', - avatar: 'https://free.picui.cn/free/2025/08/10/689845496a283.png', + avatar: config.fallbackAvatar || window.SITE_CONFIG?.author?.avatar || '', date, location: '', tags: Array.isArray(item.tags) && item.tags.length ? item.tags.map(t => t.name) : ['无标签'], diff --git a/public/js/shuoshuoshouye.js b/public/js/shuoshuoshouye.js index f5a2594..282d976 100644 --- a/public/js/shuoshuoshouye.js +++ b/public/js/shuoshuoshouye.js @@ -11,6 +11,10 @@ function indexTalk() { } if (!document.getElementById('bber-talk')) return; + const config = window.SITE_CONFIG?.talks || {}; + const talksCacheKey = config.cacheKey || cacheKey; + const talksCacheTimeKey = config.cacheTimeKey || cacheTimeKey; + const talksCacheDuration = config.cacheDurationMs || cacheDuration; function toText(ls) { return ls.map(item => { @@ -57,29 +61,31 @@ function indexTalk() { }, 3000); } - const cachedData = localStorage.getItem(cacheKey); - const cachedTime = localStorage.getItem(cacheTimeKey); + const cachedData = localStorage.getItem(talksCacheKey); + const cachedTime = localStorage.getItem(talksCacheTimeKey); const currentTime = new Date().getTime(); // 判断缓存是否有效 - if (cachedData && cachedTime && (currentTime - cachedTime < cacheDuration)) { + if (cachedData && cachedTime && (currentTime - cachedTime < talksCacheDuration)) { const data = toText(JSON.parse(cachedData)); - talk(data.slice(0, 6)); // 使用缓存渲染数据 + talk(data.slice(0, config.homeLimit || 6)); // 使用缓存渲染数据 } else { - fetch('https://mm.biss.click/api/echo/page', { + if (!config.apiUrl) return; + + fetch(config.apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ page: 1, pageSize: 30 }) + body: JSON.stringify({ page: 1, pageSize: config.pageSize || 30 }) }) .then(res => res.json()) .then(data => { // 适配新版结构:code=1 且 data.items 存在 if (data.code === 1 && data.data && Array.isArray(data.data.items)) { - localStorage.setItem(cacheKey, JSON.stringify(data.data.items)); - localStorage.setItem(cacheTimeKey, currentTime.toString()); + localStorage.setItem(talksCacheKey, JSON.stringify(data.data.items)); + localStorage.setItem(talksCacheTimeKey, currentTime.toString()); const formattedData = toText(data.data.items); - talk(formattedData.slice(0, 6)); + talk(formattedData.slice(0, config.homeLimit || 6)); } else { console.warn('Unexpected API response format:', data); } diff --git a/site.config.mjs b/site.config.mjs new file mode 100644 index 0000000..beed379 --- /dev/null +++ b/site.config.mjs @@ -0,0 +1,129 @@ +export const siteConfig = { + site: { + url: 'https://blog.biss.click', + name: "Bi's Blog", + description: 'Personal blog by biss.', + language: 'zh-CN', + domain: 'blog.biss.click', + slogan: '好奇心转化为代码的空间。', + startTime: '2025-01-01T00:00:00+08:00', + copyrightStartYear: 2025, + license: '内容采用 CC BY-NC-SA 4.0 协议', + }, + author: { + name: 'biss', + email: 'bishsh2006@gmail.com', + github: 'https://github.com/bishshi', + avatar: 'https://free.picui.cn/free/2025/08/10/689845496a283.png', + }, + assets: { + icon: '/images/Bi.ico', + background: '/images/background.png', + }, + nav: [ + { label: '归档', href: '/archives/', icon: 'fa-solid fa-box-archive' }, + { label: '分类', href: '/categories/', icon: 'fa-solid fa-folder-open' }, + { label: '标签', href: '/tags/', icon: 'fa-solid fa-tags' }, + { label: '说说', href: '/shuoshuo/', icon: 'fa-regular fa-comments' }, + { label: '关于', href: '/about/', icon: 'fa-solid fa-heart' }, + { label: '友链', href: '/link/', icon: 'fa-solid fa-link' }, + ], + footer: { + contactLabel: '联系方式', + links: [ + { label: '发展历程', href: '/archives/' }, + { label: '隐私政策', href: '/privacy/' }, + ], + poweredBy: [ + { label: 'EdgeOne', href: 'https://edgeone.ai/' }, + { label: 'ASTRO', href: 'https://astro.build/' }, + ], + }, + social: [ + { label: '发送邮件', href: 'mailto:bishsh2006@gmail.com', icon: 'fa-solid fa-envelope' }, + { label: '订阅 RSS', href: '/rss.xml', icon: 'fa-solid fa-rss' }, + { label: 'GitHub', href: 'https://github.com/bishshi', icon: 'fa-brands fa-github', external: true }, + ], + pagination: { + postsPerPage: 16, + }, + comments: { + envId: 'https://comment.biss.click', + region: '', + script: 'https://cdn.jsdelivr.net/npm/twikoo@1.7.7/dist/twikoo.min.js', + }, + externalLinks: { + waitSeconds: 10, + whitelist: [ + 'blog.biss.click', + 'biss.click', + 'github.com', + 'astro.build', + 'edgeone.ai', + 'localhost', + '127.0.0.1', + ], + }, + talks: { + apiUrl: 'https://mm.biss.click/api/echo/page', + pageSize: 30, + homeLimit: 6, + cacheKey: 'talksCache', + cacheTimeKey: 'talksCacheTime', + cacheDurationMs: 30 * 60 * 1000, + fallbackAvatar: 'https://free.picui.cn/free/2025/08/10/689845496a283.png', + }, + search: { + apiKey: '2uxQzk7eD2GBCljdSmIiuU3ause7UK9n', + server: { + host: 'typesense.biss.click', + port: '443', + protocol: 'https', + }, + indexName: 'blogs', + searchParams: { + query_by: 'title,content', + highlight_full_fields: 'title,content', + per_page: 8, + num_typos: 1, + typo_tokens_threshold: 1, + prefix: true, + }, + ui: { + maxRetries: 10, + retryDelay: 100, + animationDuration: 300, + maxInitRetries: 30, + placeholder: '输入关键词寻找故事...', + }, + }, + cdn: { + instantsearchCss: 'https://cdn.jsdmirror.com/npm/instantsearch.css/themes/reset-min.css', + fancyboxCss: 'https://cdn.jsdmirror.com/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css', + fontawesomeCss: 'https://cdn.jsdmirror.com/npm/@fortawesome/fontawesome-free@6.7.2/css/all.min.css', + instantsearchJs: 'https://cdn.jsdmirror.com/npm/instantsearch.js@4.56.0', + typesenseAdapterJs: 'https://cdn.jsdmirror.com/npm/typesense-instantsearch-adapter@2.7.0/dist/typesense-instantsearch-adapter.min.js', + fancyboxJs: 'https://cdn.jsdmirror.com/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js', + jqueryJs: 'https://code.jquery.com/jquery-4.0.0.min.js', + welcomeMessageJs: 'https://cdn.jsdmirror.com/gh/bishshi/welcomemessage/txmap.js', + }, + friends: [ + { + name: 'GitHub', + url: 'https://github.com/bishshi', + avatar: '/images/Bi.ico', + description: '站长的 GitHub 主页', + }, + ], +}; + +export const publicSiteConfig = { + site: siteConfig.site, + author: siteConfig.author, + comments: siteConfig.comments, + talks: siteConfig.talks, + search: siteConfig.search, + externalLinks: siteConfig.externalLinks, +}; + +export default siteConfig; diff --git a/src/components/ArticleSidebar.astro b/src/components/ArticleSidebar.astro index 33ccf2b..7d651d0 100644 --- a/src/components/ArticleSidebar.astro +++ b/src/components/ArticleSidebar.astro @@ -1,5 +1,6 @@ --- import { getCategoryHref, type BlogPost } from '@/lib/posts'; +import { siteConfig } from '../../site.config.mjs'; type TocItem = { depth: number; @@ -19,7 +20,7 @@ interface Props { } const { post, toc, relatedPosts } = Astro.props; -const avatar = 'https://free.picui.cn/free/2025/08/10/689845496a283.png'; +const avatar = siteConfig.author.avatar; const topDepth = toc.length > 0 ? Math.min(...toc.map((item) => item.depth)) : 1; const tocSections = toc.reduce((sections, item) => { if (item.depth <= topDepth || sections.length === 0) { @@ -34,8 +35,8 @@ const tocSections = toc.reduce((sections, item) => {