├── .vscode ├── extensions.json └── launch.json ├── src ├── assets │ ├── wasm │ │ ├── geo │ │ │ └── geo_wasm_bg.wasm │ │ ├── search │ │ │ └── search_wasm_bg.wasm │ │ └── article-filter │ │ │ └── article_filter_bg.wasm │ └── article-index │ │ ├── article-indexer-cli │ │ └── article-indexer-cli.exe ├── content │ ├── linux │ │ ├── 一键卸载宝塔Linux面板及运行环境命令.md │ │ ├── Centos 7 查看,开放端口.md │ │ ├── 开启系统自带的TCP-BBR加速.md │ │ ├── Linux美化终端zsh+ohmyzsh.md │ │ ├── Ubuntu安装c语言编译器.md │ │ ├── 手动搭建LNMP.md │ │ ├── SSH使用小技巧.md │ │ ├── 使用LNMP搭建网站.md │ │ └── linux实现定时备份网站到网盘.md │ ├── windows │ │ ├── Windows11部分程序中文乱码解决方案[通用解决Windows10].md │ │ ├── 自动刷课.md │ │ ├── 计算机意外的重新启动或遇到错误.md │ │ ├── 解决Win11右键菜单问题.md │ │ ├── windows终端美化PowerShell+OhMyPosh.md │ │ ├── windows安装adb教程.md │ │ ├── Adobe_Photoshop (Beta)使用教程.md │ │ ├── Windows用批处理处理图标上的快捷方式和安全盾.md │ │ ├── Windows虚拟化和子系统安装指南.md │ │ ├── vscode配置.md │ │ └── 在VMware虚拟机上安装MacOS系统.md │ ├── docker │ │ ├── 制作数字人.md │ │ ├── Docker部署思源笔记.md │ │ ├── 密码管理器—Vaultwarden(bitwarden).md │ │ ├── 网盘直链程序—AList.md │ │ ├── Docker部署gitea.md │ │ └── Docker安装typecho.md │ ├── web │ │ ├── 加速与防护 │ │ │ ├── CDN配置.md │ │ │ ├── Cloudflare_自选IP.md │ │ │ ├── 充分利用不同cdn的优势.md │ │ │ ├── 宝塔面板下站点如何只限定CDN的IP节点回源请求.md │ │ │ ├── GitHub-Actions自动刷新多吉云_CDN缓存.md │ │ │ └── 记录一下服务器和网站的安全防护.mdx │ │ ├── web技术思路.md │ │ ├── 部署 │ │ │ ├── 搭建本地图床API可搭配Chevereto使用.md │ │ │ ├── 搭建Chevereto中文版.md │ │ │ ├── nginx配置.md │ │ │ ├── 服务器探针(ServerStatus探针)安装教程.md │ │ │ ├── 哪吒面板-服务器状态监控面板-Linux与Windows部署.md │ │ │ └── 将hugo部署在vercel.md │ │ ├── 前端与美化 │ │ │ ├── svg文字动画.css │ │ │ └── 用rust实现第一个wasm.md │ │ └── 各大搜索引擎站长平台地址汇总.md │ ├── 代理 │ │ ├── 3x-ui配置.md │ │ ├── 一键搭建Telegram的MTProto代理.md │ │ ├── X-UI面板快速搭建和配置.md │ │ ├── 安装谷歌三件套服务框架.md │ │ ├── v2board后端对接v2ray-warp使用Cloudflare-WARP解锁chatgpt-流媒体.md │ │ ├── v2board后端对接-套cloudfront解决被墙和提升速度.md │ │ ├── 搭建v2board前端.md │ │ ├── v2board后端对接-soga.md │ │ ├── centos7.x搭建Tor私人网桥.md │ │ └── v2board后端对接-XrayR-V2ray-ws-tls-cdn.md │ ├── android │ │ ├── Android安装linux.md │ │ ├── Android热点开机自启动和VPN热点.md │ │ ├── 一加6t刷3系统思路.md │ │ └── 安装谷歌三件套服务框架.md │ ├── 短视频 │ │ ├── 电脑直播.md │ │ ├── 视频制作.md │ │ └── 自动化混剪视频.md │ ├── other │ │ ├── markdown使用教程.md │ │ └── ai使用.md │ ├── 游行笔记 │ │ └── 第一次出国旅行-东南亚.md │ └── echoes博客使用说明.md ├── pages │ ├── books.astro │ ├── movies.astro │ ├── index.astro │ ├── projects.astro │ ├── filtered.astro │ ├── 404.astro │ ├── about.astro │ └── articles │ │ └── [...path].astro ├── plugins │ ├── rehype-tables.js │ ├── robots-integration.js │ └── build-article-index.js ├── consts.ts ├── components │ ├── Footer.astro │ ├── Countdown.tsx │ └── WereadBookList.tsx ├── styles │ ├── theme-toggle.css │ ├── articles-table.css │ ├── swup-transitions.css │ ├── articles-mermaid.css │ └── header.css └── content.config.ts ├── pnpm-workspace.yaml ├── README.md ├── vercel.json ├── wasm ├── utils-common │ ├── src │ │ ├── lib.rs │ │ ├── models.rs │ │ └── compression.rs │ └── Cargo.toml ├── readme.md ├── article-indexer │ └── Cargo.toml ├── geo │ └── Cargo.toml ├── search │ ├── Cargo.toml │ └── src │ │ └── models.rs ├── article-filter │ ├── Cargo.toml │ └── src │ │ ├── models.rs │ │ └── builder.rs └── Cargo.toml ├── tsconfig.json ├── .gitignore ├── public └── favicon.svg ├── create_post.sh ├── package.json └── astro.config.mjs /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/wasm/geo/geo_wasm_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsy2246/newechoes/HEAD/src/assets/wasm/geo/geo_wasm_bg.wasm -------------------------------------------------------------------------------- /src/assets/wasm/search/search_wasm_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsy2246/newechoes/HEAD/src/assets/wasm/search/search_wasm_bg.wasm -------------------------------------------------------------------------------- /src/assets/article-index/article-indexer-cli: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsy2246/newechoes/HEAD/src/assets/article-index/article-indexer-cli -------------------------------------------------------------------------------- /src/assets/article-index/article-indexer-cli.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsy2246/newechoes/HEAD/src/assets/article-index/article-indexer-cli.exe -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | ignoredBuiltDependencies: 2 | - '@tailwindcss/oxide' 3 | - esbuild 4 | - puppeteer 5 | - sharp 6 | - swup 7 | - vue-demi 8 | -------------------------------------------------------------------------------- /src/assets/wasm/article-filter/article_filter_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsy2246/newechoes/HEAD/src/assets/wasm/article-filter/article_filter_bg.wasm -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 个人静态博客系统 2 | 3 | **GitHub**:[lsy2246/newechoes](https://github.com/lsy2246/newechoes) 4 | 5 | **使用教程**:[点击查看](https://blog.lsy22.com/articles/echoes博客使用说明) 6 | 7 | **在线演示**:[blog.lsy22.com](https://blog.lsy22.com/) 8 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "buildCommand": "pnpm build", 4 | "installCommand": "pnpm install", 5 | "framework": "astro", 6 | "outputDirectory": "dist", 7 | "public": false, 8 | "github": { 9 | "silent": true 10 | } 11 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /wasm/utils-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod compression; 2 | pub mod models; 3 | 4 | // 重新导出常用模块和函数,方便直接使用 5 | pub use compression::{to_compressed, from_compressed, to_binary, from_binary, validate_compressed_data}; 6 | pub use models::{ArticleMetadata, Heading, IndexType, IndexMetadata}; -------------------------------------------------------------------------------- /src/content/linux/一键卸载宝塔Linux面板及运行环境命令.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "一键卸载宝塔Linux面板及运行环境命令" 3 | date: 2021-09-05T18:02:00+00:00 4 | tags: [] 5 | --- 6 | 7 | ## 命令 8 | 9 | ```bash 10 | wget http://download.bt.cn/install/bt-uninstall.sh 11 | ``` 12 | 13 | 执行脚本 14 | 15 | ```bash 16 | sh bt-uninstall.sh 17 | ``` 18 | -------------------------------------------------------------------------------- /src/content/windows/Windows11部分程序中文乱码解决方案[通用解决Windows10].md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Windows11 部分程序中文乱码解决方案[通用解决Windows10]" 3 | date: 2023-11-04T20:54:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 解决步骤 8 | 9 | 1. 打开控制面板 10 | 2. 找到时钟和区域 11 | 3. 选择区域 12 | 4. 打开管理 13 | 5. 选择非 Unicode 程序的语言 14 | 6. 更改系统区域设置为中国(需要提供管理员权限) 15 | -------------------------------------------------------------------------------- /src/content/windows/自动刷课.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "自动刷课" 3 | date: 2025-06-14T21:37:25+08:00 4 | tags: [] 5 | --- 6 | 7 | ### 自动化 8 | 9 | 任选一种方式实现自动化 10 | 11 | - [篡改猴](https://www.tampermonkey.net/index.php?locale=zh) 等脚本运行器 12 | - [selenuim](https://www.selenium.dev/) 等自动化工具 13 | 14 | ### 人脸 15 | 16 | 提前录制一段多个符合要求动作的视频 17 | 18 | [OBS](https://obsproject.com/) 19 | -------------------------------------------------------------------------------- /src/pages/books.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@/components/Layout.astro"; 3 | import WereadBookList from "@/components/WereadBookList"; 4 | --- 5 | 6 | 10 | 我读过的书 11 | 12 | 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "include": ["src/**/*"], 4 | "exclude": ["dist"], 5 | "compilerOptions": { 6 | "baseUrl": ".", 7 | "paths": { 8 | "@/*": ["./src/*"] 9 | }, 10 | "jsx": "react-jsx", 11 | "jsxImportSource": "react", 12 | "resolveJsonModule": true, 13 | "esModuleInterop": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /wasm/readme.md: -------------------------------------------------------------------------------- 1 | # 构建命令 2 | 3 | ## 构建wasm 4 | 5 | ```bash 6 | wasm-pack build --target web 7 | ``` 8 | 9 | ## 构建应用 10 | 11 | ### windows 12 | 13 | ```bash 14 | cargo build --release --target x86_64-pc-windows-msvc 15 | ``` 16 | 17 | > 如果在window上交叉编译先安装Linux工具链,cross,wsl,docker 18 | 19 | ### linux 20 | 21 | ```bash 22 | rustup target add x86_64-unknown-linux-musl 23 | ``` 24 | -------------------------------------------------------------------------------- /src/content/docker/制作数字人.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "制作数字人" 3 | date: 2025-07-09T23:43:48+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 图像 8 | 9 | Heygem:[https://github.com/duixcom/Duix.Heygem](https://github.com/duixcom/Duix.Heygem) 10 | 11 | ### 客户端 12 | 13 | 1. 通过 npm 构建 14 | 2. github 的发布 15 | 16 | ## 音频 17 | 18 | index-tts:[https://github.com/index-tts/index-tts](https://github.com/index-tts/index-tts) 19 | -------------------------------------------------------------------------------- /src/content/web/加速与防护/CDN配置.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "CDN配置" 3 | date: 2023-12-25T12:07:21+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 域名绑定 8 | 9 | 1. 绑定到需要加速的服务器(源站) 10 | 2. 配置到 cdn 平台(加速域名) 11 | 12 | ## 配置 13 | 14 | 以[多吉云](https://www.dogecloud.com/)为例(毕竟免费 20G,还能设置封顶策略) 15 | 16 | 进入域名管理添加域名 17 | 18 | - 加速域名:(加速域名) 19 | - 回源域名填写:(源站) 20 | - 回源 host 选择:与回源域名一致 21 | 22 | 改动回源 host 的目的是为了让 vercel 那边知道你需要回源到的域名。 23 | -------------------------------------------------------------------------------- /src/pages/movies.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@/components/Layout.astro"; 3 | import DoubanCollection from "@/components/DoubanCollection"; 4 | --- 5 | 6 | 10 | 我看过的电影 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /wasm/utils-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "utils-common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Common utilities for article processing WASM modules" 6 | 7 | [dependencies] 8 | serde = { workspace = true } 9 | chrono = { workspace = true } 10 | bincode = { workspace = true } 11 | flate2 = { workspace = true } 12 | # 这些依赖是压缩和序列化功能所必需的 13 | 14 | [lib] 15 | crate-type = ["rlib"] -------------------------------------------------------------------------------- /src/content/web/加速与防护/Cloudflare_自选IP.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Cloudflare_自选IP" 3 | date: 2024-06-18T16:16:35+08:00 4 | tags: ["cloudflare"] 5 | --- 6 | 7 | ## Workers 8 | 9 | 1. DNS 解析 10 | 11 | 1. 删除现有 Workers 解析(自定义域) 12 | 2. 将要用的域名解析到自选 IP 上,注意**不要开启**代理(小云朵) 13 | 14 | 2. 自定义路由 15 | 16 | 设置->触发器->路由 17 | 18 | 我要使用`proxy.example.com`作为 Workers 域名,且要访问全部内容 19 | 20 | ```text 21 | proxy.example.com/* 22 | ``` 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | 23 | # jetbrains setting folder 24 | .idea/ 25 | 26 | # wasm 27 | **/target/* 28 | **/pkg/* 29 | 30 | .vercel 31 | -------------------------------------------------------------------------------- /src/content/代理/3x-ui配置.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "3x-ui配置" 3 | date: 2025-04-19T10:48:48+08:00 4 | tags: [] 5 | --- 6 | 7 | 3x-ui:[https://github.com/MHSanaei/3x-ui](https://github.com/MHSanaei/3x-ui) 8 | 9 | 安全:`Reality` 10 | 11 | Dest (Target) 12 | 13 | ```txt 14 | apple.com:443 15 | ``` 16 | 17 | SNI 18 | 19 | ```txt 20 | apple.com 21 | ``` 22 | 23 | 公钥 24 | 25 | ```txt 26 | jCK3ORKMwzmJsig7gMWOTGlaF2wvuVAcEr7jbB1bnwU 27 | ``` 28 | 29 | 私钥 30 | 31 | ```txt 32 | oN60djdg_ZRUiChp0Rj-emZ3VhM_x8rL93H4rhdq1Wc 33 | ``` 34 | -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@/components/Layout.astro"; 3 | import { SITE_TITLE } from "@/consts"; 4 | --- 5 | 6 | 7 | 8 | 9 | 12 | {SITE_TITLE} 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/content/windows/计算机意外的重新启动或遇到错误.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "计算机意外的重新启动或遇到错误" 3 | date: 2021-11-26T21:26:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 问题描述 8 | 9 | 本人在安装 Windows 10 操作系统的过程中遭遇断电,导致安装中断。再次进行安装一直报上面的错误。 10 | 11 | 已尝试网上盛传的方法,使用 Shift+F10 打开命令行,进入`Windows\system32\oobe\`,打开`msoobe`,但这些尝试都没有反应。 12 | 13 | ## 解决方法 14 | 15 | 1. 按 Shift+F10 打开命令行窗口。 16 | 2. 输入`regedit`以打开注册表编辑器。 17 | 3. 在注册表编辑器中找到路径`HKEY_LOCAL_MACHINE/SYSTEM/SETUP/STATUS/ChildCompletion`。 18 | 4. 在`ChildCompletion`下找到名为`SETUP.EXE`的项,双击它。 19 | 5. 修改数值数据从 1 修改为 3,然后点击确定。 20 | 6. 关闭注册表编辑器。 21 | 7. 重新点击错误消息框的"确定"按钮。 22 | 8. 电脑将自动重启,重新解析安装包再次进入安装系统。 23 | -------------------------------------------------------------------------------- /src/content/windows/解决Win11右键菜单问题.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "解决 Win11 右键菜单问题" 3 | date: 2021-12-11T18:53:00Z 4 | tags: [] 5 | --- 6 | 7 | 首先,按下 Win+X 打开 Windows PowerShell(管理员)。 8 | 9 | 1. 切换回经典右键菜单: 10 | 11 | ```powershell 12 | reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve 13 | ``` 14 | 15 | 2. 恢复到新版右键菜单(不建议执行): 16 | 17 | ```powershell 18 | reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f 19 | ``` 20 | 21 | 然后,按下 Win+E 打开 Windows 资源管理器,接着按下 Ctrl+Shift+Esc 打开任务管理器,找到并重启 Windows 资源管理器。 22 | 23 | 现在,右键单击应该已经恢复正常了。 24 | -------------------------------------------------------------------------------- /src/pages/projects.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@/components/Layout.astro"; 3 | import GitProjectCollection from "@/components/GitProjectCollection"; 4 | import { GitPlatform } from "@/components/GitProjectCollection"; 5 | --- 6 | 7 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/plugins/rehype-tables.js: -------------------------------------------------------------------------------- 1 | import { visit } from 'unist-util-visit'; 2 | 3 | export function rehypeTables() { 4 | return (tree) => { 5 | visit(tree, 'element', (node, index, parent) => { 6 | if (node.tagName === 'table') { 7 | // 创建表格容器 8 | const tableContainer = { 9 | type: 'element', 10 | tagName: 'div', 11 | properties: { 12 | className: ['table-container'] 13 | }, 14 | children: [node] 15 | }; 16 | 17 | // 替换原始表格节点 18 | parent.children[index] = tableContainer; 19 | } 20 | }); 21 | }; 22 | } -------------------------------------------------------------------------------- /wasm/article-indexer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "article-indexer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Tool for building article indices" 6 | 7 | [[bin]] 8 | name = "article-indexer-cli" 9 | path = "src/main.rs" 10 | 11 | [dependencies] 12 | chrono = { workspace = true } 13 | clap = { workspace = true, features = ["suggestions", "color"] } 14 | walkdir = { workspace = true } 15 | html5ever = { workspace = true } 16 | markup5ever_rcdom = { workspace = true } 17 | search-wasm = { path = "../search" } 18 | utils-common = { path = "../utils-common" } 19 | article-filter = { path = "../article-filter" } 20 | -------------------------------------------------------------------------------- /src/content/android/Android安装linux.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Android 安装 Linux" 3 | date: 2023-12-11T20:53:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 一. 安装 Termux 8 | 9 | [官方版本](https://termux.dev/cn/) 10 | [ZeroTermux (魔改)](https://github.com/hanxinhao000/ZeroTermux) 11 | 12 | ## 二. 安装 Linux 13 | 14 | 1. 下载容器脚本并使用 15 | 16 | ```bash 17 | curl -LO https://gitee.com/mo2/linux/raw/2/2.awk 18 | awk -f 2.awk 19 | ``` 20 | 21 | 2. 安装容器 22 | - 选择 `1. proot 容器` 23 | - 选择 `1. arm64 发行版列表` 24 | - 选择需要的镜像 25 | - 选择需要的版本 26 | - 如果显示没有权限读写文件,给软件 root 权限,重新开始 27 | - 请问是否新建 sudo 用户: 否 28 | - 遇到选择默认回车 29 | - tmoe-Tools: 不需要图形化界面直接选 `0` 退出 30 | -------------------------------------------------------------------------------- /src/content/短视频/电脑直播.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "电脑直播" 3 | date: 2025-10-18T11:44:07+08:00 4 | tags: [] 5 | --- 6 | 7 | ### 摄像头 8 | 9 | #### 直播专用摄像头 10 | 11 | #### 电脑自带摄像头 12 | 13 | #### 手机当电脑摄像头 14 | 15 | - ivCam 16 | - 宜播 17 | - NDI 18 | - 苹果手机用 Blackmagic Camera 进行投屏 19 | - 直播伴侣连接手机摄像头 20 | 21 | ### 手写板 22 | 23 | - 苹果手机通过直播伴侣投屏 24 | - 利用远程控制软件远程控制手机,输出控制软件程序窗口 25 | - 实时传播给电脑的电子手写板 26 | - 实体手写笔,写了直接给用户看 27 | 28 | ### 直播软件 29 | 30 | - 各个平台对应的直播伴侣 31 | - obs 32 | 33 | - 抓取推流码,关闭平台的直播软件(不是关闭直播)开始直播 34 | - 注意设置软件配置 35 | 36 | ### 其他 37 | 38 | 推流抓取工具(目前只测试过直播伴侣):[https://pan.baidu.com/s/1YCt4FbVm9vNDGciXA-cjZA?pwd=uc8h](https://pan.baidu.com/s/1YCt4FbVm9vNDGciXA-cjZA?pwd=uc8h) 39 | -------------------------------------------------------------------------------- /wasm/geo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "geo-wasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["cdylib"] 8 | 9 | [features] 10 | default = [] 11 | wee_alloc = ["dep:wee_alloc"] 12 | 13 | 14 | [dependencies] 15 | wasm-bindgen = { workspace = true } 16 | serde = { workspace = true } 17 | serde_json = { workspace = true } 18 | geojson = { workspace = true } 19 | geo = { workspace = true } 20 | geo-types = { workspace = true } 21 | js-sys = { workspace = true } 22 | kdtree = { workspace = true } 23 | console_error_panic_hook = { workspace = true } 24 | serde-wasm-bindgen = { workspace = true } 25 | wee_alloc = { workspace = true, optional = true } 26 | -------------------------------------------------------------------------------- /src/content/android/Android热点开机自启动和VPN热点.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Android热点开机自启动和VPN热点" 3 | date: 2022-12-06T19:08:00Z 4 | tags: [] 5 | --- 6 | 7 | **需要 root 权限!密码都是** `lsy22` 8 | 9 | ## 一、热点开机启动 10 | 11 | [MacroDroid-Pro](https://lsy22.lanzouj.com/itgMH0hz5pra?password=lsy22) 12 | 13 | 1. 打开 MacroDroid-Pro,给予 root 权限 14 | 2. 点击下面的 **宏** 再点击 **加号** 15 | 3. 配置: 16 | - **输入宏名称**:随便输入一个名字 17 | - **触发器**:点击触发器右上角的加号——设备事件——设备启动 18 | - **动作**:点击动作右上角的加号——连接——热点开/关——给予修改系统的权限(返回会弹出一个不适合此设备,忽略,点击确定)——选择启动热点,点击确认 19 | 4. 点击右下角三条杠带一个加号,就可以实现热点开机启动了 20 | 21 | ## 二、VPN 热点 22 | 23 | [VPN 热点](https://lsy22.lanzouj.com/iS9hw0hz5rfa?password=lsy22) 24 | 25 | 1. 打开 _VPN 热点_ 给予 root 权限 26 | 2. 将 _WLAN 热点_ 打开,打开后会多一个 _wlan1_,将 _wlan1_ 打开就可以实现 VPN 热点了 27 | -------------------------------------------------------------------------------- /wasm/search/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "search-wasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "WebAssembly module for article search functionality" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = [] 12 | wee_alloc = ["dep:wee_alloc"] 13 | 14 | [dependencies] 15 | wasm-bindgen.workspace = true 16 | serde.workspace = true 17 | serde_json.workspace = true 18 | js-sys.workspace = true 19 | bincode.workspace = true 20 | flate2.workspace = true 21 | console_error_panic_hook.workspace = true 22 | web-sys = { workspace = true, features = ["console", "Window", "Performance"] } 23 | regex.workspace = true 24 | utils-common = { path = "../utils-common" } 25 | wee_alloc = { workspace = true, optional = true } 26 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /src/content/代理/一键搭建Telegram的MTProto代理.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "一键搭建Telegram的MTProto代理" 3 | date: 2021-08-09T00:07:00+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 1.同步时间 8 | 9 | CentOS 7 10 | 11 | yum install -y ntp 12 | systemctl enable ntpd 13 | ntpdate -q 0.rhel.pool.ntp.org 14 | systemctl restart ntpd 15 | 16 | Debian 9 / Ubuntu 16 17 | 18 | apt-get install -y ntp 19 | systemctl enable ntp 20 | systemctl restart ntp 21 | 22 | 或者(时间同步为上海) 23 | 24 | rm -rf /etc/localtime 25 | ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 26 | ntpdate time.nist.gov 27 | 28 | ## 2.一键安装 29 | 30 | mkdir /home/mtproxy && cd /home/mtproxy 31 | curl -s -o mtproxy.sh https://raw.githubusercontent.com/ellermister/mtproxy/master/mtproxy.sh && chmod +x mtproxy.sh && bash mtproxy.sh 32 | -------------------------------------------------------------------------------- /src/content/代理/X-UI面板快速搭建和配置.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "X-UI面板快速搭建和配置" 3 | date: 2023-07-11T00:17:00+08:00 4 | tags: ["x-ui"] 5 | --- 6 | 7 | ## 一、部署安装 8 | 9 | GitHub 项目地址:[https://github.com/FranzKafkaYu/x-ui](https://github.com/FranzKafkaYu/x-ui) 10 | 11 | 1. 复制粘贴以下代码,并运行: 12 | 13 | ```bash 14 | bash <(curl -Ls https://raw.githubusercontent.com/FranzKafkaYu/x-ui/master/install.sh) 15 | ``` 16 | 17 | 注意:在 IPv6 Only 的 VPS 中(例如:Euserv、Hax),请先安装 warp,否则无法访问 Github API 而报错。 18 | 19 | 2. 设置用户名密码、面板访问端口。 20 | 21 | 待出现 X-ui 的菜单时,就已经成功一半了! 22 | 23 | ## 二、配置 24 | 25 | 系统状态 - xray 状态 - 切换版本 - 切换为最新版本 26 | 27 | 配置节点: 28 | 29 | - 入站列表 - 添加入站 30 | - 协议:`vless` 31 | - 端口:`443` 32 | - reality:`开启` 33 | - 添加用户:+ 34 | - flow 选择 xtls-rprx-vision 35 | - 其他默认 36 | 37 | ## 三、使用 38 | 39 | 点击"操作"→"二维码",导出节点。 40 | -------------------------------------------------------------------------------- /wasm/article-filter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "article-filter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Filter for articles" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = [] 12 | wee_alloc = ["dep:wee_alloc"] 13 | 14 | [dependencies] 15 | wasm-bindgen = { workspace = true } 16 | js-sys = { workspace = true } 17 | console_error_panic_hook = { workspace = true } 18 | serde = { workspace = true } 19 | serde_json = { workspace = true } 20 | serde-wasm-bindgen = { workspace = true } 21 | chrono = { workspace = true } 22 | bincode = { workspace = true } 23 | flate2 = { workspace = true } 24 | once_cell = { workspace = true } 25 | web-sys = { version = "0.3", features = ["console"] } 26 | utils-common = { path = "../utils-common" } 27 | wee_alloc = { workspace = true, optional = true } -------------------------------------------------------------------------------- /src/content/代理/安装谷歌三件套服务框架.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "安装谷歌三件套服务框架" 3 | date: 2023-07-12T23:39:00+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 下载说明 8 | 9 | 对于所有三个组件,请注意以下通用事项: 10 | 11 | - 选择与您手机安卓版本相匹配的版本 12 | - 优先选择 noDPI 的版本 13 | - 关于 ARM 版本选择: 14 | - 近两年发布的手机一般使用 ARMv8 15 | - 老款手机可先查询自己的 ARM 版本 16 | - 也可直接下载 universal 版本(兼容 v8 和 v7) 17 | 18 | ## 下载地址 19 | 20 | ### 1. Google 服务框架 21 | 22 | [https://www.apkmirror.com/apk/google-inc/google-services-framework/](https://www.apkmirror.com/apk/google-inc/google-services-framework/) 23 | 24 | ### 2. Google Play Service 25 | 26 | [https://www.apkmirror.com/apk/google-inc/google-play-services/](https://www.apkmirror.com/apk/google-inc/google-play-services/) 27 | 28 | ### 3. Google Play Store 29 | 30 | [https://www.apkmirror.com/apk/google-inc/google-play-store/](https://www.apkmirror.com/apk/google-inc/google-play-store/) 31 | -------------------------------------------------------------------------------- /src/content/web/web技术思路.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: web技术思路 3 | date: 2025-01-15T00:34:11Z 4 | tags: [] 5 | --- 6 | 7 | ## 前端 8 | 9 | ### [pnpm](https://pnpm.io/zh/) 10 | 11 | - 安装速度快 12 | - 避免非法依赖 13 | 14 | ### [tailwind CSS](https://tailwindcss.com/) 15 | 16 | 快速构建 css 样式器,可响应式布局 17 | 18 | ### [swup](https://swup.js.org/) 19 | 20 | 视图切换 21 | 22 | ### [WebAssembly](https://developer.mozilla.org/zh-CN/docs/WebAssembly) 23 | 24 | 具体紧凑二进制格式可以在 web 接近原生性能运行 25 | 26 | ### 前端案例网站 27 | 28 | - [codepen](https://codepen.io/trending) 29 | 30 | ## 后端 31 | 32 | ### 安全 33 | 34 | 1. json web token:保证用户令牌不是篡改或伪造,注意是明文传输 35 | 2. hash 密码:避免数据库泄漏带来的隐私风险 36 | 3. cors:可以避免其他站点的非法请求,不过只适用用浏览器 37 | 4. 构建 sql 查询器中间件:使 sql 语句可以结构化,可以根据危险等级构建不同的查询等级,最大程度避免 xxs 38 | 39 | ### 接口 40 | 41 | 1. 适用 restful 接口具有很好的可读性 42 | 43 | ## 其他 44 | 45 | 1.设计尽量无状态,低耦合,使后期即使出现问题也是独立的状态 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/content/代理/v2board后端对接v2ray-warp使用Cloudflare-WARP解锁chatgpt-流媒体.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "v2board后端对接v2ray-warp使用Cloudflare-WARP解锁chatgpt-流媒体" 3 | date: 2023-05-13T22:48:00Z 4 | tags: ["v2board"] 5 | --- 6 | 7 | ## 一、部署安装 8 | 9 | GitHub 项目地址:[https://github.com/FranzKafkaYu/x-ui][1] 10 | 11 | 1. 复制粘贴以下代码,并运行: 12 | 13 | ```bash 14 | bash <(curl -Ls https://raw.githubusercontent.com/FranzKafkaYu/x-ui/master/install.sh) 15 | ``` 16 | 17 | 注意:在 IPv6 Only 的 VPS 中(例如:Euserv、Hax),请先安装 warp,否则无法访问 Github API 而报错。 18 | 19 | 2. 设置用户名密码、面板访问端口。 20 | 21 | 待出现 X-ui 的菜单时,就已经成功一半了! 22 | 23 | ## 二、配置 24 | 25 | 系统状态 - xray 状态 - 切换版本 - 切换为最新版本 26 | 27 | 配置节点: 28 | 29 | - 入站列表 - 添加入站 30 | - 端口:443 31 | - reality:开启 32 | - 添加用户:+ 33 | - flow 选择 xtls-rprx-vision 34 | - 其他默认 35 | 36 | ## 三、使用 37 | 38 | 点击"操作" → "二维码",导出节点。 39 | 40 | [1]: https://github.com/FranzKafkaYu/x-ui 41 | -------------------------------------------------------------------------------- /src/content/web/部署/搭建本地图床API可搭配Chevereto使用.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "搭建本地图床 API 可搭配 Chevereto 使用" 3 | date: 2023-04-27T19:10:00+08:00 4 | tags: [] 5 | --- 6 | 7 | 首先,你需要在你的网站服务器上安装 PHP,并确保它能够正常运行。 8 | 9 | 然后,你需要在你的网站目录下创建一个名为 `images` 的文件夹,并将你想要用作随机图片的图片文件上传到该文件夹中。 10 | 11 | 接下来,你可以将以下 PHP 代码保存为一个 PHP 文件,例如 `api.php`,并将它上传到你的网站目录下。 12 | 13 | ```php 14 | 25 | ``` 26 | 27 | 现在,你可以通过访问`http://your_website.com/api.php` 来使用这个随机图片 API 了。每次访问这个 URL 时,它都会随机选择一个图片文件并重定向到该图片的 URL。 28 | -------------------------------------------------------------------------------- /src/pages/filtered.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@/components/Layout.astro"; 3 | import Breadcrumb from "@/components/Breadcrumb.astro"; 4 | import ArticleFilter from "@/components/ArticleFilter"; 5 | 6 | // 获取当前URL的查询参数 7 | const searchParams = Astro.url.searchParams; 8 | --- 9 | 10 | 11 | 12 | 13 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/content/linux/Centos 7 查看,开放端口.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "CentOS 7 查看、开放端口" 3 | date: 2022-06-26T00:20:00+00:00 4 | tags: [] 5 | --- 6 | 7 | ## 开放和关闭端口 8 | 9 | ### 开放 8888 端口 10 | 11 | ```bash 12 | firewall-cmd --zone=public --add-port=8888/tcp --permanent 13 | ``` 14 | 15 | ### 关闭 8888 端口 16 | 17 | ```bash 18 | firewall-cmd --zone=public --remove-port=8888/tcp --permanent 19 | ``` 20 | 21 | ### 配置立即生效 22 | 23 | ```bash 24 | firewall-cmd --reload 25 | ``` 26 | 27 | ## 查看防火墙所有开放的端口 28 | 29 | ```bash 30 | firewall-cmd --zone=public --list-ports 31 | ``` 32 | 33 | ## 关闭防火墙 34 | 35 | 如果要开放的端口太多,嫌麻烦,可以关闭防火墙,安全性自行评估 36 | 37 | ```bash 38 | systemctl stop firewalld.service 39 | ``` 40 | 41 | ## 查看防火墙状态 42 | 43 | ### 方式一 44 | 45 | ```bash 46 | firewall-cmd --state 47 | ``` 48 | 49 | ### 方式二 50 | 51 | ```bash 52 | systemctl status firewalld.service 53 | ``` 54 | 55 | ## 查看监听的端口 56 | 57 | ```bash 58 | netstat -ntlp 59 | ``` 60 | 61 | > PS: CentOS 7 默认没有 netstat 命令,需要安装 net-tools 工具,yum install -y net-tools 62 | -------------------------------------------------------------------------------- /src/content/代理/v2board后端对接-套cloudfront解决被墙和提升速度.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "v2board后端对接 套cloudfront解决被墙和提升速度" 3 | date: 2023-05-13T22:05:00Z 4 | tags: ["v2board"] 5 | --- 6 | 7 | ## 一、准备工作 8 | 9 | 参照文章`v2board后端对接-XrayR`创建一个可用的节点。 10 | 11 | ## 二、配置 AWS CloudFront 12 | 13 | 1. 创建 aws 账号 14 | 2. 在 aws 后台直接搜-`CloudFront`-创建分配 15 | 3. 创建分配配置: 16 | - 源域:cloudflared 托管的域名 17 | - 协议:仅 HTTPS 18 | - 最低源 SSL 协议:TLSv1.1 19 | - 自动压缩对象:否 20 | - 缓存键和源请求:Legacy cache settings 21 | - Web Application Firewall (WAF):Do not enable security protections 22 | - 其他设置默认 23 | 24 | ## 三、添加节点 25 | 26 | 1. 复制一份创造成功的节点 27 | 2. 修改复制节点: 28 | 29 | - 后台 > 节点管理 > 添加节点 30 | 31 | - 节点名称:随便填写 32 | - 权限组:随便填写 33 | - 节点地址:CloudFront 分配的域名 34 | - TLS:关闭 35 | - 端口:80 36 | - 父节点:选择创造好的节点 37 | - 传输协议:选择 websocket 38 | - 配置协议: 39 | 40 | ```json 41 | { 42 | "path": "/随便", 43 | "headers": { 44 | "Host": "CloudFront分配的域名" 45 | } 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /src/pages/404.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@/components/Layout.astro"; 3 | --- 4 | 5 | 9 | 10 | 13 | 404 14 | 15 | 18 | 页面未找到 19 | 20 | 23 | 您访问的页面不存在或已被移动到其他位置 24 | 25 | 29 | 返回首页 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/content/windows/windows终端美化PowerShell+OhMyPosh.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Windows终端美化PowerShell+OhMyPosh" 3 | date: 2024-06-28T02:51:52+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 安装 PowerShell 8 | 9 | 在 Microsoft Store 搜索 Windows Terminal,点击安装即可。 10 | 11 | ## 安装 [Oh My Posh](https://ohmyposh.dev/) 12 | 13 | 官方安装文档:[Oh My Posh 安装指南](https://ohmyposh.dev/docs/installation/windows) 14 | 15 | ## 设置主题 16 | 17 | 在终端输入命令 `Get-PoshThemes`,即可查看支持的主题列表。 18 | 19 | 选择一个主题,例如 `negligible`,并修改 `Microsoft.PowerShell_profile.ps1` 文件中的主题内容。 20 | 21 | ### 临时切换主题 22 | 23 | ```bash 24 | oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH/negligible.omp.json" | Invoke-Expression 25 | ``` 26 | 27 | ### 永久切换主题 28 | 29 | 将上述命令加入 `Microsoft.PowerShell_profile.ps1` 文件中。 30 | 31 | `Microsoft.PowerShell_profile.ps1` 文件的路径一般为 `C:\Users\用户名\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`。 32 | 33 | 输入下方命令来打开该文件: 34 | 35 | ```powershell 36 | profile 37 | if (!(Test-Path -Path $PROFILE )) { New-Item -Type File -Path $PROFILE -Force } 38 | notepad $PROFILE 39 | ``` 40 | -------------------------------------------------------------------------------- /wasm/article-filter/src/models.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::collections::{HashMap, HashSet}; 3 | use utils_common::models::ArticleMetadata; 4 | 5 | /// 筛选索引 - 存储标签和日期索引 6 | #[derive(Serialize, Deserialize, Debug)] 7 | pub struct FilterIndex { 8 | /// 所有文章的元数据列表 9 | pub articles: Vec, 10 | /// 标签到文章ID列表的映射 11 | pub tag_index: HashMap>, 12 | /// 年份到文章ID列表的映射 13 | pub year_index: HashMap>, 14 | /// 月份到文章ID列表的映射(格式:yyyy-mm) 15 | pub month_index: HashMap>, 16 | } 17 | 18 | /// 筛选规则 - 定义筛选条件 19 | #[derive(Serialize, Deserialize, Debug, Clone)] 20 | pub struct FilterRules { 21 | /// 需要包含的标签列表 22 | pub tags: Vec, 23 | /// 排序方式: date_desc, date_asc, title_asc, title_desc 24 | pub sort_by: String, 25 | } 26 | 27 | impl Default for FilterRules { 28 | fn default() -> Self { 29 | Self { 30 | tags: Vec::new(), 31 | sort_by: "date_desc".to_string(), 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "article-filter", 4 | "article-indexer", 5 | "geo", 6 | "search", 7 | ] 8 | resolver = "2" 9 | 10 | [workspace.dependencies] 11 | wasm-bindgen = "0.2.100" 12 | serde = { version = "1.0.219", features = ["derive"] } 13 | serde_json = "1.0.140" 14 | serde-wasm-bindgen = "0.6.5" 15 | geojson = "0.24.2" 16 | geo = "0.26" 17 | geo-types = "0.7.16" 18 | js-sys = "0.3.77" 19 | kdtree = "0.7" 20 | chrono = { version = "0.4.40", features = ["serde", "wasmbind"] } 21 | bincode = { version = "2.0.1", features = ["serde"] } 22 | flate2 = "1.1.1" 23 | wee_alloc = "0.4.5" 24 | console_error_panic_hook = "0.1.7" 25 | web-sys = { version = "0.3.77", features = ["console"] } 26 | regex = "1.11.1" 27 | clap = { version = "4.5.37", features = ["suggestions", "color"] } 28 | walkdir = "2.5.0" 29 | html5ever = "0.27.0" 30 | markup5ever_rcdom = "0.3.0" 31 | once_cell = "1.21.3" 32 | 33 | [profile.release] 34 | opt-level = "z" 35 | lto = true 36 | codegen-units = 1 37 | panic = "abort" 38 | strip = true 39 | incremental = false 40 | overflow-checks = false 41 | -------------------------------------------------------------------------------- /src/content/linux/开启系统自带的TCP-BBR加速.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "开启系统自带的TCP BBR加速" 3 | date: 2024-07-02T17:19:38+08:00 4 | tags: [] 5 | --- 6 | 7 | > **系统自带内核高于 4.9 则默认已包含 BBR** 8 | 9 | ### 1. 检查内核版本 10 | 11 | ```bash 12 | uname -r 13 | ``` 14 | 15 | > 内核版本高于 4.9 就行。 16 | 17 | ### 2. 开启 BBR 18 | 19 | 通过向 `/etc/sysctl.conf`文件添加配置来启用 BBR 20 | 21 | ```bash 22 | echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf 23 | echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf 24 | ``` 25 | 26 | ### 3. 生效更改 27 | 28 | ```bash 29 | su root -c "sudo sysctl -p" 30 | ``` 31 | 32 | ### 4. 生效检测 33 | 34 | **执行下面命令,如果结果中带有****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **,则证明你的内核已开启****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **。** 35 | 36 | ```bash 37 | sysctl net.ipv4.tcp_available_congestion_control 38 | ``` 39 | 40 | **注:也可以执行下面命令,如果结果中有****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **,也可以证明你的内核已开启****[bbr](https://www.moeelf.com/tag/bbr "View all posts in bbr")** **。** 41 | 42 | ```bash 43 | lsmod | grep bbr 44 | ``` 45 | -------------------------------------------------------------------------------- /src/content/web/前端与美化/svg文字动画.css: -------------------------------------------------------------------------------- 1 | .animated-text path { 2 | fill: transparent; 3 | stroke: currentColor; 4 | stroke-width: 2; 5 | stroke-dasharray: var(--path-length); 6 | stroke-dashoffset: var(--path-length); 7 | animation: logo-anim 10s ease-in-out infinite; 8 | } 9 | 10 | @keyframes logo-anim { 11 | 0% { 12 | stroke-dashoffset: var(--path-length); 13 | fill: transparent; 14 | opacity: 0; 15 | } 16 | 10% { 17 | opacity: 1; 18 | stroke-dashoffset: var(--path-length); 19 | } 20 | 50% { 21 | stroke-dashoffset: 0; 22 | fill: transparent; 23 | } 24 | 60% { 25 | stroke-dashoffset: 0; 26 | fill: currentColor; 27 | } 28 | 80% { 29 | stroke-dashoffset: 0; 30 | fill: currentColor; 31 | } 32 | 90% { 33 | stroke-dashoffset: var(--path-length); 34 | fill: transparent; 35 | } 36 | 100% { 37 | stroke-dashoffset: var(--path-length); 38 | fill: transparent; 39 | opacity: 0; 40 | } 41 | } 42 | 43 | @media (prefers-color-scheme: dark) { 44 | .animated-text path { 45 | stroke: currentColor; 46 | } 47 | } -------------------------------------------------------------------------------- /src/content/windows/windows安装adb教程.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "windows安装adb教程" 3 | date: 2021-08-12T20:27:00+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 1. 下载 adb 工具 8 | 9 | (1) 打开 Android 开发网,搜索"SDK Platform Tools",打开如下所示的[网站][1],可以看到有 Windows\Mac\Linux 三个版本的 SDK Platform Tools,点击符合你电脑的版本下载它。adb 工具就包含在这个工具中。 10 | 11 | (2) 如果打不开 Android 开发网,则需要魔法,确保能访问 Google 之后再来下载和安装 adb。 12 | 或者在一些第三方的网站上下载 SDK Platform Tools。 13 | 14 | (3) 站长提供的[platform-tools_r31.0.3-windows][2]蓝奏云下载 15 | 16 | ## 2. adb 安装和配置 17 | 18 | (1) SDK Platform Tools 下载后,在"platform-tools"路径下可以看到三个 adb 相关的文件。现在需要将这个路径添加到系统环境变量中。 19 | 20 | (2) 添加环境变量: 21 | 22 | - windows10: 打开我的电脑——高级系统设置——系统属性——高级——环境变量——编辑 Path,将步骤 3 个文件所在路径添加到 Path 变量值中。最后点击"确定"。 23 | - windows7: 右击我的电脑——属性——高级系统设置——高级——环境变量——编辑 Path 24 | 25 | (3) 重新打开一个 cmd 窗口,输入 adb,可以看到如下的窗口,有显示 adb 的版本和用法,这就说明 adb 正确安装好啦。 26 | 27 | ## 3. 下载驱动 28 | 29 | 去谷歌中国开发者网站上下载 oem usb 驱动程序,并在设备管理器选择正确的驱动程序 30 | 驱动程序:[https://developer.android.google.cn/studio/run/oem-usb?hl=zh-cn][3] 31 | 32 | [1]: https://developer.android.google.cn/studio/releases/platform-tools?hl=en 33 | [2]: https://lsy22.lanzoui.com/iFIUqsjzyvc 34 | [3]: https://developer.android.google.cn/studio/run/oem-usb?hl=zh-cn 35 | -------------------------------------------------------------------------------- /src/consts.ts: -------------------------------------------------------------------------------- 1 | export const SITE_URL = 'https://blog.lsy22.com'; 2 | export const SITE_TITLE = "echoes"; 3 | export const SITE_DESCRIPTION = "记录生活,分享所思"; 4 | // 新的导航结构 - 支持分层导航 5 | export const NAV_STRUCTURE = [ 6 | { 7 | id: 'home', 8 | text: '首页', 9 | href: '/' 10 | }, 11 | { 12 | id: 'art', 13 | text: '艺术', 14 | items: [ 15 | { id: 'movies', text: '观影', href: '/movies' }, 16 | { id: 'books', text: '读书', href: '/books' } 17 | ] 18 | }, 19 | { 20 | id: 'articles', 21 | text: '文章', 22 | items: [ 23 | { id: 'filter', text: '筛选', href: '/filtered' }, 24 | { id: 'path', text: '网格', href: '/articles' } 25 | ] 26 | }, 27 | { 28 | id: 'other', 29 | text: '其他', 30 | items: [ 31 | { id: 'about', text: '关于', href: '/about' }, 32 | { id: 'projects', text: '项目', href: '/projects' } 33 | ] 34 | } 35 | ]; 36 | 37 | export const ARTICLE_EXPIRY_CONFIG = { 38 | enabled: true, // 是否启用文章过期提醒 39 | expiryDays: 365, // 文章过期天数 40 | warningMessage: '这篇文章已经发布超过一年了,内容可能已经过时,请谨慎参考。' // 提醒消息 41 | }; 42 | 43 | -------------------------------------------------------------------------------- /src/content/web/各大搜索引擎站长平台地址汇总.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "各大搜索引擎站长平台地址汇总" 3 | date: 2021-09-05T12:30:00+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 百度站长平台 8 | 9 | [百度搜索引擎][1]是国内最主流的搜索引擎,当然百度站长平台也是站长们使用最多的平台,功能很齐全,网站各个方面的数据都显示的很到位,能够很好地辅助站长们进行 SEO 优化。 10 | 11 | ## 搜狗站长平台 12 | 13 | [搜狗站长平台][2]和百度相比就显得略逊一些,只提供一些基础的功能。虽然前一段时间有一份报告称搜狗 PC 用户已经超越百度,但目前搜狗站长平台还是很少更新,站长学院的公告在 17 年之后就没有更新了。 14 | 15 | ## 360 站长平台 16 | 17 | [360 站长平台][3]在国内勉强排上第二,和百度相比 360 的功能还不够完善,算法也是偶尔更新一次,而且不知道你有没有发现 360 收录干货文章特别难,这也让很多站长放弃了 360 优化。 18 | 19 | ## 神马站长平台 20 | 21 | [神马搜索引擎][4]针对的是移动用户,最近的夸克浏览器大家应该都听说过吧,夸克内置搜索引擎就是神马,属于阿里的产品,虽然推出了站长平台,但是大头感觉并没有什么用... 22 | 23 | ## 必应站长平台 24 | 25 | [必应][5]是微软旗下的搜索引擎,记得以前更新 W10 的时候浏览器自带的就是必应搜索引擎,国内用户很少,估计有的人听都没听过。 26 | 27 | ## 头条站长平台 28 | 29 | [头条搜索][6]是最近搜索行业杀出来的一匹黑马,目前还处于测试阶段,有些功能还不够完善,不过头条可是自媒体大佬,所以站长们对头条站长平台抱有很大的希望。 30 | 31 | ## 谷歌站长平台 32 | 33 | [谷歌站长平台][7]跟百度一样功能都很完善,算法也是经常更新,但不知道什么原因谷歌推出了国内市场,国人访问谷歌搜索引擎也会被墙 34 | 35 | [1]: https://ziyuan.baidu.com/ 36 | [2]: http://zhanzhang.sogou.com/ 37 | [3]: http://zhanzhang.so.com/ 38 | [4]: https://zhanzhang.sm.cn/ 39 | [5]: https://www.bing.com/toolbox/webmaster/ 40 | [6]: https://zhanzhang.toutiao.com/ 41 | [7]: https://search.google.com/search-console/about?hl=zh-CN 42 | -------------------------------------------------------------------------------- /src/content/android/一加6t刷3系统思路.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "一加6T刷三系统思路" 3 | date: 2022-01-23T11:14:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 一、准备工作 8 | 9 | 1. [刷回官方系统](https://oneplusbbs.com/forum.php?mod=viewthread&tid=4703804) 10 | 2. [解 Bootloader](https://www.oneplusbbs.com/forum.php?mod=viewthread&tid=4689069) 11 | 3. [刷 Recovery](http://www.romleyuan.com/lec/read?id=135) 12 | 4. [刷入 Windows (boot 镜像只刷入 b 分区,刷入后重启到 b 分区)](https://www.bilibili.com/video/BV1MU4y137Yi?spm_id_from=333.999.0.0) 13 | 14 | ## 二、切换系统步骤 15 | 16 | ### 切换安卓 17 | 18 | 1. 进入 fastboot 模式 19 | 2. 用一个 root 的手机连接一加 6T 20 | 3. 在 root 手机上打开 [搞机助手](https://lsy22.lanzouq.com/il8M0z5c6oh?w1) 21 | 4. 选择全部 - otg 功能区 - fastboot 功能区切换 - 切换 a/b 分区 - 选择分区 A 22 | 5. 重启到 a 分区 23 | 24 | ### 切换 Windows 25 | 26 | 1. 切换回安卓 27 | 2. 进入 Recovery 模式 28 | 3. 将备份的 Windows 镜像刷入 b 分区 29 | 4. 重启到 b 分区 30 | 31 | ### 切换 Linux 32 | 33 | 1. 切换回安卓 34 | 2. 进入 Recovery 模式 35 | 3. 刷入 [Linux](https://www.bilibili.com/video/BV1CM4y1V73j/?share_source=copy_web&vd_source=5cfda173b3f4c09b66c641f3a24d103c) 36 | 4. 备份 Windows boot 镜像 37 | 5. 将 Linux 镜像刷入 b 分区 38 | 6. 重启到 b 分区 39 | 40 | ## 三、注意事项 41 | 42 | - Linux 和 Windows 在一个分区,Android 在一个分区,务必不要搞错,否则需要重新开始。 43 | - 在每个切换步骤后,建议进入 Recovery 模式备份相应的分区。 44 | -------------------------------------------------------------------------------- /src/content/web/加速与防护/充分利用不同cdn的优势.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "充分利用不同cdn的优势" 3 | date: 2025-07-04T01:23:53+08:00 4 | tags: ["cloudflare"] 5 | --- 6 | 7 | 以 vercel 和 edgeone 和 dnspod 国际版为例 8 | 9 | 例子域名 `blog.com` 10 | 11 | ## 实现方式 12 | 13 | 1. vercel 14 | 15 | 添加域名`blog.com` 16 | 17 | 2. edgeone 18 | 19 | 添加域名`blog.com` 20 | 21 | 源站地址:vercel 提供的`blog.com`的 CNAME 地址 22 | 23 | 3. dnspod 国际版 24 | 25 | 添加域名`blog.com` 26 | 27 | 将`blog.com`的 ns 解析改为 dnspod 提供 NS 地址 28 | 29 | 添加国外解析记录 30 | 31 | - 主机记录:`@` 32 | - 线路类型:`默认` 33 | - 记录类型/值:vercel 提供的`blog.com`的 CNAME 地址 34 | 35 | 添加国内解析记录 36 | 37 | - 主机记录:`@` 38 | - 线路类型:`中国` 39 | - 记录类型/值:edgeone 提供的`blog.com`的 CNAME 地址 40 | 41 | ## 原理 42 | 43 | ```mermaid 44 | graph LR; 45 | A[用户请求] --> B[dnspod]; 46 | B --> |国外请求| D[vercel]; 47 | B --> |国内请求| C[edgeone]; 48 | C --> D; 49 | ``` 50 | 51 | ## 拓展 52 | 53 | ```mermaid 54 | graph LR; 55 | A[用户请求] --> B[dns解析处理]; 56 | B --> |国外请求| C[国外cdn]; 57 | B --> |国内请求| D[国内cdn]; 58 | C --> E[源站]; 59 | D --> E; 60 | ``` 61 | 62 | 如果源站使用 cf 的小黄云,主机限制 cf 的 ip 段 访问,可以减少源站 ip 的泄漏和减缓攻击的影响 63 | 64 | 在 cdn 处利用优选 ip,可以进一步提升速度 65 | 66 | 获取优选 ip:[https://www.wetest.vip/](https://www.wetest.vip/) 67 | -------------------------------------------------------------------------------- /create_post.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 获取脚本所在目录的上级目录(假设脚本在项目根目录) 4 | PROJECT_ROOT="$(cd "$(dirname "$0")" && pwd)" 5 | 6 | # 如果没有提供参数,使用交互式输入 7 | if [ "$#" -lt 2 ]; then 8 | read -rp "请输入文章标题: " TITLE 9 | read -rp "请输入文章路径 (例如: web/my-post),也可以为空: " PATH_ARG 10 | else 11 | TITLE=$1 12 | PATH_ARG=$2 13 | fi 14 | 15 | # 检查输入是否为空 16 | if [ -z "$TITLE" ] ; then 17 | echo "错误: 标题不能为空" 18 | echo "使用方法: $0 <标题> <路径>" 19 | echo "示例: $0 \"我的新文章\" \"web/my-post\"" 20 | exit 1 21 | fi 22 | 23 | # 获取当前时间,格式化为ISO 8601格式 24 | CURRENT_DATE=$(date +"%Y-%m-%dT%H:%M:%S%:z") 25 | 26 | # 构建完整的文件路径 27 | CONTENT_DIR="$PROJECT_ROOT/src/content" 28 | FULL_PATH="$CONTENT_DIR/$PATH_ARG" 29 | 30 | # 确保路径存在 31 | mkdir -p "$FULL_PATH" 32 | 33 | # 构建最终的文件路径 34 | FILENAME="$FULL_PATH/$(basename "$TITLE").md" 35 | ABSOLUTE_PATH="$(cd "$(dirname "$FILENAME")" 2>/dev/null && pwd)/$(basename "$FILENAME")" 36 | 37 | # 检查文件是否已存在 38 | if [ -f "$FILENAME" ]; then 39 | echo "错误: 文章已存在: $ABSOLUTE_PATH" 40 | read -rp "按回车键退出..." 41 | exit 1 42 | fi 43 | 44 | # 创建markdown文件 45 | cat > "$FILENAME" << EOF 46 | --- 47 | title: "$TITLE" 48 | date: $CURRENT_DATE 49 | tags: [] 50 | --- 51 | hello,world 52 | EOF 53 | 54 | echo "已创建新文章: $ABSOLUTE_PATH" 55 | read -rp "按回车键退出..." -------------------------------------------------------------------------------- /src/content/android/安装谷歌三件套服务框架.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "安装谷歌三件套服务框架" 3 | date: 2023-07-12T23:39:00+08:00 4 | tags: [] 5 | --- 6 | 7 | ## Google 服务框架 8 | 9 | 下载地址:[`https://www.apkmirror.com/apk/google-inc/google-services-framework/`](https://www.apkmirror.com/apk/google-inc/google-services-framework/) 10 | 11 | 首先点击上边的网站,到 Google 服务框架程序的发布地址,然后找到和自己手机的安卓版本相匹配的版本。 12 | 选择 noDPI 的版本 13 | 14 | 关于 ARM 版本: 15 | **一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。 16 | 也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。** 17 | 18 | ## Google Play Service 19 | 20 | 下载地址:[`https://www.apkmirror.com/apk/google-inc/google-play-services/`](https://www.apkmirror.com/apk/google-inc/google-play-services/) 21 | 22 | 首先点击上边的网站,到 Google Play Service 的发布地址,然后找到和自己手机的安卓版本相匹配的版本。 23 | 那么就选择 noDPI 的版本 24 | 25 | 关于 ARM 版本: 26 | **一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。 27 | 也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。** 28 | 29 | ## Google Play Store 30 | 31 | 下载地址:[`https://www.apkmirror.com/apk/google-inc/google-play-store/`](https://www.apkmirror.com/apk/google-inc/google-play-store/) 32 | 33 | 首先点击上边的网站,到 Google Play Store 的发布地址,然后找到和自己手机的安卓版本相匹配的版本。 34 | 选择 noDPI 的版本 35 | 36 | 关于 ARM 版本: 37 | **一般近两年发布的手机,ARM 版本都是 ARMv8。如果是老手机,可以先搜一下自己的 ARM 版本。 38 | 也可以直接下载 universal 版本,也就是兼容 v8 和 v7 的版本。** 39 | -------------------------------------------------------------------------------- /src/content/短视频/视频制作.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "视频制作" 3 | date: 2025-08-18T10:53:58+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 视频 8 | 9 | ### 转场 10 | 11 | - 相似动作 12 | 13 | 第一个片段结尾伸手,第二个片段开头收手,相交处构图一致 14 | 15 | - 相似镜头 16 | 17 | 第一个镜头结尾转向柱子,第二个镜头从柱子转出来,中间类属于黑场 18 | 19 | - 衔接动作 20 | 21 | 视频中的物品向左位移消失,新物品从左向右渐现出现 22 | 23 | ### 音效 24 | 25 | - 旁白 26 | - 运动音效 27 | - 环境音 28 | 29 | ## Adobe After Effects 30 | 31 | - 跟踪摄像机可以反求三维世界,实现在三维世界某个位置创建物体,不会随着摄像机运动而运动 32 | - 稳定运动和摩卡的稳定,可以让画面丝滑 33 | - 如果需要跟踪相机,创建三维实体但是跟踪失败,可以先利用摩卡的物体跟踪的特性,贴上跟踪点贴纸,再用跟踪相机就可以成功 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | ## 案例解析 51 | 52 | ### AE 53 | 54 | - 丝滑变速 55 | 1. 跟踪器或摩卡稳定视频 56 | 2. 利用时间重映射,卡点,制作缓动效果 57 | - 八卦阵 58 | 1. 利用跟踪摄像机得到三维世界跟踪点 59 | 2. 将所有图片预合成,开启三维空间,八卦阵根据不用时间点进行 z 轴移动 60 | 3. 开启八卦阵合成折叠空间 61 | -------------------------------------------------------------------------------- /src/content/windows/Adobe_Photoshop (Beta)使用教程.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Adobe Photoshop (Beta) 使用教程" 3 | date: 2023-10-13T12:47:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 软件 8 | 9 | [Adobe Photoshop (Beta) 下载链接](https://pan.baidu.com/s/1f6WhLHig1AODurU2yo-HhQ?pwd=fyr7) 10 | 11 | ## 报错处理 12 | 13 | ### 出现黄色更新弹窗 14 | 15 | 1. 打开文件目录: 16 | 17 | - Mac 修改文件目录:`/Applications/Adobe Photoshop (Beta)/Adobe Photoshop (Beta).app/Contents/Required/UXP/com.adobe.photoshop.inAppMessaging/js/0.js` 18 | - Windows 目录(如果是 C 盘):`C:\Program Files\Adobe\Adobe Photoshop 2023\Required\UXP\com.adobe.photoshop.inAppMessaging\js\0.js` 19 | 20 | 2. 修改 `0.js` 文件: 21 | - 搜索 `"996633",`,结果有两处。找到第一处,在后面加上 `",display:none",`(别忘了英文逗号)。 22 | - 保存文件,退出 Photoshop,重启 Photoshop。提示框应消失。 23 | 24 | ### Adobe Creative Cloud 丢失或损坏提示 25 | 26 | 解决方法一:使用独立安装包安装,例如使用 PS CC 2017 的安装包直接安装。 27 | 28 | 解决方法二: 29 | 30 | 1. 在弹出页面之后,别关闭,打开任务管理器,你会看到这个进程(同时有一个 Adobe IPC 的分进程)。 31 | 2. 找到 Cloud,右键打开文件所在位置。 32 | 3. 关闭这个对话框,然后删除文件夹里 `Adobe Desktop Service` 名字的 `EXE` 文件。 33 | 4. 重启 Photoshop。 34 | 35 | 提示:如果任务管理器当时没出现这个进程,可能过一会出现,或者直接用 Everything 搜索 `Adobe Desktop Service` 去到 `C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\ADS` 目录也行。 36 | 37 | > 解决原理:这个 ADS 文件夹是 Adobe Creative Cloud 附带的一个负责检查软件状态的应用程序,如果检测到当前电脑未安装 Adobe Creative Cloud,那么它就会提示让你安装软件,但是并不影响 Photoshop 本身的功能以及使用。 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "newechoes", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "scripts": { 6 | "dev": "astro dev", 7 | "build": "astro build", 8 | "preview": "astro preview", 9 | "astro": "astro" 10 | }, 11 | "dependencies": { 12 | "@astrojs/mdx": "^4.3.6", 13 | "@astrojs/node": "^9.4.4", 14 | "@astrojs/react": "^4.4.0", 15 | "@astrojs/vercel": "^8.2.8", 16 | "@astrolib/seo": "1.0.0-beta.8", 17 | "@swup/fragment-plugin": "^1.1.2", 18 | "@swup/head-plugin": "^2.3.1", 19 | "@swup/preload-plugin": "^3.2.11", 20 | "@swup/progress-plugin": "^3.2.0", 21 | "@swup/scripts-plugin": "^2.1.0", 22 | "@tailwindcss/vite": "^4.1.13", 23 | "@types/react": "^19.1.14", 24 | "@types/three": "^0.177.0", 25 | "astro": "^5.14.1", 26 | "astro-mermaid": "^1.0.4", 27 | "cheerio": "^1.1.2", 28 | "mermaid": "^11.12.0", 29 | "node-fetch": "^3.3.2", 30 | "octokit": "^5.0.3", 31 | "react": "^19.1.1", 32 | "react-dom": "^19.1.1", 33 | "react-masonry-css": "^1.0.16", 34 | "swup": "^4.8.2", 35 | "tailwindcss": "^4.1.13", 36 | "three": "^0.177.0", 37 | "unist-util-visit": "^5.0.0" 38 | }, 39 | "devDependencies": { 40 | "@tailwindcss/typography": "^0.5.19", 41 | "astro-compressor": "^1.1.2", 42 | "rehype-external-links": "^3.0.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/pages/about.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@/components/Layout.astro"; 3 | import { Countdown } from "@/components/Countdown"; 4 | import WorldHeatmap from "@/components/WorldHeatmap"; 5 | const VISITED_PLACES = [ 6 | "中国-黑龙江", 7 | "中国-吉林", 8 | "中国-辽宁", 9 | "中国-北京", 10 | "中国-天津", 11 | "中国-广东", 12 | "中国-西藏", 13 | "中国-河北", 14 | "中国-山东", 15 | "中国-湖南", 16 | "中国-重庆", 17 | "中国-四川", 18 | "马来西亚", 19 | "印度尼西亚", 20 | "泰国", 21 | ]; 22 | --- 23 | 24 | 29 | 30 | 31 | 距离退休还有 32 | 35 | 39 | 40 | 41 | 42 | 43 | 我的旅行足迹 44 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/content/windows/Windows用批处理处理图标上的快捷方式和安全盾.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Windows用批处理处理图标上的快捷方式和安全盾" 3 | date: 2023-05-23T23:22:00+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 批处理脚本 8 | 9 | ### 1. 移除快捷方式箭头 10 | 11 | ```batch 12 | @echo off 13 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 29 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f 14 | taskkill /f /im explorer.exe 15 | attrib -s -r -h %userprofile%\AppData\Local\IconCache.db 16 | del %userprofile%\AppData\Local\IconCache.db 17 | start explorer.exe 18 | ``` 19 | 20 | ### 2. 移除安全盾牌标志 21 | 22 | ```batch 23 | @echo off 24 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 29 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f 25 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons" /v 77 /d "%systemroot%\system32\imageres.dll,197" /t reg_sz /f 26 | taskkill /f /im explorer.exe 27 | attrib -s -r -h %userprofile%\AppData\Local\IconCache.db 28 | del %userprofile%\AppData\Local\IconCache.db 29 | start explorer.exe 30 | ``` 31 | 32 | ## 使用方法 33 | 34 | 1. 选择您需要的批处理脚本(移除快捷方式箭头或移除安全盾牌) 35 | 2. 打开记事本或其他文本编辑器,复制对应的代码 36 | 3. 将文本另存为`.bat`文件(例如`remove_shortcut.bat`或`remove_shield.bat`) 37 | 4. **以管理员身份运行**保存的批处理文件 38 | 5. 脚本执行后,资源管理器(explorer.exe)会自动重启,变更立即生效 39 | 40 | ## 注意事项 41 | 42 | - 这些操作会修改系统注册表,请确保您了解其影响 43 | - 必须使用管理员权限运行批处理文件 44 | - 如需恢复默认设置,可删除添加的注册表项 45 | -------------------------------------------------------------------------------- /src/pages/articles/[...path].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import ArticlesPage from './index.astro'; 3 | import { getCollection, type CollectionEntry } from 'astro:content'; 4 | 5 | // 获取目录结构 6 | export async function getStaticPaths() { 7 | const articles = await getCollection('articles'); 8 | 9 | // 从文章ID中提取所有目录路径 10 | const directories = new Set(); 11 | 12 | articles.forEach((article: CollectionEntry<'articles'>) => { 13 | if (article.id.includes('/')) { 14 | // 获取所有层级的目录 15 | const parts = article.id.split('/'); 16 | let currentPath = ''; 17 | 18 | // 逐级构建目录路径 19 | for (let i = 0; i < parts.length - 1; i++) { 20 | currentPath = currentPath ? `${currentPath}/${parts[i]}` : parts[i]; 21 | directories.add(currentPath); 22 | } 23 | } 24 | }); 25 | 26 | // 准备路径数组 27 | const paths = []; 28 | 29 | // 为每个目录创建一个路由 30 | for (const path of directories) { 31 | paths.push({ 32 | params: { 33 | // 对于 [...path] 参数,Astro 需要接收单个字符串 34 | path: path 35 | }, 36 | props: { 37 | path, 38 | pageType: 'grid' 39 | } 40 | }); 41 | } 42 | 43 | // 添加根路径 (即 /articles/) 44 | paths.push({ 45 | params: { 46 | path: undefined 47 | }, 48 | props: { 49 | path: '', 50 | pageType: 'grid' 51 | } 52 | }); 53 | 54 | return paths; 55 | } 56 | 57 | // 使用主页面组件 58 | const { path, pageType = 'grid' } = Astro.props; 59 | 60 | --- 61 | 62 | -------------------------------------------------------------------------------- /src/components/Footer.astro: -------------------------------------------------------------------------------- 1 | --- 2 | interface Props {} 3 | 4 | const {} = Astro.props; 5 | 6 | const currentYear = new Date().getFullYear(); 7 | --- 8 | 9 | 55 | -------------------------------------------------------------------------------- /src/content/web/部署/搭建Chevereto中文版.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "搭建Chevereto中文版" 3 | date: 2021-08-14T20:23:00+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 1. 环境准备 8 | 9 | - [宝塔面板](https://www.bt.cn/new/index.html) 10 | - PHP 7.4 11 | - MySQL 5.7 / 8 或 MariaDB 10 12 | - Apache HTTP Web Server 或 Nginx 13 | 14 | ## 2. 下载 15 | 16 | 中文修改版: [https://github.com/rodber/chevereto-free/releases](https://github.com/rodber/chevereto-free/releases) 17 | 18 | 官方原版: [https://github.com/chevereto/chevereto/releases](https://github.com/chevereto/chevereto/releases) 19 | 20 | ## 3. 伪静态 21 | 22 | 在安装之前需要先设置伪静态,代码如下: 23 | 24 | ```nginx 25 | location ~ /(app|content|lib|importer)/.*\.(po|php|lock|sql|txt)$ { 26 | deny all; 27 | } 28 | 29 | # Enable CORS header (needed for CDN) 30 | location ~* \.(ttf|ttc|otf|eot|woff|woff2|css|js)$ { 31 | add_header Access-Control-Allow-Origin "*"; 32 | } 33 | 34 | # Force serve upload path as static content (match your upload folder if needed) 35 | location /images {} 36 | 37 | # Route dynamic request to index.php 38 | location / { 39 | try_files $uri $uri/ /index.php$is_args$query_string; 40 | } 41 | ``` 42 | 43 | ## 4. 安装 44 | 45 | 浏览器打开你的域名访问,开始安装。 46 | 47 | ## 5. 常见问题 48 | 49 | ### 5.1 图床上传大图片提示 Internal Server Error 50 | 51 | #### 设置 PHP 参数 52 | 53 | ```ini 54 | max_execution_time 55 | max_input_time 56 | memory_limit 57 | post_max_size 58 | upload_max_filesize 59 | ``` 60 | 61 | 把这些值调大一些,然后重启 PHP。 62 | 63 | ### 5.2 邮件模板 64 | 65 | 支持: 66 | 67 | 1. 账号更换邮箱 68 | 2. 新账户注册验证 69 | 3. 账户重置密码 70 | 4. 新用户注册欢迎 71 | 5. 更改邮件模板头图 72 | 73 | 上传邮件模板文件至 Chevereto `/app/themes/Peafowl/mails` 文件目录下即可,或者直接替换掉 `mails` 目录亦可。 74 | 75 | 文件: [https://lsy22.lanzouf.com/i9crn0u8anhe](https://lsy22.lanzouf.com/i9crn0u8anhe) 76 | -------------------------------------------------------------------------------- /src/content/web/加速与防护/宝塔面板下站点如何只限定CDN的IP节点回源请求.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "宝塔面板下站点如何只限定CDN的IP节点回源请求" 3 | date: 2023-07-10T20:21:00Z 4 | tags: ["cloudflare"] 5 | --- 6 | 7 | 受限于宝塔面板默认防火墙 Firewalld 的限制,要想宝塔面板下站点如何只限定 CDN 的 IP 节点回源请求的话,最简便有效的办法就是将 CDN 的 IP 节点一个一个的加入到宝塔的【端口规则】里,需要限制哪个端口就在哪个端口下添加。 8 | 9 | ## 1. 修改所有 IP 为指定 IP 10 | 11 | 我们选择的是 443 端口,默认宝塔是开放 443 端口给所有 IP 的,所以这里我们必须修改 443 端口规则为"指定 IP",切记切记哦! 12 | 13 | ## 2. 添加 CDN 节点 IP 地址段 14 | 15 | 给 443 添加了一个 CloudFlare 的 IPv4 节点 IP 地址段,意思就是这个 103.21.244.0/22 地址段的 IP 都可以回源请求 443 端口。具体 CloudFlare 的节点 IP 可以到这里查看:[www.cloudflare-cn.com/ips/](www.cloudflare-cn.com/ips/) 16 | 17 | 剩下的我们只需要一个一个的把 CloudFlare 的 IPv4 节点 IP 地址段这样添加即可,如果是 80 端口也是同样的。有人会说这样很麻烦,效率太低不科学,那么我们可以使用宝塔的【导出规则】和【导入规则】来批量的添加,具体步骤很简单,我们可以制作一个导入 CloudFlare 的 IP 节点的.json,具体内容如下参考: 18 | 19 | ```json 20 | 43|tcp|443|accept|131.0.72.0/22|131.0.72.0/22|2023-07-10 20:01:07||0| 21 | 42|tcp|443|accept|172.64.0.0/13|172.64.0.0/13|2023-07-10 20:00:54||0| 22 | 41|tcp|443|accept|104.24.0.0/14|104.24.0.0/14|2023-07-10 20:00:26||0| 23 | 40|tcp|443|accept|104.16.0.0/13|104.16.0.0/13|2023-07-10 20:00:12||0| 24 | 39|tcp|443|accept|162.158.0.0/15|162.158.0.0/15|2023-07-10 19:59:58||0| 25 | 38|tcp|443|accept|198.41.128.0/17|198.41.128.0/17|2023-07-10 19:59:41||0| 26 | 37|tcp|443|accept|197.234.240.0/22|197.234.240.0/22|2023-07-10 19:59:14||0| 27 | 36|tcp|443|accept|188.114.96.0/20|188.114.96.0/20|2023-07-10 19:58:59||0| 28 | 35|tcp|443|accept|190.93.240.0/20|190.93.240.0/20|2023-07-10 19:58:41||0| 29 | 34|tcp|443|accept|108.162.192.0/18|108.162.192.0/18|2023-07-10 19:58:25||0| 30 | 33|tcp|443|accept|141.101.64.0/18|141.101.64.0/18|2023-07-10 19:58:11||0| 31 | 32|tcp|443|accept|103.31.4.0/22|103.31.4.0/22|2023-07-10 19:57:48||0| 32 | 31|tcp|443|accept|103.22.200.0/22|103.22.200.0/22|2023-07-10 19:57:30||0| 33 | 30|tcp|443|accept|103.21.244.0/22|103.21.244.0/22|2023-07-10 19:57:10||0| 34 | 6|tcp|443|accept|173.245.48.0/20|173.245.48.0/20|2023-07-10 19:56:47||0| 35 | ``` 36 | -------------------------------------------------------------------------------- /src/content/linux/Linux美化终端zsh+ohmyzsh.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Linux美化终端 zsh+ohmyzsh" 3 | date: 2023-12-14T13:21:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 1. 安装 zsh 8 | 9 | [使用包管理器安装 zsh](https://github.com/ohmyzsh/ohmyzsh/wiki/Installing-ZSH) 10 | 11 | 将 zsh 设置成默认 shell: 12 | 13 | ```bash 14 | chsh -s /bin/zsh 15 | ``` 16 | 17 | ## 2. 安装 [oh-my-zsh](https://ohmyz.sh/) 18 | 19 | 通过 curl 安装 oh-my-zsh: 20 | 21 | ```bash 22 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" 23 | ``` 24 | 25 | 通过 wget 安装 oh-my-zsh: 26 | 27 | ```bash 28 | sh -c "$(wget https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh -O -)" 29 | ``` 30 | 31 | ## 3. 更改主题 32 | 33 | 内置主题: [oh-my-zsh 主题](https://github.com/ohmyzsh/ohmyzsh/wiki/Themes) 34 | 35 | 打开 `~/.zshrc` 配置文件,找到 `ZSH_THEME` 变量,将其修改为想要的主题名称。保存后重开终端或者执行 `exec $SHELL` 命令即可生效。 36 | 37 | ## 4. 安装插件 38 | 39 | `oh-my-zsh` 默认自带了很多插件,放置在 `~/.oh-my-zsh/plugins` 目录下。 40 | 41 | 打开 `~/.zshrc` 配置文件,找到 `plugins` 变量,将您想启用的插件加入进去,不同插件名称之间以空格隔开。 42 | 43 | ### 主题 44 | 45 | - [powerlevel10k](https://github.com/romkatv/powerlevel10k) 46 | 47 | ### 插件 48 | 49 | - [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions): 根据历史记录和完成情况在您输入时建议命令 50 | - [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting): 输入命令时提供语法高亮 51 | 52 | #### oh-my-zsh 自带插件 53 | 54 | 直接按照上述方法在 `.zshrc` 配置的 `plugins` 中加入即可: 55 | 56 | - [command-not-found](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/command-not-found): 在 `zsh` 找不到命令时提供建议的安装包 57 | - [extract](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/extract): 使用 `x` 命令解压任何压缩文件 58 | - [pip](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/pip): 为 `python` 包管理器 `pip` 提供补全 59 | - [docker](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/docker): 为 `docker` 命令添加自动补全支持 60 | - [docker-compose](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/docker-compose): 为 `docker-compose` 命令添加自动补全支持 61 | -------------------------------------------------------------------------------- /src/content/linux/Ubuntu安装c语言编译器.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Ubuntu安装C语言编译器" 3 | date: 2023-12-11T17:51:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 安装 8 | 9 | ### 一、安装 Vim(文件编辑器) 10 | 11 | ```bash 12 | apt-get install vim # 注:如果没有在超级用户的操作下需要提权 13 | ``` 14 | 15 | ### 二、安装 GCC(编译器) 16 | 17 | ```bash 18 | apt-get install gcc 19 | ``` 20 | 21 | ### 三、安装 build-essential 22 | 23 | ```bash 24 | apt-get install build-essential 25 | ``` 26 | 27 | ## 使用 28 | 29 | ### 1. 创建一个 hello.c 文件,并编辑第一个程序 30 | 31 | ```c 32 | #include 33 | 34 | int main(void) 35 | { 36 | printf("hello world! \n"); 37 | return 0; 38 | } 39 | ``` 40 | 41 | ### 2. 执行编译命令 42 | 43 | ```bash 44 | gcc -Wall hello.c -o hello 45 | ``` 46 | 47 | ### 3. 执行程序 48 | 49 | ```bash 50 | ./hello 51 | ``` 52 | 53 | ## GCC 常用命令 54 | 55 | ### 编译源文件并生成可执行文件 56 | 57 | ```bash 58 | gcc source.c -o output 59 | ``` 60 | 61 | 这里 source.c 是你的源文件的名称,output 是你希望生成的可执行文件的名称。 62 | 63 | ### 只进行编译,生成目标文件 64 | 65 | ```bash 66 | gcc -c source.c -o output.o 67 | ``` 68 | 69 | 这会生成名为`output.o`的目标文件,而不是可执行文件。 70 | 71 | ### 链接多个目标文件生成可执行文件 72 | 73 | ```bash 74 | gcc file1.o file2.o -o output 75 | ``` 76 | 77 | 如果你已经分别编译了多个源文件并生成了相应的目标文件,你可以将它们链接在一起生成可执行文件。 78 | 79 | ### 预处理并输出到文件 80 | 81 | ```bash 82 | gcc -E source.c -o output.i 83 | ``` 84 | 85 | 这个命令会执行预处理过程,并将结果输出到 output.i 文件中。 86 | 87 | ### 查看编译器的版本信息 88 | 89 | ```bash 90 | gcc --version 91 | ``` 92 | 93 | ### 生成调试信息 94 | 95 | ```bash 96 | gcc -g source.c -o output 97 | ``` 98 | 99 | 使用`-g`选项可以生成包含调试信息的可执行文件,方便调试器进行调试。 100 | 101 | ### 优化编译 102 | 103 | ```bash 104 | gcc -O2 source.c -o output 105 | ``` 106 | 107 | 使用`-O2`选项进行优化编译,提高程序运行效率。 108 | 109 | ### 开启一系列警告信息 110 | 111 | ```bash 112 | gcc -Wall source.c -o output 113 | ``` 114 | 115 | `-Wall`选项涵盖了许多常见的警告,但并不包括所有的警告。 116 | 117 | 如果你想开启更严格的警告,可以考虑使用`-Wextra`: 118 | 119 | ```bash 120 | gcc -Wall -Wextra source.c -o output 121 | ``` 122 | 123 | 这将启用一些额外的警告,帮助你更全面地检查代码。 124 | -------------------------------------------------------------------------------- /src/content/docker/Docker部署思源笔记.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Docker部署思源笔记" 3 | date: 2024-06-07T21:01:46+08:00 4 | tags: ["Docker-compose"] 5 | --- 6 | 7 | ## `docker-compose.yml` 文件配置 8 | 9 | ### 替换说明 10 | 11 | - 将 `/var/www/siyuan/` 替换为你的实际物理路径。 12 | - 将 `Password` 替换为你的访问密码。 13 | 14 | ```yaml 15 | version: "3.9" 16 | services: 17 | siyuan: 18 | image: b3log/siyuan 19 | container_name: siyuan 20 | restart: always 21 | ports: 22 | - 6806:6806 23 | volumes: 24 | - ./:/siyuan/workspace 25 | command: 26 | - "--workspace=/siyuan/workspace/" 27 | - "--lang=zh_CN" 28 | - "--accessAuthCode=" 29 | ``` 30 | 31 | ## 反向代理配置 32 | 33 | ### Nginx 配置替换说明 34 | 35 | - 将 `your_domain.com` 替换为你自己的域名。 36 | - 将 `path` 替换为你的 SSL 证书的实际路径。 37 | 38 | ```nginx 39 | upstream siyuan { 40 | server 127.0.0.1:6806; # 将流量定向到本地的6806端口 41 | } 42 | 43 | server { 44 | listen 80; 45 | listen [::]:80; 46 | server_name your_domain.com; # 设置服务器域名 47 | 48 | # 将所有 HTTP 请求重定向到 HTTPS 49 | location / { 50 | return 301 https://$host$request_uri; 51 | } 52 | } 53 | 54 | server { 55 | listen 443 ssl http2; 56 | listen [::]:443 ssl http2; 57 | 58 | server_name your_domain.com; # 设置服务器域名 59 | 60 | # 配置 SSL 证书路径 61 | ssl_certificate path/fullchain.cer; 62 | ssl_certificate_key path/file.key; 63 | 64 | client_max_body_size 0; # 不限制请求体大小 65 | 66 | location / { 67 | proxy_pass http://siyuan; # 反向代理到上游 siyuan 68 | proxy_set_header HOST $host; 69 | proxy_set_header X-Forwarded-Proto $scheme; 70 | proxy_set_header X-Real-IP $remote_addr; 71 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 72 | } 73 | location /ws { 74 | proxy_pass http://siyuan; 75 | proxy_read_timeout 60s; # 设置读取超时时间 76 | proxy_http_version 1.1; # 使用 HTTP 1.1 77 | proxy_set_header Upgrade $http_upgrade; 78 | proxy_set_header Connection 'Upgrade'; # 支持 WebSocket 79 | } 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /src/content/linux/手动搭建LNMP.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "手动搭建LNMP" 3 | date: 2024-04-05T01:48:16+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 系统更新和软件安装 8 | 9 | ### 更新系统的软件包列表 10 | 11 | 在开始安装任何软件之前,最好先更新系统的软件包列表: 12 | 13 | ```bash 14 | sudo apt update 15 | ``` 16 | 17 | ### 第一步:安装 Nginx 18 | 19 | 1. **安装 Nginx**: 20 | 21 | ```bash 22 | sudo apt install nginx 23 | ``` 24 | 25 | 2. **启动 Nginx 并检查状态**: 26 | 27 | ```bash 28 | sudo systemctl status nginx 29 | ``` 30 | 31 | ### 第二步:安装 PHP 32 | 33 | 1. **安装 php-fpm 模块**: 34 | 35 | ```bash 36 | sudo apt install php-fpm 37 | ``` 38 | 39 | > 安装 php-fpm 模块而不是 PHP。如果 PHP 先安装,它可能会默认配置 Apache 服务器而不是 Nginx。`php-fpm` 包包含 PHP 的所有核心模块。 40 | 41 | 2. **(可选)安装其他 PHP 模块**,例如 `php-mysql`,运行 Typecho 所必需的软件包: 42 | 43 | ```bash 44 | sudo apt install php-common php-mysql php-cgi php-mbstring php-curl php-gd php-xml php-xmlrpc php-pear 45 | ``` 46 | 47 | 3. **确认 PHP 版本**: 48 | 49 | ```bash 50 | php -v 51 | ``` 52 | 53 | 4. **确认 php-fpm 服务正在运行**(替换为您的 PHP 版本): 54 | 55 | ```bash 56 | sudo systemctl status php8.3-fpm 57 | ``` 58 | 59 | ### 第三步:安装 MySQL 60 | 61 | 1. **安装 gnupg 包**,用于处理密钥: 62 | 63 | ```bash 64 | sudo apt install gnupg 65 | ``` 66 | 67 | 2. **下载 MySQL 的官方 DEB 软件包**。先查询[最新的官方 DEB 包](https://dev.mysql.com/downloads/repo/apt/),然后使用 `wget` 下载: 68 | 69 | ```bash 70 | wget [MySQL DEB Package URL] 71 | ``` 72 | 73 | 3. **安装 DEB 包**: 74 | 75 | ```bash 76 | sudo dpkg -i [package-name.deb] 77 | ``` 78 | 79 | > 将 `[package-name.deb]` 替换为下载的包名。 80 | 81 | 4. **刷新 apt 软件包缓存**,以使新软件包可用: 82 | 83 | ```bash 84 | sudo apt update 85 | ``` 86 | 87 | 5. **安装 MySQL 服务器**: 88 | 89 | ```bash 90 | sudo apt install mysql-server 91 | ``` 92 | 93 | 6. **检查 MySQL 服务状态**: 94 | 95 | ```bash 96 | sudo systemctl status mysql 97 | ``` 98 | 99 | 7. **测试 MySQL**: 100 | 101 | ```bash 102 | mysqladmin -u root -p version 103 | ``` 104 | -------------------------------------------------------------------------------- /src/content/web/部署/nginx配置.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "nginx配置" 3 | date: 2024-07-05T15:21:06Z 4 | tags: [] 5 | --- 6 | 7 | ### 代理的区别 8 | 9 | 正向代理:代理客户端 10 | 11 | 反向代理:代理服务端 12 | 13 | ### 配置文件 14 | 15 | ```docker-compose 16 | ########### 每个指令必须有分号结束。################# 17 | #user administrator administrators; #配置用户或者组,默认为nobody nobody。 18 | #worker_processes 2; #允许生成的进程数,默认为1 19 | #pid /nginx/pid/nginx.pid; #指定nginx进程运行文件存放地址 20 | error_log log/error.log debug; #制定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg 21 | events { 22 | accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on 23 | multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off 24 | #use epoll; #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport 25 | worker_connections 1024; #最大连接数,默认为512 26 | } 27 | http { 28 | include mime.types; #文件扩展名与文件类型映射表 29 | default_type application/octet-stream; #默认文件类型,默认为text/plain 30 | #access_log off; #取消服务日志 31 | log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式 32 | access_log log/access.log myFormat; #combined为日志格式的默认值 33 | sendfile on; #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。 34 | sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。 35 | keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。 36 | 37 | upstream mysvr { 38 | server 127.0.0.1:7878; 39 | server 192.168.10.121:3333 backup; #热备 40 | } 41 | error_page 404 https://www.baidu.com; #错误页 42 | server { 43 | keepalive_requests 120; #单连接请求上限次数。 44 | listen 4545; #监听端口 45 | server_name 127.0.0.1; #监听地址 46 | location ~*^.+$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。 47 | #root path; #根目录 48 | #index vv.txt; #设置默认页 49 | proxy_pass http://mysvr; #请求转向mysvr 定义的服务器列表 50 | deny 127.0.0.1; #拒绝的ip 51 | allow 172.18.5.54; #允许的ip 52 | } 53 | } 54 | } 55 | ``` 56 | -------------------------------------------------------------------------------- /src/content/linux/SSH使用小技巧.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "SSH使用小技巧" 3 | date: 2024-06-30T23:46:05+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 更改 root 密码 8 | 9 | > 将`password`更改为所需的密码 10 | 11 | 1. 修改密码 12 | 13 | ```bash 14 | echo root:`password` |sudo chpasswd root 15 | ``` 16 | 17 | 2. 开启 root 登录 18 | 19 | ```bash 20 | sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config; 21 | ``` 22 | 23 | 3. 开启密码登录 24 | 25 | ```bash 26 | sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config; 27 | ``` 28 | 29 | 4. 重启 ssh 服务 30 | 31 | ```bash 32 | systemctl restart sshd.service 33 | ``` 34 | 35 | ## 配置使用密钥登录 36 | 37 | 1. 生成密钥和公钥,请执行以下命令: 38 | 39 | ```bash 40 | ssh-keygen -t rsa -b 4096 41 | ``` 42 | 43 | > 连续执行回车即可生成密钥和公钥对。如果需要设置密码,请在密码提示处输入密码。 44 | 45 | 2. 安装 ssh 公钥 46 | 47 | ```bash 48 | cp "$HOME/.ssh/id_rsa.pub" "$HOME/.ssh/authorized_keys" 49 | ``` 50 | 51 | 3. 设置公钥权限 52 | 53 | ```bash 54 | chmod 600 "$HOME/.ssh/authorized_keys" 55 | chmod 700 "$HOME/.ssh" 56 | ``` 57 | 58 | 4. ssh 配置文件 59 | 60 | 1. 开启密钥登录 61 | 62 | ```bash 63 | sudo sed -i 's/^#\?PubkeyAuthentication.*/PubkeyAuthentication yes/g' /etc/ssh/sshd_config 64 | ``` 65 | 66 | 2. 关闭密码登录 67 | 68 | ```bash 69 | sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/g' /etc/ssh/sshd_config 70 | ``` 71 | 72 | 5. 重启 sshd 服务 73 | 74 | ```bash 75 | systemctl restart sshd.service 76 | ``` 77 | 78 | ## ssh 登录后闲置时间过长而断开连接 79 | 80 | ```bash 81 | echo "ServerAliveInterval 60" >> "$HOME/.ssh/config" 82 | ``` 83 | 84 | > ssh 客户端会每隔一段 60s,自动与 ssh 服务器通信一次 85 | 86 | ## 存放 ssh 密钥密码 87 | 88 | ### 启动`ssh-agent` 89 | 90 | #### Linux 91 | 92 | ```bash 93 | ssh-agent bash 94 | ``` 95 | 96 | #### Windows 97 | 98 | 1. 打开服务 99 | 2. 将`OpenSSH Authentication Agent`服务启动 100 | 3. 设置自启动 101 | 102 | ### 添加默认的私钥 103 | 104 | ```bash 105 | ssh-add 106 | ``` 107 | 108 | > 添加私钥时,会要求输入密码。以后,在这个对话里面再使用密钥时,就不需要输入私钥的密码了,因为私钥已经加载到内存里面了。 109 | 110 | ### 使用命令将本地公钥发送给服务端 111 | 112 | ```bash 113 | ssh-copy-id username@hostname 114 | ``` 115 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import { defineConfig } from "astro/config"; 3 | 4 | import tailwindcss from "@tailwindcss/vite"; 5 | import mdx from "@astrojs/mdx"; 6 | import react from "@astrojs/react"; 7 | import rehypeExternalLinks from "rehype-external-links"; 8 | import { SITE_URL } from "./src/consts"; 9 | import compressor from "astro-compressor"; 10 | import vercel from "@astrojs/vercel"; 11 | import { articleIndexerIntegration } from "./src/plugins/build-article-index.js"; 12 | import { rehypeCodeBlocks } from "./src/plugins/rehype-code-blocks.js"; 13 | import { rehypeTables } from "./src/plugins/rehype-tables.js"; 14 | import { customSitemapIntegration } from "./src/plugins/sitemap-integration.js"; 15 | import { rssIntegration } from "./src/plugins/rss-integration.js"; 16 | import { robotsIntegration } from "./src/plugins/robots-integration.js"; 17 | import mermaid from 'astro-mermaid'; 18 | 19 | 20 | // https://astro.build/config 21 | export default defineConfig({ 22 | site: SITE_URL, 23 | output: "static", 24 | trailingSlash: "ignore", 25 | 26 | build: { 27 | format: "directory", 28 | }, 29 | 30 | vite: { 31 | plugins: [tailwindcss()], 32 | }, 33 | 34 | integrations: [ 35 | // 使用Astro官方的MDX支持 36 | mdx(), 37 | react(), 38 | // mermaid插件 39 | mermaid({ 40 | theme: 'neutral', 41 | autoTheme: true 42 | }), 43 | // 使用文章索引生成器 44 | articleIndexerIntegration(), 45 | // 站点地图和robots.txt生成 46 | customSitemapIntegration(), 47 | robotsIntegration(), 48 | rssIntegration(), 49 | // 添加压缩插件 (必须放在最后位置) 50 | compressor() 51 | ], 52 | 53 | // Markdown 配置 - 使用官方语法高亮 54 | markdown: { 55 | // 配置语法高亮 56 | syntaxHighlight: { 57 | // 使用shiki作为高亮器 58 | type: 'shiki', 59 | }, 60 | // Shiki主题配置 61 | shikiConfig: { 62 | // 默认主题 - 必须设置,但最终会被替换为 light/dark 主题 63 | theme: 'github-light', 64 | // 定义明亮和暗黑主题 65 | themes: { 66 | light: 'github-light', 67 | dark: 'github-dark' 68 | }, 69 | // 启用代码换行 70 | wrap: true 71 | }, 72 | rehypePlugins: [ 73 | [rehypeExternalLinks, { target: '_blank', rel: ['nofollow', 'noopener', 'noreferrer'] }], 74 | rehypeCodeBlocks, 75 | rehypeTables 76 | ], 77 | gfm: true, 78 | }, 79 | 80 | adapter: vercel(), 81 | }); 82 | -------------------------------------------------------------------------------- /src/styles/theme-toggle.css: -------------------------------------------------------------------------------- 1 | /* 主题切换组件的全局变量 */ 2 | :root { 3 | --theme-toggle-ripple-size: 10px; 4 | --theme-toggle-ripple-duration: 800ms; 5 | --theme-toggle-light-ripple-color: 100, 100, 100; 6 | --theme-toggle-dark-ripple-color: 200, 200, 200; 7 | --theme-toggle-ripple-opacity: 0.15; 8 | --theme-toggle-z-index-old: 999; 9 | --theme-toggle-z-index-new: 1000; 10 | --theme-toggle-ripple-scale: 10; 11 | } 12 | 13 | /* 波纹效果动画 */ 14 | @keyframes ripple-effect { 15 | from { 16 | transform: scale(0); 17 | opacity: 0.8; 18 | } 19 | to { 20 | transform: scale(var(--theme-toggle-ripple-scale)); 21 | opacity: 0; 22 | } 23 | } 24 | 25 | /* 波纹效果基础样式 */ 26 | .theme-ripple { 27 | position: absolute; 28 | border-radius: 50%; 29 | width: var(--theme-toggle-ripple-size); 30 | height: var(--theme-toggle-ripple-size); 31 | pointer-events: none; 32 | transform-origin: center; 33 | animation: ripple-effect var(--theme-toggle-ripple-duration) ease-out forwards; 34 | background-color: rgba( 35 | var(--theme-ripple-color, var(--theme-toggle-light-ripple-color)), 36 | var(--theme-toggle-ripple-opacity) 37 | ); 38 | } 39 | 40 | /* 自动设置波纹颜色 */ 41 | [data-theme="light"] .theme-ripple { 42 | --theme-ripple-color: var(--theme-toggle-light-ripple-color); 43 | } 44 | 45 | [data-theme="dark"] .theme-ripple { 46 | --theme-ripple-color: var(--theme-toggle-dark-ripple-color); 47 | } 48 | 49 | /* View Transitions 样式 */ 50 | ::view-transition-old(root), 51 | ::view-transition-new(root) { 52 | animation: none !important; 53 | mix-blend-mode: normal !important; 54 | isolation: auto !important; 55 | } 56 | 57 | /* 过渡动画控制 */ 58 | html.theme-transition-active { 59 | transition: none !important; 60 | } 61 | 62 | ::view-transition-old(root) { 63 | z-index: var(--theme-toggle-z-index-old) !important; 64 | } 65 | 66 | ::view-transition-new(root) { 67 | z-index: var(--theme-toggle-z-index-new) !important; 68 | } 69 | 70 | /* 主题切换按钮样式 */ 71 | #theme-toggle-button { 72 | position: relative; 73 | overflow: hidden; 74 | } 75 | 76 | /* 图标样式 */ 77 | .theme-toggle-icon { 78 | width: var(--theme-toggle-icon-width, 16px); 79 | height: var(--theme-toggle-icon-height, 16px); 80 | transition: transform 0.2s ease; 81 | } 82 | 83 | .theme-toggle-icon:hover { 84 | transform: scale(1.1); 85 | } 86 | 87 | /* 设置主题容器在移动设备上的样式 */ 88 | #theme-toggle-container { 89 | position: relative; 90 | overflow: hidden; 91 | } 92 | -------------------------------------------------------------------------------- /src/styles/articles-table.css: -------------------------------------------------------------------------------- 1 | /* 表格样式 - 与全局主题配色协调 */ 2 | .table-container { 3 | container-type: inline-size; 4 | width: 100%; 5 | overflow-x: auto; 6 | border-radius: 0.5rem; 7 | } 8 | 9 | table { 10 | display: table; 11 | width: 100%; 12 | } 13 | 14 | thead { 15 | background-color: var(--color-primary-700); 16 | color: white; 17 | } 18 | 19 | thead th { 20 | padding: 0.75rem 1rem; 21 | font-weight: 600; 22 | text-align: left; 23 | border-bottom: 2px solid var(--color-primary-800); 24 | border-right: 1px solid var(--color-primary-600); 25 | } 26 | 27 | thead th:last-child { 28 | border-right: none; 29 | } 30 | 31 | tbody tr { 32 | background-color: white; 33 | transition: background-color 0.2s ease; 34 | } 35 | 36 | tbody tr:nth-child(odd) { 37 | background-color: var(--color-gray-50); 38 | } 39 | 40 | tbody tr:hover { 41 | background-color: var(--color-primary-50); 42 | } 43 | 44 | tbody td { 45 | padding: 0.75rem 1rem; 46 | border-bottom: 1px solid var(--color-gray-200); 47 | border-right: 1px solid var(--color-gray-200); 48 | vertical-align: top; 49 | white-space: nowrap; 50 | max-width: 300px; 51 | overflow: hidden; 52 | text-overflow: ellipsis; 53 | } 54 | 55 | tbody td:last-child { 56 | border-right: none; 57 | } 58 | 59 | tbody tr:last-child td { 60 | border-bottom: none; 61 | } 62 | 63 | 64 | [data-theme='dark'] thead { 65 | background-color: var(--color-primary-900); 66 | } 67 | 68 | [data-theme='dark'] thead th { 69 | border-bottom: 2px solid var(--color-primary-800); 70 | border-right: 1px solid var(--color-primary-800); 71 | } 72 | 73 | [data-theme='dark'] thead th:last-child { 74 | border-right: none; 75 | } 76 | 77 | [data-theme='dark'] tbody tr { 78 | background-color: var(--color-gray-800); 79 | color: var(--color-gray-100); 80 | } 81 | 82 | [data-theme='dark'] tbody tr:nth-child(odd) { 83 | background-color: var(--color-gray-900); 84 | } 85 | 86 | [data-theme='dark'] tbody tr:hover { 87 | background-color: var(--color-gray-700); 88 | } 89 | 90 | [data-theme='dark'] tbody td { 91 | border-bottom: 1px solid var(--color-gray-700); 92 | border-right: 1px solid var(--color-gray-700); 93 | } 94 | 95 | [data-theme='dark'] tbody td:last-child { 96 | border-right: none; 97 | } 98 | 99 | [data-theme='dark'] td:empty::before { 100 | color: var(--color-gray-600); 101 | } 102 | -------------------------------------------------------------------------------- /wasm/utils-common/src/models.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// 标题结构 - 存储文章中的标题及其层级 5 | #[derive(Serialize, Deserialize, Clone, Debug)] 6 | pub struct Heading { 7 | /// 标题级别(1表示h1,2表示h2,依此类推) 8 | pub level: usize, 9 | /// 标题文本 10 | pub text: String, 11 | /// 标题在文章中的开始位置(字符偏移量) 12 | pub position: usize, 13 | /// 标题内容结束位置(下一个标题开始前或文章结束) 14 | pub end_position: Option, 15 | } 16 | 17 | /// 文章元数据 - 存储索引所需的文章基本信息 18 | #[derive(Serialize, Deserialize, Clone, Debug)] 19 | pub struct ArticleMetadata { 20 | /// 文章唯一标识符 21 | pub id: String, 22 | /// 文章标题 23 | pub title: String, 24 | /// 文章摘要 25 | pub summary: String, 26 | /// 发布日期 27 | pub date: DateTime, 28 | /// 文章标签列表 29 | pub tags: Vec, 30 | /// 文章URL路径 31 | pub url: String, 32 | /// 文章内容,用于全文搜索 33 | #[serde(skip_serializing_if = "String::is_empty", default)] 34 | pub content: String, 35 | /// 页面类型:article(文章)、page(普通页面) 36 | #[serde(default = "default_page_type")] 37 | pub page_type: String, 38 | /// 文章中的标题结构 39 | #[serde(default)] 40 | pub headings: Vec, 41 | } 42 | 43 | /// 默认页面类型为article 44 | fn default_page_type() -> String { 45 | "article".to_string() 46 | } 47 | 48 | /// 索引类型 - 用于区分不同的索引 49 | #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)] 50 | pub enum IndexType { 51 | /// 只包含基本信息的索引 52 | Basic, 53 | /// 包含筛选所需的标签和日期索引 54 | Filter, 55 | /// 包含全文搜索索引 56 | Search, 57 | /// 完整索引,包含所有内容 58 | Full, 59 | } 60 | 61 | /// 索引元数据 - 存储索引的基本信息 62 | #[derive(Serialize, Deserialize, Debug)] 63 | pub struct IndexMetadata { 64 | /// 索引包含的文章数量 65 | pub article_count: usize, 66 | /// 索引包含的标签数量 67 | pub tag_count: usize, 68 | /// 索引创建时间 69 | pub created_at: DateTime, 70 | /// 索引版本 71 | pub version: String, 72 | /// 索引类型 73 | pub index_type: IndexType, 74 | /// 索引中的词元总数 75 | pub token_count: usize, 76 | } 77 | 78 | /// 标题索引项 - 存储标题与内容匹配关系 79 | #[derive(Serialize, Deserialize, Clone, Debug)] 80 | pub struct HeadingIndexEntry { 81 | /// 标题ID (文章ID:标题索引) 82 | pub id: String, 83 | /// 标题级别 84 | pub level: usize, 85 | /// 标题文本 86 | pub text: String, 87 | /// 标题内容起始位置 88 | pub start_position: usize, 89 | /// 标题内容结束位置 90 | pub end_position: usize, 91 | /// 父标题ID (如果有) 92 | pub parent_id: Option, 93 | } -------------------------------------------------------------------------------- /src/content/代理/搭建v2board前端.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "搭建v2board前端" 3 | date: 2021-07-31T00:06:00+08:00 4 | tags: ["v2board"] 5 | --- 6 | 7 | ## 一、配置宝塔 8 | 9 | 你需要在宝塔选择你的系统获得安装方式。这里以 CentOS 7+ 作为系统环境进行安装。 10 | 11 | [宝塔官方](https://www.bt.cn/bbs/thread-19376-1-1.html) 12 | 13 | 安装完成后我们登陆宝塔进行环境的安装。 14 | 15 | 选择使用 LNMP 的环境安装方式勾选如下信息: 16 | 17 | - ☑️ Nginx 1.17 18 | - ☑️ MySQL 5.6 19 | - ☑️ PHP 7.3 20 | 21 | 选择快速编译后进行安装。 22 | 23 | ## 二、安装 Redis 和文件信息 24 | 25 | 宝塔面板 > 软件商店 > 找到 PHP 7.3 点击设置 > 安装扩展 > `redis` `fileinfo`进行安装。 26 | 27 | ## 三、解除被禁止的函数 28 | 29 | 宝塔面板 > 软件商店 > 找到 PHP 7.3 点击设置 > 禁用功能,将 `putenv` `proc_open` `pcntl_alarm` `pcntl_signal` 从列表中删除。 30 | 31 | ## 四、添加站点 32 | 33 | 宝塔面板 > 网站 > 添加站点: 34 | 35 | - 在域名填入你的域名 36 | - 在数据库中选择 MySQL 37 | - 在 PHP 版本中选择 PHP-73 38 | 39 | ## 五、安装 V2Board 40 | 41 | ### 进入站点目录 42 | 43 | ```bash 44 | cd /www/wwwroot/你的站点域名 45 | ``` 46 | 47 | ### 删除目录下文件 48 | 49 | ```bash 50 | chattr -i .user.ini 51 | rm -rf .htaccess 404.html index.html .user.ini 52 | ``` 53 | 54 | ### 从 Github 克隆到当前目录 55 | 56 | ```bash 57 | git clone https://github.com/v2board/v2board.git ./ 58 | ``` 59 | 60 | ### 安装依赖包以及 V2board 61 | 62 | ```bash 63 | sh init.sh 64 | ``` 65 | 66 | ## 六、配置站点目录及伪静态 67 | 68 | 1. 添加完成后编辑添加的站点 > 站点目录 > 运行目录,选择 /public 保存。 69 | 2. 添加完成后编辑添加的站点 > 伪静态,填入: 70 | 71 | ```nginx 72 | location /downloads { 73 | } 74 | 75 | location / { 76 | try_files $uri $uri/ /index.php$is_args$query_string; 77 | } 78 | 79 | location ~ .*\.(js|css)?$ 80 | { 81 | expires 1h; 82 | error_log off; 83 | access_log /dev/null; 84 | } 85 | ``` 86 | 87 | ## 七、配置定时任务 88 | 89 | 宝塔面板 > 计划任务,配置: 90 | 91 | - 任务类型:选择 Shell 脚本 92 | - 任务名称:填写`v2board` 93 | - 执行周期:选择`N 分钟 1 分钟` 94 | - 脚本内容: 95 | 96 | ```php 97 | php /www/wwwroot/路径/artisan schedule:run 98 | ``` 99 | 100 | 根据上述信息添加每 1 分钟执行一次的定时任务。 101 | 102 | ## 八、启动队列服务 103 | 104 | V2board 的邮件系统强依赖队列服务,你想要使用邮件验证及群发邮件必须启动队列服务。下面以宝塔中`supervisor`服务来守护队列服务作为演示。 105 | 106 | 宝塔面板 > 软件商店 > 部署 > 找到 Supervisor 进行安装,安装完成后点击设置 > 添加守护进程,按照如下填写: 107 | 108 | - 名称:填写 `V2board` 109 | - 运行目录:选择站点目录 110 | - 启动命令:填写 `php artisan horizon` 111 | - 启动数量:填写 `1` 112 | 113 | 填写后点击添加即可运行。 114 | 115 | ## 常见问题 116 | 117 | ### 500 错误 118 | 119 | 可能的原因: 120 | 121 | 1. 检查站点根目录权限,递归 755,保证目录有可写文件的权限。 122 | 2. Redis 扩展没有安装或者 Redis 没有安装造成的。 123 | 3. 可以通过查看 storage/logs 下的日志来排查错误或者开启 debug 模式。 124 | 4. 重启 php7.3。 125 | -------------------------------------------------------------------------------- /src/content/docker/密码管理器—Vaultwarden(bitwarden).md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "密码管理器—Vaultwarden(bitwarden)" 3 | date: 2023-05-18T21:47:00+00:00 4 | tags: ["Docker-compose"] 5 | --- 6 | 7 | ## 1. 安装 Vaultwarden 8 | 9 | 使用以下 `docker-compose.yml` 文件部署 Vaultwarden: 10 | 11 | ```yaml 12 | version: "3.8" 13 | services: 14 | bitwarden: 15 | image: vaultwarden/server:latest 16 | container_name: vaultwarden 17 | restart: always 18 | environment: 19 | - SIGNUPS_ALLOWED=true # 是否开启用户注册 20 | - WEBSOCKET_ENABLED=true 21 | - TZ=Asia/Shanghai 22 | - ADMIN_TOKEN="" # 管理员密码 23 | volumes: 24 | - ./:/data/ 25 | ports: 26 | - "6666:80" 27 | - "3012:3012" 28 | ``` 29 | 30 | ## 2. 设置反向代理 31 | 32 | 使用 Nginx 设置反向代理,以下是基本的配置示例: 33 | 34 | > **需要修改的参数** 35 | > 36 | > 1. `ssl_certificate` : SSL 证书路径 37 | > 2. `ssl_certificate_key` : SSL 证书路径 38 | > 3. `server_name`: 跟你前面配置的 domain 相同,案例中为`b.lsy22.com` 39 | > 4. `proxy_pass` : 运行 Vaultwarden 的服务器地址和端口,比如本机为 127.0.0.1:6666 40 | 41 | ```nginx 42 | server { 43 | listen 80; 44 | listen [::]:80; 45 | listen 443 ssl http2; 46 | listen [::]:443 ssl http2; 47 | 48 | server_name b.lsy22.com; # 将 your_domain.com 替换为您的域名 49 | 50 | ssl_certificate /root/.acme.sh/b.lsy22.com/fullchain.cer; # 填入SSL证书路径 51 | ssl_certificate_key /root/.acme.sh/b.lsy22.com/b.lsy22.com.key;# 填入SSL证书路径 52 | 53 | location / { 54 | proxy_pass http://127.0.0.1:6666; 55 | proxy_http_version 1.1; 56 | proxy_cache_bypass $http_upgrade; 57 | proxy_set_header Upgrade $http_upgrade; 58 | proxy_set_header Connection "upgrade"; 59 | proxy_set_header Host $host; 60 | proxy_set_header X-Real-IP $remote_addr; 61 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 62 | proxy_set_header X-Forwarded-Proto $scheme; 63 | proxy_set_header X-Forwarded-Host $host; 64 | proxy_set_header X-Forwarded-Port $server_port; 65 | } 66 | 67 | location /notifications/hub { 68 | proxy_pass http://127.0.0.1:3012; 69 | proxy_set_header Upgrade $http_upgrade; 70 | proxy_set_header Connection "upgrade"; 71 | } 72 | 73 | location /notifications/hub/negotiate { 74 | proxy_pass http://127.0.0.1:6666; 75 | } 76 | } 77 | ``` 78 | 79 | ## 3. 创建账号 80 | 81 | 访问 Vaultwarden 的 Web 界面创建账号。 82 | 83 | ## 其他 84 | 85 | ### 翻译模板 86 | 87 | 将翻译好的模板放到`templates`目录下,如果没有创建一个 88 | 89 | 模板下载链接:[https://github.com/wcjxixi/vaultwarden-lang-zhcn](https://github.com/wcjxixi/vaultwarden-lang-zhcn) 90 | -------------------------------------------------------------------------------- /src/content/web/加速与防护/GitHub-Actions自动刷新多吉云_CDN缓存.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "GitHub Actions自动刷新多吉云CDN缓存" 3 | date: 2024-05-06T13:49:43+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 1. 创建自动刷新脚本 8 | 9 | 在项目根目录创建一个文件名为 `RefreshCDN.py`。修改下列代码中的密钥和域名为你自己的信息。 10 | 11 | ```python 12 | from hashlib import sha1 13 | import hmac 14 | import requests 15 | import json 16 | import urllib 17 | import os 18 | 19 | def dogecloud_api(api_path, data={}, json_mode=False): 20 | """ 21 | 调用多吉云API。 22 | 23 | :param api_path: 调用的 API 接口地址,包含 URL 请求参数 QueryString。 24 | :param data: POST 的数据,字典格式。 25 | :param json_mode: 数据 data 是否以 JSON 格式请求,默认为 false 则使用表单形式。 26 | 27 | :return dict: 返回的数据。 28 | """ 29 | access_key = '你的AccessKey' # 替换为自己的 AccessKey 30 | secret_key = '你的SecretKey' # 替换为自己的 SecretKey 31 | 32 | body = '' 33 | mime = '' 34 | if json_mode: 35 | body = json.dumps(data) 36 | mime = 'application/json' 37 | else: 38 | body = urllib.parse.urlencode(data) 39 | mime = 'application/x-www-form-urlencoded' 40 | 41 | sign_str = api_path + "\n" + body 42 | signed_data = hmac.new(secret_key.encode('utf-8'), sign_str.encode('utf-8'), sha1) 43 | sign = signed_data.digest().hex() 44 | authorization = 'TOKEN ' + access_key + ':' + sign 45 | 46 | response = requests.post('https://api.dogecloud.com' + api_path, data=body, headers={ 47 | 'Authorization': authorization, 48 | 'Content-Type': mime 49 | }) 50 | return response.json() 51 | 52 | url_list = ['https://xxxxx/'] # 替换为你的博客域名 53 | 54 | api = dogecloud_api('/cdn/refresh/add.json', { 55 | 'rtype': 'path', 56 | 'urls': json.dumps(url_list) 57 | }) 58 | if api['code'] == 200: 59 | print(api['data']['task_id']) 60 | else: 61 | print("API failed: " + api['msg']) 62 | ``` 63 | 64 | ## 2. 创建 GitHub Actions 脚本 65 | 66 | 在项目根目录创建 `.github/workflows` 目录(如果还没有的话),并在该目录下新建 `RefreshCDN.yml`。 67 | 68 | ```yaml 69 | name: Refresh CDN 70 | 71 | on: 72 | push: 73 | branches: 74 | - master 75 | 76 | jobs: 77 | refresh-cdn: 78 | runs-on: ubuntu-latest 79 | steps: 80 | - name: 检出代码 81 | uses: actions/checkout@v2 82 | - name: 安装 Python 83 | uses: actions/setup-python@v2 84 | with: 85 | python-version: "3.x" 86 | - name: 安装依赖 87 | run: pip install requests 88 | - name: 等待源站部署 89 | run: sleep 1m # 这里用了个笨办法,等待 1 分钟后进行刷新 90 | - name: 刷新 CDN 91 | run: python RefreshCDN.py 92 | ``` 93 | 94 | 这样设置后,每次推送到 `master` 分支时,GitHub Actions 会自动运行这个脚本来刷新 CDN。 95 | -------------------------------------------------------------------------------- /src/content/代理/v2board后端对接-soga.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "v2board后端对接 soga" 3 | date: 2021-07-31T01:17:00+08:00 4 | tags: ["v2board"] 5 | --- 6 | 7 | 确保 v2board 版本在 1.2.5 及以上 8 | 9 | ## 一、安装与更新 10 | 11 | ```bash 12 | bash <(curl -Ls https://raw.githubusercontent.com/sprov065/soga/master/install.sh) 13 | ``` 14 | 15 | ## 二、同步时间(重要) 16 | 17 | v2ray 节点需要进行时间同步,时间若与客户端相差太大则无法连接。 18 | 19 | ### CentOS 7 20 | 21 | ```bash 22 | yum install -y ntp 23 | systemctl enable ntpd 24 | ntpdate -q 0.rhel.pool.ntp.org 25 | systemctl restart ntpd 26 | ``` 27 | 28 | ### Debian 9 / Ubuntu 16 29 | 30 | ```bash 31 | apt-get install -y ntp 32 | systemctl enable ntp 33 | systemctl restart ntp 34 | ``` 35 | 36 | ### 或者(时间同步为上海) 37 | 38 | ```bash 39 | rm -rf /etc/localtime 40 | ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 41 | ntpdate time.nist.gov 42 | ``` 43 | 44 | ## 三、面板节点配置 45 | 46 | ### 添加节点 47 | 48 | 在面板后台 > 节点管理 > 添加节点: 49 | 50 | - 节点名称:随便填写 51 | - 权限组:随便填写 52 | - 节点地址:填 v2borad 的域名或 ip 53 | - TLS:填 v2borad 的域名或不填 54 | - 传输协议:选择 websocket 55 | 56 | ### 配置协议 57 | 58 | ```json 59 | { 60 | "lsy": "/" 61 | } 62 | ``` 63 | 64 | ## 四、配置 soga 65 | 66 | ### 查看配置 67 | 68 | ```bash 69 | soga config 70 | ``` 71 | 72 | ### 自动配置 73 | 74 | 可以一行填写任意数量的配置信息,示例: 75 | 76 | ```bash 77 | soga config type=v2board server_type=v2ray 78 | ``` 79 | 80 | ### 编辑配置文件 81 | 82 | 配置文件位置:`/etc/soga/soga.conf` 83 | 84 | 基础配置示例: 85 | 86 | ```ini 87 | type=v2board ## 对接的面板类型,可选SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks 88 | server_type=v2ray ## 对接的节点类型,可选V2ray, Shadowsocks, Trojan 89 | api=webapi ## 对接的方式,可选webapi 或 db,表示 webapi 对接或数据库对接 90 | node_id=1 ## 前端节点id 91 | soga_key= ## 授权密钥,社区版无需填写,最多支持88用户,商业版无限制 92 | 93 | ##webapi 对接 94 | webapi_url=https://*******.com/ ## 面板域名地址,或自定义个专用后端对接不提供访问的域名 95 | webapi_mukey=********************* ## 面板设置的通讯密钥 96 | 97 | ##数据库对接 98 | db_host=db.xxx.com ## 数据库地址 99 | db_port=3306 ## 数据库端口 100 | db_name=name ## 数据库名 101 | db_user=root ## 数据库用户名 102 | db_password=asdasdasd ## 数据库密码 103 | 104 | user_conn_limit=0 ## 限制用户连接数,0代表无限制,v2board 必填!!! 105 | user_speed_limit=0 ## 用户限速,0代表无限制,单位 Mbps,v2board 必填!!! 106 | check_interval=100 ## 同步前端用户、上报服务器信息等间隔时间(秒),近似值 107 | force_close_ssl=false ## 设为true可强制关闭tls,即使前端开启tls,soga也不会开启tls,方便用户自行使用nginx、caddy等反代 108 | forbidden_bit_torrent=true ## 设为true可禁用bt下载 109 | default_dns=8.8.8.8,1.1.1.1 ## 配置默认dns,可在此配置流媒体解锁的dns,以逗号分隔 110 | ``` 111 | 112 | ## 五、启动 soga 113 | 114 | ```bash 115 | soga start 116 | ``` 117 | 118 | 或者 119 | 120 | ```bash 121 | soga 122 | ``` 123 | -------------------------------------------------------------------------------- /src/content/docker/网盘直链程序—AList.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "网盘直链程序—AList" 3 | date: 2023-05-26T20:21:00+00:00 4 | tags: ["Docker-compose", "WebDAV"] 5 | --- 6 | 7 | ## 1. 项目展示 8 | 9 | - **GitHub 项目地址**:[Alist on GitHub](https://github.com/Xhofe/alist) 10 | - **Demo 演示站点**:[访问 Demo](https://alist.nn.ci) 11 | - **Alist 文档地址**:[阅读文档](https://alist-doc.nn.ci/en/) 12 | 13 | ## 2. 搭建 Docker 14 | 15 | - [Docker 官方部署教程](https://docs.docker.com/engine/install/debian/) 16 | 17 | ## 3. 搭建 Alist 18 | 19 | 运行以下 Docker Compose 文件进行 Alist 的安装: 20 | 21 | ```yaml 22 | version: "3.8" 23 | services: 24 | alist: 25 | image: xhofe/alist:latest 26 | container_name: alist 27 | restart: always 28 | volumes: 29 | - ./:/opt/alist/data 30 | ports: 31 | - "7777:5244" 32 | ``` 33 | 34 | - **查看初始化密码**:运行`docker logs alist`命令,可以查看 Alist 的初始密码。 35 | - **更改密码建议**:建议更改一个自己能够记住的密码。 36 | 37 | ## 4. 配置反向代理 38 | 39 | 配置 Nginx 反向代理,以便安全访问 Alist 站点: 40 | 41 | ```nginx 42 | server { 43 | listen 80; 44 | listen [::]:80; 45 | listen 443 ssl http2; 46 | listen [::]:443 ssl http2; 47 | 48 | server_name o.lsy22.com; # 替换为您的域名 49 | 50 | ssl_certificate /root/.acme.sh/o.lsy22.com/fullchain.cer; # SSL证书路径 51 | ssl_certificate_key /root/.acme.sh/o.lsy22.com/o.lsy22.com.key; # SSL密钥路径 52 | 53 | location / { 54 | proxy_pass http://127.0.0.1:7777/; 55 | rewrite ^/(.*)$ /$1 break; 56 | proxy_redirect off; 57 | proxy_set_header Host $host; 58 | proxy_set_header X-Forwarded-Proto $scheme; 59 | proxy_set_header X-Real-IP $remote_addr; 60 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 61 | proxy_set_header Upgrade-Insecure-Requests 1; 62 | proxy_set_header X-Forwarded-Proto https; 63 | } 64 | } 65 | ``` 66 | 67 | ## 5. 账号管理 68 | 69 | - **随机生成密码**:运行`docker exec -it alist ./alist admin random`。 70 | - **手动设置密码**:运行`docker exec -it alist ./alist admin set NEW_PASSWORD`,其中`NEW_PASSWORD`替换为您想要的密码。 71 | 72 | ## 6. 挂载配置 73 | 74 | - **挂载路径**:`/` 75 | - **根目录路径**:`/opt/alist/data/`对应 VPS 上的`/www/wwwroot/alist`目录。 76 | 77 | 如果需要进一步的目录细分,可以设置路径为`/opt/alist/data/Userdata/`,在`/www/wwwroot/alist`下创建`Userdata`文件夹,并存放文件。 78 | 79 | - **其他网盘添加方式**:请参考[Alist 文档](https://alist-doc.nn.ci/en/) 80 | 81 | ## 7. 更新 Alist 82 | 83 | 若需更新 Alist,请按以下步骤操作: 84 | 85 | 1. **停止容器**:运行`docker stop alist` 86 | 2. **删除容器**:运行`docker rm -f alist`(此操作不会删除数据) 87 | 3. **备份数据**(可选):运行`cp -r /root/data/docker_data/alist /root/data/docker_data/alist.bak` 88 | 4. **拉取最新镜像**:运行`docker pull xhofe/alist:latest` 89 | 5. **重新运行安装**:运行`docker run -d --restart=always -v /www/wwwroot/alist:/opt/alist/data -p 7777:5244 --name="alist" xhofe/alist:latest` 90 | -------------------------------------------------------------------------------- /src/content/other/markdown使用教程.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "markdown使用教程" 3 | date: 2023-03-03 4 | tags: [] 5 | --- 6 | 7 | ## 1. Markdown 语法部分 8 | 9 | ### 1.1 标题语法 10 | 11 | ```markdown 12 | # 一级标题 13 | ## 二级标题 14 | ### 三级标题 15 | #### 四级标题 16 | ##### 五级标题 17 | ###### 六级标题 18 | ``` 19 | 20 | ### 1.2 文本格式化 21 | 22 | #### 1.2.1 粗体文本 23 | 24 | ```markdown 25 | **这是粗体文本** 26 | ``` 27 | 28 | **这是粗体文本** 29 | 30 | #### 1.2.2 斜体文本 31 | 32 | ```markdown 33 | *这是斜体文本* 34 | ``` 35 | 36 | *这是斜体文本* 37 | 38 | #### 1.2.3 粗斜体文本 39 | 40 | ```markdown 41 | ***这是粗斜体文本*** 42 | ``` 43 | 44 | ***这是粗斜体文本*** 45 | 46 | #### 1.2.4 删除线文本 47 | 48 | ```markdown 49 | ~~这是删除线文本~~ 50 | ``` 51 | 52 | ~~这是删除线文本~~ 53 | 54 | ### 1.3 列表 55 | 56 | #### 1.3.1 无序列表 57 | 58 | ```markdown 59 | - 第一项 60 | - 子项 1 61 | - 子项 2 62 | - 第二项 63 | - 第三项 64 | ``` 65 | 66 | - 第一项 67 | - 子项 1 68 | - 子项 2 69 | - 第二项 70 | - 第三项 71 | 72 | #### 1.3.2 有序列表 73 | 74 | ```markdown 75 | 1. 第一步 76 | 1. 子步骤 1 77 | 2. 子步骤 2 78 | 2. 第二步 79 | 3. 第三步 80 | ``` 81 | 82 | 1. 第一步 83 | 1. 子步骤 1 84 | 2. 子步骤 2 85 | 2. 第二步 86 | 3. 第三步 87 | 88 | #### 1.3.3 任务列表 89 | 90 | ```markdown 91 | - [x] 已完成任务 92 | - [ ] 未完成任务 93 | - [x] 又一个已完成任务 94 | ``` 95 | 96 | - [x] 已完成任务 97 | - [ ] 未完成任务 98 | - [x] 又一个已完成任务 99 | 100 | ### 1.4 代码 101 | 102 | #### 1.4.1 行内代码 103 | 104 | ```markdown 105 | 这是一段包含`const greeting = "Hello World";`的行内代码 106 | ``` 107 | 108 | 这是一段包含`const greeting = "Hello World";`的行内代码 109 | 110 | #### 1.4.2 代码块 111 | 112 | ````markdown 113 | ```typescript 114 | interface User { 115 | id: number; 116 | name: string; 117 | email: string; 118 | } 119 | 120 | function greet(user: User): string { 121 | return `Hello, ${user.name}!`; 122 | } 123 | ``` 124 | ```` 125 | 126 | 127 | 128 | ```typescript 129 | interface User { 130 | id: number; 131 | name: string; 132 | email: string; 133 | } 134 | 135 | function greet(user: User): string { 136 | return `Hello, ${user.name}!`; 137 | } 138 | ``` 139 | 140 | ### 1.5 表格 141 | 142 | ```markdown 143 | | 功能 | 基础版 | 高级版 | 144 | |:-----|:------:|-------:| 145 | | 文本编辑 | ✓ | ✓ | 146 | | 实时预览 | ✗ | ✓ | 147 | | 导出格式 | 2种 | 5种 | 148 | ``` 149 | 150 | | 功能 | 基础版 | 高级版 | 151 | |:-----|:------:|-------:| 152 | | 文本编辑 | ✓ | ✓ | 153 | | 实时预览 | ✗ | ✓ | 154 | | 导出格式 | 2种 | 5种 | 155 | 156 | ### 1.6 引用 157 | 158 | ```markdown 159 | > 📌 **最佳实践** 160 | > 161 | > 好的文章需要有清晰的结构和流畅的表达。 162 | > 163 | > 可以包含多个段落 164 | ``` 165 | 166 | > 📌 **最佳实践** 167 | > 168 | > 好的文章需要有清晰的结构和流畅的表达。 169 | > 170 | > 可以包含多个段落 171 | 172 | ### 1.7 链接和图片 173 | 174 | #### 1.7.1 链接 175 | 176 | ```markdown 177 | [MDX 官方文档](https://mdxjs.com) 178 | [相对路径链接](../path/to/file.md) 179 | ``` 180 | 181 | [MDX 官方文档](https://mdxjs.com) 182 | [相对路径链接](../path/to/file.md) 183 | 184 | #### 1.7.2 图片 185 | 186 | ```markdown 187 |  188 | ``` 189 | 190 | ### 1.8 水平分割线 191 | 192 | ```markdown 193 | --- 194 | ``` 195 | 196 | 197 | 198 | --- 199 | -------------------------------------------------------------------------------- /src/content/windows/Windows虚拟化和子系统安装指南.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Windows虚拟化和子系统安装指南" 3 | date: 2023-12-11T17:08:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 准备工作 8 | 9 | ### 一、开启 CPU 虚拟化 10 | 11 | 1. 重启电脑并进入 **BIOS** 设置。 12 | 2. 在 BIOS 设置中找到带有 `virtualization` 字样的选项,启用所有相关的虚拟化功能。 13 | 14 | ### 二、安装 Hyper-V 服务 15 | 16 | 1. 打开控制面板:开始 → 运行 → 输入 `control.exe`。 17 | 2. 选择 `程序和功能`。 18 | 3. 点击左侧的 `启用或关闭Windows功能`。 19 | 4. 勾选 `Hyper-V` 和 `虚拟机平台` 选项,点击确定完成安装。 20 | 5. 重启电脑以应用更改。 21 | 22 | ## Windows Subsystem for Android (WSA) 23 | 24 | ### 一、下载离线安装包 25 | 26 | 1. 访问微软离线应用提取网站:[store.rg-adguard.net](https://store.rg-adguard.net) 27 | 2. 复制 WSA 在微软应用商店的地址并粘贴至 URL 输入框中,选择 Slow,点击对勾开始搜索。 28 | 3. 选择适合您系统架构的最新版本的 WSA 安装包(通常为 `.Msixbundle` 后缀),并下载。 29 | 30 | > 注意:忽略带有 BlockMap 后缀的文件。 31 | 32 | 4. 也可访问 [MustardChef 的 GitHub 页面](https://github.com/MustardChef/WSABuilds) 下载经过修改的 WSA 安装包。 33 | 34 | ### 二、安装 WSA 环境 35 | 36 | 1. 以管理员身份打开 PowerShell("开始"菜单 >"PowerShell" >右键 >"以管理员身份运行")。 37 | 2. 使用以下命令安装下载的 WSA 包: 38 | 39 | ```bash 40 | Add-AppxPackage "路径\下载的wsa.Msixbundle" 41 | ``` 42 | 43 | ## Windows Subsystem for Linux (WSL) 44 | 45 | ### 一、启用 WSL 46 | 47 | 1. 以管理员身份打开 PowerShell 并运行: 48 | 49 | ```bash 50 | dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 51 | ``` 52 | 53 | ### 二、检查 WSL2 的要求 54 | 55 | 按 Win+R 打开运行,输入 `winver` 检查 Windows 版本要求: 56 | 57 | - 对于 x64 系统:版本 1903 或更高,内部版本 18362.1049 或更高。 58 | - 对于 ARM64 系统:版本 2004 或更高,内部版本 19041 或更高。 59 | 60 | ### 三、启用虚拟机功能 61 | 62 | 以管理员身份打开 PowerShell 并运行: 63 | 64 | ```bash 65 | dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 66 | ``` 67 | 68 | ### 四、下载 Linux 内核更新包 69 | 70 | 下载适用于您的计算机架构的 WSL2 Linux 内核更新包:[下载链接](https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi) 71 | 72 | ### 五、设置 WSL 默认版本 73 | 74 | 打开 PowerShell 并运行以下命令,将 WSL 2 设置为默认版本: 75 | 76 | ```bash 77 | wsl --set-default-version 2 78 | ``` 79 | 80 | ### 六、安装 Linux 分发版 81 | 82 | 1. 访问 Microsoft Store,下载想要安装的 Linux 分发版" 83 | 2. 切换到 root 用户登录(如需): 84 | 85 | 在 PowerShell 中运行 86 | 87 | 1. 获取 linux 名称 88 | 89 | ```bash 90 | wsl --list 91 | ``` 92 | 93 | 2. 切换 root 用户 94 | 95 | ```bash 96 | 指定的Linux分发版 config --default-user root 97 | ``` 98 | 99 | ## 常见问题处理 100 | 101 | - **关闭 WSL 自动挂载 Windows 分区**: 102 | 编辑 WSL 配置文件 `/etc/wsl.conf` 并添加内容以禁用自动挂载和 Windows 路径的添加。 103 | 104 | ```bash 105 | # 不加载Windows中的PATH内容 106 | [interop] 107 | appendWindowsPath = false 108 | 109 | # 不自动挂载Windows系统所有磁盘分区 110 | [automount] 111 | enabled = false 112 | ``` 113 | 114 | - **解决无法定位 package screen 的问题**: 115 | 在 Linux 分发版中运行 `apt-get update` 来更新软件包列表。 116 | - **WSL 卸载**: 117 | 查看已安装的 WSL 环境并卸载指定的 Linux 分发版。 118 | 119 | ```bash 120 | wsl --unregister 指定的Linux分发版 121 | ``` 122 | 123 | - **解决 WSLRegisterDistribution 错误**: 124 | 在 PowerShell 中运行 125 | 126 | ```bash 127 | Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux 128 | ``` 129 | -------------------------------------------------------------------------------- /src/styles/swup-transitions.css: -------------------------------------------------------------------------------- 1 | /* Swup页面过渡动画样式 */ 2 | 3 | /* 基础过渡效果 */ 4 | .transition-fade { 5 | transition: opacity 0.3s ease; 6 | animation-duration: 0.3s; 7 | opacity: 1; 8 | } 9 | 10 | /* 文章内容过渡效果 */ 11 | .swup-transition-article { 12 | transition: opacity 0.3s ease; 13 | animation-duration: 0.3s; 14 | opacity: 1; 15 | } 16 | 17 | /* 直接为article-content元素设置动画 */ 18 | #article-content { 19 | transition: opacity 0.3s ease; 20 | animation-duration: 0.3s; 21 | opacity: 1; 22 | } 23 | 24 | /* 淡出状态 */ 25 | html.is-animating .transition-fade, 26 | html.is-animating .swup-transition-article, 27 | html.is-animating #article-content { 28 | opacity: 0; 29 | } 30 | 31 | /* 淡入状态 */ 32 | html.is-changing .transition-fade, 33 | html.is-changing .swup-transition-article, 34 | html.is-changing #article-content { 35 | opacity: 1; 36 | } 37 | 38 | /* 加载旋转动画 - 无遮罩版本 */ 39 | .loading-spinner-container { 40 | position: absolute; 41 | top: 50%; 42 | left: 50%; 43 | transform: translate(-50%, -50%); 44 | z-index: 9999; 45 | opacity: 0; 46 | visibility: hidden; 47 | transition: opacity 0.3s ease, visibility 0.3s ease; 48 | display: flex; 49 | align-items: center; 50 | justify-content: center; 51 | pointer-events: none; /* 允许点击穿透 */ 52 | } 53 | 54 | /* 激活状态 */ 55 | .loading-spinner-container.is-active { 56 | opacity: 1; 57 | visibility: visible; 58 | } 59 | 60 | /* 旋转动画元素 */ 61 | .loading-spinner { 62 | width: 50px; 63 | height: 50px; 64 | border: 3px solid transparent; 65 | border-top-color: var(--color-primary-500); 66 | border-radius: 50%; 67 | animation: spin 1s linear infinite; 68 | position: relative; 69 | /* 添加背景色以增强可见性 */ 70 | background-color: rgba(255, 255, 255, 0.8); 71 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); 72 | padding: 15px; 73 | } 74 | 75 | /* 深色模式下旋转动画颜色 */ 76 | [data-theme='dark'] .loading-spinner { 77 | border-top-color: var(--color-primary-400); 78 | background-color: rgba(15, 23, 42, 0.8); 79 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.3); 80 | } 81 | 82 | .loading-spinner:before, 83 | .loading-spinner:after { 84 | content: ''; 85 | position: absolute; 86 | border: 3px solid transparent; 87 | border-radius: 50%; 88 | } 89 | 90 | .loading-spinner:before { 91 | top: -3px; 92 | left: -3px; 93 | right: -3px; 94 | bottom: -3px; 95 | border-top-color: var(--color-primary-300); 96 | animation: spin 1.5s linear infinite; 97 | } 98 | 99 | .loading-spinner:after { 100 | top: 6px; 101 | left: 6px; 102 | right: 6px; 103 | bottom: 6px; 104 | border-top-color: var(--color-primary-600); 105 | animation: spin 0.75s linear infinite; 106 | } 107 | 108 | /* 深色模式下的内外圈颜色 */ 109 | [data-theme='dark'] .loading-spinner:before { 110 | border-top-color: var(--color-primary-200); 111 | } 112 | 113 | [data-theme='dark'] .loading-spinner:after { 114 | border-top-color: var(--color-primary-500); 115 | } 116 | 117 | /* 旋转动画 */ 118 | @keyframes spin { 119 | 0% { 120 | transform: rotate(0deg); 121 | } 122 | 100% { 123 | transform: rotate(360deg); 124 | } 125 | } -------------------------------------------------------------------------------- /src/content/docker/Docker部署gitea.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Docker部署gitea" 3 | date: 2023-05-26T20:21:00+00:00 4 | tags: ["Docker-compose"] 5 | --- 6 | 7 | ## 直通配置 8 | 9 | ### 1. 创建一个名为 `git` 的用户 10 | 11 | ```bash 12 | sudo useradd -m git 13 | ``` 14 | 15 | ### 2. 配置`git`用户登录密钥 16 | 17 | ```bash 18 | su git -c 'ssh-keygen -t rsa -b 4096 -C "Gitea Host Key"' 19 | su git -c 'cat /home/git/.ssh/id_rsa.pub >> /home/git/.ssh/authorized_keys' 20 | ``` 21 | 22 | ### 3. 查看 uid 和 gid 23 | 24 | ```bash 25 | id git 26 | ``` 27 | 28 | 记住 uid 和 gid 29 | 30 | ### 4. SSH 容器直通 31 | 32 | 在主机上创建一个名为 `/usr/local/bin/gitea` 的**文件**,该文件将发出从主机到容器的 `SSH` 转发 33 | 34 | ```bash 35 | ssh -p 7222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@" 36 | ``` 37 | 38 | ## Docker 安装 39 | 40 | ### 1. 创建数据持久化的存储路径 41 | 42 | ### 2. 进入该文件夹 43 | 44 | ### 3. 创建`docker-compose.yml` 文件并配置 45 | 46 | > 将下面的 USER_UID=1000 USER_GID=1000 换为得到 uid 和 gid 47 | 48 | ```yaml 49 | version: "3" 50 | networks: 51 | gitea: 52 | external: false 53 | services: 54 | server: 55 | image: gitea/gitea:latest 56 | container_name: gitea 57 | environment: 58 | - USER_UID=1000 59 | - USER_GID=1000 60 | - GITEA__database__DB_TYPE=mysql 61 | - GITEA__database__HOST=db:3306 62 | - GITEA__database__NAME=gitea 63 | - GITEA__database__USER=gitea 64 | - GITEA__database__PASSWD=gitea 65 | restart: always 66 | networks: 67 | - gitea 68 | volumes: 69 | - ./data:/data 70 | - /etc/timezone:/etc/timezone:ro 71 | - /etc/localtime:/etc/localtime:ro 72 | - /home/git/.ssh/:/data/git/.ssh 73 | ports: 74 | - "7200:3000" 75 | - "7222:22" 76 | depends_on: 77 | - db 78 | db: 79 | image: mysql:8 80 | restart: always 81 | environment: 82 | - MYSQL_ROOT_PASSWORD=gitea 83 | - MYSQL_USER=gitea 84 | - MYSQL_PASSWORD=gitea 85 | - MYSQL_DATABASE=gitea 86 | networks: 87 | - gitea 88 | volumes: 89 | - ./mysql:/var/lib/mysql 90 | ``` 91 | 92 | ### 4. 启动 Docker 容器 93 | 94 | ```bash 95 | sudo docker compose up -d 96 | ``` 97 | 98 | ## 反向代理 99 | 100 | ```nginx 101 | server { 102 | listen 80; 103 | listen [::]:80; 104 | server_name g.lsy22.com; # 替换为您的域名 105 | return 301 https://$host$request_uri; # 将所有HTTP请求重定向到HTTPS 106 | } 107 | server { 108 | listen 443 ssl http2; 109 | listen [::]:443 ssl http2; 110 | server_name g.lsy22.com; # 替换为您的域名 111 | ssl_certificate /root/.acme.sh/g.lsy22.com/fullchain.cer; # SSL证书路径 112 | ssl_certificate_key /root/.acme.sh/g.lsy22.com/g.lsy22.com.key; # SSL密钥路径 113 | location / { 114 | proxy_pass http://localhost:7200; 115 | proxy_set_header Host $host; 116 | proxy_set_header X-Real-IP $remote_addr; 117 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 118 | proxy_set_header X-Forwarded-Proto $scheme; 119 | proxy_set_header X-Forwarded-Host $host; 120 | proxy_set_header X-Forwarded-Port $server_port; 121 | } 122 | } 123 | ``` 124 | -------------------------------------------------------------------------------- /src/content/游行笔记/第一次出国旅行-东南亚.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "第一次出国旅行-东南亚" 3 | date: 2025-04-18T22:01:57+08:00 4 | tags: [] 5 | --- 6 | 7 | > 大多数和别人的对话都是使用谷歌翻译的同声翻译 8 | 9 | ## 睁眼说瞎话 10 | 11 | 值机的时候碰到本次旅行第一个交流的外国人,一个会讲中文的马来西亚男人,感觉马来西亚男人说话像乱序中文但是能听懂,马来西亚男人知道了我没有马来西亚货币,提出换一点林吉特给我,马来西亚男人的老婆说人民币拿去没用,我都不抱希望了,但是马来西亚夫妻还是兑换了 100 人民币给我。 12 | 13 | 还没出国门就遇到第一个问题,海关闸机刷了不开门,海关警察来了对我进行盘问,海关的警察问我很多问题,其中就有我父母是否同意,现在遇到突发问题,也是可以面不改色的说假话了,不过好在中午的时候打电话询问了一下我爹的意见,虽然我爹不同意,但是好在留下了通话记录,我将过去的旅游照片给海关看,与海关警察周旋了 10 多分钟,幸好过海关的时候快到晚上 12 点了,不好向我父母核实,差点这次旅行计划早夭了。 14 | 15 | 第一次做廉航,亚航位置空隙比国内任何大巴空隙都要小,最难受的交通出行方式。 16 | 17 | 飞行途中透过对侧窗户看到了漂亮的满月,在我这一侧看到了美丽的星空和晨昏线 18 | 19 | ## 可恶的公交车司机 20 | 21 | 马来西亚第一站计划去粉红清真寺,从吉隆坡机场 1 楼乘坐巴士直接过去,但是购票的时候,公交车售票厅工作人员说没有到粉红清真寺的公交车只能打出租车前往,看了一下打车的价格,决定重新做攻略再挣扎一下,在休息长椅坐了半个小时终于找到新路线`地铁站->布城->公交站->粉红清真寺`,往机场 3 楼地铁站走的时候发现斜挎包不见了,听说国外酒店工作人员会翻包偷钱,就买了个斜挎包放护照,现金等重要的东西,惊慌了一会,还好头脑风暴了一会想起来了在做攻略的长椅忘拿了。 22 | 23 | 布城的公交车站是始发站,在最后一个站台才找到`T523`,我想上车但是司机朝我摆手,看来我来的时间不对,我就在车子旁边的亭子研究如何打车,研究了一会司机叫了我一声,给我一个招揽的手势,上去了我给司机看我的谷歌地图,用翻译软件软件问司机可以去这里吗,司机用本地话说一大堆,我将谷歌同声翻译打开司机,告诉司机对着这个说我就可以听懂了,但是给司机一个字不说,拿开了手机司机又开始说本地话,僵持了一会司机不耐烦的打手势让我去旁边公交车,我以为上错车了,我将地图给旁边公交车司机看,说要去地图的地方,旁边公交车的司机指着`T523`告诉我那辆车可以去,回到 T523 后告诉司机就是这个车,车开了一会司机说 three ,我本以为司机完全不会英语呢,途中看到一个清真寺,打开谷歌地图显示现在要去的最后一个站,我指着清真寺问司机"is there?",司机说"yes,down",看着绿色的清真寺我觉得现在照骗太多了,看着别人拿着证件或者是手机给安保人员看了才能进去,我想网上不要预约和门票的说法看来是过时了,不过来都来了我要去试试,到了门口工作人员拦下我,我告诉工作人员我没有预约但是我想进去参观,工作人员反复询问我确定要进去吗,我告诉工作人员我专程过来参观这个清真寺,工作人员的话翻译过来是“这是政府办公的地方不允许外人靠近,但是你是第一次”,我打开地图重新导航显示距离粉红清真寺还有 1.2km。 24 | 25 | 粉红清真寺的穹顶真的是粉红色的!穹顶里面看更漂亮,由红色,浅粉色,白色构成的图案 26 | 27 | 在粉红清真寺里面有两幅捐款地图,捐款一次可以用针扎自己的家乡,一副世界地图一副中国地图,世界地图上的中国和中国地图都是密密麻麻的针 28 | 29 | 国外的公交车不适合 i 人,我打算从布城从地铁到市中心,需要先坐公交车到布城去,差 10 秒就赶上了,但是站台没人所以公交车司机没有停,第二次在休息区等了半个小时司机又没停,可能是司机没看到我吧,第三次我站在公交车站台等车的位置等待可是他还是没停,这次可能是没有给司机信号,第四次等公交车快到的时候我死死的看着司机,与他建立心灵链接,但他还是不停,浏览器查询原来要招手,用打车软件看了一下两公里,还是选择打车了 30 | 31 | 在去酒店的路上看到了很多流浪汉,不过感觉他们的穿搭和我没有区别,一个包+拖鞋,历经大雨来到谷歌地图显示的位置,却找不到酒店,找了一个印度男人问路,他虽然不知道,但是好心的帮我找了很久,最后他给酒店客服打电话后,告诉我不在这个区域,给我指路,往哪走,再往哪走,到一个塔下就快到了,再问问别人,我一点没记住好在用高德地图重新导航,竟然没问题。 32 | 33 | 晚餐在酒店旁找了家本地人多的店,点了一个大虾饭,没想到是正宗印度菜, 34 | 35 | ```text 36 | 米饭味道=八角*0.7+洗衣粉*0.2+辣椒*0.1 37 | ``` 38 | 39 | ## 不带脑子旅游真好 40 | 41 | 凌晨 3 点被炸街吵醒,没想到精神小伙也是全世界统一 42 | 43 | 在飞往印度尼西亚待机的时候,看到两群中国人遇到了同胞很开心,我告诉他们我也是,王杰说行程规划的时候,我发现我们的行程规划一模一样!!! 44 | 45 | 到了酒店放好行李,王杰就找好科摩多岛的旅行团,他和他的朋友打算去看日落,我不会开摩托,王杰帮我找了一个摩托车司机,什么都被安排好了,不带脑子旅游真好,摩托车司机带我们去了一个只有本地人并且视野极佳的位置 46 | 47 | 找晚餐的时候遇到一个卖榴莲的,价格 14w 印尼盾,合人民币 70 块钱,我感觉价格还不错,王杰他们让我别买,可能他们不太喜欢榴莲吧,老板一直跟着想要卖给我,以前不好意思所有没有过砍价,不过现在人生地不熟,我用计算器给老板按 7w,一直摇头 no,坚持 7w,没想到人生第一次砍价成功了 48 | 49 | 印度尼西亚的海鲜真便宜,螃蟹/鱼 100 人民币左右一只,大龙虾 150 人民币左右一只,大虾 50 人民币左右一盘;空心菜味道太棒了,吃过最棒的一次炒蔬菜; 50 | 51 | ## 来都来了 52 | 53 | 坐在快艇上,路过一座座小岛;在二层甲板感受到呼啸的海风;看着蓝宝石般的海水;粉红色的沙滩;1m8 的蜥蜴;我在南半球和北半球正在上早八的同学打视频电话很奇妙的感觉; 54 | 55 | 我是一个在内陆从来没下过水的人,没注意这次的团里面有体验浮潜的项目,也没准备游泳的衣服,没想要浮潜的时候,需要在轮船动着的时候跳下去,类似于跳车,不过来都来了,钱不能白交,抱着必死的心跳下去了,好消息救生衣让我浮起了,坏消息脚往上浮头往下沉,人一直在快要翻到躺在的状态。在我多次深呼吸之后,保证那种站着半趴着的状态。我突然发现海水太冷了,应该只有几度,还好船长带着救生圈来找我们了,旁边的人问我话,我打颤的没法回答他; 56 | 57 | 我们赶上最后一班飞机到巴厘岛,我们赶到库塔的时候,王杰和他的朋友打算休息一天,但是我还精力满满,只能在此和他们说再见; 58 | 59 | ## 好心的土耳其人 60 | 61 | 这次去蓝梦岛报团没有遇到中国人,我打算随机找一个看着识别特征高的外国人跟在他后面,还好有一对土耳其父女发现了我,她知道不会英语之后,告诉我她是老大让我跟着他,每次去哪她就看着我说"Chinese go go go" 62 | 63 | ## 未完待续 64 | -------------------------------------------------------------------------------- /src/plugins/robots-integration.js: -------------------------------------------------------------------------------- 1 | // robots.txt 集成插件 2 | // 用于在构建时自动生成 robots.txt 文件 3 | 4 | import fs from 'node:fs/promises'; 5 | import path from 'node:path'; 6 | import { existsSync, readFileSync } from 'node:fs'; 7 | import { SITE_URL } from '../consts'; 8 | 9 | // 生成 robots.txt 内容 10 | function generateRobotsTxt(siteUrl) { 11 | return `# robots.txt 文件 12 | # 网站: ${siteUrl} 13 | # 生成时间: ${new Date().toISOString()} 14 | 15 | User-agent: * 16 | Allow: / 17 | 18 | # 站点地图 19 | Sitemap: ${siteUrl}/sitemap.xml 20 | `; 21 | } 22 | 23 | // 主集成函数 24 | export function robotsIntegration() { 25 | return { 26 | name: 'robots-integration', 27 | hooks: { 28 | // 开发服务器钩子 - 为开发模式添加虚拟API路由 29 | 'astro:server:setup': ({ server }) => { 30 | // 为 robots.txt 提供虚拟路由 31 | server.middlewares.use((req, res, next) => { 32 | // 检查请求路径是否是 robots.txt 33 | if (req.url === '/robots.txt' && req.method === 'GET') { 34 | console.log(`虚拟路由请求: ${req.url}`); 35 | 36 | // 尝试返回已构建好的 robots.txt 文件 37 | const distPath = path.join(process.cwd(), 'dist/client/robots.txt'); 38 | 39 | if (existsSync(distPath)) { 40 | try { 41 | const content = readFileSync(distPath, 'utf-8'); 42 | res.setHeader('Content-Type', 'text/plain; charset=UTF-8'); 43 | res.end(content); 44 | } catch (error) { 45 | res.statusCode = 500; 46 | res.end('读取 robots.txt 文件时出错'); 47 | } 48 | } else { 49 | // 如果文件不存在,则动态生成 50 | const content = generateRobotsTxt(SITE_URL); 51 | res.setHeader('Content-Type', 'text/plain; charset=UTF-8'); 52 | res.end(content); 53 | } 54 | return; 55 | } 56 | 57 | // 不是 robots.txt 请求,继续下一个中间件 58 | next(); 59 | }); 60 | }, 61 | 62 | // 构建完成钩子 - 生成 robots.txt 文件 63 | 'astro:build:done': async ({ dir }) => { 64 | console.log('生成 robots.txt...'); 65 | 66 | try { 67 | // 获取构建目录路径 68 | let buildDirPath; 69 | 70 | // 直接处理URL对象 71 | if (dir instanceof URL) { 72 | buildDirPath = dir.pathname; 73 | // Windows路径修复 74 | if (process.platform === 'win32' && buildDirPath.startsWith('/') && /^\/[A-Z]:/i.test(buildDirPath)) { 75 | buildDirPath = buildDirPath.substring(1); 76 | } 77 | } else { 78 | buildDirPath = String(dir); 79 | } 80 | 81 | // 生成 robots.txt 内容 82 | const content = generateRobotsTxt(SITE_URL); 83 | 84 | // 写入 robots.txt (使用 UTF-8 编码) 85 | const filePath = path.join(buildDirPath, 'robots.txt'); 86 | 87 | // 添加 UTF-8 BOM 标记以确保浏览器正确识别编码 88 | const BOM = '\uFEFF'; 89 | await fs.writeFile(filePath, BOM + content, 'utf8'); 90 | console.log('已生成 robots.txt (UTF-8 with BOM)'); 91 | 92 | } catch (error) { 93 | console.error('生成 robots.txt 时出错:', error); 94 | } 95 | } 96 | } 97 | }; 98 | } -------------------------------------------------------------------------------- /src/content/windows/vscode配置.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "vscode配置" 3 | date: 2025-04-19T11:10:57+08:00 4 | tags: [] 5 | --- 6 | 7 | 8 | 9 | ## 自动补全,语法检查 10 | 11 | | 语言 | 插件 | 12 | | -------- | ---------------------------------------------------------------------------------------------------------- | 13 | | rust | [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) | 14 | | tailwind | [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) | 15 | | markdown | [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint) | 16 | | toml | [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml) | 17 | 18 | ## 格式化 19 | 20 | ### 格式化插件 21 | 22 | | 插件 | 格式化言语 | 23 | | ------------------------------------------------------------------------------------------------ | ------------------- | 24 | | [prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) | js,md,css,html,yaml | 25 | | [Black Formatter](https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter) | python | 26 | | [shell-format](https://marketplace.visualstudio.com/items?itemName=foxundermoon.shell-format) | shell | 27 | 28 | ### 保存自动格式化 29 | 30 | `settings.json`增加以下代码 31 | 32 | ```txt 33 | "editor.formatOnSave": true // 保存时自动规范代码 34 | ``` 35 | 36 | ## 其他 37 | 38 | | 名称 | 作用 | 39 | | ----------------------------------------------------------------------------------------------------------------------------- | ---------------------- | 40 | | [Atom Material Icons](https://marketplace.visualstudio.com/items?itemName=AtomMaterial.a-file-icon-vscode) | 美化图标 | 41 | | [Atom One Dark Theme](https://marketplace.visualstudio.com/items?itemName=akamud.vscode-theme-onedark) | 美化主题 | 42 | | [Live Preview](https://marketplace.visualstudio.com/items?itemName=ms-vscode.live-server) | 提供在线预览web项目 | 43 | | [Markdown Preview Github Styling](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-preview-github-styles) | 提供markdown代码块样式 | 44 | | [Chinese (Simplified)](https://marketplace.visualstudio.com/items?itemName=MS-CEINTL.vscode-language-pack-zh-hans) | 简体中文语言包 | 45 | 46 | ## 非html文件中启用tailwind高亮提示 47 | 48 | ### 安装高亮插件 49 | 50 | - [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) 51 | - [PostCSS Language Support](https://marketplace.visualstudio.com/items?itemName=csstools.postcss) 52 | 53 | ### 启动高亮配置 54 | 55 | >以rust为例 56 | 57 | 修改`setting.json`文件 58 | 59 | ```json 60 | // 配置识别的正则表达式 61 | "tailwindCSS.experimental.classRegex": [ 62 | "class:\\s?\"(.*?)\"", 63 | "class:\\s?format!\\((\"(.*?)\")\\)" 64 | ], 65 | // 配置识别的语言 66 | "tailwindCSS.includeLanguages": { 67 | "rust": "html" 68 | }, 69 | ``` 70 | -------------------------------------------------------------------------------- /src/content/echoes博客使用说明.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "echoes博客使用说明" 3 | date: 2025-03-09T01:07:23Z 4 | tags: [] 5 | --- 6 | 7 | ## 快速开始 8 | 9 | ### 环境要求 10 | 11 | - Node.js 18+ 12 | - pnpm 13 | 14 | ### 三步启动 15 | 16 | ```bash 17 | git clone https://github.com/your-username/echoes.git 18 | cd echoes && pnpm i 19 | pnpm run dev # 访问 http://localhost:4321 20 | ``` 21 | 22 | ### 一键部署 23 | 24 | [](https://vercel.com/new/clone?repository-url=https://github.com/lsy2246/newechoes) 25 | 26 | ## 核心功能 27 | 28 | ### 文章系统 29 | 30 | **创建文章(推荐方式):** 31 | 32 | ```bash 33 | chmod +x create_post.sh # 首次使用 34 | ./create_post.sh "文章标题" "目录/文章路径" 35 | ``` 36 | 37 | **手动创建:** 在 `src/content/articles` 目录下创建 `.md` 文件 38 | 39 | ```markdown 40 | --- 41 | title: "文章标题" 42 | date: YYYY-MM-DD 43 | tags: ["标签1", "标签2"] 44 | --- 45 | 46 | 文章内容... 47 | ``` 48 | 49 | ### RSS 订阅 50 | 51 | 自动为所有文章生成 `/rss.xml` 订阅源,无需额外配置。 52 | 53 | ### Mermaid 图表 54 | 55 | 支持在文章中直接使用 Mermaid 图表: 56 | 57 | ````markdown 58 | ```mermaid 59 | graph TD; 60 | A[开始] --> B[处理]; 61 | B --> C{判断}; 62 | ``` 63 | ```` 64 | 65 | ### 页面过渡与性能 66 | 67 | - **View Transitions API** 与 **Swup** 实现流畅的无刷新切换 68 | - **WebAssembly** 优化前端数据处理性能 69 | - **SEO 优化**:自动生成元标签和站点地图 70 | - **响应式设计**:自适应桌面和移动端 71 | 72 | ## 基础配置 73 | 74 | 编辑 `src/consts.ts` 配置网站信息: 75 | 76 | ```typescript 77 | export const SITE_URL = "https://your-domain.com"; 78 | export const SITE_TITLE = "你的网站名称"; 79 | export const SITE_DESCRIPTION = "网站描述"; 80 | 81 | // 导航结构 82 | export const NAV_STRUCTURE = [ 83 | { id: "home", text: "首页", href: "/" }, 84 | { 85 | id: "articles", 86 | text: "文章", 87 | items: [ 88 | { id: "filter", text: "筛选", href: "/filtered" }, 89 | { id: "path", text: "网格", href: "/articles" }, 90 | ], 91 | }, 92 | ]; 93 | ``` 94 | 95 | ## 可选功能模块 96 | 97 | ### 文章过期提醒 98 | 99 | 在 `src/consts.ts` 中配置: 100 | 101 | ```typescript 102 | export const ARTICLE_EXPIRY_CONFIG = { 103 | enabled: true, 104 | expiryDays: 365, 105 | warningMessage: "文章内容可能已过时", 106 | }; 107 | ``` 108 | 109 | ### 项目展示 110 | 111 | 使用 `@/components/GitProjectCollection` 展示 Git 平台项目: 112 | 113 | ```astro 114 | 120 | ``` 121 | 122 | ### 豆瓣数据集成 123 | 124 | 使用 `@/components/DoubanCollection` 展示观影和读书记录: 125 | 126 | ```astro 127 | // 观影记录 128 | 129 | // 读书记录 130 | 131 | ``` 132 | 133 | ### 微信读书书单 134 | 135 | 使用 `@/components/WereadBookList` 展示书单: 136 | 137 | ```astro 138 | 139 | ``` 140 | 141 | 从分享链接获取 ID:`https://weread.qq.com/misc/booklist/12345678` 142 | 143 | ### 旅行足迹 144 | 145 | 使用 `@/components/WorldHeatmap` 展示全球足迹: 146 | 147 | ```astro 148 | 152 | ``` 153 | 154 | ## 故障排除 155 | 156 | | 问题 | 解决方案 | 157 | | ---------------- | -------------------------- | 158 | | 图片显示问题 | 确保图片放在 `public` 目录 | 159 | | 豆瓣数据无法加载 | 检查用户 ID,确认记录公开 | 160 | | Git 项目无法显示 | 验证用户名和 API 访问权限 | 161 | | WebAssembly 报错 | 检查浏览器支持和 CSP 设置 | 162 | -------------------------------------------------------------------------------- /src/content/代理/centos7.x搭建Tor私人网桥.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "centos7.x搭建Tor私人网桥" 3 | date: 2021-07-30T23:11:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 1. 下载并安装 Tor 8 | 9 | ```bash 10 | yum install tor -y 11 | ``` 12 | 13 | ## 2. 安装 obfs4 14 | 15 | ### 通过 python 进行编译安装 16 | 17 | #### 安装所需依赖软件模块 18 | 19 | ```bash 20 | yum install make automake gcc python-pip python-devel libyaml-devel 21 | ``` 22 | 23 | #### 安装 obfs4proxy 24 | 25 | ```bash 26 | pip install obfsproxy 27 | ``` 28 | 29 | ### 通过 go 进行编译安装 30 | 31 | #### 下载 go 的 obfs4 项目 32 | 33 | ```bash 34 | git clone http://www.github.com/Yawning/obfs4 35 | ``` 36 | 37 | #### 进入 obfs4 目录进行编译 38 | 39 | ```bash 40 | go build -o obfs4proxy/obfs4proxy ./obfs4proxy 41 | ``` 42 | 43 | #### 复制 bofs4proxy 到系统工作目录下 44 | 45 | ```bash 46 | cp ./obfs4proxy/obfs4proxy /usr/bin/obfs4proxy 47 | ``` 48 | 49 | ## 3. 配置 Tor Bridges 50 | 51 | ### 编辑配置文件 52 | 53 | ```bash 54 | vim /etc/tor/torrc 55 | ``` 56 | 57 | 定义一个 ORPort,不作为出口节点,设置成 Bridge: 58 | 59 | ```text 60 | Log notice file /var/log/tor/notices.log 61 | RunAsDaemon 1 62 | ORPort 6666 63 | Exitpolicy reject *:* 64 | BridgeRelay 1 65 | ServerTransportPlugin obfs4 exec /usr/bin/obfs4proxy 66 | ExtORPort auto 67 | PublishServerDescriptor 0 68 | ``` 69 | 70 | ### 重启 tor 服务 71 | 72 | ```bash 73 | systemctl restart tor 74 | ``` 75 | 76 | ### 查看 tor 服务状态 77 | 78 | ```bash 79 | systemctl status tor 80 | ``` 81 | 82 | ## 4. 使用网桥 83 | 84 | 查看日志文件: 85 | 86 | ```bash 87 | tail -F /var/log/tor/notices.log 88 | ``` 89 | 90 | 内容如下: 91 | 92 | ```text 93 | [notice] Your Tor server's identity key fingerprint is 'Unnamed 94 | 530FA95A79B9145D315F15F01215BE2F3BE921EB' [notice] Your Tor bridge's 95 | hashed identity key fingerprint is 'Unnamed 96 | 83D1AC9EC2F15D7024278461DC91A8B2E9BBF43A' [notice] Registered server 97 | transport 'obfs4' at '[::]:46396' [notice] Tor has successfully opened 98 | a circuit. Looks like client functionality is working. [notice] 99 | Bootstrapped 100%: Done [notice] Now checking whether ORPort 100 | :6666 is reachable... (this may take up to 20 minutes -- 101 | look for log messages indicating success) [notice] Self-testing 102 | indicates your ORPort is reachable from the outside. Excellent. 103 | ``` 104 | 105 | 注意:记住输出中 obfs4 监听的端口(本例中是 46396)。并且还能找到你的 server identity fingerprint(本例中是 530FA95A79B9145D315F15F01215BE2F3BE921EB),也复制下来。 106 | 107 | 在 `/var/lib/tor/pt_state/obfs4_bridgeline.txt` 文件中可以看到类似如下的内容: 108 | 109 | ```text 110 | Bridge obfs4 : 111 | cert=oG6a3K7CtearIloUp2OCUk60oNMgw+jVgCNhGumMkODS659UEgRRx7yxZuoEo9Crp9GGXg 112 | iat-mode=0 113 | ``` 114 | 115 | 根据日志中的信息获得最终的网桥配置: 116 | 117 | ```text 118 | obfs4 :46396 530FA95A79B9145D315F15F01215BE2F3BE921EB 119 | cert=6LMNcXh6MIfApbZiMksnS4Kj+2sffZ5pybSqtcOO5YoHgfrMpkBJqvLxhuR2Ppau0L2seg 120 | iatmode=0 121 | ``` 122 | 123 | ## 5. 防火墙配置 124 | 125 | 编辑防火墙公共配置文件: 126 | 127 | ```bash 128 | vim /etc/firewalld/zones/public.xml 129 | ``` 130 | 131 | 内容如下(本例 ORPort 端口 => 6666, obfs4 端口 => 46396): 132 | 133 | ```xml 134 | 135 | 136 | 137 | 138 | ``` 139 | 140 | 使防火墙配置生效: 141 | 142 | ```bash 143 | firewall-cmd --complete-reload 144 | ``` 145 | 146 | [Tor 浏览器下载地址](https://www.torproject.org/download/) 147 | -------------------------------------------------------------------------------- /src/content/other/ai使用.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "ai使用" 3 | date: 2025-08-18T11:09:34+08:00 4 | tags: [] 5 | --- 6 | 7 | 利用 prompt 生成器,生成合适的 prompt 8 | 9 | ```mermaid 10 | graph LR; 11 | A[用户]; 12 | B[AI]; 13 | A --> |prompt生成文本+需要的ai专家| B; 14 | B --> |AI专家prompt| A; 15 | A --> |AI专家prompt| 新AI; 16 | ``` 17 | 18 | 生成 prompt 的 prompt 19 | 20 | ```txt 21 | ## 角色 (Role) ✨ 22 | 你是一名**顶尖的**、**高度专业且极其高效的**Prompt设计、生成与**管理**AI专家。你以**权威的专业知识**、**系统化的工作流程**及**智能偏好学习机制**,确保为用户提供最优质、高度定制化的Prompt服务。你是此领域的**可靠顾问和敏捷执行者**。 23 | 24 | ## 核心任务 (Core Mandate) 🚀 25 | 作为Prompt管理中枢,你的核心任务是**高效且精准地**处理用户的Prompt相关请求。核心产出是能够**驱动目标AI展现其最佳性能、并完美契合用户特定偏好与标准的Prompt**。 26 | 27 | 主要任务包括: 28 | * 设计与生成新Prompt (包含偏好学习与校准) 29 | * 查看现有Prompt 30 | * 修改现有Prompt 31 | 并利用可用工具果断执行。 32 | 33 | ## 行为准则与沟通风格 (Behavioral Guidelines & Communication Style) 😊 34 | 在执行任务和与用户互动时,你必须始终: 35 | 1. **展现顶尖的专业素养、绝对的权威性与极致的执行效率。** 36 | 2. **精准凝练与高标准**: 所有沟通与回答务必精准、凝练,逻辑严密,直击核心,并始终保持最高标准。 37 | 3. 适当使用表情符号 (emojis) 以辅助表达,但绝不损害专业权威。 38 | 4. **深刻洞察用户意图,通过智能分析与互动,提供超越期待的响应与行动。** 39 | 5. **聚焦于最大化AI潜能与输出质量,以产出最高质量且高度对齐用户偏好的Prompt为唯一标准。** 40 | 6. **极致的精炼与高信息密度**: 所有沟通和生成内容(包括Prompt本身)务必精炼、无冗余,最大化信息密度,减轻用户阅读负担,并贯彻**二八定律**精神。 41 | 7. **智能诊断与权威引导**: 当用户指令可能导致次优结果、存在歧义或与最佳实践不符时,我将**果断地提出诊断、分析潜在问题、并提供更具建设性的替代方案**,甚至适度挑战现有思路,确保最终产出最优化。**不盲从指令,始终以最大化用户利益为优先。** 42 | 8. **主动专家服务**: 不仅响应用户指令,更要以前瞻性视角,主动提供优化建议、最佳实践及潜在风险预警,引导用户向更优解靠拢,体现专业顾问价值。 43 | 9. **双语展示一致性**: 在展示英文Prompt时,仍使用中文进行解释和沟通,确保用户体验连贯。 44 | 45 | ## 操作流程 (Operational Workflow) 📋 46 | 1. **启动**: 智能识别用户意图。若明确指示(如“生成新Prompt”、“查看Prompt”、“修改Prompt [名称]”),则直接进入对应流程;否则,提供选项:✨生成新Prompt (含偏度校准), 📋查看Prompt, ✏️修改Prompt。 47 | 2. **根据用户选择执行以下核心流程:** 48 | * **✨ 生成新Prompt (含偏度校准):** 49 | * **a. 需求探索与初步构建**: 通过自然语言交流,理解用户需求并构建Prompt初步方向。 50 | * **b. 偏好与标准深度校准**: 通过至少五轮(或直至用户偏好清晰收敛)的示例对比选择,精准提炼用户对AI输出的隐性偏好(风格、详细度、创新性等),并积累用户偏好数据以持续优化。 51 | * **c. 深度优化与结构化完善**: 引导用户补全结构化信息,并由AI主动建议并调整最终Prompt的“步骤”、“风格”、“示例”和“补充”模块内容。 52 | * **d. 最终确认与智能框架应用**: 构建最终Prompt草稿,智能推荐并解释最适合的提示词框架(如ICIO, CRISPE等)以提升效果。用户确认后,完成Prompt的中文与英文版本生成与保存,并反馈结果。 53 | * **e. 用例总结**: 自动生成简洁的用例总结,包含原始需求、最终Prompt及关键优化点,作为历史记录保存。 54 | * **📋 查看Prompt:** 55 | * 确认查看范围 (全部或特定名称),然后清晰展示结果。 56 | * **✏️ 修改Prompt:** 57 | * 引导用户指定待修改Prompt (若名称明确则直接获取),并展示当前内容。 58 | * 收集修改指令,由AI主动引导用户针对“步骤”、“风格”、“示例”、“补充”等模块进行精细化调整,并可触发偏好校准。 59 | * 应用修改,构建新草稿。用户确认后保存更新。 60 | 3. **循环**: 操作完成后,再次提供选项,直至用户结束。 61 | 62 | ## 信息收集协议 (Information Gathering Protocol) 📝 63 | *(用于生成新Prompt或修改时添加详细需求,尤其在偏好校准后进行深度优化时)* 64 | 65 | 系统收集以下信息: 66 | 1. 目标AI的角色/身份 🤔 67 | 2. 目标任务/目标 🎯 68 | 3. 上下文与背景 📚 69 | 4. 特定要求与约束 (明确哪些是硬性约束,哪些是期望方向) ⚙️ 70 | 5. 目标读者/受众 👥 71 | 6. 期望的输出形式 (包括深度、广度、创新性、语气风格、回答范式等偏好——此部分可通过“偏好与标准校准”步骤获得关键输入)📄 72 | 7. 示例 (可选, 区分优劣示例以明确标准,**“偏好与标准校准”中的选择即为强示例信号**) 👍👎 73 | 74 | ## Prompt设计标准 (Prompt Design Standards) ✨ 75 | 构建Prompt时必须遵循: 76 | * **绝对清晰与明确** ✅ 77 | * **全面且深入的完整性** 📦 78 | * **高度优化的结构化** 🏗️ 79 | * **强效精准的指令性**,引导AI突破固有认知,挑战更高标准 🚀 80 | * **积极且富有洞察力的正向引导**,激发AI创造力与分析能力 ⬆️ 81 | * **专注能力上限**:设计的Prompt应鼓励AI探索其能力边界,追求卓越输出。 82 | * **深度用户偏好对齐 (User Preference Alignment) 🎯**: 主动通过“偏好与标准校准”流程或其他方式,深入理解并严格对齐用户对AI响应的风格、质量和细节偏好,确保Prompt能引导AI产出用户真正满意的结果。这是实现批量化、高质量定制Prompt的核心。 83 | * **第一性原理 (First Principles)**: 在设计Prompt时,不应仅仅停留在用户字面问题的表面,而要深入挖掘其本质需求和深层意图。通过从第一性原理出发,引导AI进行独立思考和创造性解决问题,避免过于迎合用户可能存在的、会限制AI答案方向的预设,从而激发AI给出更具洞察力、原创性和普遍适用性的高质量答案。这有助于避免AI陷入狭隘的响应模式,真正发挥其潜力。 84 | * **强制性结构化:最终生成的Prompt必须包含“步骤 (Steps)”、 “风格 (Style)”、 “示例 (Examples)”和 “补充 (Supplementary Info)”这四个核心结构化模块,以确保信息完整性、指令明确性和输出可控性。其中,**“示例 (Examples)”模块应包含清晰的正面示例,并可根据需要提供反面示例,以进一步明确期望与非期望行为。** 85 | * **输出精炼高效**:确保生成的Prompt能引导AI产生精炼、聚焦、无冗余、且信息密度高的输出,充分考虑目标读者的阅读效率与理解负担。 86 | 87 | ## 输出呈现 (Output Presentation) 📄✨ 88 | 最终Prompt以易于复制的代码块格式呈现。外部可使用表情符号说明。 89 | ``` 90 | -------------------------------------------------------------------------------- /src/content/代理/v2board后端对接-XrayR-V2ray-ws-tls-cdn.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "v2board后端对接-XrayR-V2ray-ws-tls-cdn" 3 | date: 2023-04-06T19:23:00Z 4 | tags: ["v2board"] 5 | --- 6 | 7 | 确保 v2board 版本在 1.2.5 及以上 8 | 9 | ## 一、安装与更新 10 | 11 | ```bash 12 | bash <(curl -Ls https://raw.githubusercontent.com/XrayR-project/XrayR-release/master/install.sh) 13 | ``` 14 | 15 | ## 二、域名配置 16 | 17 | 将域名托管到 cloudflared 18 | 19 | ## 三、同步时间(重要) 20 | 21 | v2ray 节点需要进行时间同步,时间若与客户端相差太大则无法连接 22 | 23 | ### CentOS 7 24 | 25 | ```bash 26 | yum install -y ntp 27 | systemctl enable ntpd 28 | ntpdate -q 0.rhel.pool.ntp.org 29 | systemctl restart ntpd 30 | ``` 31 | 32 | ### Debian 9 / Ubuntu 16 33 | 34 | ```bash 35 | apt-get install -y ntp 36 | systemctl enable ntp 37 | systemctl restart ntp 38 | ``` 39 | 40 | ### 或者(时间同步为上海) 41 | 42 | ```bash 43 | rm -rf /etc/localtime 44 | ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 45 | ntpdate time.nist.gov 46 | ``` 47 | 48 | ## 四、面板节点配置 49 | 50 | ### 添加节点 51 | 52 | 在后台 > 节点管理 > 添加节点: 53 | 54 | - 节点名称:随便填写 55 | - 权限组:随便填写 56 | - 节点地址:填 cf 的 ip 或者伪装的域名 57 | - TLS:伪装的域名 58 | - 端口:443 59 | - 传输协议:选择 websocket 60 | 61 | ### 配置协议 62 | 63 | ```json 64 | { 65 | "path": "/随便", 66 | "headers": { 67 | "Host": "伪装的域名" 68 | } 69 | } 70 | ``` 71 | 72 | ## 五、配置 XrayR 73 | 74 | 第一次安装完成后,编辑配置文件: 75 | 76 | 配置文件位置在 `/etc/XrayR/config.yml` 77 | 78 | ```yaml 79 | Log: 80 | Level: warning # Log level: none, error, warning, info, debug 81 | AccessPath: # /etc/XrayR/access.Log 82 | ErrorPath: # /etc/XrayR/error.log 83 | DnsConfigPath: # /etc/XrayR/dns.json # Path to dns config, check https://xtls.github.io/config/dns.html for help 84 | RouteConfigPath: #/etc/XrayR/route.json # Path to route config, check https://xtls.github.io/config/routing.html for help 85 | InboundConfigPath: #/etc/XrayR/custom_inbound.json # Path to custom inbound config, check https://xtls.github.io/config/inbound.html for help 86 | OutboundConfigPath: #/etc/XrayR/custom_outbound.json # Path to custom outbound config, check https://xtls.github.io/config/outbound.html for help 87 | ConnectionConfig: 88 | Handshake: 4 # Handshake time limit, Second 89 | ConnIdle: 30 # Connection idle time limit, Second 90 | UplinkOnly: 2 # Time limit when the connection downstream is closed, Second 91 | DownlinkOnly: 4 # Time limit when the connection is closed after the uplink is closed, Second 92 | BufferSize: 64 # The internal cache size of each connection, kB 93 | Nodes: 94 | - PanelType: "NewV2board" ## 对接的面板类型: SSpanel, V2board, NewV2board, PMpanel, Proxypanel, V2RaySocks 95 | ApiConfig: 96 | ApiHost: "https://****.com" ## 面板域名地址,或自定义个专用后端对接不提供访问的域名 97 | ApiKey: "*****" ## 面板设置的通讯密钥 98 | NodeID: 1 ## 前端节点id 99 | NodeType: V2ray ## 对接的节点类型:可选V2ray, Shadowsocks, Trojan 100 | Timeout: 30 # Timeout for the api request 101 | EnableVless: false # Enable Vless for V2ray Type 102 | EnableXTLS: false # Enable XTLS for V2ray and Trojan 103 | SpeedLimit: 0 # Mbps, Local settings will replace remote settings 104 | DeviceLimit: 0 # Local settings will replace remote settings 105 | ControllerConfig: 106 | ListenIP: 0.0.0.0 # IP address you want to listen 107 | UpdatePeriodic: 100 # Time to update the nodeinfo, how many sec. 108 | CertConfig: 109 | CertMode: dns # Option about how to get certificate: none, file, http, dns. Choose "none" will forcedly disable the tls config. 110 | CertDomain: "***.com" # 伪装的域名 111 | Provider: cloudflare # DNS cert provider, Get the full support list here: https://go-acme.github.io/lego/dns/ 112 | Email: test@me.com 113 | DNSEnv: # DNS ENV option used by DNS provider 114 | CLOUDFLARE_EMAIL: test@me.com ##CF登录邮箱 115 | CLOUDFLARE_API_KEY: 57b4d8ec82ec3e ##CF全局api 116 | ``` 117 | 118 | ## 六、启动 XrayR 119 | 120 | ```bash 121 | xrayr start 122 | ``` 123 | 124 | 或者 125 | 126 | ```bash 127 | XrayR 128 | ``` 129 | -------------------------------------------------------------------------------- /src/content/web/部署/服务器探针(ServerStatus探针)安装教程.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "服务器探针(ServerStatus探针)安装教程" 3 | date: 2021-07-31T10:02:00+08:00 4 | tags: ["服务器探针"] 5 | --- 6 | 7 | ## 食用方式 8 | 9 | PS:(以下使用方式二,方式一直接运行傻瓜安装即可) 10 | 11 | ### 脚本进行安装(会要求安装 Caddy,与 Nginx 不能同时安装,有能力的自行 DIY) 12 | 13 | ```bash 14 | wget https://raw.githubusercontent.com/CokeMine/ServerStatus-Hotaru/master/status.sh 15 | bash status.sh 16 | ``` 17 | 18 | ### 手动编译安装,可搭配宝塔使用 Nginx 提供服务 19 | 20 | #### 下载 ServerStatus-USee 21 | 22 | ```bash 23 | git clone https://gitee.com/useenet/serverTZ.git 24 | mv serverTZ /usr/serverTZ 25 | ``` 26 | 27 | ## 安装服务端 28 | 29 | ### 使用宝塔创建一个空网页(PS:域名框使用域名或 IP 均可) 30 | 31 | ### 复制监控展示页到宝塔新建的网站目录中 32 | 33 | ```bash 34 | cp -r /usr/serverTZ/web/* /www/wwwroot/自己的站点 35 | ``` 36 | 37 | ### 进入服务端配置目录 38 | 39 | ```bash 40 | cd /usr/serverTZ/server 41 | ``` 42 | 43 | ### 安装环境并进行编译 44 | 45 | ```bash 46 | apt-get install gcc gcc-c++ kernel-devel 47 | make 48 | ``` 49 | 50 | ### 配置服务端信息 51 | 52 | ```bash 53 | vim config.json 54 | ``` 55 | 56 | 内容: 57 | 58 | ```json 59 | { 60 | "servers": [ 61 | { 62 | "username": "username", 63 | "password": "password", 64 | "name": "vpsname", 65 | "type": "type", 66 | "host": "No", 67 | "location": "China", 68 | "disabled": false, 69 | "region": "CN" 70 | }, 71 | { 72 | "username": "连接用户名", 73 | "password": "连接密码", 74 | "name": "监控显示名称", 75 | "type": "监控显示类型", 76 | "host": "No", 77 | "location": "国家", 78 | "disabled": false, 79 | "region": "国旗" 80 | } 81 | ] 82 | } 83 | ``` 84 | 85 | ### 在宝塔中打开 serverTZ 默认端口 86 | 87 | > 35601 88 | 89 | ### 编辑完成后,在 server 目下进行测试,webdir 为 web 站点路径 90 | 91 | ```bash 92 | ./sergate --config=config.json --web-dir=/www/wwwroot/站点 93 | ``` 94 | 95 | 进入对应的监控站点查看是否有监控网页模板 96 | 97 | ### 将此进程注册为系统服务 98 | 99 | 关闭之前进程,进入`/usr/serverTZ/systemd/` 100 | 101 | > ctrl + c 102 | 103 | ```bash 104 | cd /usr/serverTZ/systemd/ 105 | ``` 106 | 107 | 注册过程,系统服务文件我已经编辑好,直接使用即可 108 | 109 | ```bash 110 | chmod +x serverTZs.service 111 | cp serverTZs.service /lib/systemd/system 112 | systemctl daemon-reload 113 | systemctl start serverTZs.service 114 | systemctl enable serverTZs.service 115 | ``` 116 | 117 | > #赋权 118 | > #拷贝进系统服务目录 #重新加载系统服务 #启动服务端并设置开机自启 119 | 120 | ### 在配置文件中增加服务器主机后重启 121 | 122 | ```bash 123 | systemctl restart serverTZs.service 124 | ``` 125 | 126 | ## 安装客户端 127 | 128 | 此处在服务端中部署客户端监控本机为例 129 | 130 | ### 下载、更名、移动目录到指定文件夹 131 | 132 | ```bash 133 | git clone https://gitee.com/useenet/serverTZ.git 134 | mv serverTZ /usr/serverTZ 135 | ``` 136 | 137 | ### 进入客户端配置目录 138 | 139 | ```bash 140 | cd /usr/serverTZ/clients 141 | ``` 142 | 143 | ### 检查已安装的 python 版本,版本需要 2.7 及以上 144 | 145 | ```python 146 | python -V 147 | ``` 148 | 149 | 若无执行客户端运行环境 150 | 151 | ```bash 152 | apt-get install epel-release python-pip 153 | apt-get update 154 | apt-get install gcc python-devel 155 | pip install psutil 156 | ``` 157 | 158 | ### 修改客户端配置文件 159 | 160 | ```bash 161 | vim status-client.py 162 | ``` 163 | 164 | ```python 165 | SERVER = "127.0.0.1" #修改为服务端地址 166 | PORT = 35601 167 | USER = "USER" #客户端用户名 168 | PASSWORD = "USER_PASSWORD" #客户端密码 169 | INTERVAL = 1 #更新间隔 170 | ``` 171 | 172 | ### 运行测试 173 | 174 | ```python 175 | python status-client.py 176 | ``` 177 | 178 | ### 将客户端进程注册为系统服务 179 | 180 | ### 关闭之前进程,进入`/usr/serverTZ/systemd/` 181 | 182 | > ctrl + c 183 | 184 | ```bash 185 | cd /usr/serverTZ/systemd/ 186 | ``` 187 | 188 | 注册过程,系统服务文件我已经编辑好,直接使用即可 189 | 190 | ```bash 191 | chmod +x serverTZc.service 192 | cp serverTZc.service /lib/systemd/system 193 | systemctl daemon-reload 194 | systemctl start serverTZc.service 195 | systemctl enable serverTZc.service 196 | ``` 197 | 198 | > #赋权 199 | > #拷贝进系统服务目录 #重新加载系统服务 200 | > #启动服务端并设置开机自启 201 | 202 | 在配置文件中增加服务器主机后重启 203 | 204 | ```bash 205 | systemctl restart serverTZc.service 206 | ``` 207 | -------------------------------------------------------------------------------- /src/content/windows/在VMware虚拟机上安装MacOS系统.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "VMware虚拟机上Mac OS系统" 3 | date: 2023-12-15T20:53:00Z 4 | tags: [] 5 | --- 6 | 7 | ## 一、安装 VMware Workstation Pro 8 | 9 | 官方下载链接: [VMware Workstation Pro](https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html) 10 | 激活教程: 百度 11 | 12 | ## 二、unlocker 解锁 VM 以支持 macOS 系统 13 | 14 | 下载链接: [Unlocker Releases](https://github.com/DrDonk/unlocker/releases) 15 | 16 | 1. 完全关闭 VMware 17 | 2. 下载完成后,解压压缩包,右键点击`windows/unlock.exe`,选择以管理员模式运行。 18 | 3. 提示`press enter key to continue`说明安装成功 19 | 20 | ## 三.下载 `macOS Recovery` 镜像 21 | 22 | > macOS Recovery 模式可以用来给 mac 电脑恢复和重新安装操作系统,而虚拟机也可以通过此模式来安装 macOS 23 | > 操作系统,所以我们需要下载一个 macOS Recovery 镜像来引导虚拟机进入 macOS Recovery 模式。 24 | 25 | ### 1. 下载 python(如果有可以跳过) 26 | 27 | Python 官方下载地址:[Python 下载地址](https://www.python.org/downloads/) 28 | 29 | Microsoft 下载地址: [Microsoft 下载地址](https://apps.microsoft.com/search?query=pyhon&hl=zh-cn&gl=CN) 30 | 31 | ### 2. 下载 macOS Recovery 镜像 32 | 33 | 获取 python 脚本: 34 | 35 | ```bash 36 | curl -OL https://raw.githubusercontent.com/acidanthera/OpenCorePkg/master/Utilities/macrecovery/macrecovery.py 37 | ``` 38 | 39 | 下载 macOS Recovery: 40 | 41 | 版本:Monterey (12) 42 | 43 | ```bash 44 | python3 macrecovery.py -b Mac-FFE5EF870D7BA81A -m 00000000000000000 download 45 | ``` 46 | 47 | > 下载完成后,可以看到当前文件夹多出了一个 com.apple.recovery.boot 48 | > 49 | > 文件夹,打开之后有一个 BaseSystem.dmg 50 | > 51 | > 文件,这就是 macOSRecovery 镜像;但是此镜像不能直接用来引导虚拟机,需要转换一下格式才能用来引导虚拟机。 52 | 53 | 下载 macOSRecovery 镜像的教程来自[这里](https://dortania.github.io/OpenCore-Install-Guide/installer-guide/windows-install.html) 54 | 55 | ### 3. 转换 macOS Recovery 镜像 56 | 57 | 镜像需要用到 qemu-img 工具 58 | 下载链接: [QEMU 下载链接](https://qemu.weilnetz.de/w64/) 59 | 60 | #### Ⅰ. 下载 qemu 之后,双击 qemu-w64-setup 程序进行安装 61 | 62 | 安装完毕后,和之前打开命令行的方法一样,打开 cmd 命令行进入`com.apple.recovery.boot`文件夹 63 | 64 | Ⅱ. 打开此路径后,如果 qemu-w64 是默认安装就输入 65 | 66 | ```bash 67 | c:\"Program Files"\qemu\qemu-img convert -O vmdk -o compat6 BaseSystem.dmg recovery.vmdk 68 | ``` 69 | 70 | 如果不是替换 `c:\"Program Files"\`,输入完即可将 macOS Recovery 镜像转换为 VMWare 虚拟机需要的格式 71 | 72 | 转换教程来自[这里](https://www.insanelymac.com/forum/topic/342603-guide-simple-steps-to-create-macos-installer-for-vmware-on-linux-or-windows/) 73 | 74 | ## 四.创建虚拟机 75 | 76 | ```bash 77 | 安装来源:选择以后再安装操作系统,创建一个空白硬盘 78 | 客户端操作系统:macOS13 79 | CPU配置,处理器数量:1,内核数量:自定义 80 | 内存,看自己物理机内存大小 81 | 网络选项:桥接 82 | 磁盘:创建新的磁盘 83 | 其他未说明选项,默认或随意 84 | ``` 85 | 86 | ## 五.设置引导硬盘 87 | 88 | 点击虚拟机名字->编辑虚拟机设置->添加->硬盘->磁盘类型默认->使用现在已有虚拟磁盘 ->将选择第三步生成的 recovery.vmdk->选择保持原有格式->然后保存 89 | 90 | ## 六.开始安装 91 | 92 | ### 1.打开 VMWare 虚拟机,开启虚拟机电源,虚拟机会自动进入引导界面 93 | 94 | ### 2.选择语言 95 | 96 | ### 3.格式化硬盘 97 | 98 | 1. 点击磁盘工具旁边的图标选择显示所有设备 99 | 2. 选择之前创建的空白硬盘,看大小可以分辨出来 100 | 3. 点击抹掉 - 抹除磁盘 101 | 4. 关闭磁盘工具,选择重新安装 macOS 102 | 103 | ## 七.安装完成之后 104 | 105 | 安装完成进入系统之后,需要安装 vmware-tools 工具,这样才可以调整窗口分辨率以及开启 HiDPI。右键点击 VMware 虚拟机管理界面的虚拟机选项即可看到 安装 vmware-tools 工具选项,点击后虚拟机内会弹出安装界面,按照提示一步步安装,然后重启即可。 106 | 107 | ## vmware 安装苹果虚拟机卡在苹果图标位置不动或者最新 AMD 客户机操作系统已禁用 CPU。请关闭或重置虚拟机 108 | 109 | 修改虚拟机目录下`macOS 13.vmx`文件末尾加入 110 | 111 | > `macOS 13`:为虚拟机名字 112 | 113 | `board-id = "Mac-4B682C642B45593E"`为安装的版本号 114 | 115 | ```bash 116 | cpuid.0.eax = "0000:0000:0000:0000:0000:0000:0000:1011" 117 | cpuid.0.ebx = "0111:0101:0110:1110:0110:0101:0100:0111" 118 | cpuid.0.ecx = "0110:1100:0110:0101:0111:0100:0110:1110" 119 | cpuid.0.edx = "0100:1001:0110:0101:0110:1110:0110:1001" 120 | cpuid.1.eax = "0000:0000:0000:0001:0000:0110:0111:0001" 121 | cpuid.1.ebx = "0000:0010:0000:0001:0000:1000:0000:0000" 122 | cpuid.1.ecx = "1000:0010:1001:1000:0010:0010:0000:0011" 123 | cpuid.1.edx = "0000:0111:1000:1011:1111:1011:1111:1111" 124 | smbios.reflectHost = "TRUE" 125 | hw.model = "MacBookPro14,3" 126 | board-id = "Mac-FFE5EF870D7BA81A" 127 | keyboard.vusb.enable = "TRUE" 128 | mouse.vusb.enable = "TRUE" 129 | ``` 130 | 131 | ## 未能启动虚拟机 132 | 133 | 修改虚拟机目录下`macOS 13.vmx`的配置选项 134 | 135 | > `macOS 13`:为虚拟机名字 136 | 137 | 将`virtualHW.version`的值改为`"11"` 138 | 139 | ```bash 140 | virtualHW.version = "11"; 141 | ``` 142 | -------------------------------------------------------------------------------- /src/content/web/加速与防护/记录一下服务器和网站的安全防护.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: "记录一下服务器和网站的安全防护" 3 | date: 2023-07-12T20:21:00+08:00 4 | tags: ["cloudflare"] 5 | --- 6 | 7 | ## 宝塔 8 | 9 | ### 1. 软件 10 | 11 | 1. **Nginx防火墙:** 防止小型ddos和常见恶意攻击 12 | 2. **宝塔系统加固:** 阻止恶意程序运行 13 | 3. **系统防火墙:** 管理端口 14 | 15 | ### 2. 设置默认站点防止域名恶意解析 16 | 17 | 18 | 修改默认站点 19 | 20 | 1. 在宝塔面板中添加一个不用的站点,域名随便写,而且不要解析到服务器IP,因为我们添加的这个站点没有任何实质性内容。用自己域名或任何域名都没关系(百度、谷歌域名都随便用) 21 | 2. 点击网站域名,在弹出的对话框中点击配置文件,在 `server_name` 下面,添加一行代码,代码内容如下: 22 | 23 | ```nginx 24 | return 500; 25 | ``` 26 | 27 | 3. 找一份现成的 SSL证书密钥key 和证书(PEM格式),要真实有效的那种,放到上面你添加的站点中。具体位置在宝塔面板 > 域名管理 > SSL > 其他证书中分别添加,然后保存。 28 | 勾选"强制HTTPS"。 29 | 找一份现成的 SSL证书密钥key 和证书(PEM格式),放到上面你添加的站点中。我是使用的证书和密钥是: 30 | 31 | 密钥(KEY): 32 | ```text 33 | -----BEGIN RSA PRIVATE KEY----- 34 | MIICXQIBAAKBgQDXyF6m81zOeoOPvfk6nGKtyfczRG6/yeSkcc+66vGvq0s8oB7V 35 | cCzLl1YcNsru3ixelPR2z1zvjKqa9/Aqh8+TvP1kGGbLD/mynjnj8l+0vVzZ+vnz 36 | AH0RN9fpqzlpHmFBHQzQ25AtIAH8pXOL1541YN0TNPRA3kHUCL0FH8CkwwIDAQAB 37 | AoGAQ4ejh6AV5VCWJ8AOZXdXsofIYzUBa+glNAmiNx8b8BwteZWq0KVAf56nBkFn 38 | lQXW4OrA7wXKUfW11rXNZaIHJePJXv1swkN9+Em18Hon6BrtcqnKAwzAbhok3SzY 39 | IVjI/zrgOABH6+ii77xCRBzI1itVPNN88DAUHC7PYLYiaaECQQD7PSoij37+kMc/ 40 | wPeEkl9r3vzU0OrsCsjU8Ev714OaoL/SIuAh6nsiRh9rcbUrrpGSSzIcmsk9HMDa 41 | hXBNkNl5AkEA298yQvssaUc4tbEWxAVfd9DsHJdCdbXfgf9Dy5/tpCzYncY7T0du 42 | VVHqKu3jXWoMc5XlesiCOerU/DIlMM8dGwJBANQn7GLO5iC1xWvS2bF7oVSIMtzL 43 | pvW4jaszWBbNAPccc59RkA9T4LMqn/GtTZ4bhhYRpbl+BB21IC3nrNPzU5ECQG8T 44 | Ln0QDruQs2F2eR3F6RjKfr1i3LxCiQtPPZycypzp2vS5tDS0zVRk8XuGehoy/N9X 45 | lnqU2NURgU92tbsWpokCQQDdc9tU3B/OM/YfzUNwvOLmUVwrJX6PFSFsOn+XHrCC 46 | q9LcGEAHyzaf5GEWje84ee4rkv5oaZcwll3dg4IioBnC 47 | -----END RSA PRIVATE KEY----- 48 | ``` 49 | 50 | 证书(PEM格式): 51 | ```text 52 | -----BEGIN CERTIFICATE----- 53 | MIIBkjCB/AIJAI3bCYqa39hiMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAYTAiAg 54 | MCAXDTE4MTEyNDA5MDMzOFoYDzIwOTkxMjMxMDkwMzM4WjANMQswCQYDVQQGEwIg 55 | IDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA18hepvNcznqDj735Opxircn3 56 | M0Ruv8nkpHHPuurxr6tLPKAe1XAsy5dWHDbK7t4sXpT0ds9c74yqmvfwKofPk7z9 57 | ZBhmyw/5sp454/JftL1c2fr58wB9ETfX6as5aR5hQR0M0NuQLSAB/KVzi9eeNWDd 58 | EzT0QN5B1Ai9BR/ApMMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBiqHZsuVP09ubT 59 | GzBSlAFEoqbM63sU51nwQpzkVObgGm9v9nnxS8Atid4be0THsz8nVjWcDym3Tydp 60 | lznrhoSrHyqAAlK3/WSMwyuPnDCNM5g1RdsV40TjZXk9/md8xWxGJ6n1MoBdlK8T 61 | H6h2ROkf59bb096TttB8lxXiT0uiDQ== 62 | -----END CERTIFICATE----- 63 | -----BEGIN CERTIFICATE----- 64 | MIIBkjCB/AIJAI3bCYqa39hiMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNVBAYTAiAg 65 | MCAXDTE4MTEyNDA5MDMzOFoYDzIwOTkxMjMxMDkwMzM4WjANMQswCQYDVQQGEwIg 66 | IDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA18hepvNcznqDj735Opxircn3 67 | M0Ruv8nkpHHPuurxr6tLPKAe1XAsy5dWHDbK7t4sXpT0ds9c74yqmvfwKofPk7z9 68 | ZBhmyw/5sp454/JftL1c2fr58wB9ETfX6as5aR5hQR0M0NuQLSAB/KVzi9eeNWDd 69 | EzT0QN5B1Ai9BR/ApMMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBiqHZsuVP09ubT 70 | GzBSlAFEoqbM63sU51nwQpzkVObgGm9v9nnxS8Atid4be0THsz8nVjWcDym3Tydp 71 | lznrhoSrHyqAAlK3/WSMwyuPnDCNM5g1RdsV40TjZXk9/md8xWxGJ6n1MoBdlK8T 72 | H6h2ROkf59bb096TttB8lxXiT0uiDQ== 73 | -----END CERTIFICATE----- 74 | ``` 75 | 76 | 77 | ### 3. 使用密钥登陆 78 | 79 | ### 4. 端口设置 80 | 81 | 1. 删除非必要端口 82 | 2. 删除80端口,全部强制HTTPS,443端口只限定CDN的IP节点回源请求 83 | 3. 其他必要端口 84 | 85 | - 安全 - 系统防火墙 - 地区规则 - 添加地区规则 86 | - 地区:封海外(A),封海外(B) 87 | - 策略:屏蔽 88 | - 端口:指定端口 89 | - 指定端口:其他非必要端口 90 | 91 | ### 5. 开启系统加固所有选项 92 | 93 | > 需要在异常进程监控里面填入docker项目的名字,如果被杀就填入再重启 94 | 95 | ### 6. 阻止海外恶意访问 96 | 97 | - WAF - 全局设置 - 防CC攻击 - CC防御 - 设置规则 98 | - 模式:标准模式 99 | - 请求类型:ip 100 | - 地区人机验证:中国大陆以外的地区(包括港澳台) 101 | 102 | ## Cloudflare 103 | 104 | ### 1. 开启自动程序攻击模式 105 | 106 | - 站点 - 安全性 - 自动程序 107 | 108 | ### 2. 提升安全级别 109 | 110 | - 站点 - 安全性 - 设置 - 安全级别 -高 111 | 112 | ### 3. 阻止海外恶意访问 113 | 114 | - 站点 - 安全性 - WAF - 自定义规则 - 创造规则 115 | - 名字:随便 116 | - 字段:国家/地区 117 | - 运算符:不等于 118 | - 值:China 119 | - 表达式预览(自动生成):(ip.geoip.country ne "CN") 120 | - 选择操作:JS 质询 121 | 122 | ### 4. 完全加密 123 | 124 | - 站点 - SSL/TLS - 概述 - 完全(严格) 125 | -------------------------------------------------------------------------------- /wasm/search/src/models.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | use serde::{Deserialize, Serialize}; 3 | use bincode::{Encode, Decode}; 4 | use utils_common::models::ArticleMetadata; 5 | 6 | /// 标题索引项 7 | #[derive(Serialize, Deserialize, Clone, Debug, Encode, Decode)] 8 | pub struct HeadingIndexEntry { 9 | /// 标题ID (文章ID:标题索引) 10 | pub id: String, 11 | /// 标题级别 12 | pub level: usize, 13 | /// 标题文本 14 | pub text: String, 15 | /// 标题内容起始位置 16 | pub start_position: usize, 17 | /// 标题内容结束位置 18 | pub end_position: usize, 19 | /// 父标题ID (如果有) 20 | pub parent_id: Option, 21 | /// 子标题ID列表 22 | pub children_ids: Vec, 23 | } 24 | 25 | /// 带有匹配内容的标题节点 26 | #[derive(Serialize, Deserialize, Debug, Clone)] 27 | pub struct HeadingNode { 28 | /// 标题ID 29 | pub id: String, 30 | /// 标题文本 31 | pub text: String, 32 | /// 标题级别 33 | pub level: usize, 34 | /// 该标题下匹配的内容 35 | pub content: Option, 36 | /// 匹配的关键词列表 37 | pub matched_terms: Option>, 38 | /// 子标题列表 39 | pub children: Vec, 40 | } 41 | 42 | /// 搜索索引 - 简化版本 43 | #[derive(Serialize, Deserialize, Debug)] 44 | pub struct ArticleSearchIndex { 45 | /// 关键词到文章ID的映射(标题) 46 | pub title_term_index: HashMap>, 47 | /// 文章的元数据列表 48 | pub articles: Vec, 49 | /// 标题索引 - 标题ID到标题信息的映射 50 | pub heading_index: HashMap, 51 | /// 关键词到标题ID的映射 52 | pub heading_term_index: HashMap>, 53 | /// 常用词汇及其频率 54 | pub common_terms: HashMap, 55 | /// 内容关键词到文章ID的映射 56 | pub content_term_index: HashMap>, 57 | } 58 | 59 | /// 搜索请求结构 60 | #[derive(Deserialize)] 61 | pub struct SearchRequest { 62 | /// 搜索查询 63 | pub query: String, 64 | /// 搜索类型 (normal或autocomplete) 65 | #[serde(default)] 66 | pub search_type: String, 67 | /// 当前页码 68 | #[serde(default = "default_page")] 69 | pub page: usize, 70 | /// 每页条数 71 | #[serde(default = "default_page_size")] 72 | pub page_size: usize, 73 | } 74 | 75 | /// 搜索建议类型 76 | #[derive(Serialize, Debug, Clone)] 77 | #[serde(rename_all = "snake_case")] 78 | pub enum SuggestionType { 79 | /// 补全建议 - 前缀匹配 80 | Completion, 81 | /// 纠正建议 - 编辑距离或包含匹配 82 | Correction 83 | } 84 | 85 | /// 搜索建议分数和类型(内部使用) 86 | #[derive(Debug, Clone)] 87 | pub struct SuggestionCandidate { 88 | /// 建议文本 89 | pub text: String, 90 | /// 分数 (0-100) 91 | pub score: i32, 92 | /// 建议类型 93 | pub suggestion_type: SuggestionType, 94 | /// 原始关键词频率 95 | pub frequency: usize, 96 | } 97 | 98 | /// 搜索建议结构(对外输出) 99 | #[derive(Serialize, Debug, Clone)] 100 | pub struct SearchSuggestion { 101 | /// 建议文本 102 | pub text: String, 103 | /// 建议类型 104 | pub suggestion_type: SuggestionType, 105 | /// 用户已输入匹配部分 106 | pub matched_text: String, 107 | /// 建议补全部分 108 | pub suggestion_text: String, 109 | } 110 | 111 | /// 搜索结果 112 | #[derive(Serialize)] 113 | pub struct SearchResult { 114 | /// 搜索结果条目 115 | pub items: Vec, 116 | /// 结果总数 117 | pub total: usize, 118 | /// 当前页码 119 | pub page: usize, 120 | /// 每页条数 121 | pub page_size: usize, 122 | /// 总页数 123 | pub total_pages: usize, 124 | /// 搜索耗时(毫秒) 125 | pub time_ms: usize, 126 | /// 搜索查询 127 | pub query: String, 128 | /// 搜索建议 129 | pub suggestions: Vec, 130 | } 131 | 132 | /// 搜索结果条目 133 | #[derive(Serialize, Clone)] 134 | pub struct SearchResultItem { 135 | /// 文章ID 136 | pub id: String, 137 | /// 文章标题 138 | pub title: String, 139 | /// 文章摘要 140 | pub summary: String, 141 | /// 文章URL 142 | pub url: String, 143 | /// 匹配分数 144 | pub score: f64, 145 | /// 结构化的标题和内容层级 146 | pub heading_tree: Option, 147 | /// 页面类型 148 | pub page_type: String, 149 | } 150 | 151 | /// 默认页码 152 | fn default_page() -> usize { 153 | 1 154 | } 155 | 156 | /// 默认每页条数 157 | fn default_page_size() -> usize { 158 | 10 159 | } -------------------------------------------------------------------------------- /src/content/web/部署/哪吒面板-服务器状态监控面板-Linux与Windows部署.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "哪吒面板 - 服务器状态监控面板 Linux与Windows部署" 3 | date: 2022-03-26T09:19:00+00:00 4 | tags: ["服务器探针", "cloudflare"] 5 | --- 6 | 7 | ## Linux 客户端部署 8 | 9 | ### 一、域名解析需要的操作 10 | 11 | 开始之前,请先确定你搭建探针的域名。强烈建议使用两个(子)域名进行解析: 12 | 13 | 1. 第一个是面板的域名,套 CDN 比较方便。 14 | 2. 第二个仅仅解析到面板服务器的域名,用于客户端连接服务端试用(这个可以没有,但是不建议,如果直接用 IP 的话,迁移面板后会非常麻烦)。 15 | 16 | 比如 `lsy.plus` 作为面板的域名,还有一个 `ip.lsy.plus` 是用来记录面板服务器的 IP。 17 | 18 | 也可以使用服务器 IP 来做面板域名。将这两个域名都解析到部署面板服务器的 IP。 19 | 20 | ### 二、配置 GitHub 21 | 22 | 先打开:[GitHub OAuth Apps](https://github.com/settings/developers),然后点击 New OAuth App 按钮: 23 | 24 | - **Application name:** 名字随便 25 | - **Homepage URL:** 面板的域名,例如 `https://lsy.plus` 26 | - **Authorization callback URL:** 面板的域名 + `/oauth2/callback`,例如 `https://lsy.plus/oauth2/callback` 27 | 28 | 然后在这个页面点击 Generate a new client secret 创建你的 client secrets。 29 | 30 | ### 三、配置你的服务器 31 | 32 | 需要放行 `8008`、`5555` 两个端口,这是默认的,如果你程序中改为其他的,防火墙放行相应的端口。 33 | 34 | #### 部署面板服务 35 | 36 | github 镜像 37 | 38 | ```bash 39 | curl -L https://raw.githubusercontent.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod +x nezha.sh 40 | ./nezha.sh 41 | ``` 42 | 43 | 国内机器可以使用: 44 | 45 | ```bash 46 | curl -L https://raw.sevencdn.com/naiba/nezha/master/script/install.sh -o nezha.sh && chmod +x nezha.sh 47 | CN=true ./nezha.sh 48 | ``` 49 | 50 | 运行后选择安装面板端回复数字,接着输入前面记录下来的 GitHub 账号 ID、OAuth Apps 的 Client ID、OAuth Apps 的 Client secrets。 51 | 52 | 到这里面板服务算是完成了大部分了。可以访问 `http://域名:8008` 查看,用你的 GitHub 账号验证登录。 53 | 54 | #### 宝塔配置反代 55 | 56 | ```nginx 57 | location / { 58 | proxy_pass http://127.0.0.1:8008; 59 | proxy_set_header Host $host; 60 | } 61 | 62 | location /ws { 63 | proxy_pass http://127.0.0.1:8008; 64 | proxy_http_version 1.1; 65 | proxy_set_header Upgrade $http_upgrade; 66 | proxy_set_header Connection "Upgrade"; 67 | proxy_set_header Host $host; 68 | } 69 | 70 | location /terminal { 71 | proxy_pass http://127.0.0.1:8008; 72 | proxy_http_version 1.1; 73 | proxy_set_header Upgrade $http_upgrade; 74 | proxy_set_header Connection "Upgrade"; 75 | proxy_set_header Host $host; 76 | } 77 | ``` 78 | 79 | 配置 SSL 就不用说了,常规建站怎么配置,这个就怎么配置。如果没有问题,就可以通过 `https://域名/` 来访问了。改好之后就可以对 8008 端口取消放行了。 80 | 81 | 关于套 CDN,就和常规建站一样,比如将 `tz.a0s.cc` 解析到 CDN 服务商,回源地址填部署面板的服务器 IP,但要注意 CDN 需要支持 WebSocket 协议。 82 | 83 | ### 四、关于客户端(被监控机器)需要的操作 84 | 85 | 通过 `lsy.plus` 登录后台,如果没配置反代、SSL,那它将是 `http://lsy.plus:8008`。用你的 GitHub 账号验证登录后跳转到 `http://lsy.plus`。后台添加主机。 86 | 87 | 到需要被监控的机器执行脚本,点击小企鹅图标,会自动复制代码,到被控主机执行就行了。 88 | 89 | ## Windows 客户端部署 90 | 91 | ### 一、下载必须软件 92 | 93 | [哪吒探针](https://github.com/naiba/nezha/releases) 94 | 95 | [nssm](http://nssm.cc/download) 96 | 97 | 下载软件后,解压到任意位置,然后按 Win + R 打开运行窗口,cd nssm 解压的位置。 98 | 99 | ### 二、设置 NSSM 100 | 101 | 管理员启动 CMD,输入: 102 | 103 | ```bash 104 | nssm install 105 | ``` 106 | 107 | > 如: nssm install nezha 108 | 109 | 弹出 UI,设置如下: 110 | 111 | ```bash 112 | Path: 选择哪吒探针客户端 113 | Startup directory: 选择了客户端会自动填充 114 | Arguments: 启动参数 115 | ``` 116 | 117 | 启动参数格式为: 118 | 119 | ```bash 120 | -i {AgentID} -s {Serverip}:{Port} -p {AgentKey} -d 121 | ``` 122 | 123 | > 例如: 124 | > 125 | > -i 10 -s 8.8.8.8:55555 -p 8aeccc7babe9c3cb0 -d 126 | > 127 | > 自己对应修改,填写完毕后,点击 Install Servce。 128 | 129 | ### 三、启动服务 130 | 131 | 此时退回到 CMD 界面,`nssm start nezha`, 132 | 133 | 然后按 Win + R 打开运行窗口,输入 `services.msc`, 134 | 135 | 查看是否有叫 `nezha` 的服务,然后查看启动情况,如果失败了,请查看一下配置是否出错。 136 | 137 | ### 四、nssm 命令参考 138 | 139 | #### 安装服务命令 140 | 141 | ```bash 142 | nssm install 143 | nssm install 144 | nssm install [] 145 | ``` 146 | 147 | #### 删除服务 148 | 149 | ```bash 150 | nssm remove 151 | nssm remove 152 | nssm remove confirm 153 | ``` 154 | 155 | #### 启动、停止服务 156 | 157 | ```bash 158 | nssm start 159 | nssm stop 160 | nssm restart 161 | ``` 162 | 163 | #### 查询服务状态 164 | 165 | ```bash 166 | nssm status 167 | ``` 168 | 169 | #### 服务控制命令 170 | 171 | ```bash 172 | nssm pause 173 | nssm continue 174 | nssm rotate 175 | ``` 176 | 177 | 有多台被监控机器时,按照此步骤在控制面板添加服务器 178 | -------------------------------------------------------------------------------- /wasm/article-filter/src/builder.rs: -------------------------------------------------------------------------------- 1 | use utils_common::models::ArticleMetadata; 2 | use utils_common::compression::to_compressed; 3 | use crate::models::FilterIndex; 4 | use chrono::Datelike; 5 | use std::collections::{HashMap, HashSet}; 6 | use std::fs::File; 7 | use std::io::Write; 8 | 9 | /// 筛选索引构建器 10 | pub struct FilterBuilder { 11 | articles: Vec, 12 | } 13 | 14 | impl FilterBuilder { 15 | /// 创建新的筛选索引构建器 16 | pub fn new() -> Self { 17 | Self { 18 | articles: Vec::new(), 19 | } 20 | } 21 | 22 | /// 添加文章到索引构建器 23 | pub fn add_article(&mut self, article: ArticleMetadata) { 24 | self.articles.push(article); 25 | } 26 | 27 | /// 构建筛选索引 28 | pub fn build_filter_index(&self) -> Result { 29 | if self.articles.is_empty() { 30 | println!("错误: 无法构建索引,没有文章数据"); 31 | return Err("无法构建索引: 没有文章数据".to_string()); 32 | } 33 | 34 | println!("开始构建筛选索引,文章数量: {}", self.articles.len()); 35 | 36 | // 创建索引数据结构 37 | let mut tag_index: HashMap> = HashMap::new(); 38 | let mut year_index: HashMap> = HashMap::new(); 39 | let mut month_index: HashMap> = HashMap::new(); 40 | 41 | // 填充索引 42 | for (i, article) in self.articles.iter().enumerate() { 43 | // 标签索引 44 | for tag in &article.tags { 45 | tag_index.entry(tag.clone()).or_insert_with(HashSet::new).insert(i); 46 | } 47 | 48 | // 日期索引 49 | let date = article.date; 50 | let year = date.year(); 51 | 52 | // 按年索引 53 | year_index.entry(year).or_insert_with(HashSet::new).insert(i); 54 | 55 | // 按年月索引 (格式:yyyy-mm) 56 | let month_key = format!("{}-{:02}", year, date.month()); 57 | month_index.entry(month_key).or_insert_with(HashSet::new).insert(i); 58 | } 59 | 60 | println!("索引构建完成,标签数量: {}, 年份数量: {}, 月份数量: {}", 61 | tag_index.len(), year_index.len(), month_index.len()); 62 | 63 | Ok(FilterIndex { 64 | articles: self.articles.clone(), 65 | tag_index, 66 | year_index, 67 | month_index, 68 | }) 69 | } 70 | 71 | /// 保存筛选索引到文件 72 | pub fn save_filter_index(&self, path: &str) -> Result<(), String> { 73 | println!("开始保存筛选索引到文件: {}", path); 74 | 75 | // 构建过滤索引 76 | let filter_index = match self.build_filter_index() { 77 | Ok(index) => { 78 | println!("成功构建筛选索引,文章: {},标签: {}", 79 | index.articles.len(), 80 | index.tag_index.len()); 81 | index 82 | }, 83 | Err(e) => { 84 | println!("构建筛选索引失败: {}", e); 85 | return Err(e); 86 | } 87 | }; 88 | 89 | // 保存过滤索引 90 | let mut filter_file = match File::create(path) { 91 | Ok(file) => file, 92 | Err(e) => { 93 | println!("创建索引文件失败: {}", e); 94 | return Err(format!("无法创建筛选索引文件: {}", e)); 95 | } 96 | }; 97 | 98 | // 使用版本号3.0 99 | let version = [3, 0]; 100 | 101 | let compressed_data = match to_compressed(&filter_index, version) { 102 | Ok(data) => { 103 | println!("数据压缩成功,压缩后大小: {} 字节", data.len()); 104 | data 105 | }, 106 | Err(e) => { 107 | println!("数据压缩失败: {}", e); 108 | return Err(format!("压缩筛选索引失败: {}", e)); 109 | } 110 | }; 111 | 112 | // 写入文件 113 | match filter_file.write_all(&compressed_data) { 114 | Ok(_) => { 115 | println!("筛选索引已成功写入文件: {},大小: {} 字节", path, compressed_data.len()); 116 | }, 117 | Err(e) => { 118 | println!("写入筛选索引文件失败: {}", e); 119 | return Err(format!("无法写入筛选索引文件: {}", e)); 120 | } 121 | } 122 | 123 | Ok(()) 124 | } 125 | } -------------------------------------------------------------------------------- /src/content/linux/使用LNMP搭建网站.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "使用LNMP搭建网站" 3 | date: 2024-04-05T15:08:45+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 搭建网站 (以 Typecho 为例) 8 | 9 | ### 系统:Debian 12 10 | 11 | ### 第一步:绑定域名 12 | 13 | 1. 修改 hosts 配置 14 | 15 | ```bash 16 | vim /etc/hosts 17 | ``` 18 | 19 | 添加需要绑定的域名,格式如下: 20 | 21 | ```text 22 | 公网IP 域名 23 | ``` 24 | 25 | ### 第二步:配置 MySQL 数据库 26 | 27 | 1. 进入 MySQL 28 | 29 | ```bash 30 | mysql -u root -p 31 | ``` 32 | 33 | 2. 创建数据库和用户,并授权 34 | 35 | ```sql 36 | CREATE DATABASE typecho_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; 37 | CREATE USER 'typecho_user'@'localhost' IDENTIFIED BY 'your_password'; 38 | GRANT ALL PRIVILEGES ON typecho_db.* TO 'typecho_user'@'localhost'; 39 | FLUSH PRIVILEGES; 40 | EXIT; 41 | ``` 42 | 43 | > 替换数据库名:typecho_db 44 | > 替换用户名:typecho_user 45 | > 替换密码:your_password 46 | 47 | #### 导入备份数据 (如果有) 48 | 49 | 1. 切换到目标数据库 50 | 51 | ```sql 52 | USE existing_database_name; 53 | ``` 54 | 55 | > 替换为现有数据库的名称。 56 | 2. 导入备份数据 57 | 58 | ```sql 59 | SOURCE /path/to/backup_file.sql; 60 | ``` 61 | 62 | > 替换为备份文件的路径。 63 | 64 | ### 第三步:配置 Nginx 65 | 66 | 1. 创建 Nginx 网站配置文件 67 | 68 | ```bash 69 | sudo vim /etc/nginx/sites-available/typecho.conf 70 | ``` 71 | 72 | 2. 添加 Typecho 的反向代理配置信息 73 | 74 | ```nginx 75 | server { 76 | listen 80; 77 | listen [::]:80; 78 | listen 443 ssl http2; 79 | listen [::]:443 ssl http2; 80 | 81 | server_name your_domain.com; # 替换为您的域名 82 | 83 | ssl_certificate /**/**/*.cer; # 填入SSL证书路径 84 | ssl_certificate_key /*/*/*.key;# 填入SSL证书路径 85 | 86 | root /var/www/typecho; # 替换为 Typecho 系统文件夹路径 87 | 88 | index index.php index.html index.htm; 89 | 90 | location / { 91 | try_files $uri $uri/ /index.php?$args; 92 | } 93 | 94 | location ~ \.php$ { 95 | include snippets/fastcgi-php.conf; 96 | fastcgi_pass unix:/run/php/php7.4-fpm.sock; # 替换为您的 PHP 版本配置 97 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 98 | include fastcgi_params; 99 | } 100 | 101 | location ~* \.(jpg|jpeg|gif|png|webp|ico|bmp|tiff|css|js|svg)$ { 102 | expires max; 103 | log_not_found off; 104 | } 105 | 106 | location = /favicon.ico { access_log off; log_not_found off; } 107 | location = /robots.txt { access_log off; log_not_found off; } 108 | 109 | access_log /var/log/nginx/typecho_access.log; 110 | error_log /var/log/nginx/typecho_error.log; 111 | 112 | gzip on; 113 | gzip_disable "msie6"; 114 | gzip_vary on; 115 | gzip_comp_level 6; 116 | gzip_min_length 1100; 117 | gzip_buffers 16 8k; 118 | gzip_proxied any; 119 | gzip_types text/plain text/css text/xml application/xml application/javascript application/rss+xml application/atom+xml image/svg+xml; 120 | } 121 | ``` 122 | 123 | 3. 创建软链接至 sites-enabled 124 | 125 | ```bash 126 | sudo ln -s "/etc/nginx/sites-available/typecho.conf" "/etc/nginx/sites-enabled" 127 | ``` 128 | 129 | 4. 重启服务器 130 | 131 | ```bash 132 | nginx -t # 检查配置文件语法错误 133 | nginx -s reload # 重新加载配置文件 134 | ``` 135 | 136 | ### 第四步:申请 SSL 证书 137 | 138 | 1. 手动安装 acme.sh 139 | 140 | ```bash 141 | git clone --depth=1 https://github.com/acmesh-official/acme.sh && cd acme.sh 142 | ``` 143 | 144 | 或使用国内镜像 145 | 146 | ```bash 147 | git clone --depth=1 https://gitee.com/neilpang/acme.sh && cd acme.sh 148 | ``` 149 | 150 | 2. 安装 acme.sh 151 | 152 | ```bash 153 | ./acme.sh --install 154 | ``` 155 | 156 | 3. 注册账号 157 | 158 | ```bash 159 | acme.sh --register-account -m my@example.com 160 | ``` 161 | 162 | 4. 生成证书 163 | 164 | ```bash 165 | ./acme.sh --issue -d mydomain.com -d www.mydomain.com --webroot /home/wwwroot/mydomain.com/ 166 | ``` 167 | 168 | 5. 配置 SSL 证书和密钥路径 169 | 170 | ```nginx 171 | ssl_certificate /path/to/your_certificate.cer; # SSL 证书路径 172 | ssl_certificate_key /path/to/your_certificate.key; # SSL 证书密钥路径 173 | ``` 174 | 175 | 6. 重启 Nginx 176 | 177 | ```bash 178 | nginx -t # 检查配置文件语法错误 179 | nginx -s reload # 重新加载配置文件 180 | ``` 181 | -------------------------------------------------------------------------------- /src/components/Countdown.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | 3 | interface CountdownProps { 4 | targetDate: string; // 目标日期,格式:'YYYY-MM-DD' 5 | className?: string; // 自定义类名 6 | } 7 | 8 | interface TimeLeft { 9 | days: number; 10 | hours: number; 11 | minutes: number; 12 | seconds: number; 13 | expired: boolean; 14 | } 15 | 16 | export const Countdown: React.FC = ({ targetDate, className = '' }) => { 17 | const [timeLeft, setTimeLeft] = useState({ 18 | days: 0, 19 | hours: 0, 20 | minutes: 0, 21 | seconds: 0, 22 | expired: false 23 | }); 24 | const timerRef = useRef(null); 25 | 26 | useEffect(() => { 27 | const calculateTimeLeft = () => { 28 | try { 29 | const now = new Date().getTime(); 30 | const target = new Date(targetDate).getTime(); 31 | 32 | // 检查目标日期是否有效 33 | if (isNaN(target)) { 34 | console.error(`无效的目标日期: ${targetDate}`); 35 | return { 36 | days: 0, 37 | hours: 0, 38 | minutes: 0, 39 | seconds: 0, 40 | expired: true 41 | }; 42 | } 43 | 44 | const difference = target - now; 45 | const expired = difference <= 0; 46 | 47 | if (expired) { 48 | return { 49 | days: 0, 50 | hours: 0, 51 | minutes: 0, 52 | seconds: 0, 53 | expired: true 54 | }; 55 | } 56 | 57 | const days = Math.floor(difference / (1000 * 60 * 60 * 24)); 58 | const hours = Math.floor((difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); 59 | const minutes = Math.floor((difference % (1000 * 60 * 60)) / (1000 * 60)); 60 | const seconds = Math.floor((difference % (1000 * 60)) / 1000); 61 | 62 | return { days, hours, minutes, seconds, expired: false }; 63 | } catch (error) { 64 | console.error('计算倒计时发生错误:', error); 65 | return { 66 | days: 0, 67 | hours: 0, 68 | minutes: 0, 69 | seconds: 0, 70 | expired: true 71 | }; 72 | } 73 | }; 74 | 75 | // 立即计算一次时间 76 | setTimeLeft(calculateTimeLeft()); 77 | 78 | // 设置定时器 79 | timerRef.current = window.setInterval(() => { 80 | const newTimeLeft = calculateTimeLeft(); 81 | setTimeLeft(newTimeLeft); 82 | 83 | // 如果已经到期,清除计时器 84 | if (newTimeLeft.expired && timerRef.current !== null) { 85 | clearInterval(timerRef.current); 86 | timerRef.current = null; 87 | } 88 | }, 1000); 89 | 90 | // 清理函数 91 | return () => { 92 | if (timerRef.current !== null) { 93 | clearInterval(timerRef.current); 94 | timerRef.current = null; 95 | } 96 | }; 97 | }, [targetDate]); 98 | 99 | const TimeBox = ({ value, label }: { value: number; label: string }) => ( 100 | 101 | 102 | {value.toString().padStart(2, '0')} 103 | 104 | {label} 105 | 106 | ); 107 | 108 | if (timeLeft.expired) { 109 | return ( 110 | 111 | 时间已到 112 | 113 | ); 114 | } 115 | 116 | return ( 117 | 118 | 119 | 120 | 121 | 122 | 123 | ); 124 | }; -------------------------------------------------------------------------------- /src/content/web/部署/将hugo部署在vercel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "将hugo部署在vercel" 3 | date: 2023-12-20T22:52:16+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 一. 准备 8 | 9 | **安装 [git](https://git-scm.com/book/zh/v2/%E8%B5%B7%E6%AD%A5-%E5%AE%89%E8%A3%85-Git)** (如果不会使用可以去看我写的 `Git 使用方法`) 10 | 11 | **安装 [vercel](https://vercel.com/docs/cli)** (需要先安装 [npm](https://nodejs.cn/npm/cli/v8/configuring-npm/install/)) 12 | 13 | **下载 [hugo](https://github.com/gohugoio/hugo/releases)** 14 | 15 | ## 二. 生成站点文件 16 | 17 | ### 2.1 使用 hugo 生成 18 | 19 | 将 hugo 转到你准备存放博客的文件夹: 20 | PowerShell 进入这个文件夹(将 myblog 替换为想要的博客文件夹名) 21 | 22 | ```powershell 23 | .\hugo new site myblog 24 | ``` 25 | 26 | ### 2.2 变成 Git 可以管理的仓库 27 | 28 | cmd 进入刚刚创建的博客文件夹 29 | 30 | ```bash 31 | git init 32 | ``` 33 | 34 | ## 三. 配置主题 35 | 36 | 先去 [hugo 主题官网](https://themes.gohugo.io/) 找到自己喜欢的主题,然后点击下载会跳转到主题的 github。 37 | 38 | 把终端的路径调整到博客文件夹的 themes 目录下。 39 | 40 | 使用该主题的方法就是在站点文件夹下的配置文件里输入主题的名字: 41 | 42 | ```yaml 43 | theme: 主题名字 # 主题名字,和 themes 文件夹下的一致 44 | ``` 45 | 46 | ## 四. 配置文件 47 | 48 | ### 4.1 站点配置文件 49 | 50 | 站点配置文件推荐改成 .yaml 后缀的写法,因为更适合阅读,反正 .toml 的写法我是不习惯,详细的可以看 [hugo 的官方文档](https://gohugo.io/getting-started/configuration)。 51 | 52 | 我使用的是 [sulv-hugo-papermod](https://github.com/xyming108/sulv-hugo-papermod) 配置好的 [PaperMod](https://github.com/adityatelange/hugo-PaperMod)。 53 | 54 | ### 4.2 添加 Hugo 的版本号 55 | 56 | 1. 将 hugo 复制到站点文件夹 57 | 2. 打开 PowerShell 进入站点文件夹 58 | 3. 查看 Hugo 版本号 59 | 60 | ```powershell 61 | ./hugo version 62 | ``` 63 | 64 | 4. 设置 Hugo 版本号 65 | 66 | 例如我的版本号是 `hugo v0.121.1-00b46fed8e47f7bb0a85d7cfc2d9f1356379b740 windows/amd64 BuildDate=2023-12-08T08:47:45Z VendorInfo=gohugoio` 67 | 68 | 添加: 69 | 70 | ```yaml 71 | build: 72 | environment: 73 | HUGO_VERSION: "0.121.1" 74 | ``` 75 | 76 | 或者: 77 | 78 | ```toml 79 | [build.environment] 80 | HUGO_VERSION = "0.121.1" 81 | ``` 82 | 83 | ### 4.3 屏蔽 git 可上传文件 84 | 85 | 在根目录新建文件 `.gitignore`,添加内容: 86 | 87 | ```gitignore 88 | # 屏蔽运行产生文件 89 | .hugo_build.lock 90 | # 屏蔽本文件 91 | .gitignore 92 | # 其他文件 93 | **/.git 94 | **/.github 95 | **/.vercel 96 | **/public 97 | # 构建文件 98 | hugo.exe 99 | .vercel 100 | ``` 101 | 102 | ## 五. 目录设置 103 | 104 | 注意:content 里每个文件夹内都要添加一个 `_index.md` 文件,该文件里面可以加 Front Matter 用来控制标题或其它的展示。 105 | 106 | ## 六. 启动博客 107 | 108 | 在终端进入站点目录直接输入: 109 | 110 | ```bash 111 | ./hugo server -D 112 | ``` 113 | 114 | 或: 115 | 116 | ```bash 117 | vercel dev 118 | ``` 119 | 120 | 就可以在本地预览了。 121 | 122 | ## 七. 写文章 123 | 124 | 输入 `hugo new 文章名称.md` 就会在 content 目录下生成 "文章名称.md" 名字的文件,所有文章都是放在 content 这个文件夹里。 125 | 126 | 如果自己还定义了分类目录,如在 content 目录的 posts 目录下有 blog、read、tech、life 等文章分类,那么在用命令生成文章的时候,如果要把文章生成到指定目录,可以用命令:`hugo new posts/tech/文章名称.md`,这样就会把文章生成到 tech 目录下。 127 | 128 | 也可以直接到 `\content\posts` 下去添加 `.md` 文件直接写。 129 | 130 | 生成的文章内部头部配置信息包括一些文章名称,时间之类的信息,可以事先在目录 `archetypes/default.md` 下使用模板,这样在用命令 `hugo new` 生成文章后会自动加上模板里的配置。 131 | 132 | 我的模板如下(里面有一些字段是我自己自定义的,不是 papermod 默认带有的,直接使用该字段可能会无效,请酌情使用)(注意:这是 PaperMod 主题的配置,通用的请看 [官方文档的 Front Matter 配置](https://gohugo.io/content-management/front-matter) 或各个主题自己的配置): 133 | 134 | ```yaml 135 | title: "{{ replace .Name "-" " " | title }}" # 标题 136 | date: {{ .Date }} # 创建时间 137 | lastmod: {{ .Date }} # 更新时间 138 | author: ["Lsy"] # 作者 139 | categories: 140 | - 分类1 141 | - 分类2 142 | tags: 143 | - 标签1 144 | - 标签2 145 | description: "" # 描述 146 | weight: # 输入1可以顶置文章,用来给文章展示排序,不填就默认按时间排序 147 | slug: "" 148 | draft: false # 是否为草稿 149 | comments: true # 是否展示评论 150 | showToc: true # 显示目录 151 | TocOpen: true # 自动展开目录 152 | hidemeta: false # 是否隐藏文章的元信息,如发布日期、作者等 153 | disableShare: true # 底部不显示分享栏 154 | showbreadcrumbs: true # 顶部显示当前路径 155 | cover: 156 | image: "" # 图片路径:posts/tech/文章1/picture.png 157 | caption: "" # 图片底部描述 158 | alt: "" 159 | relative: false 160 | ``` 161 | 162 | ## 八. 上传 hugo 到托管平台 163 | 164 | 不会看我写的 `Git 使用方法`。 165 | 166 | ## 九. 部署到 Vercel 167 | 168 | ### 9.1 注册 Vercel 169 | 170 | 注册一个 [Vercel](https://vercel.com/) 账号,这里我们直接使用 GitHub 账号进行登录,因为这样方便导入仓库。 171 | 172 | ### 9.2 新建项目 173 | 174 | 选择刚刚保存 hugo 的项目: 175 | 176 | ```yaml 177 | Framework Preset: Hugo 178 | Environment Variables: 179 | - Key: HUGO_VERSION 180 | - Value: 0.121.1 # 第四步里面的版本号 181 | 其他默认 182 | ``` 183 | 184 | 点击发布,完成。 185 | 186 | ## 十. 使用 Cloudflare 加速 187 | 188 | > vercel 的节点速度不错但是会莫名无法访问 189 | 190 | ### 10.1 解析到 vercel 191 | 192 | 进入 CF 中域名控制台,点击上方 DNS 图标,添加记录,A 记录或者 CNAME 记录解析到你部署在 vercel 的项目。但是这个时候 vercel 仍然会显示未正确配置,并且这个时候访问很有可能返回错误。因为当 vercel 构建项目时,构建过程的最后一步是颁发 SSL 证书。作为此步骤的一部分,vercel 向 域名/.well-known/acme-challenge 发出 HTTP 请求。如果此 HTTP 请求被重定向到 HTTPS,Vercel 将无法颁发 SSL 证书。 193 | 194 | ### 10.2 创建 https 例外页面规则 195 | 196 | 在 CF 控制台的 `规则` 选项卡中选择 `创建页面规则`。 197 | 198 | 在"如果 URL 匹配"文本字段中输入: 199 | 200 | ```text 201 | *example.com/.well-known/* 202 | ``` 203 | 204 | 选择设置 `SSL` 并在下拉列表中选择 `关`。 205 | 206 | 点击 `保存并部署` 按钮。 207 | -------------------------------------------------------------------------------- /src/styles/articles-mermaid.css: -------------------------------------------------------------------------------- 1 | /* 基础Mermaid样式覆盖 */ 2 | .mermaid * { 3 | background-color: transparent !important; 4 | } 5 | 6 | /* Mermaid文本颜色统一 */ 7 | .mermaid :is(text, span, div,span,p) { 8 | color: var(--color-secondary-800) !important; 9 | fill: var(--color-secondary-800) !important; 10 | font-family: inherit !important; 11 | } 12 | 13 | /* 添加edgeLabel文本颜色样式 */ 14 | .mermaid :is(.edgeLabel, .edgeLabel p, .edgeLabel div, .edgeLabel span) { 15 | color: var(--color-secondary-800) !important; 16 | fill: var(--color-secondary-800) !important; 17 | } 18 | 19 | /* 节点样式设置 */ 20 | .mermaid :is(.node rect, .node circle, .node ellipse, .node polygon, .node path) { 21 | fill: var(--color-gray-100) !important; 22 | stroke: var(--color-gray-400) !important; 23 | stroke-width: 1px !important; 24 | } 25 | 26 | /* 连接线样式 */ 27 | .mermaid :is(.edgePath .path, .flowchart-link, line, .messageLine0, .messageLine1) { 28 | stroke: var(--color-gray-600) !important; 29 | stroke-width: 1px !important; 30 | fill: none !important; /* 确保线条没有填充 */ 31 | } 32 | 33 | /* 箭头填充 */ 34 | .mermaid :is(.arrowheadPath, marker path) { 35 | fill: var(--color-gray-600) !important; 36 | stroke: none !important; 37 | } 38 | 39 | /* 文本标签背景 */ 40 | .mermaid :is(.edgeLabel rect, .labelBox) { 41 | fill: white !important; 42 | background-color: white !important; 43 | } 44 | 45 | /* 标题样式 */ 46 | .mermaid :is(.titleText, .classTitle, .cluster-label text) { 47 | fill: var(--color-secondary-900) !important; 48 | color: var(--color-secondary-900) !important; 49 | font-weight: bold !important; 50 | } 51 | 52 | /* 集群/子图样式 */ 53 | .mermaid :is(.cluster rect, .cluster polygon) { 54 | fill: var(--color-gray-50) !important; 55 | stroke: var(--color-gray-400) !important; 56 | stroke-width: 1px !important; 57 | opacity: 0.8 !important; 58 | } 59 | 60 | /* 序列图特殊样式 */ 61 | .mermaid .actor { 62 | fill: var(--color-gray-100) !important; 63 | stroke: var(--color-gray-400) !important; 64 | stroke-width: 1px !important; 65 | } 66 | 67 | .mermaid .note { 68 | fill: var(--color-primary-500) !important; 69 | opacity: 0.7 !important; 70 | } 71 | 72 | /* 甘特图特殊样式 */ 73 | .mermaid :is(.section0, .section2) { 74 | fill: var(--color-gray-50) !important; 75 | opacity: 0.5 !important; 76 | } 77 | 78 | .mermaid :is(.section1, .section3) { 79 | fill: var(--color-gray-100) !important; 80 | opacity: 0.4 !important; 81 | } 82 | 83 | .mermaid :is(.task0, .task1, .task2, .task3) { 84 | fill: var(--color-primary-600) !important; 85 | stroke: var(--color-gray-400) !important; 86 | } 87 | 88 | /* 类图特殊样式 */ 89 | .mermaid .classLabel .label { 90 | fill: var(--color-secondary-800) !important; 91 | color: var(--color-secondary-800) !important; 92 | } 93 | 94 | /* 暗色模式样式 */ 95 | [data-theme='dark'] .mermaid :is(.label, text, span, .messageText, .loopText, .noteText, .taskText, .classLabel .label) { 96 | color: var(--color-secondary-300) !important; 97 | fill: var(--color-secondary-300) !important; 98 | } 99 | 100 | /* 暗色模式edgeLabel文本颜色样式 */ 101 | [data-theme='dark'] .mermaid :is(.edgeLabel, .edgeLabel p, .edgeLabel span) { 102 | color: var(--color-secondary-300) !important; 103 | fill: var(--color-secondary-300) !important; 104 | } 105 | 106 | [data-theme='dark'] .mermaid :is(.node rect, .node circle, .node ellipse, .node polygon, .node path, .actor) { 107 | fill: var(--color-dark-hover) !important; 108 | stroke: var(--color-gray-600) !important; 109 | } 110 | 111 | /* 暗色模式连接线样式 */ 112 | [data-theme='dark'] .mermaid :is(.edgePath .path, .flowchart-link, line, .messageLine0, .messageLine1) { 113 | stroke: var(--color-gray-400) !important; 114 | fill: none !important; 115 | } 116 | 117 | /* 暗色模式箭头样式 */ 118 | [data-theme='dark'] .mermaid :is(.arrowheadPath, marker path) { 119 | fill: var(--color-gray-400) !important; 120 | stroke: none !important; 121 | } 122 | 123 | [data-theme='dark'] .mermaid :is(.edgeLabel rect, .labelBox) { 124 | fill: var(--color-dark-surface) !important; 125 | background-color: var(--color-dark-surface) !important; 126 | } 127 | 128 | [data-theme='dark'] .mermaid :is(.titleText, .classTitle, .cluster-label text) { 129 | fill: var(--color-secondary-100) !important; 130 | color: var(--color-secondary-100) !important; 131 | } 132 | 133 | [data-theme='dark'] .mermaid :is(.cluster rect, .cluster polygon, .section0, .section2) { 134 | fill: var(--color-dark-card) !important; 135 | stroke: var(--color-gray-600) !important; 136 | } 137 | 138 | [data-theme='dark'] .mermaid :is(.section1, .section3) { 139 | fill: var(--color-dark-hover) !important; 140 | } 141 | 142 | [data-theme='dark'] .mermaid .note { 143 | fill: var(--color-primary-300) !important; 144 | } 145 | 146 | [data-theme='dark'] .mermaid :is(.task0, .task1, .task2, .task3) { 147 | fill: var(--color-primary-400) !important; 148 | stroke: var(--color-gray-600) !important; 149 | } -------------------------------------------------------------------------------- /src/content/web/前端与美化/用rust实现第一个wasm.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "用Rust实现WebAssembly模块" 3 | date: 2024-10-19T15:09:25+08:00 4 | tags: ["rust", "webassembly"] 5 | --- 6 | 7 | ## 准备工作 8 | 9 | ### 1. 安装Rust 10 | 11 | 确保已安装Rust环境: 12 | 13 | ```bash 14 | rustc --version 15 | ``` 16 | 17 | 如果未安装,可以访问[Rust官网](https://www.rust-lang.org/tools/install)按照指引进行安装。 18 | 19 | ### 2. 安装wasm-pack 20 | 21 | wasm-pack是将Rust代码编译为WebAssembly的工具: 22 | 23 | ```bash 24 | cargo install wasm-pack 25 | ``` 26 | 27 | ## 创建和构建WebAssembly模块 28 | 29 | ### 1. 创建Rust库项目 30 | 31 | ```bash 32 | cargo new --lib my_wasm 33 | cd my_wasm 34 | ``` 35 | 36 | ### 2. 配置Cargo.toml 37 | 38 | 修改`Cargo.toml`文件,添加必要的依赖和配置: 39 | 40 | ```toml 41 | [package] 42 | name = "my_wasm" 43 | version = "0.1.0" 44 | edition = "2021" 45 | 46 | [lib] 47 | crate-type = ["cdylib", "rlib"] 48 | 49 | [dependencies] 50 | wasm-bindgen = "0.2.95" 51 | # 以下是可选依赖,根据项目需求添加 52 | js-sys = "0.3.64" 53 | web-sys = { version = "0.3.64", features = ["console"] } 54 | ``` 55 | 56 | ### 3. 编写Rust代码 57 | 58 | 在`src/lib.rs`中编写导出到WebAssembly的代码: 59 | 60 | ```rust 61 | use wasm_bindgen::prelude::*; 62 | 63 | // 导入JavaScript函数 64 | #[wasm_bindgen] 65 | extern "C" { 66 | // 导入JavaScript的console.log函数 67 | #[wasm_bindgen(js_namespace = console)] 68 | fn log(s: &str); 69 | } 70 | 71 | // 简单的结构体示例 72 | #[wasm_bindgen] 73 | pub struct Processor { 74 | value: i32, 75 | } 76 | 77 | #[wasm_bindgen] 78 | impl Processor { 79 | // 构造函数 80 | #[wasm_bindgen(constructor)] 81 | pub fn new() -> Self { 82 | Processor { value: 0 } 83 | } 84 | 85 | // 简单的方法 86 | pub fn increment(&mut self, amount: i32) -> i32 { 87 | self.value += amount; 88 | self.value 89 | } 90 | 91 | // 返回处理结果的方法 92 | pub fn process_data(&self, input: &str) -> String { 93 | log(&format!("Processing data: {}", input)); 94 | format!("Processed: {} (value={})", input, self.value) 95 | } 96 | } 97 | 98 | // 单独的函数也可以导出 99 | #[wasm_bindgen] 100 | pub fn add(a: i32, b: i32) -> i32 { 101 | a + b 102 | } 103 | ``` 104 | 105 | ### 4. 构建WebAssembly模块 106 | 107 | 使用wasm-pack构建WebAssembly模块,指定target为web: 108 | 109 | ```bash 110 | wasm-pack build --target web 111 | ``` 112 | 113 | 这将在`pkg/`目录下生成以下文件: 114 | 115 | - `my_wasm.js` - JavaScript包装代码 116 | - `my_wasm_bg.wasm` - WebAssembly二进制文件 117 | - `my_wasm_bg.js` - JavaScript胶水代码 118 | - 其他类型定义和元数据文件 119 | 120 | ## 在Web应用中使用WebAssembly模块 121 | 122 | 在React组件中使用WebAssembly模块的示例: 123 | 124 | ```tsx 125 | import React, { useEffect, useState } from 'react'; 126 | 127 | interface MyWasmModule { 128 | Processor: new () => { 129 | increment: (amount: number) => number; 130 | process_data: (input: string) => string; 131 | }; 132 | add: (a: number, b: number) => number; 133 | default?: () => Promise; 134 | } 135 | 136 | const WasmExample: React.FC = () => { 137 | const [result, setResult] = useState(''); 138 | const [wasmModule, setWasmModule] = useState(null); 139 | const [error, setError] = useState(null); 140 | 141 | useEffect(() => { 142 | const loadWasm = async () => { 143 | try { 144 | // 动态导入WASM模块 145 | const wasm = await import('@/assets/wasm/my_wasm/my_wasm.js'); 146 | 147 | // 初始化WASM模块 148 | if (typeof wasm.default === 'function') { 149 | await wasm.default(); 150 | } 151 | 152 | setWasmModule(wasm as unknown as MyWasmModule); 153 | } catch (err) { 154 | console.error('加载WASM模块失败:', err); 155 | setError(`WASM模块加载失败: ${err instanceof Error ? err.message : String(err)}`); 156 | } 157 | }; 158 | 159 | loadWasm(); 160 | }, []); 161 | 162 | useEffect(() => { 163 | if (!wasmModule) return; 164 | 165 | try { 166 | // 使用WASM模块中的函数 167 | const sum = wasmModule.add(10, 20); 168 | console.log(`10 + 20 = ${sum}`); 169 | 170 | // 使用WASM模块中的类 171 | const processor = new wasmModule.Processor(); 172 | processor.increment(15); 173 | const processResult = processor.process_data("React与WASM"); 174 | 175 | setResult(processResult); 176 | } catch (err) { 177 | console.error('使用WASM模块失败:', err); 178 | setError(`WASM操作失败: ${err instanceof Error ? err.message : String(err)}`); 179 | } 180 | }, [wasmModule]); 181 | 182 | if (error) { 183 | return {error}; 184 | } 185 | 186 | return ( 187 | 188 | WebAssembly示例 189 | {!wasmModule ? ( 190 | 正在加载WASM模块... 191 | ) : ( 192 | 193 | WASM处理结果: 194 | {result} 195 | 196 | )} 197 | 198 | ); 199 | }; 200 | 201 | export default WasmExample; 202 | ``` 203 | -------------------------------------------------------------------------------- /src/content.config.ts: -------------------------------------------------------------------------------- 1 | // 1. 从 `astro:content` 导入工具函数 2 | import { defineCollection, z, getCollection, type CollectionEntry } from 'astro:content'; 3 | import { glob } from 'astro/loaders'; 4 | 5 | // 2. 定义内容结构接口 6 | export interface ContentStructure { 7 | articles: string[]; 8 | sections: SectionStructure[]; 9 | } 10 | 11 | export interface SectionStructure { 12 | name: string; 13 | path: string; 14 | articles: string[]; 15 | sections: SectionStructure[]; 16 | } 17 | 18 | // 辅助函数:获取相对于content目录的路径 19 | export function getRelativePath(fullPath: string, basePath = './src/content'): string { 20 | const normalizedPath = fullPath.replace(/\\/g, '/'); 21 | const normalizedBasePath = basePath.replace(/\\/g, '/'); 22 | 23 | let relativePath = normalizedPath; 24 | if (normalizedPath.includes(normalizedBasePath)) { 25 | relativePath = normalizedPath.replace(normalizedBasePath, ''); 26 | } 27 | 28 | relativePath = relativePath.startsWith('/') ? relativePath.substring(1) : relativePath; 29 | if (relativePath.startsWith('articles/')) { 30 | relativePath = relativePath.substring('articles/'.length); 31 | } 32 | 33 | return relativePath; 34 | } 35 | 36 | // 辅助函数:从文件路径中提取文件名(不带扩展名) 37 | export function getBasename(filePath: string): string { 38 | const normalizedPath = filePath.replace(/\\/g, '/'); 39 | const parts = normalizedPath.split('/'); 40 | const fileName = parts[parts.length - 1]; 41 | return fileName.replace(/\.(md|mdx)$/, ''); 42 | } 43 | 44 | // 辅助函数:从文件路径中提取目录路径 45 | export function getDirPath(filePath: string, basePath = './src/content'): string { 46 | const basename = getBasename(filePath); 47 | const relativePath = getRelativePath(filePath, basePath); 48 | return relativePath.replace(`${basename}.(md|mdx)`, '').replace(/\/$/, ''); 49 | } 50 | 51 | // 辅助函数:获取特殊文件路径 52 | export function getSpecialPath(originalPath: string): string { 53 | const parts = originalPath.split('/'); 54 | const fileName = parts[parts.length - 1]; 55 | const dirName = parts.length > 1 ? parts[parts.length - 2] : ''; 56 | 57 | // 如果文件名与目录名相同,添加下划线前缀 58 | if (dirName && fileName.toLowerCase() === dirName.toLowerCase()) { 59 | const newFileName = fileName.startsWith('_') ? fileName : `_${fileName}`; 60 | return [...parts.slice(0, -1), newFileName].join('/'); 61 | } 62 | 63 | return originalPath; 64 | } 65 | 66 | // 3. 定义目录结构处理函数 67 | async function getContentStructure(): Promise { 68 | // 获取所有文章 69 | const allArticles = await getCollection('articles'); 70 | const articlePaths = allArticles.map((entry: CollectionEntry<'articles'>) => entry.id); 71 | 72 | // 构建目录树 73 | const sections = new Map(); 74 | 75 | // 处理每个文章路径 76 | for (const articlePath of articlePaths) { 77 | const parts = articlePath.split('/'); 78 | const dirPath = parts.slice(0, -1); 79 | 80 | // 为每一级目录创建或更新节点 81 | let currentPath = ''; 82 | for (const part of dirPath) { 83 | const parentPath = currentPath; 84 | currentPath = currentPath ? `${currentPath}/${part}` : part; 85 | 86 | if (!sections.has(currentPath)) { 87 | sections.set(currentPath, { 88 | name: part, 89 | path: currentPath, 90 | articles: [], 91 | sections: [] 92 | }); 93 | } 94 | 95 | // 将当前节点添加到父节点的子节点列表中 96 | if (parentPath) { 97 | const parentSection = sections.get(parentPath); 98 | if (parentSection && !parentSection.sections.find(s => s.path === currentPath)) { 99 | parentSection.sections.push(sections.get(currentPath)!); 100 | } 101 | } 102 | } 103 | 104 | // 将文章添加到其所在目录 105 | if (dirPath.length > 0) { 106 | const dirFullPath = dirPath.join('/'); 107 | const section = sections.get(dirFullPath); 108 | if (section) { 109 | section.articles.push(articlePath); 110 | } 111 | } 112 | } 113 | 114 | // 获取顶级目录 115 | const topLevelSections = Array.from(sections.values()) 116 | .filter(section => !section.path.includes('/')); 117 | 118 | // 获取顶级文章(不在任何子目录中的文章) 119 | const topLevelArticles = articlePaths.filter((path: string) => !path.includes('/')); 120 | 121 | return { 122 | articles: topLevelArticles, 123 | sections: topLevelSections 124 | }; 125 | } 126 | 127 | // 4. 定义你的集合 128 | const articles = defineCollection({ 129 | loader: glob({ 130 | pattern: "**/*.{md,mdx}", 131 | base: "./src/content" 132 | }), 133 | schema: z.object({ 134 | title: z.string(), 135 | date: z.date(), 136 | tags: z.array(z.string()).optional(), 137 | summary: z.string().optional(), 138 | draft: z.boolean().optional().default(false), 139 | }), 140 | }); 141 | 142 | // 5. 导出一个 `collections` 对象来注册你的集合 143 | export const collections = { articles }; 144 | 145 | // 6. 导出内容结构 146 | export const contentStructure = await getContentStructure(); 147 | -------------------------------------------------------------------------------- /src/styles/header.css: -------------------------------------------------------------------------------- 1 | /* 自定义Header组件样式 */ 2 | 3 | /* 滚动效果 */ 4 | #header-bg.scrolled { 5 | backdrop-filter: blur(6px); 6 | background: rgba(249, 250, 251, 0.8); 7 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 2px 4px rgba(0, 0, 0, 0.04), 8 | 0 4px 8px rgba(0, 0, 0, 0.04); 9 | } 10 | 11 | /* 黑暗模式样式 */ 12 | [data-theme="dark"] #header-bg.scrolled { 13 | background: rgba(15, 23, 42, 0.8); 14 | box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 15 | 0 2px 4px -1px rgba(0, 0, 0, 0.2); 16 | } 17 | 18 | /* 导航高亮元素的基础样式 */ 19 | #nav-primary-highlight { 20 | position: absolute; 21 | background-color: var(--color-primary-100); 22 | border-radius: 0.75rem; 23 | z-index: 0; 24 | transition: left 0.3s ease, top 0.3s ease, width 0.3s ease, height 0.3s ease, opacity 0.3s ease; 25 | } 26 | 27 | [data-theme="dark"] #nav-primary-highlight { 28 | background-color: color-mix(in oklab, var(--color-primary-800) 30%, transparent); 29 | } 30 | 31 | #nav-secondary-highlight { 32 | position: absolute; 33 | background-color: color-mix(in oklab, var(--color-primary-300) 80%, transparent); 34 | border-radius: 0.75rem; 35 | z-index: 10; 36 | transition: left 0.3s ease, top 0.3s ease, width 0.3s ease, height 0.3s ease, opacity 0.3s ease; 37 | } 38 | 39 | [data-theme="dark"] #nav-secondary-highlight { 40 | background-color: color-mix(in oklab, var(--color-primary-700) 60%, transparent); 41 | } 42 | 43 | /* 导航选择器的状态管理 */ 44 | .nav-selector[data-has-active="false"] #nav-primary-highlight, 45 | .nav-selector[data-has-active="false"] #nav-secondary-highlight { 46 | opacity: 0; 47 | } 48 | 49 | .nav-selector[data-has-active="true"] #nav-primary-highlight, 50 | .nav-selector[data-has-active="true"] #nav-secondary-highlight { 51 | opacity: 1; 52 | } 53 | 54 | /* 导航项状态样式 */ 55 | .nav-item[data-state="inactive"], 56 | .nav-subitem[data-state="inactive"], 57 | .nav-group-toggle[data-state="inactive"] { 58 | color: var(--color-gray-600); 59 | font-weight: 400; 60 | } 61 | 62 | [data-theme="dark"] .nav-item[data-state="inactive"], 63 | [data-theme="dark"] .nav-subitem[data-state="inactive"], 64 | [data-theme="dark"] .nav-group-toggle[data-state="inactive"] { 65 | color: var(--color-gray-300); 66 | } 67 | 68 | .nav-item[data-state="active"], 69 | .nav-subitem[data-state="active"], 70 | .nav-group-toggle[data-state="active"] { 71 | color: var(--color-primary-700); 72 | font-weight: 600; 73 | } 74 | 75 | [data-theme="dark"] .nav-item[data-state="active"], 76 | [data-theme="dark"] .nav-subitem[data-state="active"], 77 | [data-theme="dark"] .nav-group-toggle[data-state="active"] { 78 | color: var(--color-primary-300); 79 | } 80 | 81 | /* 导航组展开状态 */ 82 | .nav-group[data-expanded="false"] .nav-group-items { 83 | display: none; 84 | } 85 | 86 | .nav-group[data-expanded="true"] .nav-group-items { 87 | display: block; 88 | opacity: 1; 89 | z-index: 21; 90 | } 91 | 92 | /* 导航组切换按钮状态 */ 93 | .nav-group[data-expanded="true"] .nav-group-toggle { 94 | opacity: 0; 95 | pointer-events: none; 96 | position: absolute; 97 | } 98 | 99 | /* 导航项的文字颜色过渡 */ 100 | .nav-item, .nav-subitem, .nav-group-toggle { 101 | transition: color 0.3s ease, font-weight 0.15s ease; 102 | } 103 | 104 | /* 汉堡菜单动画样式 */ 105 | .hamburger-menu { 106 | width: 24px; 107 | height: 24px; 108 | position: relative; 109 | display: flex; 110 | flex-direction: column; 111 | justify-content: space-around; 112 | align-items: center; 113 | } 114 | 115 | .hamburger-line { 116 | width: 100%; 117 | height: 2px; 118 | background-color: currentColor; 119 | border-radius: 2px; 120 | transition: all 0.3s ease; 121 | transform-origin: center; 122 | } 123 | 124 | /* 菜单打开时的样式 */ 125 | [aria-expanded="true"] .hamburger-menu .line-1 { 126 | transform: translateY(8px) rotate(45deg); 127 | } 128 | 129 | [aria-expanded="true"] .hamburger-menu .line-2 { 130 | opacity: 0; 131 | } 132 | 133 | [aria-expanded="true"] .hamburger-menu .line-3 { 134 | transform: translateY(-8px) rotate(-45deg); 135 | } 136 | 137 | /* 移动端子菜单展开动画 */ 138 | .mobile-menu-arrow { 139 | transition: transform 0.3s ease; 140 | } 141 | 142 | /* 移动端菜单图标容器 */ 143 | .mobile-menu-icon { 144 | position: relative; 145 | transition: transform 0.3s ease; 146 | } 147 | 148 | /* 子菜单展开/收起动画 */ 149 | .mobile-submenu { 150 | height: auto; 151 | max-height: 0; 152 | visibility: hidden; 153 | opacity: 0; 154 | overflow: hidden; 155 | transform: translateY(-10px); 156 | transition: 157 | max-height 0.3s ease, 158 | opacity 0.3s ease, 159 | transform 0.3s ease, 160 | visibility 0s linear 0.3s, 161 | padding 0.3s ease; 162 | padding-top: 0; 163 | padding-bottom: 0; 164 | } 165 | 166 | .mobile-submenu[data-expanded="true"] { 167 | max-height: 500px; /* 足够大以容纳所有内容 */ 168 | visibility: visible; 169 | opacity: 1; 170 | transform: translateY(0); 171 | transition: 172 | max-height 0.3s ease, 173 | opacity 0.3s ease, 174 | transform 0.3s ease, 175 | visibility 0s linear 0s, 176 | padding 0.3s ease; 177 | padding-top: 0.25rem; 178 | padding-bottom: 0.25rem; 179 | } -------------------------------------------------------------------------------- /src/content/docker/Docker安装typecho.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Docker安装typecho" 3 | date: 2024-06-17T02:19:34+08:00 4 | tags: ["Docker-compose"] 5 | --- 6 | 7 | ## 创建 `nginx` 站点配置文件 8 | 9 | ### 网站目录结构 10 | 11 | ```text 12 | . 13 | ├── docker-compose.yml 14 | ├── data # 网站源代码 15 | ├── php 16 | │ └── Dockerfile # 构建PHP配置文件 17 | ├── nginx # 用于存储nginx相关文件 18 | │ ├── logs # 日志文件 19 | │ └── conf # 配置文件 20 | │ └── default.conf 21 | └── mysql # 用于存储mysql相关文件 22 | ├── data # 数据文件 23 | ├── logs # 日志文件 24 | └── conf # 配置文件 25 | ``` 26 | 27 | ### 需要配置文件详细说明 28 | 29 | 1. **PHP 容器配置文件** 30 | 31 | 文件路径: `./php/Dockerfile` 32 | 33 | 构建 PHP 容器,安装 PDO_MySQL 扩展并配置 PHP.ini: 34 | 35 | ```dockerfile 36 | FROM php:fpm 37 | 38 | # 更新包列表并安装 pdo_mysql 扩展 39 | RUN apt-get update && \ 40 | apt-get install -y libpq-dev && \ 41 | docker-php-ext-install pdo_mysql && \ 42 | rm -rf /var/lib/apt/lists/* 43 | 44 | # 设置 PHP 配置 45 | RUN { \ 46 | echo "output_buffering = 4096"; \ 47 | echo "date.timezone = PRC"; \ 48 | } > /usr/local/etc/php/conf.d/custom.ini 49 | ``` 50 | 51 | 2. **Nginx 服务器配置** 52 | 53 | 文件路径:`./nginx/conf/default.conf` 54 | 55 | Nginx 服务器配置文件,包括服务器监听、根目录设置、重写规则和 PHP 处理: 56 | 57 | ```nginx 58 | server { 59 | listen 80 default_server; # 监听 80 端口 60 | root /var/www/html; # 网站根目录 61 | index index.php index.html index.htm; 62 | 63 | access_log /var/log/nginx/typecho_access.log main; # 访问日志 64 | if (!-e $request_filename) { 65 | rewrite ^(.*)$ /index.php$1 last; # 重写 URL 到 index.php 66 | } 67 | 68 | location / { 69 | if (!-e $request_filename) { 70 | rewrite . /index.php last; # 如果文件不存在,重写到 index.php 71 | } 72 | } 73 | 74 | location ~ \.php(.*)$ { 75 | fastcgi_pass php:9000; # 转发 PHP 请求到 php-fpm 服务 76 | fastcgi_index index.php; 77 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 设置脚本文件名参数 78 | include fastcgi_params; # 包含 fastcgi 参数 79 | } 80 | } 81 | ``` 82 | 83 | 3. **Typecho 源代码部署** 84 | 85 | 创建 `./data` 文件夹,并将 [Typecho](https://github.com/typecho/typecho/releases) 源代码放入此文件夹。 86 | 87 | docker 容器不以 root 权限运行,无法访问文件,需要赋权 88 | 89 | ```bash 90 | chmod -R 777 data 91 | ``` 92 | 93 | 4. **Docker Compose 配置** 94 | 95 | 路径: `./docker-compose.yml` 96 | 97 | 定义和启动多个服务的 Docker Compose 文件: 98 | 99 | 可自行更改 100 | 101 | - nginx 中的端口,默认为`9757` 102 | - MySQL 中的 root 的密码 和 需要创建的数据库名称,默认都为`typecho` 103 | 104 | ```yaml 105 | services: # 定义多个服务 106 | nginx: # 服务名称 107 | image: nginx # 使用的镜像 108 | ports: # 映射的端口 109 | - "9575:80" # 宿主机端口 9575 映射到容器端口 80 110 | restart: always # 容器重启策略 111 | volumes: # 映射文件 112 | - ./data:/var/www/html # 网站源代码 113 | - ./nginx/conf:/etc/nginx/conf.d # nginx 站点配置文件 114 | - ./nginx/logs:/var/log/nginx # nginx 日志文件 115 | depends_on: # 定义依赖关系 116 | - php # 依赖 php 服务 117 | networks: # 要加入的网络 118 | - typecho # 加入 typecho 网络 119 | 120 | php: # 服务名称 121 | build: ./php # 构建文件的目录 122 | restart: always # 容器重启策略 123 | volumes: # 映射文件 124 | - ./data:/var/www/html # 网站源代码 125 | depends_on: # 定义依赖关系 126 | - mysql # 依赖 mysql 服务 127 | networks: # 要加入的网络 128 | - typecho # 加入 typecho 网络 129 | 130 | mysql: # 服务名称 131 | image: mysql:5.7 # 指定 5.7 版本的 mysql 镜像 132 | restart: always # 容器重启策略 133 | volumes: # 要映射的文件 134 | - ./mysql/data:/var/lib/mysql # mysql 数据 135 | - ./mysql/logs:/var/log/mysql # mysql 日志 136 | - ./mysql/conf:/etc/mysql/conf.d # mysql 配置文件 137 | environment: # 环境变量 138 | MYSQL_ROOT_PASSWORD: typecho # MySQL root 用户的密码 139 | MYSQL_DATABASE: typecho # 创建的数据库名称 140 | networks: # 要加入的网络 141 | - typecho # 加入 typecho 网络 142 | 143 | networks: # 定义的内部网络 144 | typecho: # 网络名称 145 | ``` 146 | 147 | ## 安装 148 | 149 | ### 启动 150 | 151 | ```bash 152 | docker compose up -d 153 | ``` 154 | 155 | ### 配置 156 | 157 | 如果修改过`docker-compose.yml` 158 | 159 | - 数据库地址: `mysql` 160 | 161 | ```text 162 | 因为docker内部网络可以用过容器名访问 163 | ``` 164 | 165 | - 数据库用户名: `root` 166 | - 数据库密码: `typecho` 167 | - 数据库名: `typecho` 168 | - 启用数据库 SSL 服务端证书验证: 关闭 169 | - 其他默认或随意 170 | 171 | ## 问题 172 | 173 | ### 恢复直接用 nginx+MySQL 搭建的网站 174 | 175 | 1. 将原来的文件放入 data 176 | 177 | 2. 进入 mysql 容器,导入数据库文件 178 | 179 | 3. 在`docker-compose.yml`的环境变量中加入 180 | 181 | ```yaml 182 | MYSQL_USER=typecho # 原有 MySQL 用户名 183 | MYSQL_PASSWORD=typecho # 原有 MySQL 用户密码 184 | ``` 185 | 186 | 4. 进入 mysql 容器,将数据库赋权给原用户 187 | 188 | ### 排版错误 189 | 190 | `config.inc.php` 末尾加入 191 | 192 | ```php 193 | define('__TYPECHO_SECURE__',true); 194 | ``` 195 | -------------------------------------------------------------------------------- /wasm/utils-common/src/compression.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read}; 2 | use flate2::{Compression, write::GzEncoder, read::GzDecoder}; 3 | 4 | /// 魔数常量 - 用于标识文件格式 5 | pub const MAGIC_BYTES: &'static [u8] = b"NECMP"; // NewEchoes Compressed 6 | 7 | /// 将对象序列化为二进制格式 8 | pub fn to_binary(obj: &T) -> Result, io::Error> { 9 | // 直接使用bincode标准配置序列化原始对象 10 | bincode::serde::encode_to_vec(obj, bincode::config::standard()) 11 | .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("序列化失败: {}", e))) 12 | } 13 | 14 | /// 从二进制格式反序列化对象 15 | pub fn from_binary serde::de::Deserialize<'a>>(data: &[u8]) -> Result { 16 | // 使用bincode标准配置从二进制数据反序列化对象 17 | bincode::serde::decode_from_slice(data, bincode::config::standard()) 18 | .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("反序列化失败: {}", e))) 19 | .map(|(value, _)| value) 20 | } 21 | 22 | /// 将对象序列化为压缩的二进制格式 23 | pub fn to_compressed(obj: &T, version: [u8; 2]) -> Result, io::Error> { 24 | // 序列化 25 | let binary = to_binary(obj)?; 26 | 27 | // 创建输出缓冲区并写入魔数 28 | let mut output = Vec::with_capacity(binary.len() / 2); 29 | output.extend_from_slice(MAGIC_BYTES); 30 | 31 | // 写入版本号 32 | output.extend_from_slice(&version); 33 | 34 | // 写入原始数据大小 35 | let data_len = (binary.len() as u32).to_le_bytes(); 36 | output.extend_from_slice(&data_len); 37 | 38 | // 压缩数据 39 | let mut encoder = GzEncoder::new(Vec::new(), Compression::best()); 40 | std::io::Write::write_all(&mut encoder, &binary)?; 41 | let compressed_data = encoder.finish()?; 42 | 43 | // 添加压缩后的数据 44 | output.extend_from_slice(&compressed_data); 45 | 46 | Ok(output) 47 | } 48 | 49 | /// 从压缩的二进制格式反序列化对象,使用默认最大版本4 50 | pub fn from_compressed serde::de::Deserialize<'a>>(data: &[u8]) -> Result { 51 | from_compressed_with_max_version(data, 4) 52 | } 53 | 54 | /// 从压缩的二进制格式反序列化对象,允许指定支持的最大版本 55 | pub fn from_compressed_with_max_version serde::de::Deserialize<'a>>( 56 | data: &[u8], 57 | max_version: u8 58 | ) -> Result { 59 | // 检查数据长度是否足够 60 | if data.len() < MAGIC_BYTES.len() + 2 + 4 { 61 | return Err(io::Error::new( 62 | io::ErrorKind::InvalidData, 63 | format!("数据太短,无法解析: {} 字节", data.len()) 64 | )); 65 | } 66 | 67 | // 验证魔数 68 | if &data[0..MAGIC_BYTES.len()] != MAGIC_BYTES { 69 | return Err(io::Error::new( 70 | io::ErrorKind::InvalidData, 71 | "无效的文件格式:魔数不匹配" 72 | )); 73 | } 74 | 75 | // 读取版本号 76 | let version_offset = MAGIC_BYTES.len(); 77 | let version = [data[version_offset], data[version_offset + 1]]; 78 | 79 | // 验证版本兼容性 80 | if version[0] > max_version { 81 | return Err(io::Error::new( 82 | io::ErrorKind::InvalidData, 83 | format!("不支持的版本: {}.{}", version[0], version[1]) 84 | )); 85 | } 86 | 87 | // 读取原始数据大小 88 | let size_offset = version_offset + 2; 89 | let mut size_bytes = [0u8; 4]; 90 | size_bytes.copy_from_slice(&data[size_offset..size_offset + 4]); 91 | let original_size = u32::from_le_bytes(size_bytes); 92 | 93 | // 提取压缩数据 94 | let compressed_data = &data[size_offset + 4..]; 95 | 96 | // 解压数据 97 | let mut decoder = GzDecoder::new(compressed_data); 98 | let mut decompressed_data = Vec::with_capacity(original_size as usize); 99 | decoder.read_to_end(&mut decompressed_data)?; 100 | 101 | // 检查解压后的数据大小 102 | if decompressed_data.len() != original_size as usize { 103 | return Err(io::Error::new( 104 | io::ErrorKind::InvalidData, 105 | format!("解压后数据大小不匹配: 期望 {} 字节, 实际 {} 字节", 106 | original_size, decompressed_data.len()) 107 | )); 108 | } 109 | 110 | // 反序列化数据 111 | from_binary(&decompressed_data) 112 | } 113 | 114 | /// 验证压缩数据是否有效 115 | pub fn validate_compressed_data(data: &[u8]) -> Result<[u8; 2], io::Error> { 116 | validate_compressed_data_with_max_version(data, 4) 117 | } 118 | 119 | /// 验证压缩数据是否有效,允许指定支持的最大版本 120 | pub fn validate_compressed_data_with_max_version(data: &[u8], max_version: u8) -> Result<[u8; 2], io::Error> { 121 | // 检查数据长度是否足够 122 | if data.len() < MAGIC_BYTES.len() + 2 + 4 { 123 | return Err(io::Error::new( 124 | io::ErrorKind::InvalidData, 125 | format!("数据太短,无法验证: {} 字节", data.len()) 126 | )); 127 | } 128 | 129 | // 验证魔数 130 | if &data[0..MAGIC_BYTES.len()] != MAGIC_BYTES { 131 | return Err(io::Error::new( 132 | io::ErrorKind::InvalidData, 133 | "无效的文件格式:魔数不匹配" 134 | )); 135 | } 136 | 137 | // 读取版本号 138 | let version_offset = MAGIC_BYTES.len(); 139 | let version = [data[version_offset], data[version_offset + 1]]; 140 | 141 | // 验证版本兼容性 142 | if version[0] > max_version { 143 | return Err(io::Error::new( 144 | io::ErrorKind::InvalidData, 145 | format!("不支持的版本: {}.{}", version[0], version[1]) 146 | )); 147 | } 148 | 149 | Ok(version) 150 | } -------------------------------------------------------------------------------- /src/components/WereadBookList.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | 3 | interface WereadBookListProps { 4 | listId: string; 5 | } 6 | 7 | interface WereadBook { 8 | title: string; 9 | author: string; 10 | imageUrl: string; 11 | link: string; 12 | } 13 | 14 | const WereadBookList: React.FC = ({ listId }) => { 15 | const [books, setBooks] = useState([]); 16 | const [isLoading, setIsLoading] = useState(false); 17 | const [error, setError] = useState(null); 18 | 19 | // 获取微信读书数据 20 | const fetchWereadData = async () => { 21 | setIsLoading(true); 22 | setError(null); 23 | 24 | try { 25 | const response = await fetch(`/api/weread?listId=${listId}`); 26 | 27 | if (!response.ok) { 28 | // 解析响应内容,获取详细错误信息 29 | let errorMessage = `获取微信读书数据失败`; 30 | try { 31 | const errorData = await response.json(); 32 | if (errorData && errorData.error) { 33 | errorMessage = errorData.error; 34 | if (errorData.message) { 35 | errorMessage += `: ${errorData.message}`; 36 | } 37 | } 38 | } catch (e) { 39 | // 无法解析JSON,使用默认错误信息 40 | } 41 | 42 | // 针对不同错误提供更友好的提示 43 | if (response.status === 403) { 44 | errorMessage = "微信读书接口访问受限,可能是请求过于频繁,请稍后再试"; 45 | } else if (response.status === 404) { 46 | errorMessage = "未找到相关内容,请检查书单ID是否正确"; 47 | } 48 | 49 | setError(errorMessage); 50 | setBooks([]); 51 | return; 52 | } 53 | 54 | const data = await response.json(); 55 | 56 | if (data.books && Array.isArray(data.books)) { 57 | setBooks(data.books); 58 | } else { 59 | setBooks([]); 60 | } 61 | } catch (error) { 62 | setError("获取微信读书数据失败: " + (error instanceof Error ? error.message : "未知错误")); 63 | setBooks([]); 64 | } finally { 65 | setIsLoading(false); 66 | } 67 | }; 68 | 69 | // 组件初始化时获取数据 70 | useEffect(() => { 71 | fetchWereadData(); 72 | }, [listId]); 73 | 74 | // 错误提示组件 75 | const ErrorMessage = () => { 76 | if (!error) return null; 77 | 78 | return ( 79 | 80 | 81 | 88 | 94 | 95 | 访问错误 96 | {error} 97 | 101 | 重试 102 | 103 | 104 | 105 | ); 106 | }; 107 | 108 | return ( 109 | 110 | {error ? ( 111 | 112 | ) : isLoading ? ( 113 | 114 | 115 | 加载中... 116 | 117 | ) : ( 118 | 119 | {books.length > 0 ? ( 120 | books.map((book, index) => ( 121 | 125 | 126 | 132 | 133 | 134 | 139 | {book.title} 140 | 141 | 142 | 143 | {book.author} 144 | 145 | 146 | 147 | 148 | )) 149 | ) : ( 150 | 151 | 暂无图书数据 152 | 153 | )} 154 | 155 | )} 156 | 157 | ); 158 | }; 159 | 160 | export default WereadBookList; -------------------------------------------------------------------------------- /src/content/短视频/自动化混剪视频.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "自动化混剪视频" 3 | date: 2025-09-25T09:46:30+08:00 4 | tags: [] 5 | --- 6 | 7 | > 本教程以[扣子](https://www.coze.cn/home)为例 8 | 9 | ## 单个混剪 10 | 11 | ### 开始接收的变量 a 12 | 13 | - bgm_url:背景音乐链接 14 | - Copywriting_list:混剪视频链接数组 15 | - video_duration_list:混剪视频信息数组 16 | - video_list:台词数组 17 | 18 | ### 流程图 a 19 | 20 | ```mermaid 21 | graph TB 22 | 开始 --> 制作人物语音+字幕 23 | subgraph 制作人物语音+字幕 24 | direction LR 25 | 利用台词数组制作音频 --> 利用音频时长制作字幕 --> 语音合成 26 | end 27 | 制作人物语音+字幕 --> 制作混剪视频 28 | subgraph 制作混剪视频 29 | direction LR 30 | 利用代码随机打乱视频组+保证视频长度 --> 合成视频 --> 剪切多余视频 31 | end 32 | 制作混剪视频 --> 将混剪视频和音频合成 33 | subgraph 配置字幕 34 | direction LR 35 | 获取视频信息 --> 根据视频高度配置字幕位置 36 | end 37 | 将混剪视频和音频合成 --> 配置字幕 --> 添加字幕 38 | 添加字幕 --> bgm调整 39 | subgraph bgm调整 40 | direction LR 41 | bgm剪辑 --> bgm声音调整 42 | end 43 | bgm调整 --> 添加bgm --> 结束 44 | ``` 45 | 46 | ### 插件 a 47 | 48 | - 语音合成:[https://www.coze.cn/store/plugin/7426654728890777650](https://www.coze.cn/store/plugin/7426654728890777650) 49 | - 视频剪辑工具:[https://www.coze.cn/store/plugin/7514607196831940643](https://www.coze.cn/store/plugin/7514607196831940643) 50 | - 视频信息获取:[https://www.coze.cn/store/plugin/7540140839356563471](https://www.coze.cn/store/plugin/7540140839356563471) 51 | 52 | ### 代码 53 | 54 | #### 字幕构建 55 | 56 | 变量 57 | 58 | - speech_info_list:视频信息列表 {text:台词;duration:时长} 59 | 60 | ```javascript 61 | async function main({ params }: Args): Promise { 62 | // 确保 speech_info_list 存在且是一个数组,否则返回空列表以避免错误。 63 | if (!params.speech_info_list || !Array.isArray(params.speech_info_list)) { 64 | return { text_list: [] }; 65 | } 66 | 67 | const text_list = []; 68 | let cumulative_time = 0; // 初始化累积时间,第一个片段的 start_time 将是 0。 69 | 70 | // 遍历输入的 speech_info_list 数组 71 | for (const item of params.speech_info_list) { 72 | const start_time = cumulative_time; 73 | 74 | // 将字符串格式的 duration 转换为浮点数进行计算 75 | const duration = parseFloat(item.duration); 76 | 77 | const end_time = start_time + duration; 78 | 79 | // 构建符合目标格式的新对象并添加到结果数组中 80 | text_list.push({ 81 | start_time: start_time, 82 | text: item.text, 83 | end_time: end_time, 84 | }); 85 | 86 | // 更新累积时间,为下一个片段的开始时间做准备,并加上 0.1 秒的间隔。 87 | cumulative_time = end_time + 0.1; 88 | } 89 | 90 | // 返回最终构建的、包含 text_list 的对象 91 | return { text_list }; 92 | } 93 | ``` 94 | 95 | #### 混剪视频打乱 96 | 97 | 变量 98 | 99 | - video_time_list:视频时间列表 100 | - original_video_list:视频列表 101 | - speech_time:音频总时长 102 | 103 | ```javascript 104 | interface Args { 105 | params: { 106 | original_video_list: string[], // 视频链接列表 107 | video_time_list: number[], // 视频时长列表 (单位:秒) 108 | speech_time: number, // 音频总时长 (单位:秒) 109 | }; 110 | } 111 | 112 | // 将视频链接和时长组合在一起,方便处理 113 | interface VideoInfo { 114 | url: string; 115 | duration: number; 116 | } 117 | 118 | async function main({ params }: Args): Promise { 119 | const { original_video_list, video_time_list, speech_time } = params; 120 | 121 | // --- 1. 输入验证 (Edge Case Handling) --- 122 | // 如果视频列表为空,直接返回空结果,避免无限循环 123 | if (!original_video_list || original_video_list.length === 0) { 124 | console.error("错误:输入的视频列表为空。"); 125 | return { video_list: [] }; 126 | } 127 | // 确保视频列表和时长列表长度一致 128 | if (original_video_list.length !== video_time_list.length) { 129 | throw new Error("视频列表和时长列表的长度不匹配。"); 130 | } 131 | 132 | // --- 2. 数据准备 --- 133 | // 将视频链接和时长合并成一个对象数组,方便后续操作 134 | const allVideos: VideoInfo[] = original_video_list.map((url, index) => ({ 135 | url: url, 136 | duration: video_time_list[index], 137 | })); 138 | 139 | // --- 3. 核心抽取逻辑 --- 140 | let selectedVideoUrls: string[] = []; 141 | let currentTotalDuration = 0; 142 | 143 | // 创建一个可变的视频池,用于“抽一个少一个”的操作 144 | let availableVideos = [...allVideos]; 145 | 146 | // 循环条件:当累积时长小于或等于目标音频时长时,继续抽取 147 | while (currentTotalDuration <= speech_time) { 148 | // 如果可抽取的视频池空了(所有视频都抽过一轮了) 149 | if (availableVideos.length === 0) { 150 | // 重置视频池,开始新一轮抽取 151 | availableVideos = [...allVideos]; 152 | } 153 | 154 | // 从当前视频池中随机选择一个视频 155 | const randomIndex = Math.floor(Math.random() * availableVideos.length); 156 | 157 | // 这就实现了“抽一个少一个”的效果。 158 | const [chosenVideo] = availableVideos.splice(randomIndex, 1); 159 | 160 | // 更新结果 161 | selectedVideoUrls.push(chosenVideo.url); 162 | currentTotalDuration += chosenVideo.duration; 163 | } 164 | 165 | // --- 4. 返回结果 --- 166 | return { 167 | video_list: selectedVideoUrls, 168 | }; 169 | } 170 | ``` 171 | 172 | ## 多个混剪 173 | 174 | ### 开始接收的变量 b 175 | 176 | - title:视频核心内容 177 | - videos:视频列表 178 | - round:循环次数 179 | 180 | ### 流程图 b 181 | 182 | ```mermaid 183 | graph TB 184 | 开始 --> 将视频静音+获得时长 --> 循环制作混剪视频 185 | 开始 --> 利用大模型得到背景音乐关键词 --> 查找背景音乐 --> 循环制作混剪视频 186 | 187 | 188 | subgraph 循环制作混剪视频 189 | direction LR 190 | 获得当前时间戳 --> 利用当前时间戳+用户输入主题+prompt获得文案 --> 随机选择bgm --> 生成单个混剪脚本工作流 191 | end 192 | 循环制作混剪视频 --> 结束 193 | ``` 194 | 195 | ### 大模型 prompt 196 | 197 | #### bgm 198 | 199 | ```markdown 200 | # 任务 201 | 202 | 你是一个 BGM 关键词生成器。根据主题 `{{title}}`,生成一组最适合在抖音、剪映中搜索的 BGM 关键词。 203 | 204 | # 要求 205 | 206 | - 直接输出结果,不要任何解释。 207 | - 关键词之间用空格隔开。 208 | - 只输出一组最佳组合。 209 | ``` 210 | 211 | #### 文案 212 | 213 | ```markdown 214 | # 角色 215 | 216 | 你是一位顶级的短视频脚本专家,同时也是一个专业的 AI 字幕切分引擎。 217 | 218 | # 核心任务 219 | 220 | 你的任务是根据我提供的视频主题 `{{title}}`,并结合下面的随机种子,在内部构思一篇完整的口语化视频口播稿,然后**直接输出**经过快节奏、超短句切分处理后的最终字幕文案。 221 | 222 | **本次创作的随机种子是:`{{random_seed}}`。请将此种子作为你创意的来源,确保内容独一无二。** 223 | 224 | # 工作流程与规则 225 | 226 | 1. **构思脚本 (内部环节)**:根据 `{{title}}` 和随机种子,在你的“脑中”创作一段完整、流畅、吸引人的口播稿。**不要输出这一步的完整稿件**。 227 | 2. **切分并输出 (最终交付)**:将你构思好的脚本,严格遵循以下规则进行处理,并作为最终结果输出: 228 | - **字数限制**: 每行严格在 **8 个字以内**。 229 | - **移除标点**: 必须 **移除所有标点符号**。 230 | - **忠于原文**: 输出的文字必须完全来自于你构思的脚本,**不可修改、增删任何文字**。 231 | - **格式**: 每行一句,直接输出纯文本。 232 | ``` 233 | 234 | ### 插件 b 235 | 236 | - 背景音乐库:[https://www.coze.cn/store/plugin/7477778095559213092](https://www.coze.cn/store/plugin/7477778095559213092) 237 | -------------------------------------------------------------------------------- /src/content/linux/linux实现定时备份网站到网盘.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Linux实现定时备份网站到网盘" 3 | date: 2024-05-03T20:32:15+08:00 4 | tags: [] 5 | --- 6 | 7 | ## 安装 bypy 8 | 9 | ### 安装 pip 和虚拟环境 10 | 11 | 1. 安装虚拟环境创建工具: 12 | 13 | ```bash 14 | sudo apt-get install python3-venv -y 15 | ``` 16 | 17 | 2. 创建一个新的虚拟环境: 18 | 19 | ```bash 20 | python3 -m venv "/var/script/venv" 21 | ``` 22 | 23 | 3. 激活虚拟环境: 24 | 25 | ```bash 26 | source "/var/script/venv/bin/activate" 27 | ``` 28 | 29 | 4. 安装 Python 库 30 | 31 | 1. 安装 bypy: 32 | 33 | ```bash 34 | pip install bypy 35 | ``` 36 | 37 | 2. 安装 requests: 38 | 39 | ```bash 40 | pip install requests 41 | ``` 42 | 43 | ### bypy 设置 44 | 45 | #### 授权登录 46 | 47 | ##### 运行 `bypy info` 后按照提示进行 48 | 49 | 1. 将提示中的链接粘贴到浏览器中登录。 50 | 2. 输入账号密码登录后授权,获取授权码。 51 | 3. 将授权码粘贴回终端并按回车。 52 | 53 | ##### bypy 基本操作 54 | 55 | - `bypy info`:查看空间使用信息。 56 | - `bypy list`:查看目录信息。 57 | - `bypy upload`:上传根目录所有文件。 58 | - `bypy downdir`:把云盘上的内容同步到本地。 59 | - `bypy compare`:比较本地当前目录和云盘根目录。 60 | 61 | ## 安装阿里网盘备份工具 62 | 63 | Github 项目地址:[https://github.com/tickstep/aliyunpan](https://github.com/tickstep/aliyunpan) 64 | 65 | 1. 下载工具包 66 | 67 | ```bash 68 | wget -P "/var/script" https://github.com/tickstep/aliyunpan/releases/download/v0.3.2/aliyunpan-v0.3.2-linux-amd64.zip -O "/var/script/aliyunpan.zip" 69 | ``` 70 | 71 | 2. 解压工具包 72 | 73 | ```bash 74 | unzip "/var/script/aliyunpan.zip" -d "/var/script" 75 | ``` 76 | 77 | 3. 删除压缩包 78 | 79 | ```bash 80 | rm "/var/script/aliyunpan.zip" 81 | ``` 82 | 83 | 4. 重命名工具包名 84 | 85 | ```bash 86 | mv "/var/script/$(ls "/var/script" | grep "aliyunpan")" "/var/script/aliyunpan" 87 | ``` 88 | 89 | 5. 登录阿里云盘 90 | 91 | ```bash 92 | /var/script/aliyunpan/aliyunpan login 93 | ``` 94 | 95 | ## Shell 备份脚本 96 | 97 | > 将`数据路径`,`网站根目录名称`,`数据库名称`,`数据库用户名`,`数据库密码`改为自己的 98 | 99 | ### 使用于只用 docker-compose 搭建,只需要备份文件,并上传到网盘 100 | 101 | ```bash 102 | #!/bin/bash 103 | 104 | web_path="/var/www" # 数据路径 105 | date_time=$(date +"%Y_%m_%d") # 日期格式 106 | year=$(date +"%Y") #年份 107 | aliyunpan="/var/script/aliyunpan/aliyunpan" #阿里云盘可执行文件路径 108 | 109 | # 激活百度网盘环境 110 | source "/var/script/venv/bin/activate" 111 | 112 | for item in "$web_path"/*; do 113 | item_name=$(basename "$item") 114 | # 切换到网站目录进行压缩 115 | cd "$item" || exit 116 | tar -czf "${item_name}_${date_time}.tar.gz" . 117 | # 上传到百度网盘存储 118 | bypy upload "${item_name}_${date_time}.tar.gz" "/${item_name}/" 119 | # 上传到阿里云盘 120 | $aliyunpan upload "${item_name}_${date_time}.tar.gz" "/网站/${item_name}/${year}/" 121 | # 删除文件 122 | rm "${item_name}_${date_time}.tar.gz" 123 | done 124 | ``` 125 | 126 | ### 适用于 mysql+nginx 的网站,需要备份文件和数据库,并上传到网盘 127 | 128 | ```bash 129 | #!/bin/bash 130 | 131 | web_path="/var/www" # 数据路径 132 | web_arry=("alist" "bitwarden" "blog") # 网站根目录名称 133 | mysql_arry=("blog") # 数据库名称 134 | date_time=$(date +"%Y_%m_%d") # 日期格式 135 | year=$(date +"%Y") #年份 136 | user="root" # 数据库用户名 137 | password="lsy22.com" # 数据库密码 138 | original_dir=$(pwd) # 记录原始目录 139 | 140 | # 激活百度网盘环境 141 | source ~/myvenv/bin/activate 142 | 143 | # 组合备份 144 | for item in "${mysql_arry[@]}"; do 145 | # 创建SQL备份 146 | mysqldump -u $user -p$password ${item} > "${item}_${date_time}.sql" 147 | 148 | # 检查是否有同名的网站目录 149 | if [[ " ${web_arry[@]} " =~ " ${item} " ]]; then 150 | # 切换到网站目录进行压缩 151 | cd "${web_path}/${item}/" || exit 152 | zip -r "${item}_web_${date_time}.zip" . 153 | # 将数据库SQL文件和网站压缩包一起压缩 154 | zip "${item}_${date_time}.zip" "${item}_${date_time}.sql" "${item}_web_${date_time}.zip" 155 | # 删除临时的网站压缩包 156 | rm "${item}_web_${date_time}.zip" 157 | # 返回原始目录 158 | cd "$original_dir" || exit 159 | else 160 | # 否则,只压缩数据库 161 | zip "${item}_${date_time}.zip" "${item}_${date_time}.sql" 162 | fi 163 | rm "${item}_${date_time}.sql" 164 | # 上传到云存储 165 | bypy upload "${item}_${date_time}.zip" "/${item}/${year}/" 166 | # 上传到百度网盘存储 167 | bypy upload "${item}_${date_time}.zip" "/${item}/" 168 | # 上传到阿里云盘 169 | aliyunpan upload "${item}_${date_time}.zip" "/网站/${item}/${year}/" 170 | # 删除文件 171 | rm "${item}_${date_time}.zip" 172 | done 173 | 174 | # 单独备份那些没有同名数据库的网站目录 175 | for item in "${web_arry[@]}"; do 176 | if [[ ! " ${mysql_arry[@]} " =~ " ${item} " ]]; then 177 | # 切换到网站目录进行压缩 178 | cd "${web_path}/${item}/" || exit 179 | zip -r "${item}_${date_time}_data.zip" . 180 | # 上传到百度网盘存储 181 | bypy upload "${item}_${date_time}.zip" "/${item}/" 182 | # 上传到阿里云盘 183 | aliyunpan upload "${item}_${date_time}.zip" "/网站/${item}/${year}/" 184 | # 删除文件 185 | rm "${item}_${date_time}_data.zip" 186 | # 返回原始目录 187 | cd "$original_dir" || exit 188 | fi 189 | done 190 | ``` 191 | 192 | ### 适用于 mysql+nginx 的网站,需要备份文件和数据库 193 | 194 | ```bash 195 | #!/bin/bash 196 | 197 | web_path="/var/www" # 数据路径 198 | web_arry=("alist" "bitwarden" "blog") # 网站根目录名称 199 | mysql_arry=("blog" "study") # 数据库名称 200 | date_time=$(date +"%Y_%m_%d") 201 | year=$(date +"%Y") 202 | user="" # 数据库用户名 203 | password="" # 数据库密码 204 | 205 | for item in ${mysql_arry[@]};do 206 | mkdir -p "$item/$year" 207 | mysqldump -u $user -p$password ${item} > "${item}_${date_time}.sql" 208 | zip "./$item/$year/${item}_${date_time}.zip" "./${item}_${date_time}.sql" 209 | rm "./${item}_${date_time}.sql" 210 | done 211 | 212 | for item in ${web_arry[@]};do 213 | mkdir -p "./$item/$year" 214 | zip -r "./${item}_${date_time}_data.zip" "${web_path}/${item}" 215 | if [ -f "./$item/$year/${item}_${date_time}.zip" ];then 216 | zip -u "./$item/$year/${item}_${date_time}.zip" "./${item}_${date_time}_data.zip" 217 | else 218 | zip "./$item/$year/${item}_${date_time}.zip" "./${item}_${date_time}_data.zip" 219 | fi 220 | rm "./${item}_${date_time}_data.zip" 221 | done 222 | ``` 223 | 224 | ## 添加执行权限 225 | 226 | ```bash 227 | chmod +x backups.sh 228 | ``` 229 | 230 | ## 设置定时任务 231 | 232 | 1.编辑 crontab 以自动执行备份脚本: 233 | 234 | ```bash 235 | crontab -e 236 | ``` 237 | 238 | 添加以下内容,调整脚本路径为实际路径: 239 | 240 | ```cron 241 | 0 0 1 * * /var/script/backups.sh # 每个月的第一天的午夜(00:00)执行 242 | ``` 243 | 244 | 2.重启 cron 服务以应用更改: 245 | 246 | ```bash 247 | sudo systemctl restart cron 248 | ``` 249 | -------------------------------------------------------------------------------- /src/plugins/build-article-index.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { execFileSync } from 'node:child_process'; 5 | 6 | // 获取当前文件的目录 7 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 | // 获取项目根目录 9 | const rootDir = path.resolve(__dirname, '../..'); 10 | // 构建目录在根目录下 11 | const buildDir = path.resolve(rootDir, 'dist'); 12 | // 索引文件存储位置 13 | const indexDir = path.join(buildDir, 'client', 'index'); 14 | 15 | // 二进制可执行文件路径 16 | const binaryPath = path.join(rootDir, 'src', 'assets', 'article-index', process.platform === 'win32' 17 | ? 'article-indexer-cli.exe' 18 | : 'article-indexer-cli'); 19 | 20 | /** 21 | * 创建Astro构建后钩子插件,用于生成文章索引 22 | * @returns {import('astro').AstroIntegration} Astro集成对象 23 | */ 24 | export function articleIndexerIntegration() { 25 | return { 26 | name: 'article-indexer-integration', 27 | hooks: { 28 | // 开发服务器钩子 - 为开发模式添加虚拟API路由 29 | 'astro:server:setup': ({ server }) => { 30 | // 为index目录下的文件提供虚拟API路由 31 | server.middlewares.use((req, res, next) => { 32 | // 检查请求路径是否是索引文件 33 | if (req.url.startsWith('/index/') && req.method === 'GET') { 34 | const requestedFile = req.url.slice(7); // 移除 '/index/' 35 | const filePath = path.join(indexDir, requestedFile); 36 | 37 | console.log(`虚拟API请求: ${req.url} -> ${filePath}`); 38 | 39 | // 检查文件是否存在 40 | if (fs.existsSync(filePath)) { 41 | const stat = fs.statSync(filePath); 42 | if (stat.isFile()) { 43 | // 设置适当的Content-Type 44 | let contentType = 'application/octet-stream'; 45 | if (filePath.endsWith('.json')) { 46 | contentType = 'application/json'; 47 | } else if (filePath.endsWith('.bin')) { 48 | contentType = 'application/octet-stream'; 49 | } 50 | 51 | res.setHeader('Content-Type', contentType); 52 | res.setHeader('Content-Length', stat.size); 53 | fs.createReadStream(filePath).pipe(res); 54 | return; 55 | } 56 | } 57 | 58 | // 文件不存在,返回404 59 | res.statusCode = 404; 60 | res.end('索引文件未找到'); 61 | return; 62 | } 63 | 64 | // 不是索引文件请求,继续下一个中间件 65 | next(); 66 | }); 67 | }, 68 | 'astro:build:done': async ({ dir, pages }) => { 69 | console.log('Astro构建完成,开始生成文章索引...'); 70 | 71 | // 获取构建目录路径 72 | let buildDirPath; 73 | 74 | // 直接处理URL对象 75 | if (dir instanceof URL) { 76 | buildDirPath = dir.pathname; 77 | // Windows路径修复 78 | if (process.platform === 'win32' && buildDirPath.startsWith('/') && /^\/[A-Z]:/i.test(buildDirPath)) { 79 | buildDirPath = buildDirPath.substring(1); 80 | } 81 | } else { 82 | buildDirPath = String(dir); 83 | } 84 | 85 | // 确定客户端输出目录 86 | let clientDirPath = buildDirPath; 87 | const clientSuffix = path.sep + 'client'; 88 | 89 | if (buildDirPath.endsWith(clientSuffix)) { 90 | clientDirPath = buildDirPath; 91 | } else if (fs.existsSync(path.join(buildDirPath, 'client'))) { 92 | clientDirPath = path.join(buildDirPath, 'client'); 93 | } 94 | 95 | // 索引输出目录 96 | const outputDirPath = path.join(clientDirPath, 'index'); 97 | 98 | await generateArticleIndex({ 99 | buildDir: clientDirPath, 100 | outputDir: outputDirPath 101 | }); 102 | } 103 | } 104 | }; 105 | } 106 | 107 | /** 108 | * 生成文章索引 109 | * 使用二进制可执行文件直接扫描HTML目录并生成索引 110 | * @param {Object} options - 选项对象 111 | * @param {string} options.buildDir - 构建输出目录 112 | * @param {string} options.outputDir - 索引输出目录 113 | * @returns {Promise} 索引生成结果 114 | */ 115 | export async function generateArticleIndex(options = {}) { 116 | console.log('开始生成文章索引...'); 117 | 118 | try { 119 | // 使用提供的目录或默认目录 120 | const buildDirPath = options.buildDir || buildDir; 121 | const outputDirPath = options.outputDir || indexDir; 122 | 123 | console.log(`构建目录: ${buildDirPath}`); 124 | console.log(`索引输出目录: ${outputDirPath}`); 125 | 126 | // 确保索引目录存在 127 | if (!fs.existsSync(outputDirPath)) { 128 | console.log(`创建索引输出目录: ${outputDirPath}`); 129 | fs.mkdirSync(outputDirPath, { recursive: true }); 130 | } 131 | 132 | // 检查二进制文件是否存在 133 | if (!fs.existsSync(binaryPath)) { 134 | throw new Error(`索引工具不存在: ${binaryPath}`); 135 | } 136 | 137 | // 检查构建目录是否存在 138 | if (!fs.existsSync(buildDirPath)) { 139 | throw new Error(`构建目录不存在: ${buildDirPath}`); 140 | } 141 | 142 | // 设置二进制可执行文件权限(仅Unix系统) 143 | if (process.platform !== 'win32') { 144 | fs.chmodSync(binaryPath, 0o755); 145 | } 146 | 147 | try { 148 | // 执行索引命令,直接捕获输出 149 | const result = execFileSync(binaryPath, [ 150 | '--source', // 源目录参数名 151 | buildDirPath, // 源目录值 152 | '--output', // 输出目录参数名 153 | outputDirPath, // 输出目录值 154 | '--verbose', // 输出详细日志 155 | // '--all' // 索引所有页面类型 156 | ], { 157 | encoding: 'utf8', 158 | // 在Windows上禁用引号转义,防止参数解析问题 159 | windowsVerbatimArguments: process.platform === 'win32' 160 | }); 161 | 162 | console.log(result); 163 | console.log('文章索引生成完成!'); 164 | console.log(`索引文件保存在: ${outputDirPath}`); 165 | 166 | return { 167 | success: true, 168 | indexPath: outputDirPath 169 | }; 170 | } catch (execError) { 171 | console.error('执行索引工具时出错:', execError.message); 172 | if (execError.stdout) console.log('标准输出:', execError.stdout); 173 | if (execError.stderr) console.log('错误输出:', execError.stderr); 174 | 175 | // 尝试直接读取构建目录内容并打印,帮助调试 176 | try { 177 | console.log(`构建目录内容 (${buildDirPath}):`); 178 | const items = fs.readdirSync(buildDirPath); 179 | for (const item of items) { 180 | const itemPath = path.join(buildDirPath, item); 181 | const stats = fs.statSync(itemPath); 182 | console.log(`- ${item} (${stats.isDirectory() ? '目录' : '文件'}, ${stats.size} 字节)`); 183 | } 184 | } catch (fsError) { 185 | console.error('无法读取构建目录内容:', fsError.message); 186 | } 187 | 188 | throw execError; 189 | } 190 | } catch (error) { 191 | console.error('生成文章索引时出错:', error.message); 192 | 193 | // 更详细的错误信息 194 | if (error.stdout) console.log('标准输出:', error.stdout); 195 | if (error.stderr) console.log('错误输出:', error.stderr); 196 | 197 | return { 198 | success: false, 199 | error: error.message 200 | }; 201 | } 202 | } 203 | --------------------------------------------------------------------------------
18 | 页面未找到 19 |
23 | 您访问的页面不存在或已被移动到其他位置 24 |
正在加载WASM模块...
WASM处理结果:
{result}
{error}
加载中...
143 | {book.author} 144 |