115 lines
2.8 KiB
Go
115 lines
2.8 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// TemplateConfig holds metadata for a single mail template type.
|
|
type TemplateConfig struct {
|
|
Name string `json:"name"`
|
|
Desc string `json:"desc"`
|
|
Path []string `json:"-"` // Discovered from the pinned upstream snapshot.
|
|
Params map[string]any `json:"params"`
|
|
}
|
|
|
|
// TemplatesConfig is the root structure of templates_config.json.
|
|
type TemplatesConfig struct {
|
|
Templates map[string]TemplateConfig `json:"templates"`
|
|
}
|
|
|
|
// Load reads and parses a templates_config.json file.
|
|
func Load(path string) (*TemplatesConfig, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot read config file %s: %w", path, err)
|
|
}
|
|
|
|
var cfg TemplatesConfig
|
|
decoder := json.NewDecoder(strings.NewReader(string(data)))
|
|
decoder.UseNumber()
|
|
if err := decoder.Decode(&cfg); err != nil {
|
|
return nil, fmt.Errorf("cannot parse config file %s: %w", path, err)
|
|
}
|
|
|
|
if len(cfg.Templates) == 0 {
|
|
return nil, fmt.Errorf("config file %s contains no template definitions", path)
|
|
}
|
|
|
|
for id, t := range cfg.Templates {
|
|
t.Params = normalizeNumbers(t.Params).(map[string]any)
|
|
cfg.Templates[id] = t
|
|
}
|
|
return &cfg, nil
|
|
}
|
|
|
|
func normalizeNumbers(v any) any {
|
|
switch v := v.(type) {
|
|
case json.Number:
|
|
if n, err := v.Int64(); err == nil {
|
|
return n
|
|
}
|
|
n, _ := v.Float64()
|
|
return n
|
|
case map[string]any:
|
|
for k, value := range v {
|
|
v[k] = normalizeNumbers(value)
|
|
}
|
|
return v
|
|
case []any:
|
|
for i, value := range v {
|
|
v[i] = normalizeNumbers(value)
|
|
}
|
|
return v
|
|
default:
|
|
return v
|
|
}
|
|
}
|
|
|
|
// PathStr returns the file path of a template definition joined with OS separator.
|
|
func (t *TemplateConfig) PathStr() string {
|
|
return filepath.Join(t.Path...)
|
|
}
|
|
|
|
// Registry builds a lookup map suitable for serializing into rendered.js
|
|
// as window.__REGISTRY__: maps template ID → {name, desc, path}.
|
|
func Registry(cfg *TemplatesConfig) map[string]map[string]string {
|
|
reg := make(map[string]map[string]string, len(cfg.Templates))
|
|
for id, t := range cfg.Templates {
|
|
reg[id] = map[string]string{
|
|
"name": t.Name,
|
|
"desc": t.Desc,
|
|
"path": strings.Join(t.Path, "/"),
|
|
}
|
|
}
|
|
return reg
|
|
}
|
|
|
|
// FlattenParams converts nested template params into a flat dot-notation map
|
|
// suitable for the preview panel display in rendered.js.
|
|
func FlattenParams(params map[string]any) map[string]string {
|
|
flat := make(map[string]string)
|
|
flatten("", params, flat)
|
|
return flat
|
|
}
|
|
|
|
func flatten(prefix string, val any, out map[string]string) {
|
|
switch v := val.(type) {
|
|
case map[string]any:
|
|
for k, inner := range v {
|
|
key := k
|
|
if prefix != "" {
|
|
key = prefix + "." + k
|
|
}
|
|
flatten(key, inner, out)
|
|
}
|
|
case []any:
|
|
out[prefix] = fmt.Sprintf("[%d items]", len(v))
|
|
default:
|
|
out[prefix] = fmt.Sprintf("%v", v)
|
|
}
|
|
}
|