{"id":17948736,"url":"https://github.com/y80/bmm","last_synced_at":"2025-04-05T04:08:37.746Z","repository":{"id":259352228,"uuid":"856806269","full_name":"Y80/bmm","owner":"Y80","description":"BMM，你的专属书签管家 🤵","archived":false,"fork":false,"pushed_at":"2025-03-28T03:46:51.000Z","size":7050,"stargazers_count":148,"open_issues_count":1,"forks_count":43,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T04:31:33.032Z","etag":null,"topics":["bookmark-manager","bookmarks","bookmarks-manager","next","tagging"],"latest_commit_sha":null,"homepage":"https://bmm.lccl.cc","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Y80.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-13T08:37:51.000Z","updated_at":"2025-03-26T14:00:19.000Z","dependencies_parsed_at":"2024-10-24T19:12:13.882Z","dependency_job_id":"be1ac3db-e9f8-41aa-87b8-444f1cbb4390","html_url":"https://github.com/Y80/bmm","commit_stats":null,"previous_names":["y80/bmm"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y80%2Fbmm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y80%2Fbmm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y80%2Fbmm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Y80%2Fbmm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Y80","download_url":"https://codeload.github.com/Y80/bmm/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246131309,"owners_count":20728302,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bookmark-manager","bookmarks","bookmarks-manager","next","tagging"],"created_at":"2024-10-29T09:09:28.247Z","updated_at":"2025-03-29T03:02:36.468Z","avatar_url":"https://github.com/Y80.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg width=\"120\" src=\"./doc/images/logo.svg\"\u003e\n  \u003cbr\u003e\n  \u003ch3 style=\"font-size: 3rem\"\u003eBMM\u003c/h3\u003e\n  \u003cp\u003e收纳、分享、探索优质网站\u003c/p\u003e\n  \u003ci\u003eBMM / bookmark manager / 你的专属书签管家\u003c/i\u003e\n\u003c/div\u003e\n\n\u003cbr\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n  \u003cimg alt=\"PC 端明亮/暗夜主题\" src=\"./doc/images/screenshot-pc-light-dark.webp\"\u003e\n\n  \u003cimg alt=\"后台管理\" src=\"./doc/images/screenshot-pc-cms-light-dark.png\"\u003e\n\n  \u003cimg width=\"680\" alt=\"移动端\" src=\"./doc/images/screenshot-mobile.png\"\u003e\n\n  \u003cimg alt=\"AI 解析网站\" width=\"680\" src=\"./doc/images/screenshot-ai-analyse.gif\"\u003e\n\n\u003c/div\u003e\n\n\n## ✨ 功能\n\n- [x] 搜索书签、标签\n- [x] 根据标签筛选书签\n- [x] 支持移动端/桌面端、明亮主题/暗夜主题\n\n后台管理功能：\n\n- [x] 基于 Github OAuth 的身份认证\n- [x] 导入浏览器导出的书签\n- [x] 标签、书签的增删改查\n- [x] 标签间的相互关联\n- [x] 标签和书签的相互关联\n- [x] 标签排序\n- [x] 爬取网站标题、图标、简介\n- [x] AI 智能解析网站标题、图标、简介、关联标签\n- [x] AI 为标签关联标签\n- [x] 多个 API 自动获取网站图标\n\n待实现：\n\n- [ ] 定期检测书签可用性\n- [ ] 书签置顶、仅登录用户可见\n- [ ] 稍后阅读系统\n- [ ] 用户系统\n\n## 🗂️ 目录\n\n- [🍽️ 准备内容](#%EF%B8%8F-准备内容) \n- [🚀 项目部署](#-项目部署)\n  - [方式一：Node 项目常规部署](#方式一node-项目常规部署)\n  - [方式二：部署至 Vercel](#方式二部署至-vercel)\n  - [方式三：使用 Docker 部署](#方式三使用-docker-部署)\n- [🤖 接入 AI 服务（可选）](#-接入-ai-服务可选)\n- [🤔 常见问题](#-常见问题)\n\n## 🍽️ 准备内容\n\n需要准备的内容包括 **数据库** 和 **Github OAuth App 密钥对**，下面分别介绍。\n\n### 数据库\n\nBMM 使用 Drizzle ORM 持久化存储数据，当前开箱即用的支持 SQLite 和 PostgreSQL 数据库。\n\n默认的环境变量配置使用本地的 SQLite 数据库。但是这种方式只适合本地开发调试，不适合线上部署。\n\n如果你需要线上数据库，下面提供两种方式：\n\n1. [BMM 接入 Turso](https://github.com/Y80/bmm/wiki/%E4%BD%BF%E7%94%A8-Turso-%E6%95%B0%E6%8D%AE%E5%BA%93%E6%9C%8D%E5%8A%A1)\n2. [一些免费的 PostgreSQL 数据库](https://juejin.cn/post/7411047482651951119)\n\n\n**创建好数据库后，在 `.env` 中配置相关环境变量即可。**\n\n### Github OAuth App\n\nBMM 使用 Github 授权登录，认证管理员身份，因此需要配置 Github OAuth。\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n  查看创建步骤\n  \u003c/summary\u003e\n\n1. 登录您的 Github 账户后，访问 https://github.com/settings/applications/new\n\n2. 依次填写表单内容\n\n\u003cimg width=\"480\" src=\"./doc/images/github-oauth-new.png\"\u003e\n\n其他内容可随意填写，最重要的是 `Authorization callback URL` 这一项，请保证它和你的项目最终部署的 **线上访问地址** 一致！\n\n3. 创建一个 Client secret\n\n\u003cimg width=\"480\" src=\"./doc/images/github-oauth-new-secret.png\"\u003e\n\n\u003c/details\u003e\n\nGithub OAuth App 的 Client ID 和 Client Secret 将分别用作环境变量 `AUTH_GITHUB_ID` 和 `AUTH_GITHUB_SECRET`，填写的 Authorization callback URL 要和环境变量 `AUTH_URL` 保持一致。\n\n## 🚀 项目部署\n\n### 方式一：Node 项目常规部署\n\n1. git clone 项目\n\n```sh\ngit clone https://github.com/Y80/bmm.git\n```\n\n2. `pnpm install` 安装依赖\n\n3. `pnpm dev` 启动项目\n\n对于开发环境，`AUTH_URL` 可以被自动侦测到，`AUTH_GITHUB_ID` 和 `AUTH_GITHUB_SECRET` 也临时提供了一对可用的配置，因此可暂时跳过配置。\n\n通过 `pnpm build` 构建生产产物时，需要明确配置上面这 3 个变量。\n\n### 方式二：部署至 Vercel\n\n1. fork 当前 Github 仓库\n\n2. 登入 \u003ca href=\"https://vercel.com\" target=\"_blank\"\u003eVercel\u003c/a\u003e，新建项目，并关联 fork 的 Github 仓库\n\n3. 在当前项目下的 Environment Variables 页面中配置环境变量：\n`DB_DRIVER`、`DB_CONNECTION_URL`、`AUTH_URL`、`AUTH_GITHUB_SECRET` 和 `AUTH_GITHUB_ID`。\n\n\u003cdetails\u003e\n  \u003csummary\u003e查看截图\u003c/summary\u003e\n  \n  ![vercel-settings-env](./doc/images/vercel-settings-env.png)\n\n  Vercel 上每个项目都会被自动分配一个域名，如 bmm.vercel.app，如果你最终使用这个域名访问 BMM 服务，那么可以不用配置 `AUTH_URL`，否则必须配置该环境变量。\n\u003c/details\u003e\n\n\n4. 在 「Deployments 面板」再重新部署一下即可\n\n### 方式三：使用 Docker 部署\n\n```sh\n# 拉取镜像\ndocker pull lcclcc/bmm\n\n# 启动容器（使用本地 SQLite， 通过 docker volume bmm 查看数据库文件地址）\ndocker run --rm  \\\n-e DB_DRIVER=sqlite \\\n-e DB_CONNECTION_URL=file:/app/volume/sqlite.db \\\n-v bmm:/app/volume \\\n-p 3000:3000 \\\nlcclcc/bmm \\\npnpm start\n\n# 启动容器（使用 Turso ）\ndocker run --rm  \\\n-e DB_DRIVER=sqlite \\\n-e DB_CONNECTION_URL=libsql://Turso数据库地址  \\\n-e DB_AUTH_TOKEN=\u003cTurso数据库令牌\u003e \\\n-p 3000:3000 \\\nlcclcc/bmm \\\npnpm start\n\n# 启动容器（使用 PostgreSQL ）\ndocker run --rm  \\\n-e DB_DRIVER=postgresql \\\n-e DB_CONNECTION_URL=postgresql://数据库地址 \\\n-p 3000:3000 \\\nlcclcc/bmm \\\npnpm start\n\n```\n\n\n## 🤖 接入 AI 服务（可选）\n\n本项目通过 AI 实现了 **分析总结网站、给网站打标签、分析相关联的标签** 的功能，可大大减少维护书签数据的工作量。\n\n由于目前 AI 服务商众多，且不同服务商提供的 API 并不相同，因此这里会有轻微的编码工作。\n\n下面是使用 [字节跳动-扣子](https://www.coze.cn/docs/developer_guides/coze_api_overview) AI 能力的示例：\n\n```ts\nexport const getServer = coze\n\nfunction coze() {\n  if (!env.COZE_API_KEY || !env.COZE_BOT_ID) {\n    throw new Error('请配置环境变量 COZE_API_KEY、COZE_BOT_ID')\n  }\n  return {\n    responseContentPath: 'messages[0].content',\n    sendRequest(query: string) {\n      return commonFetch({\n        url: 'https://api.coze.cn/open_api/v2/chat',\n        token: process.env.COZE_API_KEY!,\n        body: {\n          bot_id: process.env.COZE_BOT_ID,\n          user: 'user',\n          query,\n          stream: false,\n        },\n      })\n    },\n  }\n}\n```\n\n`src/lib/ai/servers.ts` 文件提供了使用 **扣子** 和 **OpenAI** 的代码示例可供参考。\n\n\u003e 配置环境变量注意敏感数据泄露！不同的环境可以配置不同的环境变量！更多内容可参考 [.env](./.env)\n\n## 🤔 常见问题\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    如何设置环境变量 AUTH_URL 和 Github 中的 Authorization callback URL?\n  \u003c/summary\u003e\n\n  首先需要明确， `AUTH_URL` 和 Github OAuth App 中的 Authorization callback URL 是要一致的，用于指定用户在 Github 确认授权后，浏览器需要重定向的服务器地址。\n\n  它们的值如何设定，简单来说，通过什么地址访问 BMM 服务，就把该地址作为它们的值，例如：\n\n  - http://localhost:3000 - 本地开发\n  - https://bmm.vercel.app - 部署到 Vercel 的平台上，使用 Vercel 为你分配的域名\n  - https://example.com - 用 nginx 代理了本机地址，线上通过域名访问服务\n  - http://10.1.2.3:3000 - 线上通过 IP:PORT 直接访问服务\n  \n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    Github 登录失败：redirect_uri 错误\n  \u003c/summary\u003e\n\n如果在 Github 授权之后出现如下错误提示：\n\n![github-redirect-uri-error](./doc/images/github-redirect-uri-err.png)\n\n这表示授权之后 Github 需要跳转的地址和 [Github:OAuth Apps](https://github.com/settings/developers) 中的配置不一致。\n\n**请保证下方配置的 Authorization callback URL 和你访问的域名地址、 `AUTH_URL` 一致。**\n\n![github-oauth-cb-url](./doc/images/github-oauth-cb-url.png)\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    支持其他数据库吗？\n  \u003c/summary\u003e\n\n  由于 `drizzle-orm` 除了支持 PostgreSQL，还支持 MySQL 和 SQLite，因此对项目做少许编码改造，即可切换数据库。\n\u003c/details\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy80%2Fbmm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fy80%2Fbmm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fy80%2Fbmm/lists"}