无聊人的中继站。发现独立博客,也让独立博客彼此发现。
无聊湾是一个基于成员互链和匿名访问统计的独立博客联盟。它保留原有的成员列表、UV/RV、等级、排行榜、实时动态与 Badge,同时加入每日航线、公平随机探索、本地护照、漂流瓶文章和分享卡等轻量玩法。
V2 采用渐进增强:关闭功能开关时仍提供原有体验;开启后也不要求注册账号,成员已有的链接和 Badge 无需修改。
- 成员站点展示、UV(独立访客)、RV(链入访客)与等级
- 经典总排行、加入流程和实时湾内动态
/api/badge/:domain、/api/icon/:domain、/api/favicon/:domain/api/wsWebSocket 接口及现有成员数据、历史统计
- 今日航线:每天稳定生成同一条五站路线,兼顾主题差异与曝光公平
- 随便逛逛:优先探索尚未访问、近期曝光较少的站点
- 无聊护照:访问、收藏、邮戳与成就保存在浏览器本地,无需登录
- 最新漂流瓶:成员自愿提供 RSS/Atom,只展示标题、短摘要、时间和原文链接
- 可分享路线:固定日期链接、Open Graph 信息和 SVG 分享卡
- 三类榜单:30 天活跃榜、7 天上升榜、保留历史意义的经典总榜
- 状态观察:按最后活动和连续健康检测分为活跃、安静、观察、可移除候选;永不自动删除成员
- 公开流量分析:任何人可查看全站或单个成员的 UV、RV、导出点击、趋势、时段、国家与来源
/analytics 提供全站总览,/analytics/:domain 提供公开成员页。数据只来自无聊湾现有可验证链路:
- 成员页面加载 Badge 产生的去重 UV;
- 访客从成员域名进入无聊湾产生的去重 RV;
- 访客从首页、排行榜、路线、随机探索和漂流瓶跳向成员的导出点击。
这些数字不代表成员博客的全部访问量,也不要求成员安装新的统计脚本。UV/RV 保持原有四小时去重语义,导出点击不会影响排行榜。
最近 90 天保留小时级聚合,日级总量长期保留。国家、来源域名和内部入口的单项计数小于 3 时只显示在“其他”中;总 UV、RV 和导出点击仍精确展示。系统不保存原始访问事件或可回溯的访客轨迹。
- Rust 2021(工具链固定为 1.98.1)
- Axum 0.8 + Askama 0.16
- Diesel 2.3 + SQLite
- 服务端渲染,少量原生 ES Modules
- 加法数据库迁移;启动时自动执行待处理迁移
项目默认监听 0.0.0.0:3000。
- Rust stable(项目已使用 Rust 1.98.1 验证)
- SQLite 由
libsqlite3-sysbundled feature 提供,无需单独安装开发库 - Node.js 仅用于浏览器本地护照测试
export SYSTEM_DOMAIN=localhost:3000
export DATABASE_URL=/tmp/boringbay.db
export TRUSTED_PROXY_MODE=disabled
cargo run --release浏览器打开 http://localhost:3000。首次启动会创建数据库并自动执行全部迁移。
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
SYSTEM_DOMAIN |
是 | 无 | 对外域名;本地可用 localhost:3000 |
DATABASE_URL |
是 | 无 | SQLite 文件路径 |
BORINGBAY_V2_ENABLED |
否 | true |
V2 默认开启;false/no/off/0 可临时回退到旧界面 |
TRUSTED_PROXY_MODE |
否 | 正式域名为 boringbay.com 时是 cloudflare,其他域名为 disabled |
可选 disabled 或 cloudflare |
正式站为兼容原有 UV/RV 统计与实时访问事件,在没有显式配置时继续信任 Cloudflare 请求头。其他域名默认保持 disabled;只有服务确实位于 Cloudflare 后方时才应设置 TRUSTED_PROXY_MODE=cloudflare,直连部署必须显式保持 disabled,避免信任客户端伪造的请求头。
- Fork 本仓库。
- 在
resources/membership.json中使用未占用的数字 ID 添加站点。 - 将经典 Badge 或 V2 Badge 放到你的网站。
- 提交 Pull Request。
成员示例:
{
"123": {
"domain": "example.com",
"icon": "https://example.com/icon.png",
"name": "示例博客",
"description": "一句话介绍你的站点",
"github_username": "owner/repository",
"tags": ["技术", "生活"],
"feed_url": "https://example.com/feed.xml"
}
}domain不要包含协议或路径。tags与feed_url均为可选字段,不填写不会影响成员资格。feed_url只接受公开可访问的 HTTPS RSS/Atom 地址;正文不会被镜像,点击始终回到原博客。- 暂时隐藏站点可使用
"hidden": true。
经典 Badge(继续兼容):
<a title="无聊湾 🥱 The Boring Bay" href="https://boringbay.com">
<img height="18" src="https://boringbay.com/api/badge/example.com" alt="无聊湾">
</a>V2 Badge(可选):
<a title="无聊湾 🥱 The Boring Bay" href="https://boringbay.com">
<img height="36" src="https://boringbay.com/api/badge-v2/example.com" alt="无聊湾 V2">
</a>仅展示图标、不记录访问:
https://boringbay.com/api/icon/example.com
https://boringbay.com/api/favicon/example.com
- 30 天活跃榜:默认榜,按近期 UV/RV 展示真实互动。
- 7 天上升榜:比较最近 7 天与此前 7 天,让小站和新站也有曝光机会。
- 经典总榜:保留现有历史累计数据。
- 状态观察:最后活动同时考虑最后受访和最后链入;30 天安静、60 天且连续检测异常进入观察、90 天且持续异常才成为人工复核候选。
探索、护照、分享事件与正式 UV/RV 分开存储,不会因为完成航线或分享链接而刷榜。任何状态都不会触发自动删除。
- Feed 仅接受成员主动配置的 HTTPS 地址。
- DNS 解析、目标 IP 与每次重定向都会重新校验,拒绝私网、回环、链路本地等地址。
- 最多跟随 2 次重定向;单次响应上限 1 MiB;只接受 XML/RSS/Atom 类型。
- 拒绝 DTD/外部实体,摘要转为纯文本并统一转义。
- Feed、健康检查失败只降级对应模块,不影响首页、排行或 Badge。
- 不记录或通过 WebSocket 广播完整 IP;访客去重使用进程内短期 HMAC 标识。
- 实时动态由服务端先脱敏 IP:IPv4 仅保留首尾段,IPv6 仅保留前后各两段。脱敏值只进入当次 WebSocket 弹窗,不写数据库、日志或历史分析。
- 公开分析只存小时/日聚合;国家、来源和入口小于 3 次时合并为“其他”。
cargo fmt -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --locked --all-targets
node --test tests/*.test.mjs
node --check resources/static/app.js
node --check resources/static/discovery.js
node --check resources/static/passport.js
cargo build --release --locked测试覆盖旧 API 兼容、数据库迁移、三类排行、30/60/90 天状态边界、确定性每日航线、公平随机、Feed 解析与安全清洗、V1/V2 页面、本地护照、脱敏 IP 与公开聚合分析。
生产升级建议:
- 备份
DATABASE_URL指向的 SQLite 文件。 - 使用同一份
migrations/、resources/、templates/与新二进制启动。 - 不设置
BORINGBAY_V2_ENABLED,按默认开启的 V2 检查首页、三类排行、今日航线、Badge、Feed 和 WebSocket。 - 如果需要快速回退界面,可临时设置
BORINGBAY_V2_ENABLED=false;旧页面和原有 API 仍可继续使用。 - 保留上一版二进制与数据库备份,以便快速回滚。
SQLite 在建池前由单连接切换到 WAL;池连接启用外键和 5 秒 busy timeout,降低统计、Feed 与健康任务并发时的锁竞争。
Dockerfile 面向 CI 生成的 artifact/$TARGETPLATFORM/naive,并把 migrations/、resources/、templates/ 复制到 /webapp。运行容器时应持久化 /webapp/data,并设置上述环境变量。
- Pull Request 会运行格式检查、严格 Clippy、Rust/Node 测试,分别构建 amd64 与 arm64 发布二进制,并实际组装多架构容器但不推送。
- 合并到
main后,同一工作流使用同一提交产生的两个二进制构建多架构镜像,不再从另一个工作流猜测或重新检出提交。 - 镜像发布到
ghcr.io/cantoblanco/boringbay:latest,同时保留sha-<完整提交 SHA>标签便于追溯和回滚。 - 第三方 Actions 均固定到完整提交 SHA;发布镜像包含 provenance 与 SBOM。
| 路由 | 用途 |
|---|---|
/ |
首页和成员列表 |
/rank |
排行榜和成员状态 |
/analytics |
全站公开流量分析 |
/analytics/:domain |
单个成员公开流量分析 |
/route/:date |
指定日期的五站航线 |
/join-us |
加入说明与 Badge 示例 |
/api/discovery/today |
今日航线 JSON |
/api/share/route/:date.svg |
航线 SVG 分享卡 |
/api/events |
白名单匿名产品事件 |
/api/ws |
实时湾内动态 |
src/ Rust 应用、排行、分析、探索、Feed、健康与分享逻辑
templates/ Askama 服务端模板
resources/static/ CSS 与原生 JavaScript
resources/membership.json
migrations/ Diesel SQLite 迁移
tests/ 兼容、迁移、页面、Feed 与护照测试
docs/superpowers/ V2 设计、风险登记与实施计划
详细方案:
项目最初名为 NAiVe。无聊湾图标修改自熊大的 The Boring Studio。