freefire 40bbad2c82 公开页补齐自引用 Canonical 与按类型的结构化数据,图片补上说明文字。
后台设置和内容编辑改成可导航布局;部署用访客 origin 生成 Canonical,避免落到容器地址。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-20 00:11:24 +08:00
2026-09-19 04:14:13 +08:00
2026-09-12 01:11:05 +08:00
2026-09-12 01:11:05 +08:00

姜十三论坛(jiang13-bbs)

一个基于 Go + Next.js 的现代社区论坛系统,采用 SSR 直出 + HttpOnly Cookie 认证方案,具备完整的帖子/评论/通知/签到/管理后台功能。

许可声明: 本项目为专有软件(Proprietary),源码可见但不构成开源授权。详见 LICENSE。未经书面许可,不得使用、部署或分发。


技术栈

层级 技术
后端 Go 1.27 · Gin · GORM · PostgreSQL 17
前端 Next.js 16 (App Router) · TypeScript · Tailwind CSS v4
认证 JWT (HttpOnly Cookie) + Refresh Token 轮转 + CSRF 双提交 + Token Version 撤销
部署 官方:deploy/ Docker Compose(Next SSR + Go + Postgres,反代自备);本地开发仅 Compose 起数据库

功能概览

用户

  • 注册 / 登录 / 登出(注册即登录,免二次跳转)
  • 个人资料编辑:昵称、头像、邮箱、个性签名
  • 修改密码(改密后全端踢下线)
  • 个人主页:发帖列表、评论列表(分页)、签到积分
  • 每日签到(积分 + 连续签到天数)

内容

  • 帖子发帖 / 编辑 / 删除(作者本人或管理员)
  • 帖子类型:普通帖 / 问答帖
  • 帖子置顶 / 推荐(仅管理员)
  • 评论楼层制:主评论 = 楼层,子评论不占楼层,最多 10 层嵌套
  • 楼层分页 + 内存拼装评论树(单次查询,无 N+1)
  • Markdown 渲染(代码高亮、引用、列表等)
  • 帖子点赞(联合唯一索引防重复)

通知

  • 站内通知:评论你的帖子、回复你的评论、点赞你的帖子
  • 未读红点 SSR 直出,实时靠 WebSocket 推送,回前台时 HTTP 校准
  • 单条标记已读 / 全部已读

管理后台

  • 公告管理(草稿 / 发布、标签预设色)
  • 仪表盘

SEO

  • 全站 SSR/ISR,SEO 关键内容在初始 HTML 中
  • 帖子详情页输出 DiscussionForumPosting 类型 JSON-LD
  • 动态 sitemap.ts + robots.ts(由 Next.js 对外提供,后端不重复挂 /sitemap.xml / /robots.txt)

安全

  • CSP / HSTS / X-Frame-Options: DENY / X-Content-Type-Options / Referrer-Policy
  • 速率限制:登录 20/min、注册 10/min、发帖新用户 24h 冷静期
  • Refresh Token 一次性轮转 + 盗用检测(吊销后重放 → 杀全家族)
  • 生产 HTTPS 启用 __Host- Cookie 前缀

项目结构

jiang13-bbs/
├── backend/                  # Go 后端
│   ├── cmd/jiang13/          # 入口 main.go
│   ├── config/               # 配置解析(app.ini + 环境变量)
│   ├── handler/              # HTTP 处理器
│   ├── middleware/            # 认证 / CSRF / 限流 / 安全头
│   ├── model/                # GORM 模型 + 数据库初始化
│   ├── router/               # 路由注册
│   ├── service/              # 业务逻辑层
│   ├── app.ini.example       # 配置模板
│   └── go.mod
├── frontend/                 # Next.js 前端
│   ├── app/                  # App Router 页面
│   │   ├── settings/         # 账号设置(资料 + 密码)
│   │   ├── u/[id]/           # 用户主页
│   │   ├── post/[id]/        # 帖子详情
│   │   ├── compose/          # 发帖
│   │   ├── notifications/    # 通知列表
│   │   ├── admin/            # 管理后台
│   │   └── ...
│   ├── components/           # 复用组件
│   ├── lib/                  # API 封装 / 工具函数
│   ├── middleware.ts         # SSR 登录态静默轮转
│   ├── next.config.ts        # Rewrite 代理 / 图片缓存
│   └── .env.example
├── deploy/                   # 官方运行时(web + api + Postgres,反代自备)
├── docker-compose.yml        # 仅本地开发 Postgres
├── LICENSE                   # 专有软件许可协议
└── .gitignore

快速开始

前置条件

  • Go 1.27+
  • Node.js 20+ / pnpm 或 npm
  • PostgreSQL 17(或通过 Docker Compose 启动)

1. 启动数据库

docker compose up -d postgres

2. 启动后端

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/ 执行 go run,否则会另建一套 data,数据库里头像 URL 会对不上文件。启动日志应含 workPath=.../backend 和 dataDir=.../backend/data。需要覆盖时设置 JIANG13_WORK_PATH 指向 backend/。

后端默认监听 :3001,首次启动自动执行数据库迁移并写入默认板块。同一时间只跑一个后端进程。

3. 启动前端

cd frontend
cp .env.example .env.local
npm install
npm run dev

前端默认监听 :3000,通过 next.config.ts rewrite 将 /api/* 代理到后端。


配置说明

后端(backend/app.ini)

[server]
HTTP_PORT = 3001

[database]
DSN = postgres://postgres:postgres@localhost:5432/jiang13?sslmode=disable

[security]
; 留空则自动生成并持久化到 data/.jwt_secret
; 生产环境务必显式指定强随机值
JWT_SECRET =

[paths]
DATA = data

[app]
DEV_MODE = true

环境变量优先级高于 app.ini:HTTP_PORT、DB_DSN、JWT_SECRET、DEV_MODE。JIANG13_WORK_PATH 可强制指定后端根目录(app.ini 与 data/ 所在处)。

前端(frontend/.env.local)

# SSR 与 middleware 直连后端(本地开发)
NEXT_PUBLIC_API_URL=http://localhost:3001

# 部署到 Cloudflare Workers 等边缘环境时必须显式配置
# BACKEND_URL=https://your-backend.example.com

认证架构

采用三 Cookie + Token Version 方案,不使用 localStorage 存储 token:

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 在页面/RSC 请求前本地解码 access token 的 exp,过期时静默调后端 refresh 轮转,新 cookie 注入请求头供 SSR 使用并透传浏览器。
  • 客户端续期: lib/api.ts 的 fetchWithRefresh 遇到 401 自动调 /api/auth/refresh 重试。
  • 撤销机制: 改密码 / 封禁 / 管理员强制下线时递增 User.TokenVersion,所有旧 JWT 立即失效。
  • 盗用检测: Refresh Token 一次性轮转,已吊销 token 重放时撤销该用户全部 refresh token。

生产 HTTPS 下 cookie 名自动启用 __Host- 前缀。

若本机曾跑过早期构建,浏览器里可能还留着 Path=/api/auth 的 j13_refresh(HttpOnly,页面脚本删不掉)。打开开发者工具 → Application → Cookies,删掉该条,或对该源执行「清除站点数据」。之后只应存在 Path=/ 的三枚 cookie。


部署

官方运行时是 deploy/docker-compose.yml(Next SSR + Go + Postgres,不含反代)。 说明与升级命令见 deploy/README.md,Nginx 示例见 deploy/nginx.example.conf,契约见 deploy/CONTRACT.md。

本地开发仍用仓库根 start.bat / start.sh 与根目录 docker-compose.yml(只起 Postgres)。不要把开发流程改成必须打应用镜像。

更新应用(镜像已发布时):

cd deploy
docker compose pull
docker compose up -d

后台一键更新、预编译包不在本期。Cloudflare Workers 不是官方运行时。

生产环境要点

  1. 官方 Compose: DEV_MODE=false,强随机 JWT_SECRET 与 POSTGRES_PASSWORD;由你现有的 Nginx/Caddy 提供 HTTPS,并把页面与 /api、/api/ws、/uploads 挂到同一 Host。
  2. CORS 与 Canonical: 设 SITE_URL 为对外 origin(不要改 router.go 写死域名)。官方 Compose 会同时注入 api 与 web;web 用它生成 Canonical / sitemap,不要填 http://api:3001。
  3. 自管部署: 仍须同源反代。Workers 前端须保持 middleware.ts 文件名(OpenNext 不识别 proxy.ts),实时通道由反代承接 /api/ws。

WebSocket(开发 vs 生产)

浏览器发起 WebSocket 握手一定会带 Origin。后端拒绝空 Origin,这只挡住 curl / 脚本探测,不影响 npm run dev,也不影响生产浏览器。

开发环境(DEV_MODE=true)

  • 页面在 http://localhost:3000,WS 直连 ws://localhost:3001/api/ws(Next.js rewrite 不透传 Upgrade)。
  • 二者端口不同,不是浏览器意义上的同源。后端因此放行 Origin 主机为 localhost / 127.0.0.1 的任意端口。
  • Cookie 不按端口隔离,:3000 上的 j13_token 会随握手带到 :3001。不要把 DEV_MODE 改成 false 再跑这对端口,否则 Origin localhost:3000 对不上 Host localhost:3001,握手会被拒。

生产环境(DEV_MODE=false)

  • Origin 的 host(含端口)必须与请求 Host 完全一致。请用 Nginx/Caddy 把前端与 /api/、/api/ws 挂到同一域名,例如 https://bbs.example.com。
  • 反向代理需透传 Upgrade、Connection,并保留原始 Host / Origin。
  • 生产 cookie 使用 __Host- 前缀,要求 Secure、Path=/、不能设 Domain。因此 API 不能放到 api.example.com 这类另一子域,否则登录 cookie 与 WS 都会失效。
  • Cloudflare Workers / OpenNext 不能透传任意 WebSocket。若前端部署在 Workers,实时通道必须由同源反代(Nginx 等)承接 /api/ws,而不是指望 Workers 升级连接。
  • 可用 NEXT_PUBLIC_WS_URL 覆盖前端连线地址;一旦覆盖,Origin 仍须与后端看到的 Host 一致。

开发约定

  • Next.js 16: params / searchParams / cookies() / headers() 必须 await。
  • SEO: 所有公开页面 SSR/ISR,SEO 关键内容(title、description、OG、JSON-LD)必须在初始 HTML 中。
  • 权限: 权限校验只在 Go 后端执行,前端不做判定。
  • 配色: 仅修改 frontend/app/globals.css 中的 CSS 令牌,禁止在组件内硬编码颜色。
  • 无障碍: 文字对比度 ≥4.5:1,交互控件边界 ≥3:1(WCAG 2.1 AA)。

许可证

专有软件(Proprietary) — Copyright © 2026 姜十三,保留所有权利。

源码仅可见,未经书面许可不得使用、部署、修改或分发。详见 LICENSE。

联系方式:aarbbs@88.com

Description
No description provided
Readme 46 MiB
v0.2.8 Latest
2026-10-01 17:49:55 +00:00
Languages
TypeScript 60.1%
Go 33%
CSS 6.4%
JavaScript 0.3%
Shell 0.1%