From 060b7707cb883f33c6f23265c961145734c11562 Mon Sep 17 00:00:00 2001 From: freefire Date: Sat, 1 Aug 2026 12:29:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=85=AC=E5=BC=80=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=B8=BB=E9=A1=B5=E3=80=81=E5=B8=96=E5=AD=90=E5=9B=BE?= =?UTF-8?q?=E7=BC=A9=E7=95=A5=E5=9B=BE=E4=B8=8E=E7=BC=96=E8=BE=91=E5=99=A8?= =?UTF-8?q?=E5=9B=BE=E7=BB=84=E6=8E=92=E7=89=88=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增用户签名与活动统计、图片灯箱;正文按需生成缩略图;TipTap 支持多图分组与环绕排版,并注入站点标题避免刷新闪烁。 Co-authored-by: Cursor --- docs/introduction.md | 158 +++ embed_static/embed.go | 19 + frontend/index.html | 2 +- frontend/src/App.tsx | 2 + frontend/src/api/client.ts | 12 +- frontend/src/api/types.ts | 25 + frontend/src/components/ArticleEditor.tsx | 147 ++- frontend/src/components/AvatarCropDialog.tsx | 10 +- frontend/src/components/CommentThreadList.tsx | 37 +- frontend/src/components/ImageLightbox.tsx | 58 + frontend/src/components/PostContent.tsx | 49 +- frontend/src/components/PostListItem.tsx | 31 +- frontend/src/components/PostRevisionPanel.tsx | 17 +- frontend/src/components/RightPanel.tsx | 41 +- frontend/src/components/Sidebar.tsx | 2 +- frontend/src/components/UserLink.tsx | 77 ++ .../editor/ArticleImageExtension.tsx | 67 ++ .../components/editor/ClearFloatParagraph.ts | 144 +++ .../components/editor/ImageGroupExtension.tsx | 371 ++++++ frontend/src/hooks/useForumLimits.ts | 1 + frontend/src/hooks/useSiteBranding.ts | 9 +- frontend/src/layouts/MainLayout.tsx | 93 +- frontend/src/pages/ComposePage.tsx | 159 +-- frontend/src/pages/PostDetailPage.tsx | 16 +- frontend/src/pages/ProfilePage.tsx | 477 ++++++-- frontend/src/pages/UserProfilePage.tsx | 211 ++++ .../src/pages/admin/AdminCommentsPage.tsx | 8 +- .../src/pages/admin/AdminDashboardPage.tsx | 8 +- frontend/src/pages/admin/AdminPostsPage.tsx | 8 +- .../src/pages/admin/AdminSettingsPage.tsx | 3 +- frontend/src/pages/admin/AdminUsersPage.tsx | 8 +- frontend/src/styles/global.css | 1071 +++++++++++++++-- frontend/src/utils/avatarCrop.ts | 20 +- frontend/src/utils/markdownContent.ts | 36 +- frontend/src/utils/postContent.ts | 144 ++- frontend/src/utils/userPath.ts | 4 + frontend/vite.config.ts | 1 + go.mod | 3 +- go.sum | 4 + handler/api.go | 2 + handler/handlers.go | 52 + handler/thumb.go | 32 + model/models.go | 1 + model/user_view.go | 44 +- router/router.go | 9 + service/comment.go | 2 + service/post.go | 4 + service/settings.go | 18 + service/thumb.go | 182 +++ service/upload.go | 11 +- service/user.go | 50 + 51 files changed, 3563 insertions(+), 397 deletions(-) create mode 100644 docs/introduction.md create mode 100644 frontend/src/components/ImageLightbox.tsx create mode 100644 frontend/src/components/UserLink.tsx create mode 100644 frontend/src/components/editor/ArticleImageExtension.tsx create mode 100644 frontend/src/components/editor/ClearFloatParagraph.ts create mode 100644 frontend/src/components/editor/ImageGroupExtension.tsx create mode 100644 frontend/src/pages/UserProfilePage.tsx create mode 100644 frontend/src/utils/userPath.ts create mode 100644 handler/thumb.go create mode 100644 service/thumb.go diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 0000000..40ae6e7 --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,158 @@ +# 姜十三论坛:为小圈子而生的轻量论坛 + +> 轻量 · 好看 · 单文件部署 +> 面向内部交流,不堆功能,把「能聊、好看、好装」做到位。 + +--- + +## 它是什么 + +**姜十三论坛(Jiang13 Forum)** 是一款面向小圈子、团队与兴趣社群的现代化论坛软件。 + +它不做「大而全」的社区平台,而是聚焦一件事:让几个人到几百人的内部交流,有一个干净、顺手、自己能掌控的地方。 + +技术上,它是一个编译为 **单个 Go 二进制** 的 Web 应用:前端 SPA(单页应用)通过 `go:embed` 内嵌,数据库用内置 **SQLite**,拷贝到服务器就能跑——没有复杂的中间件矩阵,也没有「先装一堆依赖再祈祷能起来」的仪式感。 + +--- + +## 为什么做它 + +市面上的论坛方案往往落在两端: + +- **重型产品**(Discourse、phpBB 等):功能完整,但部署与运维成本高,对小团队过重。 +- **即时通讯 / 文档工具**:适合聊天与协作,却缺少「发帖—回复—沉淀」的社区节奏。 + +小圈子真正需要的,常常是中间态: + +- 有板块、帖子、楼层回复,讨论可追溯; +- 界面清爽、信息密度够高,桌面与手机都好用; +- 部署简单到「一个文件 + 一份配置」; +- 数据在自己手里,备份一眼能懂。 + +姜十三论坛正是为这个缺口而生。 + +--- + +## 谁适合用 + +| 场景 | 说明 | +|------|------| +| 团队 / 工作室内部论坛 | 需求讨论、进度同步、知识沉淀 | +| 兴趣小圈子 | 同好交流、作品分享、活动组织 | +| 开源 / 私有项目配套社区 | 与 Gitea 等工具通过 OIDC(开放身份连接)做 SSO(单点登录) | +| 想自己托管的个人站长 | 单机即可,无需云数据库与一堆微服务 | + +如果你需要百万用户级的公网社区、复杂插件生态或企业级工单流,它可能不是最优选;如果你要的是 **小而美、自己能装、自己能管**,它会对胃口。 + +--- + +## 用起来是什么感觉 + +### 浏览:高密度、不臃肿 + +前台采用熟悉的 **三栏布局**: + +- **左栏**:板块导航,可折叠; +- **中间**:帖子 Feed(信息流),支持虚拟滚动,长列表依然流畅; +- **右栏**:热门帖与最新评论,社区动态一目了然。 + +Feed 可按 **最新发帖 / 最新回复 / 热门讨论** 切换。浅色与暗色主题一键切换,并会记住你的偏好。平板与手机上侧栏会自动收起,触控浏览同样顺手。 + +整体气质更接近 V2EX / NGA 一类的信息密度——一屏能看更多内容,而不是大留白的营销站。 + +### 发帖:富文本,够用就好 + +发帖使用 **TipTap** 富文本编辑器:标题、排版、标签、板块选择、正文图片本地上传,日常写帖够用。帖子支持置顶;编辑后保留 **修订历史**,可做 diff(差异)对比。管理员还可配置普通用户的 **编辑时限**,避免无限制改稿。 + +### 讨论:楼层回复,聊得清楚 + +评论是楼层式的:可回复指定楼层、引用原文、@ 高亮。点赞、收藏、热门帖,把活跃内容自然推到前面。 + +### 管理:后台也是 SPA + +管理后台统一在 `/admin`,与前台同一套 React 体验: + +- 仪表盘、板块与帖子管理 +- 用户禁言、删帖删评 +- 论坛参数、限流、敏感词 +- SQLite **一键备份** + +权限模型很简单:**普通用户 / 管理员**;站点 **第一个注册用户自动成为管理员**,省去安装向导里的一堆步骤。 + +--- + +## 部署:一个文件,真正开箱 + +这大概是姜十三论坛最「硬核」的卖点之一。 + +```text +编译 → 得到一个 jiang13(或 jiang13.exe) +放到目录里运行 → 自动生成 app.ini +打开浏览器注册 → 第一个账号就是管理员 +``` + +要点: + +- **单二进制**:静态资源已内嵌,不必再配 Nginx 专门反代前端文件; +- **零外部数据库**:SQLite 落在数据目录,备份就是拷贝文件; +- **app.ini 配置**:风格类似 Gitea,端口、数据目录、站点根地址一眼能改; +- **系统服务**:内置 Linux systemd / Windows Service 安装与启停; +- **跨平台**:Windows / Linux / macOS 均可编译与运行。 + +典型启动后访问 `http://localhost:3000`,注册即可开始。 + +--- + +## 技术素描(给好奇的人) + +| 层级 | 技术 | +|------|------| +| 后端 | Go · Gin · GORM · SQLite | +| 前端 | React 18 · TipTap · Radix UI · Tailwind CSS · TanStack Virtual | +| 构建 | Vite 构建 SPA,再由 `go:embed` 打进二进制 | +| 认证 | bcrypt + JWT Cookie;可选 OIDC Provider,对接 Gitea 等 SSO | + +对运维者:数据目录结构清晰(数据库、日志、上传、敏感词、备份)。对开发者:前后端分离开发,`dev` 模式下 Vite HMR(热模块替换)可秒级预览前端改动。 + +--- + +## 现在走到哪一步 + +项目仍在积极开发中,核心体验已经可用: + +- ✅ 三栏布局、主题切换、虚拟滚动、Feed 排序 +- ✅ 发帖 / 评论 / 点赞收藏 / 修订历史 +- ✅ React 管理后台与论坛参数配置 +- ✅ OIDC Provider(可作 Gitea 等站点的登录源) +- ✅ 单二进制部署与系统服务 + +计划中的方向包括通知动态优化、搜索增强、邮件提醒等——完整列表见仓库 [ROADMAP.md](../ROADMAP.md)。 + +--- + +## 一句话总结 + +**姜十三论坛** = 小圈子论坛该有的功能 × 清新好用的界面 × 真正能单文件带走的部署方式。 + +如果你正在给团队或同好找一个「自己的小论坛」,不妨编译跑一下:注册第一个账号,从发第一帖开始。 + +--- + +## 快速上手 + +```bash +# Windows +.\build.bat + +# Linux / macOS +make build + +# 运行 +./dist/jiang13 # 或 Windows: .\dist\jiang13.exe +``` + +浏览器打开 `http://localhost:3000/register`,第一个注册用户即为管理员。 + +更多细节(配置项、系统服务、前端开发)见项目 [README](../README.md)。欢迎通过 Issue / PR 参与共建。 + +**许可证:** MIT diff --git a/embed_static/embed.go b/embed_static/embed.go index 1b22d2d..fa77bd1 100644 --- a/embed_static/embed.go +++ b/embed_static/embed.go @@ -2,9 +2,11 @@ package embed_static import ( "embed" + "html" "html/template" "io/fs" "net/http" + "regexp" "strings" "github.com/gin-gonic/gin" @@ -16,6 +18,16 @@ var staticFS embed.FS //go:embed templates/* var templatesFS embed.FS +var ( + spaTitleRe = regexp.MustCompile(`(?s).*?`) + spaBrandTitleFn func() string +) + +// SetSPADocumentTitle 注册站点标题提供者,ServeSPA 会注入到入口 HTML,避免刷新闪烁 +func SetSPADocumentTitle(fn func() string) { + spaBrandTitleFn = fn +} + // SetupEmbed 配置内嵌资源:SPA 前端 + 后台 HTML 模板 func SetupEmbed(r *gin.Engine) error { tmpl, err := LoadTemplates() @@ -44,6 +56,12 @@ func ServeSPA(c *gin.Context) { c.String(http.StatusNotFound, "前端未构建,请运行: cd frontend && npm run build") return } + if spaBrandTitleFn != nil { + if title := strings.TrimSpace(spaBrandTitleFn()); title != "" { + escaped := html.EscapeString(title) + data = spaTitleRe.ReplaceAll(data, []byte(""+escaped+"")) + } + } c.Data(http.StatusOK, "text/html; charset=utf-8", data) } @@ -52,6 +70,7 @@ func IsSPARoute(path string) bool { if strings.HasPrefix(path, "/api") || strings.HasPrefix(path, "/admin") || strings.HasPrefix(path, "/uploads") || + strings.HasPrefix(path, "/media") || strings.HasPrefix(path, "/legacy") || strings.HasPrefix(path, "/assets") || strings.HasPrefix(path, "/oauth") || diff --git a/frontend/index.html b/frontend/index.html index 0dbe26d..b2d5884 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,7 +3,7 @@ - 姜十三论坛 Jiang13 Forum + 姜十三论坛 - 拾三一隅,自在交流