技术文档
← 首页7ch 技术文档
#项目概览
7ch 是一个面向匿名交流的文本社区,主打低门槛发言与轻量身份识别。
生产环境采用前后端分离:浏览器 SPA 负责界面与交互,API 服务提供业务能力,Postgres 存储内容。前端部署在 Vercel,后端部署在 Render,数据库位于 Neon。
前端技术栈
核心概念
源码未对外公开,但本文档公开关键机制与数据字段,尤其是每日匿名 ID 的计算方式,便于用户理解与验证。
#架构与部署
系统由浏览器客户端、API 服务与数据库三层组成。客户端仅通过 /api 与服务交互,服务端负责限流、过滤与身份计算后再读写数据库。
路由与页面结构
主要页面路径(简化示意):
Routing Map
/ -> Home (Boards) /board/:boardId -> Board /board/:boardId/thread/:threadId -> Thread /favorites -> Favorites /docs | /help | /terms | /privacy | /QA -> Static Pages
所有业务数据(板块、线程、回帖)都由 API 提供,客户端仅持久化语言、关注与隐藏等偏好。
#数据模型与契约
客户端看到的核心实体为 Board / Thread / Post。服务端保存更多字段(如 IP、时间戳),但对外仅返回公开字段。
实体
- Board: 板块为固定配置,包含 ID、名称、描述。
- Thread: 线程包含标题、回复数、浏览数、更新时间,以及 OP 预览。
- Post: 帖子为楼层记录,包含显示名、Tripcode、内容、每日 ID 等。
Public Post Shape
{
"id": 12,
"threadId": "<uuid>",
"name": "Anonymous",
"tripcode": "◆abcd1234ef",
"content": "...",
"createdAt": "2026-02-03T12:00:00Z",
"uid": "A1b2C3d4",
"isOp": false
}#匿名与核心机制
1. 每日匿名 ID(Daily ID)
每日 ID 用于区分同一天、同板块内的发言者,同时避免长期追踪。
- 输入:客户端来源 IP、当前日期(UTC)、板块 ID、服务器私有盐值。
- 处理:SHA-256 哈希 → Base64 URL Safe(无补位)→ 截断前 8 位。
- 输出:形如 ID:A1b2C3d4 的短标识。
同一 IP 在同一天同一板块 ID 稳定;跨天、换板块或更换网络会变化。共享出口 IP(NAT/校园网)可能显示相同 ID。
Daily ID Algorithm (Pseudo)
Raw = IP + Date(UTC: YYYY-MM-DD) + BoardId + SecretSalt Hash = SHA256(Raw) Encoded = Base64UrlSafeNoPad(Hash) DailyId = Encoded.substring(0, 8)
2. Tripcode(绊码)
名字栏输入 Name#password 会生成 Tripcode,用于“证明同一人”但不需要账号。密码只参与哈希计算,存储的是短哈希片段。
Input: "Name#password" Name = "Name" Tripcode = "◆" + hex(SHA256(password + SecretSalt)).slice(0, 10)
3. Sage 下沉
Email 字段包含 sage(不区分大小写)时,回帖不会顶帖,只增加回复数。
const isSage = email?.toLowerCase().includes('sage');
if (!isSage) {
thread.updatedAt = now; // bump only when NOT sage
}4. 引用与预览
客户端解析 >>123 引用并提供悬停预览;服务器只存原文,不做特殊解析。
5. 内容过滤与风控
服务端对敏感词进行替换,并进行基础限流以降低刷屏与攻击风险。
#国际化与本地化
目前提供中文(zh-CN)与日文(ja-JP)。
基于 react-i18next,包含针对日文日期格式的本地化处理:
- 日本纪元日期:如 2025 显示为 R7。
- 星期几:显示为 (月)、(火) 等。
Date Format (ja-JP)
if (d.getFullYear() >= 2019) y = 'R' + (d.getFullYear() - 2018); // Reiwa else if (d.getFullYear() >= 1989) y = 'H' + (d.getFullYear() - 1988); // Heisei