Files
jiang13-bbs/README.md

372 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div align="center">
# 姜十三论坛(jiang13-bbs)
一个基于 **Go + Next.js** 构建的现代化社区论坛系统,SSR 直出、HttpOnly Cookie 认证,内置内容社区、代码片段、书库、积分体系、实时群聊与完整管理后台,开箱即用。
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE)
[![Version](https://img.shields.io/badge/version-0.2.9-green.svg)](./VERSION)
[![Go](https://img.shields.io/badge/Go-1.27-00ADD8?logo=go&logoColor=white)](https://go.dev/)
[![Next.js](https://img.shields.io/badge/Next.js-16-black?logo=nextdotjs&logoColor=white)](https://nextjs.org/)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](#贡献指南)
</div>
---
## 目录
- [项目截图](#项目截图)
- [功能特性](#功能特性)
- [技术栈](#技术栈)
- [快速开始](#快速开始)
- [配置说明](#配置说明)
- [Docker 部署](#docker-部署)
- [项目结构](#项目结构)
- [安全设计](#安全设计)
- [开发与测试](#开发与测试)
- [更新日志](#更新日志)
- [贡献指南](#贡献指南)
- [许可证](#许可证)
- [联系方式](#联系方式)
- [赞赏支持](#赞赏支持)
---
## 项目截图
| 首页信息流 | 帖子详情 |
| :---: | :---: |
| ![首页](docs/images/首页.png) | ![帖子详情](docs/images/帖子详情.png) |
| 代码片段广场 | 书库在线阅读 |
| ![代码片段](docs/images/代码片段.png) | ![书库](docs/images/书库.png) |
| 管理后台仪表盘 | 访问统计分析 |
| ![管理后台](docs/images/管理后台.png) | ![访问统计](docs/images/访问统计.png) |
<!--
后台设置类截图(邮件 / 过滤 / 维护 / 安全 / 存储,含移动端与暗色模式):
docs/screenshots/settings/
-->
---
## 功能特性
### 内容社区
- 帖子发布 / 编辑 / 删除,支持普通帖、**问答帖**(采纳答案、状态流转)、**投票帖**、**悬赏帖**、**抽奖帖**,以及密码帖与付费解锁内容
- Markdown 写作与渲染:Shiki 代码高亮、目录导航、图片灯箱、视频/附件
- 楼层化评论:主评论占楼层、子评论不占楼层,最多 10 层嵌套,单次查询拼装评论树,无 N+1
- 评论与帖子均提供**编辑历史**、软删除与回收站(恢复 / 彻底删除)
- 点赞、收藏、关注、置顶、推荐、锁定,联合唯一索引防止重复互动
- 附件可设定积分价格,支持付费下载
### 积分与成长体系
- 每日签到 + 连续签到加成,积分余额与完整流水台账
- 用户等级体系(经验值自动升级)与成就徽章(手动授予 / 自动通知)
- 全站积分排行榜与个人排名
### 代码片段
- 独立的代码片段广场:多语言语法高亮、按语言筛选、全站统计
- Fork、一键复制计数、片段评论、版本历史、可见性管理
### 书库 / 文档中心
- 书籍文档管理,两级章节(章 → 节)结构
- 支持 **EPUB 解析导入**、Markdown 单篇 / 批量导入、整库导出与单本导出
- 内置在线阅读器,封面、附件下载一应俱全
### 私信与群聊
- 一对一私信、多人群聊房间、默认全站大厅
- 群成员管理:邀请 / 移出、禁言、角色设置;消息撤回、未读汇总、会话置顶
### 实时通知
- 评论、回复、点赞、@、系统通知等多类型站内消息
- 未读红点 **SSR 首屏直出**,WebSocket 实时推送,页面回到前台时 HTTP 校准,支持单条 / 全部已读
### 管理后台
- 仪表盘与分析概览(Recharts 图表)
- 内容审核队列(待审帖子 / 评论,板块管理员仅见授权板块)与内容回收站
- 板块管理、版主授权、排序
- 用户管理:角色、细粒度权限码、封禁、消息权限、登录日志、登录设备、积分调整
- 公告文章、站点单页(CMS)管理
- 广告位与赞助商配置(含用户自助购买提交、二维码、验证码)
- 友情链接:用户申请、后台审批
- 侧边栏可视化布局配置 + 自定义 HTML 小部件
- 背景图、品牌 Logo 上传裁剪;全站媒体库盘点
- **访问统计**:概览、实时、来源、页面、访客、安全、事件等多维度分析
- 旧站数据导入(WordPress / Typecho 等)
- 更新时间线:从 Git Commits / Releases 自动生成更新日志
- 维护模式、系统诊断、SMTP 邮件配置(凭据加密存储)
- 支持 S3 / MinIO 对象存储:文件内容去重、自适应图片变体、WebP 自动转换
### SEO
- 全站 SSR / ISR,title、description、OG 等关键内容均在初始 HTML 中
- 帖子详情页输出 `DiscussionForumPosting` 类型 JSON-LD
- 动态 `sitemap`、`robots`、Canonical 链接
---
## 技术栈
| 层级 | 技术选型 |
|------|----------|
| 后端 | Go 1.27 · Gin · GORM · PostgreSQL 17 |
| 前端 | Next.js 16(App Router)· React 19 · TypeScript · Tailwind CSS v4 |
| 认证 | JWT(HttpOnly Cookie)+ Refresh Token 轮转 + CSRF 双提交 + Token Version 撤销 |
| 实时 | WebSocket(gorilla/websocket) |
| 存储 | 本地存储 / S3(MinIO),Markdown 引擎 Goldmark,代码高亮 Shiki |
| 部署 | Docker Compose:Next SSR + Go API + PostgreSQL(反向代理自备) |
---
## 快速开始
### 前置条件
- Go 1.27+
- Node.js 20+(npm)
- PostgreSQL 17(可通过 Docker Compose 启动)
### 1. 启动数据库
```bash
docker compose up -d postgres
```
### 2. 启动后端
```bash
cd backend
cp app.ini.example app.ini
# 按需修改 app.ini 中的 DSN / JWT_SECRET / DEV_MODE
go mod download
go run ./cmd/jiang13
```
> **工作目录必须是 `backend/`。** 上传文件与 JWT 密钥落在 `backend/data/`(头像位于 `backend/data/uploads/avatars/`)。在仓库根或 `backend/cmd/jiang13/` 下执行会另建一套 data,导致数据库中的文件 URL 对不上。启动日志应包含 `workPath=.../backend`;需要覆盖时可设置 `JIANG13_WORK_PATH`。
后端默认监听 `:3001`,首次启动自动执行数据库迁移并写入默认板块。
### 3. 启动前端
```bash
cd frontend
cp .env.example .env.local
npm install
npm run dev
```
前端默认监听 `:3000`,通过 `next.config.ts` 的 rewrite 将 `/api/*` 代理到后端。
**空数据库首次注册的账号将自动成为站长。**
> Windows 用户也可直接使用仓库根目录的 `start.bat`(或 `start.sh`)一键启动开发环境,停止使用 `stop.bat` / `stop.sh`。
---
## 配置说明
### 后端 `backend/app.ini`
```ini
[server]
HTTP_PORT = 3001
[database]
DSN = postgres://postgres:postgres@localhost:5432/jiang13?sslmode=disable
[security]
; 留空则自动生成并持久化到 data/.jwt_secret;生产环境务必显式指定强随机值
JWT_SECRET =
; 32 字节随机值的标准 Base64(openssl rand -base64 32),用于加密邮件 / S3 凭据
SETTINGS_MASTER_KEY =
[paths]
DATA = data
[app]
DEV_MODE = true
SITE_URL = http://localhost:3000
CORS_ORIGINS =
```
环境变量优先级高于 `app.ini`:`HTTP_PORT`、`DB_DSN`、`JWT_SECRET`、`SETTINGS_MASTER_KEY`、`DEV_MODE`、`SITE_URL`、`CORS_ORIGINS`、`TRUSTED_PROXIES`。
### 前端 `frontend/.env.local`
```bash
# SSR 与 middleware 直连后端
NEXT_PUBLIC_API_URL=http://localhost:3001
# 仅服务端可见;生产构建必须显式配置
# BACKEND_URL=https://your-backend.example.com
# 与后端 DEV_MODE 对齐
DEV_MODE=true
```
完整环境变量清单见 [.env.example](./.env.example)、[frontend/.env.example](./frontend/.env.example)。
---
## Docker 部署
官方运行时位于 [`deploy/`](./deploy/),一体交付 **Next.js SSR + Go API + PostgreSQL 17**,不含反向代理(TLS 与同源路由由你的 Nginx / Caddy 处理)。
```bash
cd deploy
cp .env.example .env
# 编辑 .env:POSTGRES_PASSWORD、JWT_SECRET、SITE_URL
docker compose up -d --build
```
然后将同一 Host 反向代理到容器:
- `/` → web `:3000`
- `/api/`(含 `/api/ws`)、`/uploads/`、`/health` → api `:3001`
`GET https://你的域名/health` 返回 `{"status":"ok"}` 即部署成功。
详细说明、升级与备份命令见 [deploy/README.md](./deploy/README.md),Nginx 配置示例见 [deploy/nginx.example.conf](./deploy/nginx.example.conf),运行时契约见 [deploy/CONTRACT.md](./deploy/CONTRACT.md)。1Panel 面板部署可参考 [docs/deploy-1panel.md](./docs/deploy-1panel.md)。
---
## 项目结构
```
jiang13-bbs/
├── backend/ # Go 后端
│ ├── cmd/jiang13/ # 程序入口
│ ├── config/ # 配置解析(app.ini + 环境变量)
│ ├── handler/ # HTTP 处理器
│ ├── middleware/ # 认证 / CSRF / 限流 / 安全头 / 访问捕获
│ ├── model/ # GORM 模型与数据库初始化
│ ├── realtime/ # WebSocket Hub
│ ├── router/ # 路由注册
│ ├── service/ # 业务逻辑层
│ ├── markdown/ # Markdown 扩展(隐藏内容等)
│ └── app.ini.example
├── frontend/ # Next.js 前端
│ ├── app/ # App Router 页面(含 admin 后台)
│ ├── components/ # 通用组件
│ ├── hooks/ # 自定义 Hooks
│ ├── lib/ # API 封装与工具函数
│ ├── public/ # 静态资源(表情图等)
│ ├── middleware.ts # SSR 登录态静默轮转
│ └── next.config.ts # Rewrite 代理 / 图片缓存
├── deploy/ # 官方运行时(Docker Compose / Dockerfile)
├── docs/ # 文档与截图
├── docker-compose.yml # 本地开发用(仅 PostgreSQL)
├── start.bat / start.sh # 本地一键启动
├── VERSION
└── LICENSE
```
---
## 安全设计
采用三 Cookie 方案,令牌不落 localStorage:
| Cookie | 用途 | 属性 |
|--------|------|------|
| `j13_token` | Access JWT(15 分钟) | HttpOnly · SameSite=Lax |
| `j13_refresh` | Refresh Token(7 天,DB 存 SHA-256) | HttpOnly · SameSite=Lax · Path=/ |
| `j13_csrf` | CSRF Token(JS 可读) | SameSite=Lax |
- **SSR 直出登录态**:`middleware.ts` 在渲染前检查 Access Token 过期时间,过期则静默轮转,新 Cookie 同时供 SSR 使用并透传浏览器
- **Refresh Token 一次性轮转 + 盗用检测**:已吊销 Token 被重放时,撤销该用户全部 Refresh Token
- **Token Version 撤销**:改密 / 封禁 / 强制下线时递增版本号,旧 JWT 立即失效
- **速率限制**:登录、注册、发帖(新用户 24h 冷静期)、评论、上传等分级限流,管理员豁免
- **安全响应头**:CSP、HSTS、X-Frame-Options: DENY、X-Content-Type-Options、Referrer-Policy
- 生产 HTTPS 下 Cookie 自动启用 `__Host-` 前缀,要求前后端必须**同源部署**(不能拆分到子域)
---
## 开发与测试
```bash
# 后端测试
cd backend && go test ./...
# 前端测试(Node 内置 test runner)
cd frontend && npm test
# 前端代码检查
npm run lint
```
开发约定:
- Next.js 16 中 `params` / `searchParams` / `cookies()` / `headers()` 均需 `await`
- 权限校验只在 Go 后端执行,前端不做判定
- 配色仅修改 `frontend/app/globals.css` 中的 CSS 令牌,不在组件内硬编码颜色
- 所有公开页面保持 SSR / ISR,SEO 关键内容必须在初始 HTML 中
---
## 更新日志
版本发布与部署契约变更记录见 [deploy/CHANGELOG.md](./deploy/CHANGELOG.md),当前版本 **v0.2.9**。
---
## 贡献指南
欢迎提交 Issue 与 Pull Request!
1. Fork 本仓库并创建特性分支:`git checkout -b feature/your-feature`
2. 提交前请确保测试通过:后端 `go test ./...`,前端 `npm test`
3. 提交代码并推送分支
4. 在 GitHub 上发起 Pull Request,描述清楚改动内容与动机
- 提交信息建议遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范(`feat:` / `fix:` / `docs:` 等)
- 发现安全问题请**不要**直接提 Issue,发送邮件至 aarbbs@88.com 私下反馈
---
## 许可证
本项目基于 [Apache License 2.0](./LICENSE) 开源。
```
Copyright 2026 姜十三
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
```
---
## 联系方式
- QQ:97975089
- 邮箱:97975089@qq.com
- Issue:[GitHub Issues](https://github.com/aarbbs/jiang13-bbs/issues)
---
## 赞赏支持
如果本项目对你有帮助,欢迎请作者喝杯咖啡 ☕,你的支持是持续维护的动力。
<p align="center">
<img src="docs/images/wechat.png" width="220" alt="微信打赏" />
&nbsp;&nbsp;&nbsp;&nbsp;
<img src="docs/images/zfb.png" width="220" alt="支付宝打赏" />
</p>
<p align="center">微信打赏 &nbsp;&nbsp;|&nbsp;&nbsp; 支付宝打赏</p>