开源准备:重写 README 展示 UI/UX,更新模块路径
- 新增界面截图与 CONTRIBUTING.md - README 以三栏布局、暗色主题、移动端等视觉亮点为主 - go module 迁移至 git.iioio.com/freefire/jiang13-forum Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,6 +2,7 @@
|
|||||||
/data/
|
/data/
|
||||||
|
|
||||||
# 前端依赖与构建缓存
|
# 前端依赖与构建缓存
|
||||||
|
/node_modules/
|
||||||
/frontend/node_modules/
|
/frontend/node_modules/
|
||||||
/frontend/dist/
|
/frontend/dist/
|
||||||
|
|
||||||
|
|||||||
46
CONTRIBUTING.md
Normal file
46
CONTRIBUTING.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# 参与贡献
|
||||||
|
|
||||||
|
感谢你对姜十三论坛的关注!这是一个开源项目,欢迎提交 Issue 和 Pull Request。
|
||||||
|
|
||||||
|
## 开发环境
|
||||||
|
|
||||||
|
**要求:** Go 1.26+、Node.js 18+
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Windows:一键启动后端 + 前端热更新
|
||||||
|
.\build.ps1 -Target dev
|
||||||
|
# 浏览器访问 http://localhost:5173
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux / macOS
|
||||||
|
make dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 提交规范
|
||||||
|
|
||||||
|
- 一个 PR 只做一件事,保持 diff 小而清晰
|
||||||
|
- 前端改动请确认浅色 / 暗色主题下都正常
|
||||||
|
- 涉及 UI 变更时,建议在 PR 中附上截图
|
||||||
|
|
||||||
|
## 完整构建
|
||||||
|
|
||||||
|
发布单二进制前需先构建前端并 embed:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build.ps1 # Windows
|
||||||
|
make build # Linux / macOS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 报告问题
|
||||||
|
|
||||||
|
在 [Issues](https://git.iioio.com/freefire/jiang13-forum/issues) 中描述:
|
||||||
|
|
||||||
|
1. 复现步骤
|
||||||
|
2. 期望行为 vs 实际行为
|
||||||
|
3. 环境信息(系统、浏览器、Go/Node 版本)
|
||||||
|
4. 截图或日志(如有)
|
||||||
|
|
||||||
|
## 行为准则
|
||||||
|
|
||||||
|
请保持友善、尊重他人。骚扰、歧视或恶意行为不被容忍。
|
||||||
291
README.md
291
README.md
@@ -1,68 +1,127 @@
|
|||||||
|
<div align="center">
|
||||||
|
|
||||||
# 姜十三论坛 Jiang13 Forum
|
# 姜十三论坛 Jiang13 Forum
|
||||||
|
|
||||||
轻量自用论坛系统,面向小圈子内部交流。与 Gitea 相同,编译为**单个独立 Go 二进制**,前端静态资源 embed 内嵌,内置 SQLite,开箱即用。
|
**轻量 · 好看 · 单文件部署的现代化论坛**
|
||||||
|
|
||||||
## 项目目录结构
|
面向小圈子内部交流,编译为单个 Go 二进制,前端 SPA 内嵌,内置 SQLite,开箱即用。
|
||||||
|
|
||||||
```
|
<br>
|
||||||
js13-forum/
|
|
||||||
├── cmd/jiang13/main.go # 程序入口
|
[](LICENSE)
|
||||||
├── config/config.go # 命令行参数与配置
|
[](go.mod)
|
||||||
├── model/
|
[](frontend/package.json)
|
||||||
│ ├── models.go # GORM 模型定义
|
[](#)
|
||||||
│ └── db.go # 数据库初始化与自动迁移
|
|
||||||
├── service/ # 业务逻辑层
|
[快速开始](#-快速开始) ·
|
||||||
│ ├── auth.go # 注册/登录/JWT
|
[界面预览](#-界面预览) ·
|
||||||
│ ├── user.go # 用户资料/头像
|
[功能亮点](#-功能亮点) ·
|
||||||
│ ├── board.go # 板块 CRUD
|
[参与贡献](CONTRIBUTING.md)
|
||||||
│ ├── post.go # 帖子/点赞/收藏
|
|
||||||
│ ├── comment.go # 楼层评论
|
<br>
|
||||||
│ ├── backup.go # SQLite 备份
|
|
||||||
│ ├── ratelimit.go # 访问限流
|
<img src="docs/screenshots/home-light.png" alt="姜十三论坛首页 - 浅色主题三栏布局" width="92%">
|
||||||
│ └── common.go # 敏感词过滤等
|
|
||||||
├── handler/
|
<sub>浅色主题 · V2EX/NGA 风格三栏布局 · 虚拟滚动帖列表</sub>
|
||||||
│ ├── handlers.go # 前台页面与 API
|
|
||||||
│ └── admin.go # 后台管理
|
</div>
|
||||||
├── middleware/auth.go # JWT 鉴权与限流中间件
|
|
||||||
├── router/router.go # 路由注册
|
---
|
||||||
├── embed_static/
|
|
||||||
│ ├── embed.go # go:embed 声明
|
## 界面预览
|
||||||
│ ├── static/css/style.css # 内嵌 CSS
|
|
||||||
│ ├── static/js/app.js # 内嵌 JS
|
> 论坛用户第一眼看到的是界面。姜十三论坛采用清新绿色主题、高密度信息布局,兼顾桌面与移动端体验。
|
||||||
│ └── templates/ # 内嵌 HTML 模板
|
|
||||||
├── go.mod
|
<table>
|
||||||
├── Makefile
|
<tr>
|
||||||
└── README.md
|
<td width="50%" align="center">
|
||||||
```
|
<img src="docs/screenshots/home-light.png" alt="浅色主题首页" width="100%">
|
||||||
|
<br><b>浅色主题</b><br>
|
||||||
|
<sub>左栏板块导航 · 中间帖列表 · 右栏热门/动态/在线</sub>
|
||||||
|
</td>
|
||||||
|
<td width="50%" align="center">
|
||||||
|
<img src="docs/screenshots/home-dark.png" alt="暗色主题首页" width="100%">
|
||||||
|
<br><b>暗色主题</b><br>
|
||||||
|
<sub>一键切换 · 护眼阅读 · 全局色彩自适应</sub>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td width="50%" align="center">
|
||||||
|
<img src="docs/screenshots/post-detail.png" alt="帖子详情页" width="100%">
|
||||||
|
<br><b>帖子详情</b><br>
|
||||||
|
<sub>楼层式回复 · 引用回复 · 登录可见内容</sub>
|
||||||
|
</td>
|
||||||
|
<td width="50%" align="center">
|
||||||
|
<img src="docs/screenshots/mobile-home.png" alt="移动端首页" width="280">
|
||||||
|
<br><b>移动端适配</b><br>
|
||||||
|
<sub>侧栏自动收起 · 触控友好 · 板块快捷筛选</sub>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="docs/screenshots/compose.png" alt="登录页" width="360">
|
||||||
|
<br><b>登录 / 注册</b> — 居中卡片式表单,简洁无干扰
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 功能亮点
|
||||||
|
|
||||||
|
### 界面与交互
|
||||||
|
|
||||||
|
| 特性 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| **三栏布局** | 左栏板块菜单(可折叠)+ 中间虚拟滚动帖列表 + 右栏热门/通知/在线 |
|
||||||
|
| **虚拟滚动** | `@tanstack/react-virtual` 驱动帖列表与楼层回复,长列表依然流畅 |
|
||||||
|
| **已读 / 未读** | 未读高亮、角标提醒、批量标记已读 |
|
||||||
|
| **主题切换** | 浅色 / 暗色一键切换,跟随 `prefers-color-scheme` 与本地记忆 |
|
||||||
|
| **响应式** | 平板 / 手机自动收起侧栏,搜索、发帖、登录触手可及 |
|
||||||
|
| **高密度排版** | V2EX / NGA 风格信息密度,一屏浏览更多内容 |
|
||||||
|
|
||||||
|
### 社区功能
|
||||||
|
|
||||||
|
- 用户注册 / 登录(bcrypt + JWT Cookie)
|
||||||
|
- 普通用户 / 管理员两级权限,**首个注册用户自动成为管理员**
|
||||||
|
- 板块管理、发帖、Markdown / 富文本、标签、置顶
|
||||||
|
- 楼层式评论,支持回复指定楼层、@ 高亮、引用回复
|
||||||
|
- 点赞、收藏、热门帖、最新动态
|
||||||
|
- 管理员后台:删帖、删评论、禁言、SQLite 一键备份
|
||||||
|
- 内置敏感词过滤、发帖 / 评论限流
|
||||||
|
|
||||||
|
### 部署体验
|
||||||
|
|
||||||
|
- **单二进制部署** — 与 Gitea 同款 `go:embed` 打包,无需 Nginx 反代静态资源
|
||||||
|
- **零依赖数据库** — SQLite 内建,数据目录 `--data` 一处管理
|
||||||
|
- **跨平台** — Windows / Linux / macOS 一键编译
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
### 1. 编译
|
### 1. 编译
|
||||||
|
|
||||||
**Windows(推荐,无需 GNU Make):**
|
**Windows(推荐):**
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# PowerShell
|
|
||||||
.\build.ps1
|
.\build.ps1
|
||||||
|
# 或双击 build.bat
|
||||||
# 或双击 / cmd
|
|
||||||
build.bat
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Linux / macOS(需 GNU Make):**
|
**Linux / macOS:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make build
|
make build
|
||||||
```
|
```
|
||||||
|
|
||||||
**手动分步(全平台通用):**
|
**手动分步(全平台):**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend && npm install && npm run build
|
cd frontend && npm install && npm run build
|
||||||
cd .. && go build -trimpath -ldflags "-s -w" -o dist/jiang13.exe ./cmd/jiang13
|
cd .. && go build -trimpath -ldflags "-s -w" -o dist/jiang13 ./cmd/jiang13
|
||||||
```
|
```
|
||||||
|
|
||||||
> Windows 自带的 `make` 通常是 Embarcadero MAKE,**不能**识别本项目 Makefile。请用 `.\build.ps1` 或安装 GNU Make(`choco install make`)后再用 `make build`。
|
> Windows 自带的 `make` 通常是 Embarcadero MAKE,不能识别本项目 Makefile。请用 `.\build.ps1` 或安装 GNU Make 后再用 `make build`。
|
||||||
|
|
||||||
跨平台编译:
|
跨平台编译:
|
||||||
|
|
||||||
@@ -84,104 +143,90 @@ cd .. && go build -trimpath -ldflags "-s -w" -o dist/jiang13.exe ./cmd/jiang13
|
|||||||
|
|
||||||
### 3. 首次使用
|
### 3. 首次使用
|
||||||
|
|
||||||
1. 浏览器打开 http://localhost:3000/register 注册账号
|
1. 浏览器打开 `http://localhost:3000/register` 注册账号
|
||||||
2. **第一个注册的用户自动成为管理员**,无需额外命令
|
2. **第一个注册的用户自动成为管理员**
|
||||||
3. 登录后可访问 http://localhost:3000/admin/dashboard 进入后台
|
3. 登录后访问 `http://localhost:3000/admin/dashboard` 进入后台
|
||||||
|
|
||||||
## 启动参数
|
### 启动参数
|
||||||
|
|
||||||
| 参数 | 默认值 | 说明 |
|
| 参数 | 默认值 | 说明 |
|
||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
| `--port` | 3000 | HTTP 监听端口 |
|
| `--port` | `3000` | HTTP 监听端口 |
|
||||||
| `--data` | ./data | 数据目录(SQLite、上传、日志) |
|
| `--data` | `./data` | 数据目录(SQLite、上传、日志) |
|
||||||
| `--jwt-secret` | 自动生成 | JWT 签名密钥(留空则自动生成并持久化到 data/.jwt_secret) |
|
| `--jwt-secret` | 自动生成 | JWT 签名密钥(留空则持久化到 `data/.jwt_secret`) |
|
||||||
|
|
||||||
## 数据目录说明
|
---
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
| 层级 | 技术 |
|
||||||
|
|------|------|
|
||||||
|
| **后端** | Go 1.26 · Gin · GORM · SQLite |
|
||||||
|
| **前端** | React 18 · Radix UI · Tailwind CSS · TanStack Virtual |
|
||||||
|
| **构建** | Vite → `go:embed` 内嵌 SPA,单二进制发布 |
|
||||||
|
| **认证** | bcrypt · JWT Cookie |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前端开发
|
||||||
|
|
||||||
|
日常改前端**不需要**重新 `npm run build` 或 `go build`,Vite 开发服务器支持秒级热更新(HMR,热模块替换):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\build.ps1 -Target dev # Windows
|
||||||
|
make dev # Linux / macOS
|
||||||
|
```
|
||||||
|
|
||||||
|
浏览器访问 `http://localhost:5173`,API 自动代理到 `http://localhost:3000`。
|
||||||
|
|
||||||
|
**何时需要完整构建:**
|
||||||
|
|
||||||
|
- 修改 Go 代码、HTML 模板、embed 静态资源 → `go build` / `make build`
|
||||||
|
- 发布单二进制前 → `npm run build` + `make build`
|
||||||
|
|
||||||
|
> 直接访问 `:3000` 看到的是上次 build 嵌入的前端;开发时请用 `:5173`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
jiang13-forum/
|
||||||
|
├── cmd/jiang13/ # 程序入口
|
||||||
|
├── config/ # 命令行参数与配置
|
||||||
|
├── model/ # GORM 模型与数据库迁移
|
||||||
|
├── service/ # 业务逻辑(认证、帖子、评论…)
|
||||||
|
├── handler/ # HTTP 处理器(前台 + 后台)
|
||||||
|
├── middleware/ # JWT 鉴权、在线状态
|
||||||
|
├── router/ # 路由注册
|
||||||
|
├── embed_static/ # go:embed 内嵌的 SPA 与模板
|
||||||
|
├── frontend/ # React 源码(Vite 构建)
|
||||||
|
├── docs/screenshots/ # README 界面截图
|
||||||
|
└── scripts/ # 开发辅助脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据目录
|
||||||
|
|
||||||
```
|
```
|
||||||
data/
|
data/
|
||||||
├── jiang13.db # SQLite 主数据库
|
├── jiang13.db # SQLite 主数据库
|
||||||
├── jiang13.log # 运行日志
|
├── jiang13.log # 运行日志
|
||||||
├── filter_words.txt # 敏感词配置(可编辑)
|
├── filter_words.txt # 敏感词配置
|
||||||
├── .jwt_secret # JWT 密钥(自动生成)
|
├── .jwt_secret # JWT 密钥(自动生成)
|
||||||
├── uploads/avatars/ # 用户头像(运行时写入,非 embed)
|
├── uploads/avatars/ # 用户头像
|
||||||
└── jiang13_backup_*.db # 后台导出的备份文件
|
└── jiang13_backup_*.db # 后台导出的备份
|
||||||
```
|
```
|
||||||
|
|
||||||
## 修改前端并重新打包(Gitea 同款流程)
|
---
|
||||||
|
|
||||||
1. 编辑 `embed_static/templates/` 下的 HTML 模板
|
## 参与贡献
|
||||||
2. 编辑 `embed_static/static/css/` 或 `static/js/` 下的静态资源
|
|
||||||
3. 重新编译:`make build`
|
|
||||||
4. 替换旧二进制,重启服务
|
|
||||||
|
|
||||||
前端资源通过 `//go:embed` 编译进二进制,**运行时不需要单独的前端文件夹**。只有用户上传的头像保存在 `--data` 目录。
|
欢迎提交 Issue 和 Pull Request!详见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||||
|
|
||||||
```go
|
---
|
||||||
// embed_static/embed.go
|
|
||||||
//go:embed static/*
|
|
||||||
var staticFS embed.FS
|
|
||||||
|
|
||||||
//go:embed templates/*
|
|
||||||
var templatesFS embed.FS
|
|
||||||
```
|
|
||||||
|
|
||||||
## 功能概览
|
|
||||||
|
|
||||||
- 用户注册/登录(bcrypt + JWT Cookie)
|
|
||||||
- 普通用户 / 管理员两级权限
|
|
||||||
- 板块管理、发帖、富文本 HTML、标签、置顶
|
|
||||||
- 楼层式评论,支持回复指定楼层
|
|
||||||
- 点赞、收藏
|
|
||||||
- 管理员:删帖、删评论、禁言、SQLite 备份
|
|
||||||
- 内置敏感词过滤、发帖/评论限流
|
|
||||||
|
|
||||||
## 技术栈
|
|
||||||
|
|
||||||
- **后端**:Go 1.26 + Gin + GORM + SQLite
|
|
||||||
- **前端**:React 18 + Arco Design + TanStack Virtual(虚拟滚动)
|
|
||||||
- **打包**:Vite 构建 → `go:embed` 内嵌 SPA,与 Gitea 同款单二进制
|
|
||||||
|
|
||||||
## 前端开发(热更新 HMR)
|
|
||||||
|
|
||||||
日常改前端**不需要**重新 `npm run build` 或 `go build`,用 Vite 开发服务器即可秒级热更新:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 一键启动:Go 后端 + Vite 热更新(推荐)
|
|
||||||
.\build.ps1 -Target dev # Windows
|
|
||||||
make dev # Linux / macOS(需 GNU Make)
|
|
||||||
|
|
||||||
# 浏览器访问 http://localhost:5173
|
|
||||||
# API 自动代理到 http://localhost:3000
|
|
||||||
```
|
|
||||||
|
|
||||||
也可开两个终端分别启动:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 终端 1:仅后端
|
|
||||||
.\build.ps1 -Target run # 或 make run
|
|
||||||
|
|
||||||
# 终端 2:仅前端热更新
|
|
||||||
cd frontend && npm install && npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
**何时才需要完整构建:**
|
|
||||||
|
|
||||||
- 改 Go 代码、模板、embed 静态资源 → `go build` / `make build`
|
|
||||||
- 发布单二进制前 → `npm run build` + `make build`(前端产物写入 `embed_static/static/spa/` 后 embed 进二进制)
|
|
||||||
|
|
||||||
> 若直接访问 `:3000`,看到的是**上次 build 嵌入**的前端,不会有热更新。开发时请用 `:5173`。
|
|
||||||
|
|
||||||
### 前端特性(高密度 V2EX/NGA 风格)
|
|
||||||
|
|
||||||
| 特性 | 实现 |
|
|
||||||
|------|------|
|
|
||||||
| 三栏布局 | 左栏板块菜单(可折叠)+ 中间虚拟滚动帖列表 + 右栏热门/通知/在线 |
|
|
||||||
| 虚拟滚动 | `@tanstack/react-virtual` 帖列表 & 楼层回复 |
|
|
||||||
| 已读/未读 | 未读高亮 + 角标 + 批量标记已读 |
|
|
||||||
| 主题 | 浅色/暗黑一键切换,平板/手机自动收起侧栏 |
|
|
||||||
| 交互 | 滚动加载更多、快捷回帖、楼层锚点、引用回复、@高亮 |
|
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
MIT — 自用随意,欢迎修改。
|
[MIT](LICENSE) — 自由使用、修改与分发。
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jiang13/forum/config"
|
"git.iioio.com/freefire/jiang13-forum/config"
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
"github.com/jiang13/forum/router"
|
"git.iioio.com/freefire/jiang13-forum/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
BIN
docs/screenshots/compose.png
Normal file
BIN
docs/screenshots/compose.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
docs/screenshots/home-dark.png
Normal file
BIN
docs/screenshots/home-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
BIN
docs/screenshots/home-light.png
Normal file
BIN
docs/screenshots/home-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 KiB |
BIN
docs/screenshots/mobile-home.png
Normal file
BIN
docs/screenshots/mobile-home.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
BIN
docs/screenshots/post-detail.png
Normal file
BIN
docs/screenshots/post-detail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 420 KiB |
2
go.mod
2
go.mod
@@ -1,4 +1,4 @@
|
|||||||
module github.com/jiang13/forum
|
module git.iioio.com/freefire/jiang13-forum
|
||||||
|
|
||||||
go 1.26
|
go 1.26
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
"github.com/jiang13/forum/service"
|
"git.iioio.com/freefire/jiang13-forum/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
// --- 后台管理页面 ---
|
// --- 后台管理页面 ---
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
"github.com/jiang13/forum/service"
|
"git.iioio.com/freefire/jiang13-forum/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIMe 当前登录用户
|
// APIMe 当前登录用户
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jiang13/forum/config"
|
"git.iioio.com/freefire/jiang13-forum/config"
|
||||||
"github.com/jiang13/forum/middleware"
|
"git.iioio.com/freefire/jiang13-forum/middleware"
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
"github.com/jiang13/forum/service"
|
"git.iioio.com/freefire/jiang13-forum/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handlers 聚合所有 HTTP 处理器
|
// Handlers 聚合所有 HTTP 处理器
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
"github.com/jiang13/forum/service"
|
"git.iioio.com/freefire/jiang13-forum/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jiang13/forum/service"
|
"git.iioio.com/freefire/jiang13-forum/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VisitorCookieName = "j13_vid"
|
const VisitorCookieName = "j13_vid"
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jiang13/forum/config"
|
"git.iioio.com/freefire/jiang13-forum/config"
|
||||||
"github.com/jiang13/forum/embed_static"
|
"git.iioio.com/freefire/jiang13-forum/embed_static"
|
||||||
"github.com/jiang13/forum/handler"
|
"git.iioio.com/freefire/jiang13-forum/handler"
|
||||||
"github.com/jiang13/forum/middleware"
|
"git.iioio.com/freefire/jiang13-forum/middleware"
|
||||||
"github.com/jiang13/forum/service"
|
"git.iioio.com/freefire/jiang13-forum/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Setup(cfg *config.Config) (*gin.Engine, error) {
|
func Setup(cfg *config.Config) (*gin.Engine, error) {
|
||||||
|
|||||||
86
scripts/capture-screenshots.mjs
Normal file
86
scripts/capture-screenshots.mjs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { chromium } from 'playwright';
|
||||||
|
import { mkdir } from 'node:fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const outDir = path.join(__dirname, '..', 'docs', 'screenshots');
|
||||||
|
const base = process.env.J13_URL || 'http://localhost:8080';
|
||||||
|
|
||||||
|
async function shot(page, name, opts = {}) {
|
||||||
|
const file = path.join(outDir, name);
|
||||||
|
await page.screenshot({ path: file, type: 'png', ...opts });
|
||||||
|
console.log('saved', file);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setTheme(page, theme) {
|
||||||
|
await page.addInitScript((t) => {
|
||||||
|
localStorage.setItem('j13-theme', t);
|
||||||
|
document.documentElement.classList.toggle('dark', t === 'dark');
|
||||||
|
document.documentElement.style.colorScheme = t;
|
||||||
|
}, theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = await chromium.launch();
|
||||||
|
const context = await browser.newContext({ deviceScaleFactor: 2 });
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mkdir(outDir, { recursive: true });
|
||||||
|
|
||||||
|
// 首页 · 浅色
|
||||||
|
{
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
|
await setTheme(page, 'light');
|
||||||
|
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
||||||
|
await page.waitForTimeout(800);
|
||||||
|
await shot(page, 'home-light.png');
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首页 · 暗色
|
||||||
|
{
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
|
await setTheme(page, 'dark');
|
||||||
|
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
||||||
|
await page.waitForTimeout(800);
|
||||||
|
await shot(page, 'home-dark.png');
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 帖子详情
|
||||||
|
{
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
|
await setTheme(page, 'light');
|
||||||
|
await page.goto(`${base}/post/2`, { waitUntil: 'networkidle' });
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
await shot(page, 'post-detail.png');
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发帖编辑器
|
||||||
|
{
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.setViewportSize({ width: 1440, height: 900 });
|
||||||
|
await setTheme(page, 'light');
|
||||||
|
await page.goto(`${base}/compose`, { waitUntil: 'networkidle' });
|
||||||
|
await page.waitForTimeout(800);
|
||||||
|
await shot(page, 'compose.png');
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动端
|
||||||
|
{
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.setViewportSize({ width: 390, height: 844 });
|
||||||
|
await setTheme(page, 'light');
|
||||||
|
await page.goto(`${base}/`, { waitUntil: 'networkidle' });
|
||||||
|
await page.waitForTimeout(800);
|
||||||
|
await shot(page, 'mobile-home.png', { fullPage: true });
|
||||||
|
await page.close();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await browser.close();
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TokenExpire = 7 * 24 * time.Hour
|
const TokenExpire = 7 * 24 * time.Hour
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BoardService struct{}
|
type BoardService struct{}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommentService struct {
|
type CommentService struct {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
const onlineTTL = 5 * time.Minute
|
const onlineTTL = 5 * time.Minute
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jiang13/forum/model"
|
"git.iioio.com/freefire/jiang13-forum/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserService struct {
|
type UserService struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user