feat: 多租户账号体系 + 前台收藏按钮

- 新增 users 表(user_id 数据隔离,bcrypt 密码)
- 认证: 注册/登录(用户名+密码)/会话绑定用户, 首个用户成为管理员并接管旧数据
- 数据隔离: 笔记/分类/标签/回收站/版本/草稿/图谱/FTS 全部按用户隔离
- 前台: 登录/注册弹窗, 登录后★收藏自己的笔记, 游客只读公开笔记
- 后台: 用户名+密码登录, 每人管理自己的工作区, 越权访问返回404
- 冒烟测试重构+新增多租户隔离用例(78/78)
This commit is contained in:
Your Name
2026-08-11 12:58:12 +08:00
parent 2196189791
commit d9793300f9
16 changed files with 1216 additions and 278 deletions
+29 -20
View File
@@ -4,20 +4,18 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"note-manager/config"
"note-manager/middleware"
"note-manager/service"
)
// AdminHandler 后台管理处理器
// AdminHandler 后台管理处理器(登录页与页面跳转,多租户账号认证)
type AdminHandler struct {
noteSvc *service.NoteService
config *config.Config
userSvc *service.UserService
}
// NewAdminHandler 创建后台管理处理器
func NewAdminHandler(noteSvc *service.NoteService, cfg *config.Config) *AdminHandler {
return &AdminHandler{noteSvc: noteSvc, config: cfg}
func NewAdminHandler(userSvc *service.UserService) *AdminHandler {
return &AdminHandler{userSvc: userSvc}
}
// LoginPage 登录页面
@@ -27,24 +25,35 @@ func (h *AdminHandler) LoginPage(c *gin.Context) {
})
}
// Login 验证登录
// Login 验证登录(用户名 + 密码,兼容 JSON 与 form
func (h *AdminHandler) Login(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
if password == h.config.AdminPass {
// 生成随机会话 token
token, _ := middleware.NewSessionToken()
// 通过环境变量判断是否启用 HTTPS(生产建议配置)
secure := c.Request.TLS != nil
middleware.SetAuthCookie(c, token, secure)
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "登录成功",
if username == "" && password == "" {
// 尝试 JSON
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.ShouldBindJSON(&req); err == nil {
username = req.Username
password = req.Password
}
}
u, err := h.userSvc.Login(username, password)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": err.Error(),
})
return
}
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "密码错误",
token, _ := middleware.NewSession(u.ID)
secure := c.Request.TLS != nil
middleware.SetAuthCookie(c, token, secure)
c.JSON(http.StatusOK, gin.H{
"code": 0,
"message": "登录成功",
})
}
@@ -80,7 +89,7 @@ func (h *AdminHandler) CheckAuth(c *gin.Context) {
})
}
// IndexPage 后台管理首页
// IndexPage 后台管理首页(需登录,否则跳转登录页)
func (h *AdminHandler) IndexPage(c *gin.Context) {
token := middleware.GetAuthToken(c)
if !middleware.IsValidSession(token) {