整理
This commit is contained in:
26
controllers/admin/admin.go
Normal file
26
controllers/admin/admin.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ShowAdminIndexPage 渲染后台首页
|
||||
func ShowAdminIndexPage(c *gin.Context) {
|
||||
// 直接加载 web/admin 目录下的 index.tmpl 模板(需确保文件存在)
|
||||
tpl, err := template.ParseFiles("web/admin/index.tmpl")
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "加载模板失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
err = tpl.Execute(c.Writer, gin.H{
|
||||
"Title": "后台管理首页",
|
||||
})
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "渲染模板失败: "+err.Error())
|
||||
}
|
||||
}
|
||||
103
controllers/admin/themes.go
Normal file
103
controllers/admin/themes.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"go_blog/config"
|
||||
"go_blog/models"
|
||||
"go_blog/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ListThemes 显示主题管理页面(返回主题列表和当前主题)
|
||||
func ListThemes(c *gin.Context) {
|
||||
// 从上下文中获取主题管理器
|
||||
tm, exists := c.Get("ThemeManager")
|
||||
if !exists {
|
||||
c.String(http.StatusInternalServerError, "主题管理器未找到")
|
||||
return
|
||||
}
|
||||
themeManager, ok := tm.(*utils.ThemeManager)
|
||||
if !ok {
|
||||
c.String(http.StatusInternalServerError, "主题管理器类型错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取可用主题列表(读取 web/themes 目录下的所有子目录)
|
||||
entries, err := themeManager.GetAvailableThemes() // 假设 ThemeManager 新增获取主题列表方法
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "读取主题目录失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 渲染管理页面模板
|
||||
// 直接加载 web/admin/themes.tmpl 文件(路径相对于项目根目录)
|
||||
const themesTemplatePath = "web/admin/themes.tmpl" // 定义模板路径常量
|
||||
|
||||
tpl, err := template.ParseFiles(themesTemplatePath)
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "模板加载失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
err = tpl.Execute(c.Writer, gin.H{
|
||||
"CurrentTheme": themeManager.CurrentTheme(),
|
||||
"Themes": entries,
|
||||
})
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "渲染模板失败: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// SwitchTheme 处理主题切换请求
|
||||
func SwitchTheme(c *gin.Context) {
|
||||
// 从上下文中获取主题管理器
|
||||
tm, exists := c.Get("ThemeManager")
|
||||
if !exists {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题管理器未找到"})
|
||||
return
|
||||
}
|
||||
themeManager, ok := tm.(*utils.ThemeManager)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题管理器类型错误"})
|
||||
return
|
||||
}
|
||||
newTheme := c.PostForm("theme")
|
||||
if newTheme == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "主题名称不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
// 校验主题是否存在(保持原有逻辑)
|
||||
availableThemes, _ := themeManager.GetAvailableThemes()
|
||||
themeExists := false
|
||||
for _, t := range availableThemes {
|
||||
if t == newTheme {
|
||||
themeExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !themeExists {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的主题名称"})
|
||||
return
|
||||
}
|
||||
|
||||
// 加载新主题(保持原有逻辑)
|
||||
if err := themeManager.LoadTheme(newTheme); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题加载失败,请检查主题文件"})
|
||||
return
|
||||
}
|
||||
// 更新全局配置
|
||||
globalConfig := config.GetGlobalConfig()
|
||||
globalConfig.Theme.Current = newTheme
|
||||
|
||||
// 调用options.go的SetOptionValue持久化配置
|
||||
if err := models.SetOptionValue(models.DB, "current_theme", 0, newTheme); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "主题配置保存失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"status": "主题切换成功", "current_theme": newTheme})
|
||||
}
|
||||
251
controllers/admin/users.go
Normal file
251
controllers/admin/users.go
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
@Time : 2020/6/28 21:40
|
||||
@Author : xuyiqing
|
||||
@File : users.py
|
||||
*/
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"go_blog/models"
|
||||
"go_blog/pkg/util"
|
||||
"go_blog/utils"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 登录
|
||||
func UsersLoginHandler(ctx *gin.Context) {
|
||||
var loginUser models.Account
|
||||
if err := ctx.ShouldBind(&loginUser); err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "请求参数错误: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
user := &models.Account{Username: loginUser.Username}
|
||||
if err := models.DB.Where("username = ?", user.Username).First(user).Error; err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "用户不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
if !user.IsPasswordEqual(loginUser.Password) {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "密码错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 改为设置Session(不使用JWT)
|
||||
session := sessions.Default(ctx)
|
||||
session.Set("user_id", user.ID) // 存储用户ID到Session
|
||||
session.Save()
|
||||
|
||||
// 表单提交场景:直接跳转(无需设置JWT Cookie)
|
||||
if ctx.ContentType() == "application/x-www-form-urlencoded" {
|
||||
ctx.Redirect(http.StatusFound, "/admin/index")
|
||||
return
|
||||
}
|
||||
|
||||
// API请求场景:返回登录成功(无需返回token)
|
||||
data, _ := util.PrecisionLost(user)
|
||||
ctx.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "msg": "登录成功", "data": data})
|
||||
}
|
||||
|
||||
// 注册
|
||||
func UsersRegisterHandler(ctx *gin.Context) {
|
||||
var registerUser models.Account
|
||||
if err := ctx.ShouldBind(®isterUser); err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "请求参数错误: " + err.Error(),
|
||||
}) // 替换 panic 为错误响应
|
||||
return
|
||||
}
|
||||
|
||||
status := registerUser.CheckDuplicateUsername()
|
||||
if status == false {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "用户名已存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
if err := registerUser.SetPassword(registerUser.Password); err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "密码设置错误: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
registerUser.IsActive = true
|
||||
models.DB.Create(®isterUser)
|
||||
ctx.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "msg": "注册成功"})
|
||||
}
|
||||
|
||||
// 修改密码
|
||||
func UsersSetPwdHandler(ctx *gin.Context) {
|
||||
|
||||
// 从上下文中获取用户(替换原 jwt.AssertUser 调用)
|
||||
ctxuser, exists := ctx.Get("user")
|
||||
if !exists {
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||
"code": http.StatusUnauthorized,
|
||||
"msg": "未验证登录",
|
||||
})
|
||||
return
|
||||
}
|
||||
currentUser, ok := ctxuser.(*models.Account)
|
||||
if !ok {
|
||||
ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
|
||||
"code": http.StatusInternalServerError,
|
||||
"msg": "用户类型错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
if currentUser == nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
|
||||
"code": http.StatusUnauthorized,
|
||||
"msg": "未验证登录",
|
||||
})
|
||||
return
|
||||
}
|
||||
var user models.Account
|
||||
if err := ctx.ShouldBindJSON(&user); err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "参数解析失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
if user.Username != currentUser.Username {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "当前登录用户用户名与输入用户名不符",
|
||||
})
|
||||
return
|
||||
}
|
||||
if ctx.GetString("OldPwd") == ctx.GetString("NewPwd") {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "两次输入的密码相同",
|
||||
})
|
||||
return
|
||||
}
|
||||
if isPwd := currentUser.IsPasswordEqual(user.Password); !isPwd {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": "原密码错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
if err := currentUser.SetPassword(ctx.GetString("NewPwd")); err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
|
||||
"code": http.StatusBadRequest,
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
models.DB.Save(¤tUser)
|
||||
ctx.JSON(http.StatusOK, gin.H{
|
||||
"code": http.StatusOK,
|
||||
"msg": "密码修改成功",
|
||||
})
|
||||
}
|
||||
func UsersListHandler(ctx *gin.Context) {
|
||||
var pager utils.Pager
|
||||
pager.InitPager(ctx)
|
||||
var users []models.Account
|
||||
|
||||
// 先查询总记录数
|
||||
var totalCount int64
|
||||
models.DB.Model(&models.Account{}).Count(&totalCount)
|
||||
pager.Total = int(totalCount) // 正确设置总数
|
||||
|
||||
// 分页查询
|
||||
// 由于 pager.OffSet 是 int 类型,直接使用该变量,无需调用函数
|
||||
models.DB.Offset(pager.OffSet).Limit(pager.PageSize).Find(&users)
|
||||
pager.GetPager()
|
||||
ctx.JSON(http.StatusOK, gin.H{
|
||||
"Code": http.StatusOK,
|
||||
"Msg": "返回成功",
|
||||
"Data": users,
|
||||
"Pager": pager,
|
||||
})
|
||||
}
|
||||
|
||||
// ShowLoginPage 渲染登录页面
|
||||
func ShowLoginPage(c *gin.Context) {
|
||||
// 直接加载 web/admin 目录下的 login.tmpl 模板(需确保文件存在)
|
||||
tpl, err := template.ParseFiles("web/admin/login.tmpl")
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "加载模板失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
err = tpl.Execute(c.Writer, gin.H{
|
||||
"Title": "用户登录",
|
||||
})
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "渲染模板错误: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// ShowRegisterPage 渲染注册页面
|
||||
func ShowRegisterPage(c *gin.Context) {
|
||||
tm, exists := c.Get("ThemeManager")
|
||||
if !exists {
|
||||
c.String(http.StatusInternalServerError, "Theme manager not found")
|
||||
return
|
||||
}
|
||||
themeManager, ok := tm.(*utils.ThemeManager)
|
||||
if !ok {
|
||||
c.String(http.StatusInternalServerError, "Invalid theme manager type")
|
||||
return
|
||||
}
|
||||
|
||||
// 假设主题中存在 register.tmpl 模板(或使用后台固定模板)
|
||||
tpl := themeManager.GetTemplate("register")
|
||||
if tpl == nil {
|
||||
c.String(http.StatusInternalServerError, "Template 'register' not found in current theme. Make sure 'register.html' or 'register.tmpl' exists.")
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
err := tpl.Execute(c.Writer, gin.H{
|
||||
"Title": "用户注册",
|
||||
})
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error rendering template: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
func UsersLogoutHandler(ctx *gin.Context) {
|
||||
session := sessions.Default(ctx) // 获取当前请求的Session
|
||||
|
||||
// 清除Session中的用户ID
|
||||
session.Delete("user_id")
|
||||
// 保存Session修改(必须调用)
|
||||
if err := session.Save(); err != nil {
|
||||
ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
|
||||
"code": http.StatusInternalServerError,
|
||||
"msg": "退出失败: " + err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 重定向到登录页
|
||||
ctx.Redirect(http.StatusFound, "/admin/login")
|
||||
}
|
||||
Reference in New Issue
Block a user