build: add dev startup scripts and project docs
添加了Windows的start.bat和Linux/macOS的start.sh一键启动脚本,补充了完整的项目README文档,同时修复了前端类型文件的导入路径问题
This commit is contained in:
223
README.md
Normal file
223
README.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# 姜十三论坛(jiang13-bbs)
|
||||
|
||||
一个基于 Go + Next.js 的现代社区论坛系统,采用 SSR 直出 + HttpOnly Cookie 认证方案,具备完整的帖子/评论/通知/签到/管理后台功能。
|
||||
|
||||
> **许可声明:** 本项目为专有软件(Proprietary),源码可见但不构成开源授权。详见 [LICENSE](./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 撤销 |
|
||||
| 部署 | Docker Compose(数据库)· 可部署至 Cloudflare Workers(前端) |
|
||||
|
||||
---
|
||||
|
||||
## 功能概览
|
||||
|
||||
### 用户
|
||||
|
||||
- 注册 / 登录 / 登出(注册即登录,免二次跳转)
|
||||
- 个人资料编辑:昵称、头像、邮箱、个性签名
|
||||
- 修改密码(改密后全端踢下线)
|
||||
- 个人主页:发帖列表、评论列表(分页)、签到积分
|
||||
- 每日签到(积分 + 连续签到天数)
|
||||
|
||||
### 内容
|
||||
|
||||
- 帖子发帖 / 编辑 / 删除(作者本人或管理员)
|
||||
- 帖子类型:普通帖 / 问答帖
|
||||
- 帖子置顶 / 推荐(仅管理员)
|
||||
- 评论楼层制:主评论 = 楼层,子评论不占楼层,最多 10 层嵌套
|
||||
- 楼层分页 + 内存拼装评论树(单次查询,无 N+1)
|
||||
- Markdown 渲染(代码高亮、引用、列表等)
|
||||
- 帖子点赞(联合唯一索引防重复)
|
||||
|
||||
### 通知
|
||||
|
||||
- 站内通知:评论你的帖子、回复你的评论、点赞你的帖子
|
||||
- 未读红点 SSR 直出 + 30s 轮询校正
|
||||
- 单条标记已读 / 全部已读
|
||||
|
||||
### 管理后台
|
||||
|
||||
- 公告管理(草稿 / 发布、标签预设色)
|
||||
- 仪表盘
|
||||
|
||||
### SEO
|
||||
|
||||
- 全站 SSR/ISR,SEO 关键内容在初始 HTML 中
|
||||
- 帖子详情页输出 `DiscussionForumPosting` 类型 JSON-LD
|
||||
- 动态 `sitemap.ts` + `robots.ts`
|
||||
|
||||
### 安全
|
||||
|
||||
- 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
|
||||
├── docker-compose.yml # PostgreSQL 容器
|
||||
├── LICENSE # 专有软件许可协议
|
||||
└── .gitignore
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 前置条件
|
||||
|
||||
- Go 1.27+
|
||||
- Node.js 20+ / pnpm 或 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
|
||||
```
|
||||
|
||||
后端默认监听 `:3001`,首次启动自动执行数据库迁移并写入默认板块。
|
||||
|
||||
### 3. 启动前端
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
cp .env.example .env.local
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
前端默认监听 `:3000`,通过 `next.config.ts` rewrite 将 `/api/*` 代理到后端。
|
||||
|
||||
---
|
||||
|
||||
## 配置说明
|
||||
|
||||
### 后端(`backend/app.ini`)
|
||||
|
||||
```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`。
|
||||
|
||||
### 前端(`frontend/.env.local`)
|
||||
|
||||
```bash
|
||||
# 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-` 前缀。
|
||||
|
||||
---
|
||||
|
||||
## 部署
|
||||
|
||||
### 生产环境要点
|
||||
|
||||
1. **后端:** 设置 `DEV_MODE=false`,指定强随机 `JWT_SECRET`,通过反向代理(Nginx/Caddy)提供 HTTPS。
|
||||
2. **前端:** 设置 `BACKEND_URL` 为后端 HTTPS 地址,`npm run build` 后部署。Cloudflare Workers 部署需保持 `middleware.ts` 文件名不变(OpenNext 不识别 `proxy.ts`)。
|
||||
3. **数据库:** 修改默认密码,启用 SSL 连接。
|
||||
4. **CORS:** 生产环境修改 `router.go` 中的 `AllowOrigins` 为实际域名。
|
||||
|
||||
---
|
||||
|
||||
## 开发约定
|
||||
|
||||
- **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](./LICENSE)。
|
||||
|
||||
联系方式:aarbbs@88.com
|
||||
4
frontend/next-env.d.ts
vendored
4
frontend/next-env.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
import "./.next/types/root-params.d.ts";
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/dev/types/root-params.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
62
start.bat
Normal file
62
start.bat
Normal file
@@ -0,0 +1,62 @@
|
||||
@echo off
|
||||
title jiang13-bbs launcher
|
||||
|
||||
echo ============================================
|
||||
echo jiang13-bbs Dev Launcher
|
||||
echo ============================================
|
||||
echo.
|
||||
|
||||
REM ---- 1. Start PostgreSQL ----
|
||||
echo [1/3] Starting database (Docker Compose)...
|
||||
docker compose up -d postgres
|
||||
if errorlevel 1 (
|
||||
echo [FAIL] Database failed to start. Is Docker running?
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo [OK] Database started
|
||||
echo.
|
||||
|
||||
REM ---- 2. Backend ----
|
||||
echo [2/3] Checking backend config...
|
||||
if not exist "backend\app.ini" (
|
||||
copy "backend\app.ini.example" "backend\app.ini" >nul
|
||||
echo Created backend\app.ini from template
|
||||
)
|
||||
if not exist "backend\data" mkdir "backend\data"
|
||||
|
||||
echo Starting backend (port 3001)...
|
||||
start "jiang13-backend" cmd /k "cd /d %~dp0backend && go run ./cmd/jiang13"
|
||||
echo [OK] Backend started
|
||||
echo.
|
||||
|
||||
REM ---- 3. Frontend ----
|
||||
echo [3/3] Checking frontend config...
|
||||
if not exist "frontend\.env.local" (
|
||||
copy "frontend\.env.example" "frontend\.env.local" >nul
|
||||
echo Created frontend\.env.local from template
|
||||
)
|
||||
|
||||
if not exist "frontend\node_modules" (
|
||||
echo First run, installing dependencies...
|
||||
cd /d %~dp0frontend
|
||||
call npm install
|
||||
cd /d %~dp0
|
||||
)
|
||||
|
||||
echo Starting frontend (port 3000)...
|
||||
start "jiang13-frontend" cmd /k "cd /d %~dp0frontend && npm run dev"
|
||||
echo [OK] Frontend started
|
||||
echo.
|
||||
|
||||
echo ============================================
|
||||
echo All services started!
|
||||
echo.
|
||||
echo Frontend: http://localhost:3000
|
||||
echo Backend: http://localhost:3001
|
||||
echo Database: localhost:5432
|
||||
echo.
|
||||
echo Close each window to stop the service.
|
||||
echo Stop database: docker compose down
|
||||
echo ============================================
|
||||
pause
|
||||
91
start.sh
Normal file
91
start.sh
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ═══════════════════════════════════════════
|
||||
# 姜十三论坛 开发环境启动脚本 (Linux/macOS)
|
||||
# ═══════════════════════════════════════════
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
echo "══════════════════════════════════════════"
|
||||
echo " 姜十三论坛 开发环境启动脚本"
|
||||
echo "══════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
# ---- 1. 启动 PostgreSQL ----
|
||||
echo "[1/3] 启动数据库 (Docker Compose)..."
|
||||
if ! command -v docker &>/dev/null; then
|
||||
echo "✗ 未检测到 docker,请先安装 Docker"
|
||||
exit 1
|
||||
fi
|
||||
docker compose up -d postgres
|
||||
echo "√ 数据库已启动"
|
||||
echo ""
|
||||
|
||||
# ---- 2. 检查后端配置 ----
|
||||
echo "[2/3] 检查后端配置..."
|
||||
if [ ! -f "backend/app.ini" ]; then
|
||||
cp "backend/app.ini.example" "backend/app.ini"
|
||||
echo " 已从模板创建 backend/app.ini,请按需修改配置"
|
||||
fi
|
||||
mkdir -p backend/data
|
||||
|
||||
# 启动后端(后台)
|
||||
echo " 启动后端 (端口 3001)..."
|
||||
cd "$ROOT_DIR/backend"
|
||||
go run ./cmd/jiang13 &
|
||||
BACKEND_PID=$!
|
||||
cd "$ROOT_DIR"
|
||||
echo "√ 后端已启动 (PID: $BACKEND_PID)"
|
||||
echo ""
|
||||
|
||||
# ---- 3. 检查前端配置 ----
|
||||
echo "[3/3] 检查前端配置..."
|
||||
if [ ! -f "frontend/.env.local" ]; then
|
||||
cp "frontend/.env.example" "frontend/.env.local"
|
||||
echo " 已从模板创建 frontend/.env.local"
|
||||
fi
|
||||
|
||||
# 检查 node_modules
|
||||
if [ ! -d "frontend/node_modules" ]; then
|
||||
echo " 首次运行,安装前端依赖..."
|
||||
cd "$ROOT_DIR/frontend"
|
||||
npm install
|
||||
cd "$ROOT_DIR"
|
||||
fi
|
||||
|
||||
# 启动前端(后台)
|
||||
echo " 启动前端 (端口 3000)..."
|
||||
cd "$ROOT_DIR/frontend"
|
||||
npm run dev &
|
||||
FRONTEND_PID=$!
|
||||
cd "$ROOT_DIR"
|
||||
echo "√ 前端已启动 (PID: $FRONTEND_PID)"
|
||||
echo ""
|
||||
|
||||
echo "══════════════════════════════════════════"
|
||||
echo " 全部启动完成!"
|
||||
echo ""
|
||||
echo " 前端: http://localhost:3000"
|
||||
echo " 后端: http://localhost:3001"
|
||||
echo " 数据库: localhost:5432"
|
||||
echo ""
|
||||
echo " 后端 PID: $BACKEND_PID"
|
||||
echo " 前端 PID: $FRONTEND_PID"
|
||||
echo " 停止服务: kill $BACKEND_PID $FRONTEND_PID && docker compose down"
|
||||
echo "══════════════════════════════════════════"
|
||||
|
||||
# 捕获 Ctrl+C,清理子进程
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo "正在停止服务..."
|
||||
kill $BACKEND_PID 2>/dev/null || true
|
||||
kill $FRONTEND_PID 2>/dev/null || true
|
||||
docker compose down 2>/dev/null || true
|
||||
echo "已停止"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# 保持脚本运行
|
||||
wait
|
||||
Reference in New Issue
Block a user