switch search function to typesense search

This commit is contained in:
2026-02-07 20:13:03 +08:00
parent cf197c75ab
commit f2c0635ee8
9 changed files with 695 additions and 630 deletions

View File

@@ -1,7 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 20

View File

@@ -1,70 +0,0 @@
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@v5
with:
ref: master
token: ${{ secrets.GITHUB_TOKEN }}
- name: 缓存项目 npm 包
id: cache-node-modules
uses: actions/cache@v4
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@v5
with:
node-version: "22.x"
- name: 安装 Hexo
run: |
npm install hexo-cli --global
- name: 安装依赖
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: |
npm install
- name: 清理文件树
run: |
npm run clean
- name: 生成静态文件并压缩
run: |
npm run build
- name: 部署
run: |
cd ./public
git init
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
git add .
git commit -m "${{ github.event.head_commit.message }}··[$(date +"%Z %Y-%m-%d %A %H:%M:%S")]"
git push --force --quiet "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git" master:page
- name: Deploy to Server
run: |
curl -k -X POST "https://45.145.229.95:40606/hook?access_key=1XJG8IvYTSZVvD5dpm86GYIpQxgxBcucULnX1MFskZSKayXU"

View File

@@ -589,10 +589,11 @@ math:
# --------------------------------------
search:
# Choose: algolia_search / local_search / docsearch
# leave it empty if you don't need search
use: local_search
placeholder:
path: search.json
field: posts
content: true
# Algolia Search
algolia_search:
@@ -1162,6 +1163,7 @@ css_prefix: true
inject:
head:
# - <link rel="stylesheet" href="/xxx.css">
- <link rel="stylesheet" href="https://cdn.jsdmirror.com/npm/instantsearch.css/themes/reset-min.css">
- <link rel="stylesheet" href="https://cdn.jsdmirror.com/gh/bishshi/welcomemessage/welcome.css">
- <link rel="stylesheet" href="https://cdn.jsdmirror.com/gh/bishshi/sidecalendar/calendar.css">
- <link rel="stylesheet" href="/css/style.css">
@@ -1185,6 +1187,9 @@ inject:
- <script src="https://unpkg.com/chinese-lunar@0.1.4/lib/chinese-lunar.js"></script>
- <script src="/js/random.js"></script>
- <script src="/js/shuoshuoshouye.js"></script>
- <script src="https://cdn.jsdmirror.com/npm/instantsearch.js@4.56.0"></script>
- <script src="https://cdn.jsdmirror.com/npm/typesense-instantsearch-adapter@2.7.0/dist/typesense-instantsearch-adapter.min.js"></script>
# CDN Settings
# Don't modify the following settings unless you know how they work

27
genkey.js Normal file
View File

@@ -0,0 +1,27 @@
const http = require('https');
const data = JSON.stringify({
"description": "Public search only key",
"actions": ["documents:search"],
"collections": ["blogs"]
});
const options = {
hostname: 'typesense.biss.click', // 不要带 https://
port: 443,
path: '/keys',
method: 'POST',
headers: {
'X-TYPESENSE-API-KEY': 'tDPfzkghH3Zy55DHyWOnYkQiijOqN8bx',
'Content-Type': 'application/json',
'Content-Length': data.length
}
};
const req = http.request(options, res => {
res.on('data', d => { process.stdout.write(d); });
});
req.on('error', error => { console.error(error); });
req.write(data);
req.end();

77
package-lock.json generated
View File

@@ -8,7 +8,6 @@
"name": "hexo-site",
"version": "0.0.0",
"dependencies": {
"@sveltia/cms": "^0.128.1",
"axios": "^1.12.2",
"cheerio": "^1.1.2",
"hexo": "^7.3.0",
@@ -20,6 +19,7 @@
"hexo-generator-archive": "^2.0.0",
"hexo-generator-category": "^2.0.0",
"hexo-generator-index": "^4.0.0",
"hexo-generator-search": "^2.4.3",
"hexo-generator-searchdb": "^1.5.0",
"hexo-generator-sitemap": "^3.0.1",
"hexo-generator-tag": "^2.0.0",
@@ -36,7 +36,9 @@
"moment": "^2.30.1",
"node-fetch": "^3.3.2",
"p-limit": "^7.2.0",
"vite-plugin-require-transform": "^1.0.21"
"typesense": "^3.0.1",
"vite-plugin-require-transform": "^1.0.21",
"xml2js": "^0.6.2"
}
},
"node_modules/@adobe/css-tools": {
@@ -352,12 +354,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@sveltia/cms": {
"version": "0.128.6",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/@sveltia/cms/-/cms-0.128.6.tgz",
"integrity": "sha512-y5+tM0hYeFZoYVr8Hu/QEnBofjHJaSROA78NMxR6Ju64ITA1CPvBdEczZHHgc0fZuDmoq+2YaUZoEtEYSXqTpA==",
"license": "MIT"
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -2090,6 +2086,19 @@
"node": ">=18"
}
},
"node_modules/hexo-generator-search": {
"version": "2.4.3",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/hexo-generator-search/-/hexo-generator-search-2.4.3.tgz",
"integrity": "sha512-Z5hfZq2g3np/Tgdp2q9HobfIvU6Pdz89tnTurc1IIq/vW0MHgDynk0Aiv6kvMtKWthnZ5l0iEMT3YLN35NdYwQ==",
"license": "MIT",
"dependencies": {
"nunjucks": "^3.0.1",
"utils-merge": "^1.0.0"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/hexo-generator-searchdb": {
"version": "1.5.0",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/hexo-generator-searchdb/-/hexo-generator-searchdb-1.5.0.tgz",
@@ -2903,6 +2912,19 @@
"promise": "^7.0.1"
}
},
"node_modules/loglevel": {
"version": "1.9.2",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/loglevel/-/loglevel-1.9.2.tgz",
"integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/loglevel"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -4126,6 +4148,23 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/typesense": {
"version": "3.0.1",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/typesense/-/typesense-3.0.1.tgz",
"integrity": "sha512-aRzuDQlwR7s2sWw+JiR3CufrMWpzH5UAJ4XlybYczD02QPy5jCsEQiueqUu0Wiai4zW/RGYRruF3XrdEXPgPJA==",
"license": "Apache-2.0",
"dependencies": {
"axios": "^1.8.4",
"loglevel": "^1.8.1",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@babel/runtime": "^7.23.2"
}
},
"node_modules/undici": {
"version": "7.21.0",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/undici/-/undici-7.21.0.tgz",
@@ -4345,6 +4384,28 @@
"node": ">=18"
}
},
"node_modules/xml2js": {
"version": "0.6.2",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/xml2js/-/xml2js-0.6.2.tgz",
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
"license": "MIT",
"dependencies": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/xmlbuilder": {
"version": "11.0.1",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"license": "MIT",
"engines": {
"node": ">=4.0"
}
},
"node_modules/xmlchars": {
"version": "2.2.0",
"resolved": "https://mirrors.huaweicloud.com/repository/npm/xmlchars/-/xmlchars-2.2.0.tgz",

View File

@@ -12,7 +12,6 @@
"version": "7.3.0"
},
"dependencies": {
"@sveltia/cms": "^0.128.1",
"axios": "^1.12.2",
"cheerio": "^1.1.2",
"hexo": "^7.3.0",
@@ -24,6 +23,7 @@
"hexo-generator-archive": "^2.0.0",
"hexo-generator-category": "^2.0.0",
"hexo-generator-index": "^4.0.0",
"hexo-generator-search": "^2.4.3",
"hexo-generator-searchdb": "^1.5.0",
"hexo-generator-sitemap": "^3.0.1",
"hexo-generator-tag": "^2.0.0",
@@ -40,6 +40,8 @@
"moment": "^2.30.1",
"node-fetch": "^3.3.2",
"p-limit": "^7.2.0",
"vite-plugin-require-transform": "^1.0.21"
"typesense": "^3.0.1",
"vite-plugin-require-transform": "^1.0.21",
"xml2js": "^0.6.2"
}
}

69
push2typesense.js Normal file
View File

@@ -0,0 +1,69 @@
const Typesense = require('typesense');
const fs = require('fs');
const xml2js = require('xml2js');
// --- 配置区域 ---
const CONFIG = {
apiKey: 'tDPfzkghH3Zy55DHyWOnYkQiijOqN8bx', // 必须是 Admin Key
host: 'typesense.biss.click',
port: 443,
protocol: 'https',
collectionName: 'blogs'
};
const client = new Typesense.Client({
'nodes': [{ 'host': CONFIG.host, 'port': CONFIG.port, 'protocol': CONFIG.protocol }],
'apiKey': CONFIG.apiKey,
'connectionTimeoutSeconds': 5
});
async function sync() {
try {
// 1. 读取并解析 XML
const xml = fs.readFileSync('./public/search.xml', 'utf8');
const parser = new xml2js.Parser({ explicitArray: false });
const result = await parser.parseStringPromise(xml);
// 提取文章列表 (处理单篇文章和多篇文章的情况)
let entries = result.search.entry;
if (!Array.isArray(entries)) entries = [entries];
// 格式化数据以适配 Typesense
const documents = entries.map(post => ({
title: post.title,
url: post.url,
content: post.content,
categories: post.categories ? (Array.isArray(post.categories.category) ? post.categories.category : [post.categories.category]) : [],
tags: post.tags ? (Array.isArray(post.tags.tag) ? post.tags.tag : [post.tags.tag]) : [],
}));
// 2. 检查或创建 Collection (Schema)
try {
await client.collections(CONFIG.collectionName).retrieve();
} catch (err) {
const schema = {
name: CONFIG.collectionName,
fields: [
// 关键点:指定 locale 为 'zh' 开启中文分词
{ name: 'title', type: 'string', locale: 'zh' },
{ name: 'content', type: 'string', locale: 'zh' },
{ name: 'url', type: 'string' },
{ name: 'categories', type: 'string[]', facet: true },
{ name: 'tags', type: 'string[]', facet: true }
]
};
await client.collections().create(schema);
console.log('Collection created with Chinese support!');
}
// 3. 导入数据 (使用 upsert 模式:存在则更新,不存在则创建)
console.log(`Syncing ${documents.length} posts to Typesense...`);
await client.collections(CONFIG.collectionName).documents().import(documents, { action: 'upsert' });
console.log('Sync complete!');
} catch (error) {
console.error('Sync failed:', error);
}
}
sync();

View File

@@ -21,12 +21,12 @@ nav#nav
if theme.menu
!= partial('includes/header/menu_item', {}, {cache: true})
#nav-right
if theme.search.use
if theme.search.use || true
#random-post-button
a.site-page.social-icon#random-post-link(href='javascript:randomPost();')
a.site-page.social-icon#random-post-link(href='javascript:void(0);' onclick='randomPost()')
i.fas.fa-solid.fa-shuffle
#search-button
a.site-page.social-icon.search
a.site-page.social-icon.search-typesense-trigger
i.fas.fa-search.fa-fw
#toggle-menu
span.site-page

File diff suppressed because it is too large Load Diff