761 lines
23 KiB
Go
761 lines
23 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"3588AdminBackend/internal/storage"
|
|
)
|
|
|
|
var legacyBuiltinTemplateAliases = map[string]string{
|
|
"workshop_face_shoe_alarm": "std_workshop_face_recognition_shoe_alarm",
|
|
"std_service_smoke_stream": "std_service_test_stream",
|
|
}
|
|
|
|
type ConfigTemplateAsset struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
Origin string `json:"origin"`
|
|
ReadOnly bool `json:"read_only"`
|
|
Description string `json:"description"`
|
|
Source string `json:"source"`
|
|
NodeCount int `json:"node_count"`
|
|
EdgeCount int `json:"edge_count"`
|
|
MinIOEndpoint string `json:"minio_endpoint"`
|
|
MinIOBucket string `json:"minio_bucket"`
|
|
ExternalGetTokenURL string `json:"external_get_token_url"`
|
|
ExternalPutMessageURL string `json:"external_put_message_url"`
|
|
TenantCode string `json:"tenant_code"`
|
|
AdvancedParams map[string]any `json:"advanced_params"`
|
|
Raw map[string]any `json:"raw"`
|
|
}
|
|
|
|
type ConfigProfileAsset struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
Description string `json:"description"`
|
|
BusinessName string `json:"business_name"`
|
|
ObjectStorageRef string `json:"object_storage_ref"`
|
|
TokenServiceRef string `json:"token_service_ref"`
|
|
AlarmServiceRef string `json:"alarm_service_ref"`
|
|
QueueSize int `json:"queue_size"`
|
|
QueueStrategy string `json:"queue_strategy"`
|
|
Instances []ConfigProfileInstanceAsset `json:"instances"`
|
|
Raw map[string]any `json:"raw"`
|
|
}
|
|
|
|
type ConfigProfileInstanceAsset struct {
|
|
Name string `json:"name"`
|
|
Template string `json:"template"`
|
|
DisplayName string `json:"display_name"`
|
|
DeviceCode string `json:"device_code"`
|
|
SiteName string `json:"site_name"`
|
|
RTSPURL string `json:"rtsp_url"`
|
|
PublishHLSPath string `json:"publish_hls_path"`
|
|
PublishRTSPPort string `json:"publish_rtsp_port"`
|
|
PublishRTSPPath string `json:"publish_rtsp_path"`
|
|
ChannelNo string `json:"channel_no"`
|
|
AdvancedParams map[string]any `json:"advanced_params"`
|
|
}
|
|
|
|
type ConfigOverlayAsset struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
Description string `json:"description"`
|
|
OverrideTargets []string `json:"override_targets"`
|
|
OverrideTargetNum int `json:"override_target_num"`
|
|
Raw map[string]any `json:"raw"`
|
|
}
|
|
|
|
type ConfigIntegrationServiceAsset struct {
|
|
Name string `json:"name"`
|
|
Path string `json:"path"`
|
|
Type string `json:"type"`
|
|
TypeLabel string `json:"type_label"`
|
|
Description string `json:"description"`
|
|
Enabled bool `json:"enabled"`
|
|
AddressSummary string `json:"address_summary"`
|
|
RefCount int `json:"ref_count"`
|
|
ObjectStorage *ObjectStorageConfig `json:"object_storage,omitempty"`
|
|
TokenService *TokenServiceConfig `json:"token_service,omitempty"`
|
|
AlarmService *AlarmServiceConfig `json:"alarm_service,omitempty"`
|
|
Raw map[string]any `json:"raw"`
|
|
}
|
|
|
|
type ObjectStorageConfig struct {
|
|
Endpoint string `json:"endpoint"`
|
|
Bucket string `json:"bucket"`
|
|
AccessKey string `json:"access_key"`
|
|
SecretKey string `json:"secret_key"`
|
|
}
|
|
|
|
type TokenServiceConfig struct {
|
|
GetTokenURL string `json:"get_token_url"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
TenantCode string `json:"tenant_code"`
|
|
}
|
|
|
|
type AlarmServiceConfig struct {
|
|
PutMessageURL string `json:"put_message_url"`
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
TenantCode string `json:"tenant_code"`
|
|
}
|
|
|
|
func (s *ConfigPreviewService) ListTemplateAssets() ([]ConfigTemplateAsset, error) {
|
|
items := make([]ConfigTemplateAsset, 0)
|
|
seen := map[string]bool{}
|
|
|
|
root := s.mediaRepoRoot()
|
|
if root != "" {
|
|
sources, err := listConfigSources(filepath.Join(root, "configs", "templates"))
|
|
if err != nil {
|
|
if s.hasExplicitRoot() {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
for _, source := range sources {
|
|
item, err := s.templateAssetFromPath(source.Name, source.Path, "builtin", true)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
items = append(items, *item)
|
|
seen[item.Name] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if s != nil && s.assets != nil {
|
|
records, err := s.assets.ListTemplates()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, record := range records {
|
|
name := strings.TrimSpace(record.Name)
|
|
if name == "" || seen[name] || legacyBuiltinTemplateAliases[name] != "" {
|
|
continue
|
|
}
|
|
item, err := s.templateAssetFromRecord(record, "user", false)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
items = append(items, *item)
|
|
seen[item.Name] = true
|
|
}
|
|
}
|
|
|
|
sort.Slice(items, func(i, j int) bool {
|
|
if items[i].ReadOnly != items[j].ReadOnly {
|
|
return items[i].ReadOnly
|
|
}
|
|
return items[i].Name < items[j].Name
|
|
})
|
|
return items, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) GetTemplateAsset(name string) (*ConfigTemplateAsset, error) {
|
|
name = canonicalTemplateAssetName(name)
|
|
if err := validateConfigName(name); err != nil {
|
|
return nil, err
|
|
}
|
|
if path, ok := s.mediaAssetPath("templates", name); ok {
|
|
return s.templateAssetFromPath(name, path, "builtin", true)
|
|
}
|
|
if s != nil && s.assets != nil {
|
|
record, err := s.assets.GetTemplate(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if record != nil {
|
|
return s.templateAssetFromRecord(*record, "user", false)
|
|
}
|
|
}
|
|
return nil, os.ErrNotExist
|
|
}
|
|
|
|
func (s *ConfigPreviewService) SaveTemplateAsset(name string, description string, bodyJSON string) error {
|
|
if s == nil || s.assets == nil {
|
|
return fmt.Errorf("asset repository is not configured")
|
|
}
|
|
name = canonicalTemplateAssetName(name)
|
|
if err := validateConfigName(name); err != nil {
|
|
return err
|
|
}
|
|
if s.templateIsBuiltin(name) {
|
|
return fmt.Errorf("standard template %q is read-only; please copy it before editing", name)
|
|
}
|
|
return s.assets.SaveTemplate(name, description, bodyJSON)
|
|
}
|
|
|
|
func (s *ConfigPreviewService) RenameTemplateAsset(oldName string, newName string, description string, bodyJSON string) error {
|
|
if s == nil || s.assets == nil {
|
|
return fmt.Errorf("asset repository is not configured")
|
|
}
|
|
oldName = canonicalTemplateAssetName(oldName)
|
|
newName = canonicalTemplateAssetName(newName)
|
|
if err := validateConfigName(oldName); err != nil {
|
|
return err
|
|
}
|
|
if err := validateConfigName(newName); err != nil {
|
|
return err
|
|
}
|
|
if s.templateIsBuiltin(oldName) {
|
|
return fmt.Errorf("standard template %q is read-only; please copy it before editing", oldName)
|
|
}
|
|
if oldName != newName && s.templateIsBuiltin(newName) {
|
|
return fmt.Errorf("standard template name %q is reserved", newName)
|
|
}
|
|
return s.assets.RenameTemplate(oldName, newName, description, bodyJSON)
|
|
}
|
|
|
|
func (s *ConfigPreviewService) DeleteTemplateAsset(name string) error {
|
|
if s == nil || s.assets == nil {
|
|
return fmt.Errorf("asset repository is not configured")
|
|
}
|
|
name = canonicalTemplateAssetName(name)
|
|
if err := validateConfigName(name); err != nil {
|
|
return err
|
|
}
|
|
if s.templateIsBuiltin(name) {
|
|
return fmt.Errorf("standard template %q is read-only and cannot be deleted", name)
|
|
}
|
|
refs, err := s.profileNamesReferencingTemplate(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(refs) > 0 {
|
|
return fmt.Errorf("template %q is used by business configs: %s", name, strings.Join(refs, ", "))
|
|
}
|
|
return s.assets.DeleteTemplate(name)
|
|
}
|
|
|
|
func (s *ConfigPreviewService) ListProfileAssets() ([]ConfigProfileAsset, error) {
|
|
sources, err := s.ListSources()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]ConfigProfileAsset, 0, len(sources.Profiles))
|
|
for _, source := range sources.Profiles {
|
|
item, err := s.GetProfileAsset(source.Name)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
items = append(items, *item)
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) profileNamesReferencingTemplate(templateName string) ([]string, error) {
|
|
if s == nil || s.assets == nil {
|
|
return nil, nil
|
|
}
|
|
records, err := s.assets.ListProfiles()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
refs := make([]string, 0)
|
|
for _, record := range records {
|
|
if strings.TrimSpace(record.TemplateName) == templateName || strings.Contains(record.BodyJSON, `"`+"template"+`": "`+templateName+`"`) {
|
|
refs = append(refs, record.Name)
|
|
}
|
|
}
|
|
sort.Strings(refs)
|
|
return refs, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) GetProfileAsset(name string) (*ConfigProfileAsset, error) {
|
|
raw, path, err := s.readAssetJSON("profiles", name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
queueMap, _ := raw["queue"].(map[string]any)
|
|
instancesRaw, _ := raw["instances"].([]any)
|
|
instances := make([]ConfigProfileInstanceAsset, 0, len(instancesRaw))
|
|
for _, item := range instancesRaw {
|
|
instanceMap, _ := item.(map[string]any)
|
|
paramsMap, _ := instanceMap["params"].(map[string]any)
|
|
advanced := cloneMap(paramsMap)
|
|
for _, key := range []string{
|
|
"display_name",
|
|
"device_code",
|
|
"site_name",
|
|
"rtsp_url",
|
|
"publish_hls_path",
|
|
"publish_rtsp_port",
|
|
"publish_rtsp_path",
|
|
"channel_no",
|
|
} {
|
|
delete(advanced, key)
|
|
}
|
|
if len(advanced) == 0 {
|
|
advanced = nil
|
|
}
|
|
instances = append(instances, ConfigProfileInstanceAsset{
|
|
Name: stringValue(instanceMap["name"]),
|
|
Template: stringValue(instanceMap["template"]),
|
|
DisplayName: stringValue(paramsMap["display_name"]),
|
|
DeviceCode: stringValue(paramsMap["device_code"]),
|
|
SiteName: stringValue(paramsMap["site_name"]),
|
|
RTSPURL: stringValue(paramsMap["rtsp_url"]),
|
|
PublishHLSPath: stringValue(paramsMap["publish_hls_path"]),
|
|
PublishRTSPPort: valueString(paramsMap["publish_rtsp_port"]),
|
|
PublishRTSPPath: stringValue(paramsMap["publish_rtsp_path"]),
|
|
ChannelNo: stringValue(paramsMap["channel_no"]),
|
|
AdvancedParams: advanced,
|
|
})
|
|
}
|
|
return &ConfigProfileAsset{
|
|
Name: firstString(raw["name"], name),
|
|
Path: path,
|
|
Description: stringValue(raw["description"]),
|
|
BusinessName: stringValue(raw["business_name"]),
|
|
ObjectStorageRef: stringValue(raw["object_storage_ref"]),
|
|
TokenServiceRef: stringValue(raw["token_service_ref"]),
|
|
AlarmServiceRef: stringValue(raw["alarm_service_ref"]),
|
|
QueueSize: intValue(queueMap["size"]),
|
|
QueueStrategy: stringValue(queueMap["strategy"]),
|
|
Instances: instances,
|
|
Raw: raw,
|
|
}, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) ListOverlayAssets() ([]ConfigOverlayAsset, error) {
|
|
sources, err := s.ListSources()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]ConfigOverlayAsset, 0, len(sources.Overlays))
|
|
for _, source := range sources.Overlays {
|
|
item, err := s.GetOverlayAsset(source.Name)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
items = append(items, *item)
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) GetOverlayAsset(name string) (*ConfigOverlayAsset, error) {
|
|
raw, path, err := s.readAssetJSON("overlays", name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
targets := make([]string, 0)
|
|
if overrides, ok := raw["instance_overrides"].(map[string]any); ok {
|
|
for key := range overrides {
|
|
targets = append(targets, key)
|
|
}
|
|
sort.Strings(targets)
|
|
}
|
|
return &ConfigOverlayAsset{
|
|
Name: name,
|
|
Path: path,
|
|
Description: stringValue(raw["description"]),
|
|
OverrideTargets: targets,
|
|
OverrideTargetNum: len(targets),
|
|
Raw: raw,
|
|
}, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) ListIntegrationServices() ([]ConfigIntegrationServiceAsset, error) {
|
|
if s == nil || s.assets == nil {
|
|
return nil, nil
|
|
}
|
|
records, err := s.assets.ListIntegrationServices()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items := make([]ConfigIntegrationServiceAsset, 0, len(records))
|
|
for _, record := range records {
|
|
item, err := integrationServiceAssetFromRecord(record)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if refs, err := s.profileNamesReferencingIntegrationService(item.Name); err == nil {
|
|
item.RefCount = len(refs)
|
|
}
|
|
items = append(items, *item)
|
|
}
|
|
sort.Slice(items, func(i, j int) bool { return items[i].Name < items[j].Name })
|
|
return items, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) GetIntegrationService(name string) (*ConfigIntegrationServiceAsset, error) {
|
|
if s == nil || s.assets == nil {
|
|
return nil, fmt.Errorf("asset repository is not configured")
|
|
}
|
|
if err := validateConfigName(name); err != nil {
|
|
return nil, err
|
|
}
|
|
record, err := s.assets.GetIntegrationService(name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if record == nil {
|
|
return nil, os.ErrNotExist
|
|
}
|
|
item, err := integrationServiceAssetFromRecord(*record)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if refs, err := s.profileNamesReferencingIntegrationService(item.Name); err == nil {
|
|
item.RefCount = len(refs)
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) DeleteIntegrationService(name string) error {
|
|
if s == nil || s.assets == nil {
|
|
return fmt.Errorf("asset repository is not configured")
|
|
}
|
|
if err := validateConfigName(name); err != nil {
|
|
return err
|
|
}
|
|
refs, err := s.profileNamesReferencingIntegrationService(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(refs) > 0 {
|
|
return fmt.Errorf("third-party service %q is used by scene configs: %s", name, strings.Join(refs, ", "))
|
|
}
|
|
return s.assets.DeleteIntegrationService(name)
|
|
}
|
|
|
|
func (s *ConfigPreviewService) profileNamesReferencingIntegrationService(name string) ([]string, error) {
|
|
if s == nil || s.assets == nil {
|
|
return nil, nil
|
|
}
|
|
records, err := s.assets.ListProfiles()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name = strings.TrimSpace(name)
|
|
if name == "" {
|
|
return nil, nil
|
|
}
|
|
refs := make([]string, 0)
|
|
for _, record := range records {
|
|
var raw map[string]any
|
|
if err := json.Unmarshal([]byte(record.BodyJSON), &raw); err != nil {
|
|
continue
|
|
}
|
|
if raw == nil {
|
|
continue
|
|
}
|
|
for _, key := range []string{"object_storage_ref", "token_service_ref", "alarm_service_ref"} {
|
|
if strings.TrimSpace(stringValue(raw[key])) == name {
|
|
refs = append(refs, record.Name)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(refs)
|
|
return refs, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) readAssetJSON(kind string, name string) (map[string]any, string, error) {
|
|
if s != nil && s.assets != nil {
|
|
raw, path, ok, err := s.readRepoAssetJSON(kind, name)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
if ok {
|
|
return raw, path, nil
|
|
}
|
|
}
|
|
root := s.mediaRepoRoot()
|
|
if root == "" {
|
|
return nil, "", fmt.Errorf("media repo path is not configured")
|
|
}
|
|
if err := validateConfigName(name); err != nil {
|
|
return nil, "", err
|
|
}
|
|
path := filepath.Join(root, "configs", kind, name+".json")
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
var raw map[string]any
|
|
if err := json.Unmarshal(body, &raw); err != nil {
|
|
return nil, "", err
|
|
}
|
|
return raw, path, nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) readRepoAssetJSON(kind string, name string) (map[string]any, string, bool, error) {
|
|
if err := validateConfigName(name); err != nil {
|
|
return nil, "", false, err
|
|
}
|
|
var (
|
|
record *storage.AssetRecord
|
|
err error
|
|
)
|
|
switch kind {
|
|
case "templates":
|
|
record, err = s.assets.GetTemplate(name)
|
|
case "profiles":
|
|
record, err = s.assets.GetProfile(name)
|
|
case "overlays":
|
|
record, err = s.assets.GetOverlay(name)
|
|
default:
|
|
return nil, "", false, fmt.Errorf("unsupported asset kind: %s", kind)
|
|
}
|
|
if err != nil {
|
|
return nil, "", true, err
|
|
}
|
|
if record == nil {
|
|
return nil, "", false, nil
|
|
}
|
|
var raw map[string]any
|
|
if err := json.Unmarshal([]byte(record.BodyJSON), &raw); err != nil {
|
|
return nil, "", true, err
|
|
}
|
|
if raw == nil {
|
|
raw = map[string]any{}
|
|
}
|
|
if strings.TrimSpace(record.Description) != "" {
|
|
raw["description"] = record.Description
|
|
}
|
|
if kind == "profiles" {
|
|
if strings.TrimSpace(record.TemplateName) != "" {
|
|
raw["template_name"] = record.TemplateName
|
|
}
|
|
if strings.TrimSpace(record.BusinessName) != "" && stringValue(raw["business_name"]) == "" {
|
|
raw["business_name"] = record.BusinessName
|
|
}
|
|
}
|
|
return raw, repoAssetPath(kind, name), true, nil
|
|
}
|
|
|
|
func cloneMap(in map[string]any) map[string]any {
|
|
if len(in) == 0 {
|
|
return map[string]any{}
|
|
}
|
|
out := make(map[string]any, len(in))
|
|
for k, v := range in {
|
|
out[k] = v
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (s *ConfigPreviewService) templateAssetFromPath(name string, path string, origin string, readOnly bool) (*ConfigTemplateAsset, error) {
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var raw map[string]any
|
|
if err := json.Unmarshal(body, &raw); err != nil {
|
|
return nil, err
|
|
}
|
|
return buildTemplateAsset(raw, path, origin, readOnly), nil
|
|
}
|
|
|
|
func (s *ConfigPreviewService) templateAssetFromRecord(record storage.AssetRecord, origin string, readOnly bool) (*ConfigTemplateAsset, error) {
|
|
var raw map[string]any
|
|
if err := json.Unmarshal([]byte(record.BodyJSON), &raw); err != nil {
|
|
return nil, err
|
|
}
|
|
if raw == nil {
|
|
raw = map[string]any{}
|
|
}
|
|
if strings.TrimSpace(record.Description) != "" {
|
|
raw["description"] = record.Description
|
|
}
|
|
return buildTemplateAsset(raw, repoAssetPath("templates", record.Name), origin, readOnly), nil
|
|
}
|
|
|
|
func buildTemplateAsset(raw map[string]any, path string, origin string, readOnly bool) *ConfigTemplateAsset {
|
|
templateMap, _ := raw["template"].(map[string]any)
|
|
paramsMap, _ := raw["params"].(map[string]any)
|
|
nodes, _ := templateMap["nodes"].([]any)
|
|
edges, _ := templateMap["edges"].([]any)
|
|
advanced := cloneMap(paramsMap)
|
|
for _, key := range []string{
|
|
"minio_endpoint",
|
|
"minio_bucket",
|
|
"external_get_token_url",
|
|
"external_put_message_url",
|
|
"tenant_code",
|
|
} {
|
|
delete(advanced, key)
|
|
}
|
|
if len(advanced) == 0 {
|
|
advanced = nil
|
|
}
|
|
return &ConfigTemplateAsset{
|
|
Name: firstString(raw["name"], filepath.Base(strings.TrimSuffix(path, filepath.Ext(path)))),
|
|
Path: path,
|
|
Origin: origin,
|
|
ReadOnly: readOnly,
|
|
Description: stringValue(raw["description"]),
|
|
Source: stringValue(raw["source"]),
|
|
NodeCount: len(nodes),
|
|
EdgeCount: len(edges),
|
|
MinIOEndpoint: stringValue(paramsMap["minio_endpoint"]),
|
|
MinIOBucket: stringValue(paramsMap["minio_bucket"]),
|
|
ExternalGetTokenURL: stringValue(paramsMap["external_get_token_url"]),
|
|
ExternalPutMessageURL: stringValue(paramsMap["external_put_message_url"]),
|
|
TenantCode: valueString(paramsMap["tenant_code"]),
|
|
AdvancedParams: advanced,
|
|
Raw: raw,
|
|
}
|
|
}
|
|
|
|
func integrationServiceAssetFromRecord(record storage.IntegrationServiceRecord) (*ConfigIntegrationServiceAsset, error) {
|
|
var raw map[string]any
|
|
if err := json.Unmarshal([]byte(record.BodyJSON), &raw); err != nil {
|
|
return nil, err
|
|
}
|
|
if raw == nil {
|
|
raw = map[string]any{}
|
|
}
|
|
configMap, _ := raw["config"].(map[string]any)
|
|
sourceMap := raw
|
|
if len(configMap) > 0 {
|
|
sourceMap = configMap
|
|
}
|
|
item := &ConfigIntegrationServiceAsset{
|
|
Name: firstString(raw["name"], record.Name),
|
|
Path: repoAssetPath("integration_services", record.Name),
|
|
Type: firstString(record.ServiceType, stringValue(raw["type"])),
|
|
Description: firstString(raw["description"], record.Description),
|
|
Enabled: boolValue(raw["enabled"], record.Enabled),
|
|
Raw: raw,
|
|
}
|
|
item.TypeLabel = integrationTypeLabel(item.Type)
|
|
switch item.Type {
|
|
case "object_storage":
|
|
item.ObjectStorage = &ObjectStorageConfig{
|
|
Endpoint: stringValue(sourceMap["endpoint"]),
|
|
Bucket: stringValue(sourceMap["bucket"]),
|
|
AccessKey: firstString(sourceMap["access_key"], stringValue(sourceMap["minio_access_key"])),
|
|
SecretKey: firstString(sourceMap["secret_key"], stringValue(sourceMap["minio_secret_key"])),
|
|
}
|
|
item.AddressSummary = strings.TrimSpace(strings.Trim(strings.Join([]string{item.ObjectStorage.Endpoint, item.ObjectStorage.Bucket}, " / "), " /"))
|
|
case "token_service":
|
|
item.TokenService = &TokenServiceConfig{
|
|
GetTokenURL: stringValue(sourceMap["get_token_url"]),
|
|
Username: stringValue(sourceMap["username"]),
|
|
Password: stringValue(sourceMap["password"]),
|
|
TenantCode: stringValue(sourceMap["tenant_code"]),
|
|
}
|
|
item.AddressSummary = item.TokenService.GetTokenURL
|
|
case "alarm_service":
|
|
item.AlarmService = &AlarmServiceConfig{
|
|
PutMessageURL: stringValue(sourceMap["put_message_url"]),
|
|
Username: stringValue(sourceMap["username"]),
|
|
Password: stringValue(sourceMap["password"]),
|
|
TenantCode: stringValue(sourceMap["tenant_code"]),
|
|
}
|
|
item.AddressSummary = item.AlarmService.PutMessageURL
|
|
}
|
|
return item, nil
|
|
}
|
|
|
|
func integrationTypeLabel(v string) string {
|
|
switch strings.TrimSpace(v) {
|
|
case "object_storage":
|
|
return "对象存储"
|
|
case "token_service":
|
|
return "认证服务"
|
|
case "alarm_service":
|
|
return "告警服务"
|
|
default:
|
|
return strings.TrimSpace(v)
|
|
}
|
|
}
|
|
|
|
func (s *ConfigPreviewService) mediaAssetPath(kind string, name string) (string, bool) {
|
|
root := s.mediaRepoRoot()
|
|
if root == "" {
|
|
return "", false
|
|
}
|
|
path := filepath.Join(root, "configs", kind, name+".json")
|
|
if _, err := os.Stat(path); err != nil {
|
|
return "", false
|
|
}
|
|
return path, true
|
|
}
|
|
|
|
func (s *ConfigPreviewService) templateIsBuiltin(name string) bool {
|
|
name = canonicalTemplateAssetName(name)
|
|
_, ok := s.mediaAssetPath("templates", name)
|
|
return ok
|
|
}
|
|
|
|
func canonicalTemplateAssetName(name string) string {
|
|
name = strings.TrimSpace(name)
|
|
if next := strings.TrimSpace(legacyBuiltinTemplateAliases[name]); next != "" {
|
|
return next
|
|
}
|
|
return name
|
|
}
|
|
|
|
func stringValue(v any) string {
|
|
if s, ok := v.(string); ok {
|
|
return strings.TrimSpace(s)
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func valueString(v any) string {
|
|
switch value := v.(type) {
|
|
case string:
|
|
return strings.TrimSpace(value)
|
|
case float64:
|
|
if float64(int(value)) == value {
|
|
return fmt.Sprintf("%d", int(value))
|
|
}
|
|
return fmt.Sprintf("%v", value)
|
|
case int:
|
|
return fmt.Sprintf("%d", value)
|
|
case int64:
|
|
return fmt.Sprintf("%d", value)
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func boolValue(v any, fallback bool) bool {
|
|
switch value := v.(type) {
|
|
case bool:
|
|
return value
|
|
case float64:
|
|
return value != 0
|
|
case int:
|
|
return value != 0
|
|
case int64:
|
|
return value != 0
|
|
case string:
|
|
value = strings.TrimSpace(strings.ToLower(value))
|
|
return value == "1" || value == "true" || value == "yes" || value == "on"
|
|
default:
|
|
return fallback
|
|
}
|
|
}
|
|
|
|
func firstString(v any, fallback string) string {
|
|
if got := stringValue(v); got != "" {
|
|
return got
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func intValue(v any) int {
|
|
switch value := v.(type) {
|
|
case int:
|
|
return value
|
|
case int64:
|
|
return int(value)
|
|
case float64:
|
|
return int(value)
|
|
default:
|
|
return 0
|
|
}
|
|
}
|