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) => {
联系
- 如果你希望删除评论或询问隐私相关问题,可以发送邮件至 bishsh2006@gmail.com。
+ 如果你希望删除评论或询问隐私相关问题,可以发送邮件至 {siteConfig.author.email}。
diff --git a/src/pages/rss.xml.js b/src/pages/rss.xml.js
index 178fa2a..fc9664c 100644
--- a/src/pages/rss.xml.js
+++ b/src/pages/rss.xml.js
@@ -1,12 +1,13 @@
import rss from '@astrojs/rss';
import { getAllPosts } from '@/lib/posts';
+import { siteConfig } from '../../site.config.mjs';
export async function GET(context) {
const posts = await getAllPosts();
return rss({
- title: "Bi's Blog",
- description: 'Personal blog by biss.',
+ title: siteConfig.site.name,
+ description: siteConfig.site.description,
site: context.site,
items: posts.map((post) => ({
title: post.title,
diff --git a/src/pages/shuoshuo.astro b/src/pages/shuoshuo.astro
index 04bf33b..2c0015e 100644
--- a/src/pages/shuoshuo.astro
+++ b/src/pages/shuoshuo.astro
@@ -2,9 +2,9 @@
import BaseLayout from '@/layouts/BaseLayout.astro';
import PageHeaderCard from '@/components/PageHeaderCard.astro';
import TwikooComments from '@/components/TwikooComments.astro';
+import { siteConfig } from '../../site.config.mjs';
const title = '说说';
-const twikooEnvId = 'https://comment.biss.click';
---
@@ -24,7 +24,7 @@ const twikooEnvId = 'https://comment.biss.click';
-
+