Compare commits

..
341 Commits
228 changed files with 25394 additions and 61423 deletions
+53
View File
@@ -0,0 +1,53 @@
name: 自动部署
on:
push:
branches:
- master
release:
types:
- published
workflow_dispatch:
env:
TZ: Asia/Shanghai
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检出分支
uses: actions/checkout@v4
with:
ref: master
- name: 缓存项目 npm 包
id: cache-node-modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-nodeModules-${{ hashFiles('package-lock.json') }}-${{ hashFiles('package.json') }}
restore-keys: |
${{ runner.os }}-nodeModules-
- name: 安装 Node
uses: actions/setup-node@v4
with:
node-version: "22.x"
- name: 安装依赖
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: |
npm install
- name: 同步 Astro 类型
run: |
npm run clean
- name: 生成静态文件
run: |
npm run build
- name: 部署到 page 分支
run: |
cd ./dist
git init
git config user.name "${{ gitea.actor }}"
git config user.email "${{ gitea.actor }}@noreply.gitea.io"
git add .
git commit -m "${{ gitea.event.head_commit.message }} -- [$(date +"%Z %Y-%m-%d %A %H:%M:%S")]"
git push --force --quiet "https://${{ gitea.actor }}:${{ secrets.DEPLOY_TOKEN }}@git.biss.click/biss/blog.git" master:page
- name: 触发服务器部署
run: |
curl -k -X POST "https://panel.biss.click/hook?access_key=1XJG8IvYTSZVvD5dpm86GYIpQxgxBcucULnX1MFskZSKayXU"
+12
View File
@@ -0,0 +1,12 @@
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
.astro/
dist/
.deploy*/
_multiconfig.yml
.hintrc
genkey.js
push2typesense.js
File diff suppressed because one or more lines are too long
-384
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
[{"title":"网站迁移日志","href":"/posts/8f3b9d21/"},{"title":"用 Umami 分享链接给网站信息卡提供统计数据","href":"/posts/4c91a8f2/"},{"title":"把普通的一天过成自己的节奏","href":"/posts/efe1e805/"},{"title":"GPT Image2 初试","href":"/posts/3f8de22/"},{"title":"电子价签玩法","href":"/posts/8712baaa/"},{"title":"OpenGL- Cohen-Sutherland 编码裁剪算法","href":"/posts/9b2ef00a/"},{"title":"OpenGL-直线的扫描转换","href":"/posts/7207243b/"},{"title":"OpenGL-基础程序","href":"/posts/437a5198/"},{"title":"配置OpenGL环境","href":"/posts/c4477b0c/"},{"title":"opencv应用-算术运算","href":"/posts/b559997d/"},{"title":"opencv应用-基础操作","href":"/posts/e8f95ead/"},{"title":"配置opencv","href":"/posts/6e3332b/"},{"title":"自建bitwarden服务","href":"/posts/7baa41fc/"},{"title":"自建renovate-bot","href":"/posts/56f57c0b/"},{"title":"新年快乐!","href":"/posts/5785bd01/"},{"title":"添加网站左上角菜单","href":"/posts/7e921903/"},{"title":"将博客仓库转移到gitea","href":"/posts/d2c8521/"},{"title":"安装gitea","href":"/posts/34725d47/"},{"title":"添加typesense搜索","href":"/posts/f287c563/"},{"title":"自定义字体","href":"/posts/b5601a7e/"},{"title":"在侧边栏添加日历和倒计时","href":"/posts/5ed2f1e6/"},{"title":"和朋友们的故事","href":"/posts/a31d95d9/"},{"title":"Adguard和Openclash共存","href":"/posts/66e66374/"},{"title":"Adguard规则分享","href":"/posts/69b16001/"},{"title":"在Openwrt上安装AdguardHome","href":"/posts/b57500e9/"},{"title":"在Openwrt中安装OpenClash","href":"/posts/2b2fb1a7/"},{"title":"Cudy TR3000刷入Openwrt系统","href":"/posts/8802e397/"},{"title":"搭建Owncloud并集成Onlyoffice","href":"/posts/59c8572a/"},{"title":"为博客添加CMS系统","href":"/posts/3a26a97a/"},{"title":"为1Panel添加自己想要的应用","href":"/posts/bb18d851/"},{"title":"使用GitHub推送Hexo到服务器","href":"/posts/ce1ec3fe/"},{"title":"C# 基本语法","href":"/posts/e9a8e898/"},{"title":"C# 入门","href":"/posts/bc56f789/"},{"title":"自定义页脚(新)","href":"/posts/c6143ad3/"},{"title":"自定义分类条","href":"/posts/33249733/"},{"title":"自定义导航栏","href":"/posts/3e61a389/"},{"title":"利用 SiteMap 随机访问站内页面","href":"/posts/b5866b9e/"},{"title":"博客添加AI总结","href":"/posts/41b9aff7/"},{"title":"利用插件自定义页脚菜单","href":"/posts/a6ab0925/"},{"title":"英语文章分享-去奋力生活吧,就好像死亡近在咫尺","href":"/posts/f0a1a1f4/"},{"title":"自定义右键菜单","href":"/posts/8bdb35fb/"},{"title":"在侧边栏中添加欢迎信息","href":"/posts/da17d00/"},{"title":"配置说说页面","href":"/posts/ad244066/"},{"title":"部署历程","href":"/posts/6e5f5039/"}]
-380
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+27
View File
@@ -0,0 +1,27 @@
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
import { unified } from '@astrojs/markdown-remark';
import rehypeMathjax from 'rehype-mathjax';
import remarkMath from 'remark-math';
import remarkButterflyTags from './src/lib/remarkButterflyTags.mjs';
import { siteConfig } from './site.config.mjs';
export default defineConfig({
site: siteConfig.site.url,
output: 'static',
integrations: [sitemap()],
markdown: {
processor: unified({
remarkPlugins: [remarkMath, remarkButterflyTags],
rehypePlugins: [rehypeMathjax],
}),
shikiConfig: {
theme: 'github-dark',
wrap: true,
langAlias: {
BASH: 'bash',
YAML: 'yaml',
},
},
},
});
-1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-384
View File
File diff suppressed because one or more lines are too long
-383
View File
File diff suppressed because one or more lines are too long
-501
View File
File diff suppressed because one or more lines are too long
-626
View File
File diff suppressed because one or more lines are too long
-378
View File
File diff suppressed because one or more lines are too long
+5577
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
{
"name": "Bi's Blog",
"version": "3.1.1",
"private": true,
"scripts": {
"prebuild": "node scripts/generate-abbrlinks.mjs",
"build": "astro build",
"clean": "astro sync",
"dev": "astro dev",
"preview": "astro preview",
"server": "astro dev"
},
"dependencies": {
"@astrojs/rss": "^4.0.18",
"@astrojs/sitemap": "^3.7.3",
"astro": "^6.4.8",
"rehype-mathjax": "^7.1.0",
"remark-math": "^6.0.0",
"typesense": "^3.0.6"
}
}
-626
View File
File diff suppressed because one or more lines are too long
-626
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-382
View File
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 457 KiB

After

Width:  |  Height:  |  Size: 457 KiB

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 269 KiB

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File
View File
View File
+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"timezone": "Asia/Shanghai",
"enabledManagers": [
"npm"
],
"ignorePaths": [
"themes/**",
".gitea/**"
],
"rangeStrategy": "bump",
"prHourlyLimit": 0,
"prConcurrentLimit": 0,
"dependencyDashboard": false
}
-1
View File
File diff suppressed because one or more lines are too long
+126
View File
@@ -0,0 +1,126 @@
import { readdir, readFile, writeFile } from 'node:fs/promises';
import { basename, extname, join, relative } from 'node:path';
const POSTS_DIR = join(process.cwd(), 'src', 'content', 'posts');
const CRC32_TABLE = new Uint32Array(256);
for (let i = 0; i < 256; i += 1) {
let value = i;
for (let bit = 0; bit < 8; bit += 1) {
value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
}
CRC32_TABLE[i] = value >>> 0;
}
function crc32(input) {
const bytes = new TextEncoder().encode(input);
let crc = 0xffffffff;
for (const byte of bytes) {
crc = CRC32_TABLE[(crc ^ byte) & 0xff] ^ (crc >>> 8);
}
return ((crc ^ 0xffffffff) >>> 0).toString(16);
}
async function listMarkdownFiles(dir) {
const entries = await readdir(dir, { withFileTypes: true });
const files = await Promise.all(
entries.map(async (entry) => {
const path = join(dir, entry.name);
if (entry.isDirectory()) return listMarkdownFiles(path);
if (entry.isFile() && extname(entry.name).toLowerCase() === '.md') return [path];
return [];
}),
);
return files.flat();
}
function getFrontmatterBounds(content) {
if (!content.startsWith('---')) return null;
const newline = content.includes('\r\n') ? '\r\n' : '\n';
const lines = content.split(newline);
const closeIndex = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
if (closeIndex === -1) return null;
return { closeIndex, lines, newline };
}
function getScalar(frontmatterLines, key) {
const pattern = new RegExp(`^${key}\\s*:\\s*(.*)$`);
const match = frontmatterLines.map((line) => line.match(pattern)).find(Boolean);
if (!match) return '';
return match[1]
.trim()
.replace(/^['"]|['"]$/g, '')
.trim();
}
function makeSeed(file, frontmatterLines) {
const title = getScalar(frontmatterLines, 'title');
if (title) return title;
const filename = getScalar(frontmatterLines, 'filename');
if (filename) return filename;
return basename(file, '.md');
}
function makeUniqueAbbrlink(seed, usedAbbrlinks) {
let attempt = 0;
let abbrlink = crc32(seed);
while (usedAbbrlinks.has(abbrlink)) {
attempt += 1;
abbrlink = crc32(`${seed}:${attempt}`);
}
usedAbbrlinks.add(abbrlink);
return abbrlink;
}
async function main() {
const files = await listMarkdownFiles(POSTS_DIR);
const usedAbbrlinks = new Set();
const pending = [];
for (const file of files) {
const content = await readFile(file, 'utf8');
const bounds = getFrontmatterBounds(content);
if (!bounds) continue;
const frontmatterLines = bounds.lines.slice(1, bounds.closeIndex);
const existing = getScalar(frontmatterLines, 'abbrlink');
if (existing) {
usedAbbrlinks.add(existing);
continue;
}
pending.push({ file, content, bounds, frontmatterLines });
}
for (const item of pending) {
const seed = makeSeed(item.file, item.frontmatterLines);
const abbrlink = makeUniqueAbbrlink(seed, usedAbbrlinks);
const insertAt = item.bounds.closeIndex;
item.bounds.lines.splice(insertAt, 0, `abbrlink: ${abbrlink}`);
await writeFile(item.file, item.bounds.lines.join(item.bounds.newline), 'utf8');
console.log(`Added abbrlink ${abbrlink} to ${relative(process.cwd(), item.file)}`);
}
if (pending.length === 0) {
console.log('All posts already have abbrlink.');
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
-424
View File
File diff suppressed because one or more lines are too long
+142
View File
@@ -0,0 +1,142 @@
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: 'Fastly', href: 'https://fastly.com/' },
{ 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,
},
series: {
labels: {
webcustom: '博客定制手记',
opencv: 'OpenCV 学习笔记',
OpenGL: 'OpenGL 学习笔记',
},
},
comments: {
envId: 'https://comment.biss.click',
region: '',
script: 'https://cdn.jsdelivr.net/npm/twikoo@1.7.7/dist/twikoo.min.js',
},
analytics: {
umami: {
script: 'https://umami.biss.click/script.js',
websiteId: '3d97b310-b241-4fc6-bc9c-68e317fc7d42',
statsEndpoint: 'https://blogumami.biss.click',
},
},
externalLinks: {
waitSeconds: 10,
whitelist: [
'blog.biss.click',
'biss.click',
'github.com',
'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,
analytics: siteConfig.analytics,
talks: siteConfig.talks,
search: siteConfig.search,
externalLinks: siteConfig.externalLinks,
};
export default siteConfig;
-1
View File
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><sitemap><loc>https://blog.biss.click/sitemap-0.xml</loc></sitemap></sitemapindex>
+79
View File
@@ -0,0 +1,79 @@
---
import type { BlogPost } from '@/lib/posts';
interface Props {
posts: BlogPost[];
}
type ArchiveMonth = {
key: string;
label: string;
posts: BlogPost[];
};
type ArchiveYear = {
year: number;
count: number;
months: ArchiveMonth[];
};
const { posts } = Astro.props;
const monthFormatter = new Intl.DateTimeFormat('zh-CN', { month: 'long' });
const dayFormatter = new Intl.DateTimeFormat('zh-CN', { day: '2-digit' });
const archiveYears = posts.reduce<ArchiveYear[]>((years, post) => {
const year = post.date.getFullYear();
const monthKey = `${year}-${String(post.date.getMonth() + 1).padStart(2, '0')}`;
let yearGroup = years.find((item) => item.year === year);
if (!yearGroup) {
yearGroup = { year, count: 0, months: [] };
years.push(yearGroup);
}
let monthGroup = yearGroup.months.find((item) => item.key === monthKey);
if (!monthGroup) {
monthGroup = {
key: monthKey,
label: monthFormatter.format(post.date),
posts: [],
};
yearGroup.months.push(monthGroup);
}
yearGroup.count += 1;
monthGroup.posts.push(post);
return years;
}, []);
---
<div class="archive-timeline">
{archiveYears.map((yearGroup) => (
<section class="archive-year" aria-labelledby={`archive-year-${yearGroup.year}`}>
<div class="archive-year-marker" aria-hidden="true"></div>
<header class="archive-year-head">
<h2 id={`archive-year-${yearGroup.year}`}>{yearGroup.year}</h2>
<span>{yearGroup.count} 篇</span>
</header>
<div class="archive-months">
{yearGroup.months.map((monthGroup) => (
<section class="archive-month" aria-label={`${yearGroup.year} ${monthGroup.label}`}>
<div class="archive-month-label">
<span>{monthGroup.label}</span>
<small>{monthGroup.posts.length} 篇</small>
</div>
<div class="archive-list">
{monthGroup.posts.map((post) => (
<a href={post.href}>
<time datetime={post.date.toISOString()}>{dayFormatter.format(post.date)}</time>
<span>{post.title}</span>
</a>
))}
</div>
</section>
))}
</div>
</section>
))}
</div>

Some files were not shown because too many files have changed in this diff Show More