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("/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 } }