基于腾讯云 COS 的 Obsidian 三端同步方案:从 WebDAV 困境到 S 3 协议实践

引言 Obsidian 是一款优秀的本地知识库工具,但是其没有免费的多端同步服务,其他人我不知道,这个问题反正很困扰本人。我主要是在教室主要使用平板编辑,在寝室就换为电脑了,所以有频繁的多端同步需求。 奈何官方同步服务收费较高,而第三方网盘(iCloud、百度云、OneDrive 等)存在隐私顾虑和速度限制。所以我选择自建同步方案,但这么搞往往需要处理协议兼容性、权限配置等等细节。于是在寒假折腾一天,这里就总结一下,旨在为有类似需求的读者提供一份兼顾专业性与可操作性的参考。 本文记录的是我从最初尝试 Nextcloud+WebDAV 到最终采用腾讯云 COS(对象存储)实现电脑、手机、平板三端稳定同步的全过程,尝试深入剖析各阶段遇到的技术问题及其原理。 本文部分使用 AI 辅助编写。 WebDAV 方案的尝试与失败 初始架构:Nextcloud + WebDAV 我最初利用服务器,通过 Docker (服务器还有其他东西,求放心搞了一个隔离)部署了 Nextcloud(版本 23),并启用其 WebDAV 服务。在电脑端 Obsidian 中安装 Remotely Save 插件,配置 WebDAV 连接后,同步功能正常,文件可双向传输。这就验证了自建同步的可行性。 安卓端的异常现象 但是,当在安卓手机和平板上进行同样配置时,问题出现了:同步状态极不稳定,频繁返回 401 Unauthorized 错误,连接测试时而成功,时而失败,无固定规律。偶尔成功同步一次后,再次尝试即失败。设备重启后短暂恢复,随后再次报错。不同安卓设备表现一致,排除了个别设备故障。 排查过程: 针对上述现象,我和 AI 聊了聊,做了排查: 凭证验证:重置 Nextcloud 账户密码,重新输入插件配置,问题依旧。 域名与信任:在 Nextcloud 的 config.php 中添加了手机访问的 IP 和域名,避免请求被拒绝。 网络环境:切换 Wi-Fi、4 G/5 G,甚至使用手机热点连接,未能改善。 服务:使用手机浏览器直接访问 Nextcloud 的 WebDAV 地址,输入凭证后可以正常浏览目录,说明服务本身是正常的,且网络无阻断。 根本原因分析 走投无路,于是求助伟大的 deepseek ,他查阅 Remotely Save 插件开发者文档及相关技术讨论后,发现了问题根源:安卓系统底层对 WebDAV 协议的非标准 HTTP 方法支持存在缺陷。 ...

February 27, 2026 · 2 min · 420 words

记一次 Minecraft 服务器运维:脚本编写漏洞导致的日报数据不全

记一次 Minecraft 服务器运维:脚本编写漏洞导致的日报数据不全 问题现象:日报只记录了“半天”的活动 我搭建的 Minecraft Fabric 1.20.1 服务器每天重启四次(0、6、12、18 点),并在 23:55 生成一份 Markdown 日报。运行一段时间后,我不幸的发现:日报里的“最近聊天记录”只显示最后几小时的消息,而“活跃玩家列表”中的“最后活动”时间其实是玩家最后一次上线时间,根本不是真正的退出时间。简单来说,日报数据严重不全,几乎失去了参考价值。这令人沮丧,本来我就被脚本妙妙小 bug 折磨,现在又要来修了,于是在精心编写了提示词后与 AI 好好学习了一下,现在总结问题发出来。 为什么会出现数据不全? 要搞清原因,得先了解 Minecraft 服务器的日志机制。 1. 服务器重启会覆盖 latest.log Minecraft 服务端(包括 Fabric)运行时,会把所有日志实时写入 logs/latest.log。但当服务器重启(无论是正常关闭还是崩溃重启),latest.log 通常会被清空或覆盖。新启动的服务器会创建一个全新的 latest.log,只记录本次启动后的日志。 我之前是打算每天 23:55 生成报告,服务器这之前不重启。但后来为了保证服务器稳定(对钱包的妥协),设置了每天四次定时重启(通过维护脚本)。这就导致:23:55 生成日报时,latest.log 里只保存了最后一次重启(18 点)之后到现在的日志,而 0 点到 18 点之间的所有日志都丢失了。 2. 旧脚本只盯着 latest.log 看看旧的日报脚本是怎么获取日志的(简化版): LOG_FILE="$SERVER_DIR/logs/latest.log" TODAY_LOG=$(grep "$TODAY" "$LOG_FILE" 2>/dev/null) if [ -z "$TODAY_LOG" ]; then TODAY_LOG=$(tail -1000 "$LOG_FILE") fi 这段代码试图从 latest.log 中提取当天的日志行(按日期字符串过滤),如果找不到(比如日志里没有日期前缀,或者重启后日期已变),就退而取最后 1000 行(至少想到了这个备案)。但是显然,无论哪种方式,都只能得到部分日志。 3. 三步之内必有解药:日志归档目录 logs/ 实际上,Minecraft 服务端在日志轮转方面做得挺周到(屎山代码居然还有小巧思)——它会将旧的日志自动压缩并存放到 logs/ 目录下(缺点就是积压一堆不大但是碍眼的压缩包),文件名通常像这样: l l l o o o g g g s s s / / / 2 2 2 0 0 0 2 2 2 5 5 5 - - - 0 0 0 2 2 2 - - - 2 2 2 3 3 4 - - - 1 2 1 . . . l l l o o o g g g . . . g g g z z z 这些压缩文件里保存着全天的日志,按时间分段。如果日报脚本能读取这些文件,就能拼凑出完整的日志历史。 ...

February 26, 2026 · 2 min · 351 words

博客项目技术文档

博客项目概述 一、项目简介 这是一个基于 Hugo 静态网站生成器 + PaperMod 主题的个人博客系统。代码托管在 Gitee 私有仓库,通过 Caddy 作为 Web 服务器自动提供 HTTPS 服务,并利用服务器定时任务(crontab)实现自动拉取更新、构建和部署。整个系统部署在腾讯云服务器(Ubuntu 24.04)上,同时服务器还运行着 Nextcloud 私有云盘和 Jupyter 科学计算环境(均通过 Docker 容器管理)。 二、技术栈 静态生成器:Hugo(v 0.146.0+extended) 主题:PaperMod(手动下载,非 Git 子模块) 版本控制:Git + Gitee(国内镜像,私有仓库) Web 服务器:Caddy(Docker 容器),自动申请/续期 SSL 证书 部署方式:本地推送 → Gitee → 服务器定时拉取 → Hugo 构建 → Caddy 服务 服务器 OS:Ubuntu 24.04 LTS 容器管理:Docker + Docker Compose(管理 Caddy、Nextcloud、Jupyter 等) 自动部署:crontab 每分钟执行自定义脚本 /home/your-username/auto-deploy.sh 三、系统架构 本地电脑 │ 写文章 (Markdown) │ hugo new posts/xxx.md │ 编辑后 git add/commit/push ▼ Gitee 仓库 (私有) │ ▼ 腾讯云服务器 ├─ crontab (每分钟) │ └─ 运行 /home/your-username/auto-deploy.sh │ ├─ git pull 拉取最新源码 │ ├─ hugo --minify 生成静态文件到临时目录 │ ├─ 替换 /home/your-username/blog/public │ ├─ 修复权限为 www-data │ └─ docker restart caddy └─ Caddy 容器 (监听 80/443) └─ 托管 /home/your-username/blog/public 四、关键配置与路径 4.1 本地博客源码 路径:本地博客路径 配置文件:hugo.toml(或 config.toml),内容示例: baseURL = 'https://lingzuian.top/' languageCode = 'zh-cn' title = '静影沉璧到此一游' theme = 'PaperMod' copyright = '© 2026 静影沉璧到此一游 | 备案号:<a href="https://beian.miit.gov.cn/" target="_blank">湘ICP备2026005470号</a>' [params] math = true # 开启 KaTeX 数学公式 [menu] [[menu.main]] name = "文章" url = "/posts/" weight = 10 [[menu.main]] name = "关于" url = "/about/" weight = 20 文章存放:content/posts/ 页面存放:content/,如 about.md 自定义模板:layouts/partials/,如 math.html(加载 KaTeX) 4.2 服务器关键目录 博客源码:/home/your-username/blog-source(从 Gitee 克隆) 静态文件目录:/home/your-username/blog/public(Caddy 挂载的根目录) 自动部署脚本:/home/your-username/auto-deploy.sh 部署日志:/home/your-username/auto-deploy.log Caddy 配置:/home/your-username/caddy/Caddyfile Caddy Compose 文件:/home/your-username/caddy/docker-compose.yml 4.3 自动部署脚本(当前稳定版) #!/bin/bash SOURCE_DIR="/home/your-username/blog-source" PUBLIC_DIR="/home/your-username/blog/public" TEMP_DIR="/home/your-username/blog/temp" cd "$SOURCE_DIR" || exit 1 git fetch origin LOCAL=$(git rev-parse HEAD) REMOTE=$(git rev-parse origin/main) if [ "$LOCAL" = "$REMOTE" ]; then echo "No updates, exiting." exit 0 fi echo "Updates found, pulling changes..." if ! git pull; then echo "Git pull failed. Aborting deployment." exit 1 fi mkdir -p "$TEMP_DIR" sudo chown your-username:your-username "$TEMP_DIR" if ! hugo --minify --destination "$TEMP_DIR"; then echo "Hugo build failed, aborting deployment. Keeping old site." rm -rf "$TEMP_DIR" exit 1 fi sudo rm -rf "$PUBLIC_DIR" sudo mv "$TEMP_DIR" "$PUBLIC_DIR" sudo chown -R 33:33 "$PUBLIC_DIR" sudo chmod -R 755 "$PUBLIC_DIR" docker restart caddy echo "Deployment complete." 4.4 Caddy 配置 (Caddyfile) lingzuian.top { root * /var/www/blog file_server } cloud.internal.example.com { reverse_proxy nextcloud_app:80 } jupyter.internal.example.com { reverse_proxy math_jupyter:8888 } 4.5 Caddy Docker Compose (docker-compose.yml) services: caddy: image: caddy:latest container_name: caddy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - /home/your-username/blog/public:/var/www/blog - caddy_data:/data - caddy_config:/config networks: - web volumes: caddy_data: caddy_config: networks: web: external: true 五、自动部署流程详解 crontab 每分钟执行 /home/your-username/auto-deploy.sh。 脚本先 git fetch origin 比较本地与远程 commit hash: 若相同 → 退出(无更新)。 若不同 → 执行 git pull 拉取最新代码。 拉取成功后,创建临时目录 /home/your-username/blog/temp,确保所有者是 your-username。 运行 hugo --minify --destination 生成静态文件到临时目录。 构建成功:删除旧 public 目录,将 temp 原子移动为 public,然后修复所有者为 www-data(UID 33)并设置权限 755,最后重启 Caddy。 构建失败:保留旧 public 目录,删除临时目录,退出(网站不变)。 六、常见问题与解决方案(历史坑点) 6.1 权限问题(Permission denied) 现象:Hugo 构建时无法写入 public 目录,或 Caddy 无法读取文件。 原因:public 目录所有者不是运行 Hugo 的用户(your-username)或不是容器内的用户(www-data)。 解决方案: 在脚本中,生成前确保 temp 目录归 your-username,生成后 public 归 www-data。 手动修复:sudo chown -R 33:33 /home/your-username/blog/public 当前状态:脚本已正确处理,若手动操作后需检查权限。 6.2 Git 冲突或无法推送 现象:git push 被拒,提示 non-fast-forward 或 divergent branches。 原因:在服务器上直接提交了代码,导致本地与远程分叉。 解决方案: 永远不要在服务器上执行 git commit。所有修改必须在本地进行。 若已发生,需在服务器上 git pull 合并(可能冲突),再 git push 解决。 预防:遵守“只从本地推送”原则。 6.3 Hugo 构建失败(Front Matter 格式错误) 现象:构建时报错 unmarshal failed: toml: expected character =,导致网站无法更新。 原因:新建或修改的 Markdown 文件开头元数据格式有误(如误用 --- 代替 +++,或缺少等号)。 解决方案: 确保文件开头是 +++,每行格式如 key = "value"。 推送前用 hugo server -D 本地预览,可提前发现错误。 脚本保护:脚本在构建失败时会保留旧网站,避免 404。 6.4 Caddy 挂载失效(目录 inode 变化) 现象:网站 404,但服务器上 public 目录有文件,容器内 /var/www/blog 为空。 原因:脚本用 sudo rm -rf 删除旧目录再重建,导致目录 inode 变化,Docker 挂载指向了已删除的 inode。 解决方案: 改用 sudo rm -rf "$PUBLIC_DIR"/* 清空内容而不删除目录本身(保留 inode)。 或采用临时目录原子替换(当前脚本已使用 mv 替换整个目录,但 mv 后 inode 改变,仍需重启 Caddy)。因此脚本最后重启 Caddy 确保重新挂载。 当前状态:脚本通过重启 Caddy 解决此问题,且只在有更新时重启,无更新时不重启。 6.5 自动部署脚本误判更新 现象:即使无更新,脚本也执行构建和重启。 原因:早期脚本通过 git pull 的输出判断更新,不够准确。 解决方案:改用 git fetch 比较 commit hash(当前脚本已修复)。 6.6 KaTeX 公式不渲染 现象:文章中的 $...$ 或 $$...$$ 未渲染为数学公式。 原因:KaTeX 脚本未正确加载或定界符未配置。 解决方案: 在 layouts/partials/math.html 中添加 KaTeX 脚本和 renderMathInElement 调用(已配置)。 确保文章或全局开启了 math = true。 七、注意事项与最佳实践 绝对不要在服务器上直接修改文件(包括 nano 编辑),所有更改都应在本地完成并推送。 绝对不要在 Gitee 网页上直接编辑文件,以免造成版本混乱。 推送前务必本地预览:hugo server -D 检查格式和渲染。 新文章必须 git add:新建文件后,先用 git status 确认已加入版本控制。 定期查看服务器日志:tail -f /home/your-username/auto-deploy.log 确保脚本正常运行。 如果需紧急修改服务器文件(不推荐),改完后必须立即 git commit 并 git push,然后在本地 git pull 同步。 八、日常维护与更新指南 8.1 写一篇新文章 # 本地 cd 本地博客路径 hugo new posts/文章名.md # 编辑文章(修改 draft = false) hugo server -D # 预览 # 满意后 git add . git commit -m "新增文章:文章名" git push 8.2 修改已有文章 # 编辑文件后 git commit -am "更新文章:xxx" # 针对已追踪文件 git push 8.3 修改主题或配置 所有修改在本地进行,然后 git add/commit/push。 主题文件位于 themes/PaperMod/,若有自定义覆盖,放在 layouts/ 或 assets/css/extended/。 8.4 升级 Hugo 版本 服务器上需手动下载新版 hugo 二进制并替换 /usr/local/bin/hugo。 本地也需要同步升级,避免版本差异导致的构建问题。 8.5 处理服务器故障 Caddy 挂了:docker restart caddy 或 cd ~/caddy && docker-compose down && docker-compose up -d 磁盘空间满:检查日志大小,清理旧日志或无用的 Docker 镜像。 域名证书问题:Caddy 会自动续期,无需干预。 九、联系与备份 博客域名:https://lingzuian.top(备案号:湘 ICP 备 2026005470 号) Gitee 仓库:私有仓库,地址不公开 服务器:仅管理员 SSH 登录 数据备份:建议定期将 /home/your-username/blog/public 和 /home/your-username/blog-source 备份到 COS 或 Nextcloud。 本概述涵盖了项目架构、配置、历史问题和维护要点。希望未来无论由谁(或哪个 AI)接手,都能快速理解并有效协助。 ...

February 26, 2026 · 4 min · 694 words