safesight-control/internal/service/standard_templates.go

144 lines
4.7 KiB
Go

package service
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"safesight-control/internal/storage"
)
func ImportStandardTemplatesFromDir(repo *storage.AssetsRepo, dir string) (int, error) {
if repo == nil {
return 0, fmt.Errorf("asset repository is not configured")
}
dir = filepath.Clean(strings.TrimSpace(dir))
if dir == "" {
return 0, fmt.Errorf("standard template dir is empty")
}
files, err := os.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return 0, nil
}
return 0, err
}
imported := 0
for _, file := range files {
if file.IsDir() || strings.ToLower(filepath.Ext(file.Name())) != ".json" {
continue
}
path := filepath.Join(dir, file.Name())
body, err := os.ReadFile(path)
if err != nil {
return imported, err
}
var raw map[string]any
if err := json.Unmarshal(body, &raw); err != nil {
return imported, fmt.Errorf("%s: %w", path, err)
}
if raw == nil {
raw = map[string]any{}
}
name := strings.TrimSpace(firstString(raw["name"], strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))))
if name == "" {
return imported, fmt.Errorf("%s: template name is empty", path)
}
if err := validateConfigName(name); err != nil {
return imported, fmt.Errorf("%s: invalid template name %q: %w", path, name, err)
}
if !isStandardTemplateName(name) {
return imported, fmt.Errorf("%s: standard template name must start with std_", path)
}
if strings.TrimSpace(stringValue(raw["source"])) == "" {
raw["source"] = "standard"
}
body, err = marshalConfigJSON(raw)
if err != nil {
return imported, err
}
existing, err := repo.GetTemplate(name)
if err != nil {
return imported, err
}
if existing != nil &&
strings.TrimSpace(existing.Description) == strings.TrimSpace(stringValue(raw["description"])) &&
strings.TrimSpace(existing.BodyJSON) == strings.TrimSpace(string(body)) {
continue
}
if err := repo.SaveTemplate(name, stringValue(raw["description"]), string(body)); err != nil {
return imported, err
}
imported++
}
return imported, nil
}
func ImportStandardOverlaysFromDir(repo *storage.AssetsRepo, dir string) (int, error) {
if repo == nil {
return 0, fmt.Errorf("asset repository is not configured")
}
dir = filepath.Clean(strings.TrimSpace(dir))
if dir == "" {
return 0, fmt.Errorf("standard overlay dir is empty")
}
files, err := os.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return 0, nil
}
return 0, err
}
imported := 0
for _, file := range files {
if file.IsDir() || strings.ToLower(filepath.Ext(file.Name())) != ".json" {
continue
}
path := filepath.Join(dir, file.Name())
body, err := os.ReadFile(path)
if err != nil {
return imported, err
}
var raw map[string]any
if err := json.Unmarshal(body, &raw); err != nil {
return imported, fmt.Errorf("%s: %w", path, err)
}
if raw == nil {
raw = map[string]any{}
}
name := strings.TrimSpace(firstString(raw["name"], strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))))
if name == "" {
return imported, fmt.Errorf("%s: overlay name is empty", path)
}
if err := validateConfigName(name); err != nil {
return imported, fmt.Errorf("%s: invalid overlay name %q: %w", path, name, err)
}
if !isStandardTemplateName(name) {
return imported, fmt.Errorf("%s: standard overlay name must start with std_", path)
}
description := strings.TrimSpace(stringValue(raw["description"]))
delete(raw, "name")
delete(raw, "description")
body, err = marshalConfigJSON(raw)
if err != nil {
return imported, err
}
existing, err := repo.GetOverlay(name)
if err != nil {
return imported, err
}
if existing != nil &&
strings.TrimSpace(existing.Description) == description &&
strings.TrimSpace(existing.BodyJSON) == strings.TrimSpace(string(body)) {
continue
}
if err := repo.SaveOverlay(name, description, string(body)); err != nil {
return imported, err
}
imported++
}
return imported, nil
}