use themes
This commit is contained in:
2
main.go
2
main.go
@@ -2,7 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go_blog/conf"
|
conf "go_blog/config"
|
||||||
"go_blog/controllers"
|
"go_blog/controllers"
|
||||||
"go_blog/models"
|
"go_blog/models"
|
||||||
"go_blog/serializers"
|
"go_blog/serializers"
|
||||||
|
|||||||
18
models/article.go
Normal file
18
models/article.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go_blog/conf"
|
conf "go_blog/config"
|
||||||
"go_blog/pkg/util"
|
"go_blog/pkg/util"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
package jwt
|
package jwt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go_blog/conf"
|
conf "go_blog/config"
|
||||||
"go_blog/models"
|
"go_blog/models"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|||||||
23
routers/router.go
Normal file
23
routers/router.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package routers // Add the package declaration at the top
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(r *gin.Engine) {
|
||||||
|
// Frontend routes (dynamic themes)
|
||||||
|
r.GET("/", handlers.Home)
|
||||||
|
r.GET("/post/:id", handlers.ShowPost)
|
||||||
|
|
||||||
|
// Admin panel
|
||||||
|
admin := r.Group("/admin", middleware.AuthRequired())
|
||||||
|
{
|
||||||
|
admin.GET("/themes", handlers.ListThemes)
|
||||||
|
admin.POST("/themes/switch", handlers.SwitchTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static files for themes
|
||||||
|
r.StaticFS("/themes", http.Dir("web/themes"))
|
||||||
|
}
|
||||||
54
themes/manager.go
Normal file
54
themes/manager.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package theme // Declare the package at the top
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ThemeManager manages themes for the blog system
|
||||||
|
type ThemeManager struct {
|
||||||
|
CurrentTheme string // Name of the currently active theme
|
||||||
|
Templates map[string]*template.Template // Cached compiled HTML templates
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadTheme loads a specified theme by its name
|
||||||
|
func (tm *ThemeManager) LoadTheme(themeName string) error {
|
||||||
|
// 1. 读取theme.yaml验证主题有效性
|
||||||
|
// 2. 预编译所有HTML模板,缓存到Templates
|
||||||
|
// 3. 注册静态资源路由:/themes/[name]/static/*filepath
|
||||||
|
tm.CurrentTheme = themeName
|
||||||
|
|
||||||
|
// Step 1: Validate the theme by reading theme.yaml
|
||||||
|
themeConfigPath := fmt.Sprintf("h:/code/go_blog/themes/%s/theme.yaml", themeName)
|
||||||
|
if _, err := os.Stat(themeConfigPath); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("theme %s does not exist or is invalid", themeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Precompile all HTML templates and cache them
|
||||||
|
tm.Templates = make(map[string]*template.Template)
|
||||||
|
templateDir := fmt.Sprintf("h:/code/go_blog/themes/%s/templates", themeName)
|
||||||
|
err := filepath.Walk(templateDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() && strings.HasSuffix(info.Name(), ".html") {
|
||||||
|
tmpl, err := template.ParseFiles(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tm.Templates[info.Name()] = tmpl
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load templates: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Register static resource routes
|
||||||
|
http.Handle(fmt.Sprintf("/themes/%s/static/", themeName), http.StripPrefix(fmt.Sprintf("/themes/%s/static/", themeName), http.FileServer(http.Dir(fmt.Sprintf("h:/code/go_blog/themes/%s/static", themeName)))))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
35
themes/parser.go
Normal file
35
themes/parser.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package parser // Declare the package at the top
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"your_project_path/config" // Adjust import path as needed
|
||||||
|
"your_project_path/themes" // Adjust import path as needed
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RenderTemplate renders a template based on the current theme
|
||||||
|
func RenderTemplate(c *gin.Context, tmpl string, data gin.H) {
|
||||||
|
// Get the current theme
|
||||||
|
theme := config.GetCurrentTheme()
|
||||||
|
|
||||||
|
// Construct the template path: "themes/default/home.html"
|
||||||
|
tplPath := fmt.Sprintf("themes/%s/%s", theme, tmpl)
|
||||||
|
|
||||||
|
// Render the template using html/template
|
||||||
|
c.HTML(http.StatusOK, tplPath, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwitchTheme handles POST requests to switch the current theme
|
||||||
|
func SwitchTheme(c *gin.Context) {
|
||||||
|
newTheme := c.PostForm("theme")
|
||||||
|
manager := themes.GetManager()
|
||||||
|
|
||||||
|
if err := manager.LoadTheme(newTheme); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
} else {
|
||||||
|
config.SetCurrentTheme(newTheme) // Update the configuration
|
||||||
|
c.JSON(http.StatusOK, gin.H{"status": "success"})
|
||||||
|
}
|
||||||
|
}
|
||||||
14
utils/utils.go
Normal file
14
utils/utils.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// 示例:插件接口
|
||||||
|
type Plugin interface {
|
||||||
|
OnArticleCreate(article *models.Article) // 文章创建钩子
|
||||||
|
RegisterRoutes(r *gin.Engine) // 添加新路由
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主题ZIP上传示例
|
||||||
|
func handleThemeUpload(c *gin.Context) {
|
||||||
|
file, _ := c.FormFile("theme")
|
||||||
|
if !strings.HasSuffix(file.Filename, ".zip") {
|
||||||
|
c.AbortWithStatus(400)
|
||||||
|
}
|
||||||
|
// 解压到临时目录并校验文件结构
|
||||||
|
}
|
||||||
5
web/themes/skeleton.go
Normal file
5
web/themes/skeleton.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<!-- 示例模板变量 -->
|
||||||
|
<h1>{{ .Site.Title }}</h1>
|
||||||
|
{{ range .Posts }}
|
||||||
|
<article>{{ .Content }}</article>
|
||||||
|
{{ end }}
|
||||||
Reference in New Issue
Block a user