# 浏览器书签同步插件 **Repository Path**: yaogedahuoji/bookmark ## Basic Information - **Project Name**: 浏览器书签同步插件 - **Description**: 使用Gitee的Gist实现浏览器书签同步功能。 - **Primary Language**: JavaScript - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2026-04-19 - **Last Updated**: 2026-10-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: 书签同步, 收藏夹同步 ## README

BookmarkManager Logo

📚 BookmarkManager · 书签管理员

一款简洁高效的 Chrome 书签同步管理插件(原 CowBookmark · 奶牛书签),已原生兼容移动端。

version manifest v3 platform cloud storage license

> 💡 **项目拆分提示**:原插件集中的 **SimpleTab(新标签页)** 与 **SimpleSearch(搜索浮窗)** 已迁出,合并为独立项目 —— **[SimpleTab · 极简新标签页扩展](https://gitee.com/yaogedahuoji/newTab)**。 *** ## ✨ 核心亮点 * ☁️ **云端同步** —— 基于 Gitee Gist,本地书签随时增量同步到云端,换设备不丢书签 * 🔍 **智能检测** —— 重复书签、失效链接、同域名冗余、空收藏夹,一键清理 * 🆚 **书签对比** —— 本地 / 云端 / HTML 导出 / 历史版本任意对比,差异一目了然 * 🗑️ **回收站 & 历史版本** —— 误删可恢复,任意时间点可回滚(IndexedDB 本地存储) * 📱 **移动端适配** —— BookmarkManager 弹窗与管理页已针对触控优化,可直接在 Kiwi、狐猴等支持扩展的移动端浏览器中使用,无需单独的移动版插件 * 🔐 **隐私可控** —— 隐私书签、数据加密(AES‑CTR)、忽略隐私覆盖,隐私牢牢掌握在自己手里 *** ## 🚀 功能特性 ### BookmarkManager · 弹窗 | 功能 | 说明 | | ----------- | ------------------------------------------- | | 🔼 **增量上传** | 只上传新增或修改过地址的书签,已存在的书签不上传。以本地目录结构为基准,补充云端后上传 | | 🔽 **增量拉取** | 只拉取云端新增或修改过地址的书签。以云端目录结构为基准,补充本地后拉取 | | 📤 **书签导出** | 将本地书签导出为 HTML 文件,可分享或备份,可直接用浏览器自带功能导入 | ### BookmarkManager · 管理面板 打开方式:网页右键菜单「管理」、或快捷键 `Alt+B`(可在 `chrome://extensions/shortcuts` 修改)。右上角支持深色 / 浅色 / 跟随系统主题切换。 | 功能 | 说明 | | ------------------ | ----------------------------------------------------------------------------- | | 🖱️ **打开方式** | 在浏览器网页中右键菜单即可打开收藏夹管理页面 | | 🔁 **重复检测** | 检测书签栏中重复网址,一键或手动删除重复项,只保留首个 | | 🆚 **书签对比** | 本地 vs 云端 / HTML 导出 / 历史版本对比查看差异,一键补全本地缺失书签。⚠️ 对比云端前请先去除重复书签;开启隐私书签且含重复项时会影响结果 | | 💔 **失效检测** | 检测失效链接,可一键删除或移入「失效」收藏夹,支持 HEAD(快)/ GET(准)两种方式,可配超时与并发。⚠️ 结果仅供参考,不保证 100% 准确 | | 🧹 **清除本地数据** | 清除本地保存的 Gitee 令牌及 Gist ID | | ☁️ **清除 Gitee 数据** | 删除 Gitee 云端存储的书签 Gist | | ♻️ **重置** | 重置管理页签下的所有设置 | | 🔐 **数据加密** | 开启后上传到云端的书签以 AES‑CTR 加密存储 | | 🔑 **锁屏密码** | 开启后锁定插件管理及弹窗页面,并加密令牌 | | ⏲ **自动同步** | 后台按设定间隔(分钟)自动增量上传 | | ⏰ **定时同步** | 每天在设定时间点自动执行增量拉取 / 增量上传(时分) | | 🛡️ **忽略隐私覆盖** | 覆盖上传 / 拉取时,将云端隐私书签与本地合并后再覆盖,避免隐私书签被误删或丢失 | | ⬆️ **覆盖上传** | 用本地书签整体替换云端书签 | | ⬇️ **覆盖拉取** | 用云端书签整体替换本地书签 | | 🌐 **同域名检测** | 按域名分组展示书签,一键清理同一网站的冗余书签 | | 📂 **空收藏夹检测** | 检测书签栏中的空收藏夹,支持单个或批量删除 | | 🗑️ **回收站** | 删除的书签先移入回收站(基于 IndexedDB),支持恢复、永久删除、清空,防误删 | | 🕓 **历史版本** | 每次拉取自动保存快照到 IndexedDB,可随时回滚到任意版本 | | 🔒 **隐私书签** | 选定的一级子文件夹在拉取时不会同步到本地,保护隐私。💡 个人电脑关闭、公司电脑开启,兼顾便利与隐私 | *** ## 📦 安装方法 ### 1. 获取代码 克隆(或下载)项目到本地: ```bash git clone https://gitee.com/yaogedahuoji/CowBookmark.git ``` ### 2. 加载到 Chrome 1. 打开 Chrome,访问 `chrome://extensions/` 进入扩展管理页 2. 开启右上角的 **「开发者模式」** 3. 点击 **「加载已解压的扩展程序」** 4. 选择项目根目录下的 **`BookmarkManager`** 文件夹(书签同步管理,PC 与移动端通用,弹窗 / 管理页已触控优化) 5. 安装完成后,浏览器右上角会出现插件图标 ### 3. 移动端浏览器(可选) 在 Kiwi、狐猴等支持 Chrome 扩展的安卓浏览器中,以「开发者模式 → 加载已解压的扩展程序」方式加载 **`BookmarkManager`** 文件夹即可。BookmarkManager 本身已兼容移动端,弹窗与管理页为触控优化的界面,无需单独安装移动版插件。 *** ## 🎯 使用指南 ### 1. 注册 Gitee 账号 若还没有账号,先前往 [Gitee](https://gitee.com) 注册一个。 ### 2. 创建 Gitee 私人令牌 1. 登录 Gitee,点击右上角头像 → **「设置」** 2. 进入 **「安全设置」→「私人令牌」** 3. 填写令牌名称(如 `BookmarkManager`),勾选 **`gist`** 权限 ### 3. 配置令牌 点击插件图标,输入 Gitee 私人令牌,即可使用全部功能。 *** ## 🎨 界面预览

Popup 弹窗

收藏夹管理

书签对比

重复检测

同域名检测

失效检测

空收藏夹检测

回收站

历史版本
*** ## 🧠 实现思路 利用 Gitee 的 Gist API 实现书签的云端存储与同步。首次上传时: 1. 先从本地读取已保存的 Gist ID; 2. 若读取不到,则通过令牌 + 代码概要(`CowBookmarks`)分页查找已有 Gist; 3. 找到则复用其 Gist ID,否则创建新 Gist,并把 Gist ID 存回本地以便后续操作。 *** ## ⚙️ 技术栈 | 类别 | 技术 | | ------- | -------------------------------------------------- | | 前端 | HTML5 · CSS3 · JavaScript (ES6+) | | 浏览器 API | Chrome Extensions API · Bookmarks API | | 存储 | Chrome Storage API · IndexedDB · Unlimited Storage | | 加密 | AES‑CTR(数据加密) | *** ## 🔒 权限说明 | 权限 | 用途 | | --------------------------------- | ----------- | | `bookmarks` | 访问与管理浏览器书签 | | `storage` | 存储插件设置与状态 | | `unlimitedStorage` | 存储大量书签数据 | | `alarms` | 设置定时同步任务 | | `contextMenus` | 右键菜单 | | `notifications` | 通知提醒 | | `host_permissions` (``) | 失效检测需访问任意网址 | *** ## 🔑本地存储键 | 键 | 含义 | 备注 | | :----------------------------------------------------------------------- | :------------------------- | :--------------- | | `gitee_access_token` | Gitee 私人令牌(明文) | 开启锁屏后被删除 | | `encToken` / `lockSalt` / `lockIv` | 锁屏加密后的令牌及其参数 | 存在即视为锁屏开启 | | `gitee_gist_id` | 目标 Gist ID |
| | `data_encryption_enabled` | 数据加密开关 |
| | `encryption_key_b64` | 数据加密 AES 密钥(raw 字节 base64) | 由密码派生,本地保存 | | `privacy_bookmarks_enabled` | 隐私书签开关 |
| | `privacy_folders` | 隐私文件夹 ID 列表 |
| | `auto_sync_enabled` / `auto_sync_interval` | 自动同步开关 / 间隔(分钟) | 默认 30 | | `scheduled_task_enabled` / `scheduled_pull_time` / `scheduled_push_time` | 定时任务开关 / 拉取时刻 / 推送时刻 | 默认 09:00 / 21:00 | | `history_enabled` / `trash_enabled` | 历史版本 / 回收站开关 |
| | `newtabTheme` | 主题(system / dark / light) | 管理页与弹窗共用 | *** ## 🔏 隐私声明 本插件只读取浏览器的书签数据,且仅将其保存到 **用户自己配置的 Gitee 账号下的 Gist** 中。不会收集任何其他信息,也不会向任何第三方传输数据。 *** ## 🔐数据加密 ### 密钥策略 * AES 密钥由**密码 + PBKDF2** 确定性派生,派生出的 **raw 密钥字节**(base64)持久化在 `chrome.storage.local` 的 `encryption_key_b64`。 * **密码本身不落盘、也不上传**;只有加密后的书签内容上 Gitee。 * 因此日常解密自动完成、不弹密码;只有在**换设备 / 重装 / 清了 storage** 时才需要重新用同一密码派生(密码仍要自己记牢)。这正好满足「多设备同步 + 可靠还原」。 ### 密码学原语 * **派生**:PBKDF2-SHA256,salt 为硬编码常量 `CowBookmarkSalt`,迭代 **100000** 次,输出 **AES-CTR 128 位** 密钥。 * **加密**:AES-CTR,counter 长度 64 位(仅低 8 字节自增,高 8 字节作 nonce 段)。 > 说明:salt 目前为固定常量(非随机)。鉴于本功能 salt 本就公开,其收益有限,故未随机化;如需更强抗预计算可后续改为随机 salt 并随密文存储。 ### 加密格式(管线) ``` 明文 JSON → compressGzip:gzip 压缩,前缀 COWGZ: → encryptBookmarks:AES-CTR 加密 · 每条数据随机生成 16 字节 counter(crypto.getRandomValues) · 密文 = counter(16字节) ‖ AES-CTR(counter, 明文) · 整体前缀 Ciphertext: → 上传 Gitee ``` 下载反向(`processCloudContent`):`decryptBookmarks` 先识别 `Ciphertext:` 前缀,取前 16 字节作 counter,再解密 → `decompressGzip` → `JSON.parse`。 ### 跨设备解密 同一密码 → PBKDF2 确定性派生 → 同一密钥 → 跨设备解密正常。在电脑 B 同步时,需**先在该设备开启加密并输入同一密码**(密钥不随密文上传),之后才能解密云端密文。密码必须完全一致(区分大小写)。 ### 开启 / 修改密码 * **开启**:数据管理面板打开「数据加密」开关 → 弹出密码框输密码(二次确认)→ `deriveKeyFromPassword` 派生并 `storeEncryptionKey` 存入 `encryption_key_b64`,置 `data_encryption_enabled=true`。 * **修改**:弹窗输原密码 → 代码把「原密码派生的密钥字节」与「已存的 `encryption_key_b64`」逐字节比对校验原密码 → 用新密码重新派生并覆盖 `encryption_key_b64`。 * 注意:改密实质是**轮换本地密钥**。已上传到 Gitee 的历史快照是用旧密钥加密的,会在下一次同步时被新密钥重新加密覆盖;本地只保留新密钥。 ### 解密失败提示映射(`processCloudContent`) | 异常 | 提示 | | :------------------------------ | :----------------------- | | `ENCRYPTION_KEY_MISSING`(本地无密钥) | 「云端数据已加密,请先在数据管理中设置加密密码」 | | `DECRYPTION_FAILED`(密码错 / 数据损坏) | 「解密失败,密码错误或云端数据已损坏」 | 非加密内容(不以 `Ciphertext:` 开头)原样返回,由后续 gzip / JSON 解析正常处理。 *** ## 📝 开发说明 ### 项目结构 ``` CowBookmark/ ├── BookmarkManager/ # 书签同步管理插件(原 CowBookmark) │ ├── img/ # 插件图标 │ │ └── cadillac16.png / cadillac48.png / cadillac128.png │ ├── js/ # 核心脚本 │ │ ├── background.js # 后台服务(定时同步) │ │ ├── broken-check.js # 失效检测模块 │ │ ├── common.js # 公共工具函数 │ │ ├── compare-bookmarks.js # 书签对比模块 │ │ ├── compare.js # 收藏夹管理界面逻辑(含主题切换) │ │ ├── data-management.js # 数据管理模块 │ │ ├── db.js # IndexedDB 数据库层 │ │ ├── domain-check.js # 同域名检测模块 │ │ ├── duplicate-check.js # 重复检测模块 │ │ ├── empty-folder-check.js # 空收藏夹检测模块 │ │ ├── history.js # 历史版本模块 │ │ ├── lock-screen.js # 锁屏模块 │ │ ├── popup.js # 插件弹窗(增量同步) │ │ ├── theme-init.js # 主题初始化(消除渲染闪烁) │ │ └── trash.js # 回收站模块 │ ├── compare.html # 收藏夹管理界面 │ ├── popup.html # 插件弹窗界面 │ └── manifest.json # 插件配置文件(已兼容移动端,弹窗 / 管理页触控优化) ├── img/ # README 图片 └── README.md ``` ### 核心功能模块 | # | 模块 | 职责 | | -- | ----------------------- | --------------------------------- | | 1 | `popup.js` | 插件弹窗:增量上传 / 拉取、快速打开管理页 | | 2 | `compare.js` | 收藏夹管理界面入口,整合各模块、标签页切换、主题切换 | | 3 | `compare-bookmarks.js` | 书签对比,增量拉取、覆盖上传 / 拉取 | | 4 | `duplicate-check.js` | 重复检测,一键 / 单个删除重复书签 | | 5 | `broken-check.js` | 失效检测(HEAD/GET),可配超时与并发,移动 / 批量删除 | | 6 | `data-management.js` | 数据管理:清令牌、清云端、各功能开关、重置 | | 7 | `domain-check.js` | 同域名分组展示与删除(支持回收站软删除) | | 8 | `empty-folder-check.js` | 空收藏夹检测,单个 / 批量删除 | | 9 | `trash.js` | 基于 IndexedDB 的软删除,恢复 / 永久删除 / 清空 | | 10 | `history.js` | 基于 IndexedDB 的快照,查看与回滚历史版本 | | 11 | `theme-init.js` | 页面渲染前应用主题,消除闪烁 | | 12 | `db.js` | IndexedDB 层(trash、history 两个对象仓库) | | 13 | `background.js` | 后台服务,定时同步书签到云端 | | 14 | `lock-screen.js` | 页面锁屏,令牌 加密 | | 15 | `common.js` | 公共工具函数(加密、Gzip、Gitee API 封装等) | *** ## 🚨 弃用思路 * **多 Gist 存储**:书签过多时将其分段存储(类似分页),并把各分段 Gist ID 顺序存入一个标记 Gist,再通过标记 Gist 操作云端书签。单个 Gist 容量已相当可观——实测约 4k 条书签完全够用,故极端情况暂不处理。 ***

享受更高效的书签管理体验! 🎉