Files
go_blog/routers/router.go
2025-06-18 18:34:08 +08:00

218 lines
6.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package routers // Add the package declaration at the top
import (
"fmt"
"go_blog/controllers"
"go_blog/models"
"go_blog/serializers"
"go_blog/themes" // <-- 确保导入 themes 包
"log"
"net/http"
"strconv"
"time"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// 新增:自定义后台认证中间件(处理页面重定向)
// 自定义后台认证中间件通过Session判断登录状态
func CheckAdminAuth() gin.HandlerFunc {
return func(c *gin.Context) {
currentPath := c.Request.URL.Path
session := sessions.Default(c) // 获取当前请求的Session
userID := session.Get("user_id") // 从Session中获取用户ID
loggedIn := userID != nil && userID != ""
// 已登录状态访问登录/注册页:重定向到后台首页
if (currentPath == "/admin/login" || currentPath == "/admin/register") && loggedIn {
c.Redirect(http.StatusFound, "/admin/index")
c.Abort()
return
}
// 未登录状态访问非登录/注册页:重定向到登录页
if (currentPath != "/admin/login" && currentPath != "/admin/register") && !loggedIn {
c.Redirect(http.StatusFound, "/admin/login")
c.Abort()
return
}
c.Next()
}
}
// 新增基于Session的认证中间件
func SessionAuthRequired() gin.HandlerFunc {
return func(c *gin.Context) {
session := sessions.Default(c)
userID := session.Get("user_id")
if userID == nil {
// 未登录,重定向到登录页
c.Redirect(http.StatusFound, "/admin/login")
c.Abort()
return
}
// 可选根据userID查询用户信息并注入上下文
// user, err := models.GetAccountByID(userID.(uint))
// if err != nil { ... }
// c.Set("user", user)
c.Next()
}
}
func RegisterRoutes(r *gin.Engine) {
// 注册WebSocket路由
r.GET("/events", esSSE)
r.GET("/page", func(c *gin.Context) {
tm, exists := c.Get("ThemeManager")
if !exists {
c.String(http.StatusInternalServerError, "Theme manager not found")
return
}
themeManager, ok := tm.(*themes.ThemeManager)
if !ok {
c.String(http.StatusInternalServerError, "Invalid theme manager type")
return
}
var items []models.Content
var pager serializers.Pager
pager.InitPager(c) // 这会从查询参数中读取 page 和 pageSize
offset := (pager.Page - 1) * pager.PageSize
dbInterface, dbOk := c.Get("DB")
if !dbOk {
log.Println("未找到键 'DB' 的上下文值")
c.JSON(http.StatusInternalServerError, gin.H{"error": "内部服务器错误"})
return
}
db, typeOk := dbInterface.(*gorm.DB)
if !typeOk {
log.Println("无法将 DB 转换为 *gorm.DB 类型")
c.JSON(http.StatusInternalServerError, gin.H{"error": "内部服务器错误"})
return
}
db.Select("*").Offset(offset).Limit(pager.PageSize).Find(&items, "type = ?", "post")
var totalCount int64
db.Model(&models.Content{}).Where("type = ?", "post").Count(&totalCount)
pager.Total = int(totalCount) // 确保 Pager 结构体中的 Total 类型匹配
tpl := themeManager.GetTemplate("index") // 假设分页列表也使用 index 模板
if tpl == nil {
c.String(http.StatusInternalServerError, "Template 'index' not found in current theme: "+themeManager.CurrentTheme())
return
}
c.Status(http.StatusOK)
c.Header("Content-Type", "text/html; charset=utf-8")
err := tpl.Execute(c.Writer, gin.H{
"Items": items,
"Pager": pager, // 确保你的 index.tmpl 支持 Pager 结构
"Title": "文章列表 - 第 " + strconv.Itoa(pager.Page) + " 页",
})
if err != nil {
c.String(http.StatusInternalServerError, "Error rendering template: "+err.Error())
}
})
r.GET("/createcontent", func(c *gin.Context) {
tm, exists := c.Get("ThemeManager")
if !exists {
c.String(http.StatusInternalServerError, "Theme manager not found")
return
}
themeManager, ok := tm.(*themes.ThemeManager)
if !ok {
c.String(http.StatusInternalServerError, "Invalid theme manager type")
return
}
// 假设你的主题中有一个名为 "content" 的模板 (content.html 或 content.tmpl)
// 注意:当前的 default 主题并没有 content.tmpl你需要添加它。
// 如果这个页面是后台页面,它可能不需要主题化,或者使用一个固定的后台模板。
tpl := themeManager.GetTemplate("content")
if tpl == nil {
c.String(http.StatusInternalServerError, "Template 'content' not found in current theme: "+themeManager.CurrentTheme()+". Make sure 'content.html' or 'content.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())
}
})
r.GET("/ws", controllers.WebSocketHandler)
r.POST("/content", controllers.CreateContentHandler)
// Frontend routes (dynamic themes)
r.GET("/", controllers.Home)
r.GET("/post/:id", controllers.ShowPost)
admin := r.Group("/admin")
admin.Use(CheckAdminAuth())
{
// 无需认证的公开路由(登录/注册)
// 新增:处理 /admin 根路径跳转
admin.GET("/", func(c *gin.Context) {
// 由于 CheckAdminAuth 中间件已处理未登录场景,此处用户必定已登录
c.Redirect(http.StatusFound, "/admin/index")
})
admin.GET("/login", controllers.ShowLoginPage)
admin.GET("/register", controllers.ShowRegisterPage)
admin.POST("/login", controllers.UsersLoginHandler)
admin.POST("/register", controllers.UsersRegisterHandler)
// 需要认证的路由组
authAdmin := admin.Group("", SessionAuthRequired())
{
authAdmin.GET("/index", controllers.ShowAdminIndexPage)
authAdmin.GET("/themes", controllers.ListThemes)
authAdmin.POST("/themes/switch", controllers.SwitchTheme)
authAdmin.POST("/setinfo", controllers.UsersSetInfoHandler)
authAdmin.POST("/setpwd", controllers.UsersSetPwdHandler)
admin.GET("/logout", controllers.UsersLogoutHandler) // 添加退出路由
}
}
// Static files for themes
r.StaticFS("/themes", http.Dir("web/themes"))
}
func esSSE(c *gin.Context) {
w := c.Writer
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")
_, ok := w.(http.Flusher)
if !ok {
log.Panic("server not support") //浏览器不兼容
}
for {
// Simulate sending events every second
fmt.Fprintf(w, "data: %s\n\n", time.Now().Format(time.Stamp))
w.(http.Flusher).Flush()
time.Sleep(1 * time.Second)
}
_, err := fmt.Fprintf(w, "event: connecttime\ndata: %s\n\n", "connecttime")
if err != nil {
print("error", err)
return
}
}