使用qoder优化后更新
Some checks failed
Go build / build and run (push) Has been cancelled

This commit is contained in:
2026-02-12 13:48:35 +08:00
parent abd5962ae9
commit bc59f616fd
15 changed files with 130 additions and 278 deletions

View File

@@ -1,9 +1,3 @@
/*
@Time : 2020/6/28 21:48
@Author : xuyiqing
@File : config.py
*/
package config package config
import ( import (
@@ -57,7 +51,7 @@ type Config struct {
Security SecurityConfig Security SecurityConfig
} }
var globalConfig *Config // 新增全局配置变量 var globalConfig *Config
func LoadConfig(configPath string) (*Config, error) { func LoadConfig(configPath string) (*Config, error) {
// 1. 基础配置 // 1. 基础配置
@@ -84,10 +78,9 @@ func LoadConfig(configPath string) (*Config, error) {
return nil, fmt.Errorf("读取配置文件失败: %w", err) return nil, fmt.Errorf("读取配置文件失败: %w", err)
} }
//监控并重新读取配置文件 // 监控配置文件变更
viper.WatchConfig() viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) { viper.OnConfigChange(func(e fsnotify.Event) {
// 配置文件发生变更之后会调用的回调函数
fmt.Println("Config file changed:", e.Name) fmt.Println("Config file changed:", e.Name)
}) })

View File

@@ -1,9 +1,3 @@
/*
@Time : 2020/6/28 21:40
@Author : xuyiqing
@File : users.py
*/
package admin package admin
import ( import (

View File

@@ -2,13 +2,15 @@ package controllers
import ( import (
"go_blog/models" "go_blog/models"
"go_blog/utils"
"net/http"
"strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// 登录 // CreateContentHandler 创建内容
func CreateContentHandler(ctx *gin.Context) { func CreateContentHandler(ctx *gin.Context) {
var content models.Content var content models.Content
if err := ctx.ShouldBindJSON(&content); err != nil { if err := ctx.ShouldBindJSON(&content); err != nil {
ctx.JSON(400, gin.H{"error": err.Error()}) ctx.JSON(400, gin.H{"error": err.Error()})
@@ -22,3 +24,61 @@ func CreateContentHandler(ctx *gin.Context) {
ctx.JSON(201, content) 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": "创建新内容",
})
}

View File

@@ -6,50 +6,55 @@ import (
"net/http" "net/http"
"github.com/gin-gonic/gin" "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") tm, exists := c.Get("ThemeManager")
if !exists { if !exists {
c.String(http.StatusInternalServerError, "Theme manager not found in context") return nil, false
return
} }
themeManager, ok := tm.(*utils.ThemeManager) 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 { 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 return
} }
var items []models.Content 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") db.Select("*").Limit(5).Find(&items, "type = ?", "post")
tpl := themeManager.GetTemplate("index") // "index" 是模板的基本名 (例如 index.tmpl -> index) tpl := themeManager.GetTemplate("index")
if tpl == nil { 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 return
} }
c.Status(http.StatusOK)
c.Header("Content-Type", "text/html; charset=utf-8") c.Header("Content-Type", "text/html; charset=utf-8")
err := tpl.Execute(c.Writer, gin.H{ tpl.Execute(c.Writer, gin.H{
"Items": items, "Items": items,
"Title": "首页", // 你可以根据需要传递更多数据 "Title": "首页",
}) })
if err != nil {
// 实际项目中应记录错误
c.String(http.StatusInternalServerError, "Error rendering template: "+err.Error())
}
} }

View File

@@ -2,23 +2,18 @@ package controllers
import ( import (
"go_blog/models" "go_blog/models"
"go_blog/utils"
"net/http" "net/http"
"strconv" "strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm" // <-- 确保导入 gorm "gorm.io/gorm"
) )
// ShowPost 显示文章详情
func ShowPost(c *gin.Context) { func ShowPost(c *gin.Context) {
tm, exists := c.Get("ThemeManager") themeManager, ok := getThemeManager(c)
if !exists {
c.String(http.StatusInternalServerError, "Theme manager not found")
return
}
themeManager, ok := tm.(*utils.ThemeManager)
if !ok { if !ok {
c.String(http.StatusInternalServerError, "Invalid theme manager type") c.String(http.StatusInternalServerError, "Theme manager not found")
return return
} }
@@ -29,19 +24,13 @@ func ShowPost(c *gin.Context) {
return return
} }
var content = models.Content{Cid: int32(id)} db, ok := getDB(c)
// 从 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 { if !ok {
c.String(http.StatusInternalServerError, "Invalid DB type in context") c.String(http.StatusInternalServerError, "DB not found")
return return
} }
var content = models.Content{Cid: int32(id)}
result := db.First(&content) result := db.First(&content)
if result.Error != nil { if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {
@@ -52,19 +41,12 @@ func ShowPost(c *gin.Context) {
return return
} }
// 假设你的主题中有一个名为 "post" 的模板 (post.html 或 post.tmpl)
// 注意:当前的 default 主题 (web/themes/default/templates/) 并没有 post.tmpl你需要添加它。
tpl := themeManager.GetTemplate("post") tpl := themeManager.GetTemplate("post")
if tpl == nil { 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 return
} }
c.Status(http.StatusOK)
c.Header("Content-Type", "text/html; charset=utf-8") c.Header("Content-Type", "text/html; charset=utf-8")
// 将整个 content 对象传递给模板。模板内部通过 {{ .Title }} {{ .Text }} 等访问。 tpl.Execute(c.Writer, content)
err = tpl.Execute(c.Writer, content)
if err != nil {
c.String(http.StatusInternalServerError, "Error rendering template: "+err.Error())
}
} }

View File

@@ -49,15 +49,15 @@ func main() {
} }
// 5. 创建Gin实例 // 5. 创建Gin实例
// 根据环境设置Gin模式
if conf.Env == config.EnvProduction { if conf.Env == config.EnvProduction {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
} }
router := gin.Default() router := gin.New()
router.Use(gin.Recovery())
themeManager.RegisterStaticRoutes(router) themeManager.RegisterStaticRoutes(router)
// 初始化Session存储使用Cookie // 初始化Session存储使用Cookie
store := cookie.NewStore([]byte("your-session-secret")) // 建议从配置文件读取密钥 store := cookie.NewStore([]byte("your-session-secret"))
store.Options(sessions.Options{ store.Options(sessions.Options{
Path: "/", Path: "/",
MaxAge: 86400 * 7, // 7天 MaxAge: 86400 * 7, // 7天
@@ -66,8 +66,6 @@ func main() {
// 6. 注册中间件 // 6. 注册中间件
router.Use( router.Use(
loggerMiddleware(), loggerMiddleware(),
gin.Logger(),
gin.Recovery(),
databaseMiddleware(models.DB), databaseMiddleware(models.DB),
configMiddleware(conf), configMiddleware(conf),
themeMiddleware(themeManager), themeMiddleware(themeManager),

View File

@@ -1,9 +1,3 @@
/*
@Time : 2020/6/28 22:01
@Author : xuyiqing
@File : users.py
*/
package models package models
import ( import (
@@ -43,24 +37,13 @@ func (a *Account) SetPassword(password string) error {
return nil return nil
} }
// 验证登录帐户密码合法性 // IsPasswordEqual 验证密码是否匹配
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
}
// 验证登录帐户密码合法性
func (a *Account) IsPasswordEqual(password string) bool { func (a *Account) IsPasswordEqual(password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)) err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password))
return err == nil return err == nil
} }
// 验证用户重复 // CheckDuplicateUsername 验证用户名是否重复
func (a *Account) CheckDuplicateUsername() bool { func (a *Account) CheckDuplicateUsername() bool {
var count int64 var count int64
if DB.Model(&Account{}).Where("username=?", a.Username).Count(&count); count > 0 { if DB.Model(&Account{}).Where("username=?", a.Username).Count(&count); count > 0 {

View File

@@ -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
}

View File

@@ -1,7 +1,6 @@
package models package models
const TableNameContent = "contents" // Content 内容模型(文章/页面)
type Content struct { type Content struct {
Cid int32 `gorm:"column:cid;primaryKey;autoIncrement:true" json:"cid"` Cid int32 `gorm:"column:cid;primaryKey;autoIncrement:true" json:"cid"`
Title string `gorm:"column:title" json:"title"` Title string `gorm:"column:title" json:"title"`
@@ -22,7 +21,7 @@ type Content struct {
Parent int32 `gorm:"column:parent" json:"parent"` Parent int32 `gorm:"column:parent" json:"parent"`
} }
// TableName Content's table name // TableName 指定表名
func (*Content) TableName() string { func (*Content) TableName() string {
return TableNameContent return "contents"
} }

View File

@@ -1,9 +1,3 @@
/*
@Time : 2020/6/28 21:46
@Author : xuyiqing
@File : init.py
*/
package models package models
import ( import (
@@ -50,12 +44,8 @@ func InitDatabase(conf *config.Config) {
} }
DB = db DB = db
// 3. 自动迁移数据模型 // 自动迁移数据模型
db.AutoMigrate(&Account{}) db.AutoMigrate(&Account{}, &Content{}, &Option{})
db.AutoMigrate(&Content{})
// if err := db.AutoMigrate(&models.Article{}, &models.User{}); err != nil {
// panic("数据库迁移失败: " + err.Error())
// }
} }
type BaseModel struct { type BaseModel struct {

View File

@@ -2,8 +2,7 @@ package models
import "gorm.io/gorm" import "gorm.io/gorm"
// Option 对应 typecho_options 数据表 // Option 系统配置项模型
// 用于存储系统配置项
type Option struct { type Option struct {
Name string `gorm:"column:name;type:varchar(32);primaryKey"` // 配置名称(主键) Name string `gorm:"column:name;type:varchar(32);primaryKey"` // 配置名称(主键)
Value string `gorm:"column:value;type:text"` // 配置值 Value string `gorm:"column:value;type:text"` // 配置值
@@ -12,7 +11,7 @@ type Option struct {
// TableName 指定表名 // TableName 指定表名
func (Option) TableName() string { func (Option) TableName() string {
return "typecho_options" return "options"
} }
// GetOptionValue 根据name和user获取配置值 // GetOptionValue 根据name和user获取配置值

View File

@@ -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
}

View File

@@ -1,9 +1,3 @@
/*
@Time : 2020/7/15 23:27
@Author : xuyiqing
@File : util.py
*/
package util package util
import ( import (

View File

@@ -1,23 +1,17 @@
package routers // Add the package declaration at the top package routers
import ( import (
"fmt" "fmt"
"go_blog/controllers" "go_blog/controllers"
"go_blog/controllers/admin" "go_blog/controllers/admin"
"go_blog/models"
"go_blog/utils"
"log"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm"
) )
// 新增:自定义后台认证中间件(处理页面重定向) // CheckAdminAuth 后台认证中间件(处理页面重定向)
// 自定义后台认证中间件通过Session判断登录状态
func CheckAdminAuth() gin.HandlerFunc { func CheckAdminAuth() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
currentPath := c.Request.URL.Path currentPath := c.Request.URL.Path
@@ -43,7 +37,7 @@ func CheckAdminAuth() gin.HandlerFunc {
} }
} }
// 新增:基于Session的认证中间件 // SessionAuthRequired 基于Session的认证中间件
func SessionAuthRequired() gin.HandlerFunc { func SessionAuthRequired() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
session := sessions.Default(c) session := sessions.Default(c)
@@ -63,97 +57,16 @@ func SessionAuthRequired() gin.HandlerFunc {
} }
func RegisterRoutes(r *gin.Engine) { func RegisterRoutes(r *gin.Engine) {
// WebSocket 和 SSE 路由
// 注册WebSocket路由
r.GET("/events", esSSE) 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.GET("/ws", controllers.WebSocketHandler)
// 内容相关路由
r.POST("/content", controllers.CreateContentHandler) 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("/", controllers.Home)
r.GET("/post/:id", controllers.ShowPost) r.GET("/post/:id", controllers.ShowPost)
@@ -196,22 +109,15 @@ func esSSE(c *gin.Context) {
w.Header().Set("Connection", "keep-alive") w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Origin", "*")
_, ok := w.(http.Flusher) flusher, ok := w.(http.Flusher)
if !ok { if !ok {
log.Panic("server not support") //浏览器不兼容 c.String(http.StatusInternalServerError, "server not support SSE")
return
} }
for { for {
// Simulate sending events every second
fmt.Fprintf(w, "data: %s\n\n", time.Now().Format(time.Stamp)) fmt.Fprintf(w, "data: %s\n\n", time.Now().Format(time.Stamp))
w.(http.Flusher).Flush() flusher.Flush()
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
_, err := fmt.Fprintf(w, "event: connecttime\ndata: %s\n\n", "connecttime")
if err != nil {
print("error", err)
return
}
} }

View File

@@ -200,21 +200,6 @@ func readFile(path string) ([]byte, error) {
return os.ReadFile(path) 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 目录下的子目录) // GetAvailableThemes 获取所有可用主题(读取 themesDir 目录下的子目录)
func (m *ThemeManager) GetAvailableThemes() ([]string, error) { func (m *ThemeManager) GetAvailableThemes() ([]string, error) {
entries, err := os.ReadDir(m.themesDir) entries, err := os.ReadDir(m.themesDir)