Files
database_render/internal/template/config_loader.go
2025-08-07 20:03:53 +08:00

247 lines
6.6 KiB
Go

package template
import (
"fmt"
"log/slog"
"github.com/spf13/viper"
)
// ConfigLoader loads template configuration from YAML files
type ConfigLoader struct {
configPath string
logger *slog.Logger
}
// NewConfigLoader creates a new template config loader
func NewConfigLoader(configPath string) *ConfigLoader {
return &ConfigLoader{
configPath: configPath,
logger: slog.With("component", "template_config"),
}
}
// TemplateConfigFile represents the structure of template configuration file
type TemplateConfigFile struct {
Templates map[string]struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Description string `yaml:"description"`
Fields map[string]string `yaml:"fields"`
Config map[string]interface{} `yaml:"config"`
} `yaml:"templates"`
TemplateTypes map[string]struct {
Layout string `yaml:"layout"`
Fields map[string]string `yaml:"fields"`
Options map[string]interface{} `yaml:"options"`
} `yaml:"template_types"`
}
// LoadTemplateConfig loads template configuration from YAML files
func (cl *ConfigLoader) LoadTemplateConfig() (*TemplateConfigFile, error) {
viper.SetConfigName("templates")
viper.SetConfigType("yaml")
// Add search paths
viper.AddConfigPath(cl.configPath)
viper.AddConfigPath("./config/templates")
viper.AddConfigPath("./web/templates")
viper.AddConfigPath("/etc/database-render/templates")
// Set default values
viper.SetDefault("templates", map[string]interface{}{})
viper.SetDefault("template_types", map[string]interface{}{
"list": map[string]interface{}{
"layout": "table",
"fields": map[string]string{
"default": "raw",
"time": "time",
"tag": "tag",
},
"options": map[string]interface{}{
"striped": true,
"hover": true,
},
},
"card": map[string]interface{}{
"layout": "grid",
"fields": map[string]string{
"default": "raw",
"time": "relative",
"tag": "badge",
},
"options": map[string]interface{}{
"columns": "3",
"spacing": "md",
},
},
})
// Read configuration
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
cl.logger.Warn("template config file not found, using defaults")
return cl.createDefaultConfig(), nil
}
return nil, fmt.Errorf("failed to read template config: %w", err)
}
var config TemplateConfigFile
if err := viper.Unmarshal(&config); err != nil {
return nil, fmt.Errorf("failed to unmarshal template config: %w", err)
}
cl.logger.Info("template configuration loaded successfully",
"config_file", viper.ConfigFileUsed(),
"templates_count", len(config.Templates),
"types_count", len(config.TemplateTypes))
return &config, nil
}
// createDefaultConfig creates default template configuration
func (cl *ConfigLoader) createDefaultConfig() *TemplateConfigFile {
return &TemplateConfigFile{
Templates: map[string]struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
Description string `yaml:"description"`
Fields map[string]string `yaml:"fields"`
Config map[string]interface{} `yaml:"config"`
}{
"articles": {
Name: "文章列表",
Type: "list",
Description: "技术文章列表视图",
Fields: map[string]string{
"title": "string",
"content": "markdown",
"category": "category",
"tags": "tag",
"created_at": "time",
},
Config: map[string]interface{}{
"page_size": 15,
"show_pagination": true,
},
},
"logs": {
Name: "系统日志",
Type: "table",
Description: "系统日志表格视图",
Fields: map[string]string{
"level": "tag",
"message": "string",
"timestamp": "time",
},
Config: map[string]interface{}{
"page_size": 50,
"show_filter": true,
},
},
},
TemplateTypes: map[string]struct {
Layout string `yaml:"layout"`
Fields map[string]string `yaml:"fields"`
Options map[string]interface{} `yaml:"options"`
}{
"list": {
Layout: "table",
Fields: map[string]string{
"default": "raw",
"time": "time",
"tag": "tag",
},
Options: map[string]interface{}{
"striped": true,
"hover": true,
},
},
"card": {
Layout: "grid",
Fields: map[string]string{
"default": "raw",
"time": "relative",
"tag": "badge",
},
Options: map[string]interface{}{
"columns": "3",
"spacing": "md",
},
},
},
}
}
// ValidateTemplateConfig validates template configuration
func (cl *ConfigLoader) ValidateTemplateConfig(config *TemplateConfigFile) error {
for name, template := range config.Templates {
if template.Name == "" {
return fmt.Errorf("template %s: name cannot be empty", name)
}
if template.Type == "" {
return fmt.Errorf("template %s: type cannot be empty", name)
}
}
for name, templateType := range config.TemplateTypes {
if templateType.Layout == "" {
return fmt.Errorf("template type %s: layout cannot be empty", name)
}
}
return nil
}
// GetTemplateConfig returns template configuration for a specific table
func (cl *ConfigLoader) GetTemplateConfig(tableName string) (interface{}, bool) {
config, err := cl.LoadTemplateConfig()
if err != nil {
cl.logger.Error("failed to load template config", "error", err)
return nil, false
}
template, exists := config.Templates[tableName]
return template, exists
}
// GetTemplateType returns template type configuration
func (cl *ConfigLoader) GetTemplateType(typeName string) (interface{}, bool) {
config, err := cl.LoadTemplateConfig()
if err != nil {
cl.logger.Error("failed to load template config", "error", err)
return nil, false
}
typeConfig, exists := config.TemplateTypes[typeName]
return typeConfig, exists
}
// GetAvailableTemplates returns all available templates
func (cl *ConfigLoader) GetAvailableTemplates() map[string]string {
config, err := cl.LoadTemplateConfig()
if err != nil {
cl.logger.Error("failed to load template config", "error", err)
return map[string]string{"list": "默认列表"}
}
result := make(map[string]string)
for key, template := range config.Templates {
result[key] = template.Description
}
// Add built-in templates if not overridden
if _, exists := result["list"]; !exists {
result["list"] = "列表视图"
}
if _, exists := result["card"]; !exists {
result["card"] = "卡片视图"
}
if _, exists := result["timeline"]; !exists {
result["timeline"] = "时间轴视图"
}
return result
}