From bc59f616fd83d0b95bb4d3a501b08bb203011427 Mon Sep 17 00:00:00 2001 From: Zhang Chao Date: Thu, 12 Feb 2026 13:48:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8qoder=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=90=8E=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 11 +--- controllers/admin/users.go | 6 -- controllers/contents.go | 64 ++++++++++++++++++- controllers/home.go | 57 +++++++++-------- controllers/showpost.go | 36 +++-------- main.go | 8 +-- models/account.go | 21 +------ models/article.go | 18 ------ models/content.go | 7 +-- models/database.go | 14 +---- models/options.go | 5 +- models/user.go | 18 ------ pkg/util/util.go | 6 -- routers/router.go | 122 +++++-------------------------------- utils/thememanager.go | 15 ----- 15 files changed, 130 insertions(+), 278 deletions(-) delete mode 100644 models/article.go delete mode 100644 models/user.go diff --git a/config/config.go b/config/config.go index f262653..a0a6749 100644 --- a/config/config.go +++ b/config/config.go @@ -1,9 +1,3 @@ -/* -@Time : 2020/6/28 21:48 -@Author : xuyiqing -@File : config.py -*/ - package config import ( @@ -57,7 +51,7 @@ type Config struct { Security SecurityConfig } -var globalConfig *Config // 新增全局配置变量 +var globalConfig *Config func LoadConfig(configPath string) (*Config, error) { // 1. 基础配置 @@ -84,10 +78,9 @@ func LoadConfig(configPath string) (*Config, error) { return nil, fmt.Errorf("读取配置文件失败: %w", err) } - //监控并重新读取配置文件 + // 监控配置文件变更 viper.WatchConfig() viper.OnConfigChange(func(e fsnotify.Event) { - // 配置文件发生变更之后会调用的回调函数 fmt.Println("Config file changed:", e.Name) }) diff --git a/controllers/admin/users.go b/controllers/admin/users.go index 95dc807..18d94a9 100644 --- a/controllers/admin/users.go +++ b/controllers/admin/users.go @@ -1,9 +1,3 @@ -/* -@Time : 2020/6/28 21:40 -@Author : xuyiqing -@File : users.py -*/ - package admin import ( diff --git a/controllers/contents.go b/controllers/contents.go index 5340326..7a02d37 100644 --- a/controllers/contents.go +++ b/controllers/contents.go @@ -2,13 +2,15 @@ package controllers import ( "go_blog/models" + "go_blog/utils" + "net/http" + "strconv" "github.com/gin-gonic/gin" ) -// 登录 +// CreateContentHandler 创建内容 func CreateContentHandler(ctx *gin.Context) { - var content models.Content if err := ctx.ShouldBindJSON(&content); err != nil { ctx.JSON(400, gin.H{"error": err.Error()}) @@ -22,3 +24,61 @@ func CreateContentHandler(ctx *gin.Context) { ctx.JSON(201, content) } + +// PageList 分页文章列表 +func PageList(c *gin.Context) { + themeManager, ok := getThemeManager(c) + if !ok { + c.String(http.StatusInternalServerError, "Theme manager not found") + return + } + + db, ok := getDB(c) + if !ok { + c.String(http.StatusInternalServerError, "DB not found") + return + } + + var items []models.Content + var pager utils.Pager + pager.InitPager(c) + offset := (pager.Page - 1) * pager.PageSize + + 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) + + tpl := themeManager.GetTemplate("index") + if tpl == nil { + c.String(http.StatusInternalServerError, "Template 'index' not found") + return + } + + c.Header("Content-Type", "text/html; charset=utf-8") + tpl.Execute(c.Writer, gin.H{ + "Items": items, + "Pager": pager, + "Title": "文章列表 - 第 " + strconv.Itoa(pager.Page) + " 页", + }) +} + +// CreateContentPage 创建内容页面 +func CreateContentPage(c *gin.Context) { + themeManager, ok := getThemeManager(c) + if !ok { + c.String(http.StatusInternalServerError, "Theme manager not found") + return + } + + tpl := themeManager.GetTemplate("content") + if tpl == nil { + c.String(http.StatusInternalServerError, "Template 'content' not found") + return + } + + c.Header("Content-Type", "text/html; charset=utf-8") + tpl.Execute(c.Writer, gin.H{ + "Title": "创建新内容", + }) +} diff --git a/controllers/home.go b/controllers/home.go index b226117..e281a9a 100644 --- a/controllers/home.go +++ b/controllers/home.go @@ -6,50 +6,55 @@ import ( "net/http" "github.com/gin-gonic/gin" - "gorm.io/gorm" // <-- 确保导入 gorm + "gorm.io/gorm" ) -func Home(c *gin.Context) { +// getThemeManager 从上下文中获取主题管理器 +func getThemeManager(c *gin.Context) (*utils.ThemeManager, bool) { tm, exists := c.Get("ThemeManager") if !exists { - c.String(http.StatusInternalServerError, "Theme manager not found in context") - return + return nil, false } themeManager, ok := tm.(*utils.ThemeManager) + return themeManager, ok +} + +// getDB 从上下文中获取数据库实例 +func getDB(c *gin.Context) (*gorm.DB, bool) { + dbInterface, exists := c.Get("DB") + if !exists { + return nil, false + } + db, ok := dbInterface.(*gorm.DB) + return db, ok +} + +// Home 首页 +func Home(c *gin.Context) { + themeManager, ok := getThemeManager(c) if !ok { - c.String(http.StatusInternalServerError, "Invalid theme manager type in context") + c.String(http.StatusInternalServerError, "Theme manager not found") + return + } + + db, ok := getDB(c) + if !ok { + c.String(http.StatusInternalServerError, "DB not found") return } var items []models.Content - // 从 Gin 上下文中获取 DB 实例 - dbInterface, exists := c.Get("DB") - if !exists { - c.String(http.StatusInternalServerError, "DB not found in context") - return - } - db, ok := dbInterface.(*gorm.DB) - if !ok { - c.String(http.StatusInternalServerError, "Invalid DB type in context") - return - } - db.Select("*").Limit(5).Find(&items, "type = ?", "post") - tpl := themeManager.GetTemplate("index") // "index" 是模板的基本名 (例如 index.tmpl -> index) + tpl := themeManager.GetTemplate("index") if tpl == nil { - c.String(http.StatusInternalServerError, "Template 'index' not found in current theme: "+themeManager.CurrentTheme()) + c.String(http.StatusInternalServerError, "Template 'index' not found") return } - c.Status(http.StatusOK) c.Header("Content-Type", "text/html; charset=utf-8") - err := tpl.Execute(c.Writer, gin.H{ + tpl.Execute(c.Writer, gin.H{ "Items": items, - "Title": "首页", // 你可以根据需要传递更多数据 + "Title": "首页", }) - if err != nil { - // 实际项目中应记录错误 - c.String(http.StatusInternalServerError, "Error rendering template: "+err.Error()) - } } diff --git a/controllers/showpost.go b/controllers/showpost.go index cb5d748..6c461f6 100644 --- a/controllers/showpost.go +++ b/controllers/showpost.go @@ -2,23 +2,18 @@ package controllers import ( "go_blog/models" - "go_blog/utils" "net/http" "strconv" "github.com/gin-gonic/gin" - "gorm.io/gorm" // <-- 确保导入 gorm + "gorm.io/gorm" ) +// ShowPost 显示文章详情 func ShowPost(c *gin.Context) { - tm, exists := c.Get("ThemeManager") - if !exists { - c.String(http.StatusInternalServerError, "Theme manager not found") - return - } - themeManager, ok := tm.(*utils.ThemeManager) + themeManager, ok := getThemeManager(c) if !ok { - c.String(http.StatusInternalServerError, "Invalid theme manager type") + c.String(http.StatusInternalServerError, "Theme manager not found") return } @@ -29,19 +24,13 @@ func ShowPost(c *gin.Context) { return } - var content = models.Content{Cid: int32(id)} - // 从 Gin 上下文中获取 DB 实例 - dbInterface, exists := c.Get("DB") - if !exists { - c.String(http.StatusInternalServerError, "DB not found in context") - return - } - db, ok := dbInterface.(*gorm.DB) + db, ok := getDB(c) if !ok { - c.String(http.StatusInternalServerError, "Invalid DB type in context") + c.String(http.StatusInternalServerError, "DB not found") return } + var content = models.Content{Cid: int32(id)} result := db.First(&content) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { @@ -52,19 +41,12 @@ func ShowPost(c *gin.Context) { return } - // 假设你的主题中有一个名为 "post" 的模板 (post.html 或 post.tmpl) - // 注意:当前的 default 主题 (web/themes/default/templates/) 并没有 post.tmpl,你需要添加它。 tpl := themeManager.GetTemplate("post") if tpl == nil { - c.String(http.StatusInternalServerError, "Template 'post' not found in current theme: "+themeManager.CurrentTheme()+". Make sure 'post.html' or 'post.tmpl' exists in the theme's templates directory.") + c.String(http.StatusInternalServerError, "Template 'post' not found") return } - c.Status(http.StatusOK) c.Header("Content-Type", "text/html; charset=utf-8") - // 将整个 content 对象传递给模板。模板内部通过 {{ .Title }} {{ .Text }} 等访问。 - err = tpl.Execute(c.Writer, content) - if err != nil { - c.String(http.StatusInternalServerError, "Error rendering template: "+err.Error()) - } + tpl.Execute(c.Writer, content) } diff --git a/main.go b/main.go index 47fe77f..cf63d7d 100644 --- a/main.go +++ b/main.go @@ -49,15 +49,15 @@ func main() { } // 5. 创建Gin实例 - // 根据环境设置Gin模式 if conf.Env == config.EnvProduction { gin.SetMode(gin.ReleaseMode) } - router := gin.Default() + router := gin.New() + router.Use(gin.Recovery()) themeManager.RegisterStaticRoutes(router) // 初始化Session存储(使用Cookie) - store := cookie.NewStore([]byte("your-session-secret")) // 建议从配置文件读取密钥 + store := cookie.NewStore([]byte("your-session-secret")) store.Options(sessions.Options{ Path: "/", MaxAge: 86400 * 7, // 7天 @@ -66,8 +66,6 @@ func main() { // 6. 注册中间件 router.Use( loggerMiddleware(), - gin.Logger(), - gin.Recovery(), databaseMiddleware(models.DB), configMiddleware(conf), themeMiddleware(themeManager), diff --git a/models/account.go b/models/account.go index c278f5f..9d19263 100644 --- a/models/account.go +++ b/models/account.go @@ -1,9 +1,3 @@ -/* -@Time : 2020/6/28 22:01 -@Author : xuyiqing -@File : users.py -*/ - package models import ( @@ -43,24 +37,13 @@ func (a *Account) SetPassword(password string) error { return nil } -// 验证登录帐户密码合法性 -func (a *Account) CheckPassword() bool { - password := a.Password - - aa, err1 := bcrypt.GenerateFromPassword([]byte(a.Password), 12) - print(aa, err1) - DB.Where("username = ?", a.Username).First(&a) - err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)) - return err == nil -} - -// 验证登录帐户密码合法性 +// IsPasswordEqual 验证密码是否匹配 func (a *Account) IsPasswordEqual(password string) bool { err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)) return err == nil } -// 验证用户民重复 +// CheckDuplicateUsername 验证用户名是否重复 func (a *Account) CheckDuplicateUsername() bool { var count int64 if DB.Model(&Account{}).Where("username=?", a.Username).Count(&count); count > 0 { diff --git a/models/article.go b/models/article.go deleted file mode 100644 index 3c12d95..0000000 --- a/models/article.go +++ /dev/null @@ -1,18 +0,0 @@ -package models - -import "gorm.io/gorm" - -type Article struct { - gorm.Model - Title string - Content string `gorm:"type:text"` - AuthorID uint - Tags []Tag `gorm:"many2many:article_tags;"` -} - -// Tag represents the tag model -type Tag struct { - gorm.Model - Name string `gorm:"uniqueIndex"` // Ensures tag names are unique - Articles []Article `gorm:"many2many:article_tags;"` // Reverse relationship -} diff --git a/models/content.go b/models/content.go index 72ab367..4b7a238 100644 --- a/models/content.go +++ b/models/content.go @@ -1,7 +1,6 @@ package models -const TableNameContent = "contents" - +// Content 内容模型(文章/页面) type Content struct { Cid int32 `gorm:"column:cid;primaryKey;autoIncrement:true" json:"cid"` Title string `gorm:"column:title" json:"title"` @@ -22,7 +21,7 @@ type Content struct { Parent int32 `gorm:"column:parent" json:"parent"` } -// TableName Content's table name +// TableName 指定表名 func (*Content) TableName() string { - return TableNameContent + return "contents" } diff --git a/models/database.go b/models/database.go index 7b5dd81..4c54696 100644 --- a/models/database.go +++ b/models/database.go @@ -1,9 +1,3 @@ -/* -@Time : 2020/6/28 21:46 -@Author : xuyiqing -@File : init.py -*/ - package models import ( @@ -50,12 +44,8 @@ func InitDatabase(conf *config.Config) { } DB = db - // 3. 自动迁移数据模型 - db.AutoMigrate(&Account{}) - db.AutoMigrate(&Content{}) - // if err := db.AutoMigrate(&models.Article{}, &models.User{}); err != nil { - // panic("数据库迁移失败: " + err.Error()) - // } + // 自动迁移数据模型 + db.AutoMigrate(&Account{}, &Content{}, &Option{}) } type BaseModel struct { diff --git a/models/options.go b/models/options.go index 8e7b363..9c9f7e4 100644 --- a/models/options.go +++ b/models/options.go @@ -2,8 +2,7 @@ package models import "gorm.io/gorm" -// Option 对应 typecho_options 数据表 -// 用于存储系统配置项 +// Option 系统配置项模型 type Option struct { Name string `gorm:"column:name;type:varchar(32);primaryKey"` // 配置名称(主键) Value string `gorm:"column:value;type:text"` // 配置值 @@ -12,7 +11,7 @@ type Option struct { // TableName 指定表名 func (Option) TableName() string { - return "typecho_options" + return "options" } // GetOptionValue 根据name和user获取配置值 diff --git a/models/user.go b/models/user.go deleted file mode 100644 index e086fb9..0000000 --- a/models/user.go +++ /dev/null @@ -1,18 +0,0 @@ -package models - -type User struct { - Name string - Gender string - Age int - Password string - PasswordHash []byte -} - -// GetUserByID 根据 ID 查询用户 -func GetUserByID(id uint) (*User, error) { - var user User - if err := DB.First(&user, id).Error; err != nil { - return nil, err - } - return &user, nil -} diff --git a/pkg/util/util.go b/pkg/util/util.go index dbfd8da..28af18b 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -1,9 +1,3 @@ -/* -@Time : 2020/7/15 23:27 -@Author : xuyiqing -@File : util.py -*/ - package util import ( diff --git a/routers/router.go b/routers/router.go index 4b9f37d..9dbec02 100644 --- a/routers/router.go +++ b/routers/router.go @@ -1,23 +1,17 @@ -package routers // Add the package declaration at the top +package routers import ( "fmt" "go_blog/controllers" "go_blog/controllers/admin" - "go_blog/models" - "go_blog/utils" - "log" "net/http" - "strconv" "time" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" - "gorm.io/gorm" ) -// 新增:自定义后台认证中间件(处理页面重定向) -// 自定义后台认证中间件(通过Session判断登录状态) +// CheckAdminAuth 后台认证中间件(处理页面重定向) func CheckAdminAuth() gin.HandlerFunc { return func(c *gin.Context) { currentPath := c.Request.URL.Path @@ -43,7 +37,7 @@ func CheckAdminAuth() gin.HandlerFunc { } } -// 新增:基于Session的认证中间件 +// SessionAuthRequired 基于Session的认证中间件 func SessionAuthRequired() gin.HandlerFunc { return func(c *gin.Context) { session := sessions.Default(c) @@ -63,97 +57,16 @@ func SessionAuthRequired() gin.HandlerFunc { } func RegisterRoutes(r *gin.Engine) { - - // 注册WebSocket路由 + // WebSocket 和 SSE 路由 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.(*utils.ThemeManager) - if !ok { - c.String(http.StatusInternalServerError, "Invalid theme manager type") - return - } - - var items []models.Content - var pager utils.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.(*utils.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("/page", controllers.PageList) + r.GET("/createcontent", controllers.CreateContentPage) + + // 前端路由(动态主题) r.GET("/", controllers.Home) r.GET("/post/:id", controllers.ShowPost) @@ -196,22 +109,15 @@ func esSSE(c *gin.Context) { w.Header().Set("Connection", "keep-alive") w.Header().Set("Access-Control-Allow-Origin", "*") - _, ok := w.(http.Flusher) - + flusher, ok := w.(http.Flusher) if !ok { - log.Panic("server not support") //浏览器不兼容 + c.String(http.StatusInternalServerError, "server not support SSE") + return } for { - // Simulate sending events every second fmt.Fprintf(w, "data: %s\n\n", time.Now().Format(time.Stamp)) - w.(http.Flusher).Flush() + 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 - } } diff --git a/utils/thememanager.go b/utils/thememanager.go index 8b17ce7..daa678a 100644 --- a/utils/thememanager.go +++ b/utils/thememanager.go @@ -200,21 +200,6 @@ func readFile(path string) ([]byte, error) { return os.ReadFile(path) } -// ListThemes 获取所有可用主题(目录名) -func (m *ThemeManager) ListThemes() ([]string, error) { - entries, err := os.ReadDir(m.themesDir) - if err != nil { - return nil, fmt.Errorf("读取主题目录失败: %w", err) - } - var themes []string - for _, entry := range entries { - if entry.IsDir() { - themes = append(themes, entry.Name()) - } - } - return themes, nil -} - // GetAvailableThemes 获取所有可用主题(读取 themesDir 目录下的子目录) func (m *ThemeManager) GetAvailableThemes() ([]string, error) { entries, err := os.ReadDir(m.themesDir)