81 lines
2.6 KiB
Go
81 lines
2.6 KiB
Go
package media_shard
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"git.ilapage.cn/ila/yovision/Sense/server/app/sense/quota"
|
|
)
|
|
|
|
var ErrInvalidConfig = errors.New("MediaMTX 分片配置不符合要求")
|
|
|
|
func SpecsFromEnvironment(db *gorm.DB, mode, primaryAPI string) ([]Spec, error) {
|
|
capacity, err := quota.ReadLimit(db)
|
|
if raw := strings.TrimSpace(os.Getenv("SENSE_MEDIAMTX_CAPACITY")); raw != "" {
|
|
capacity, err = strconv.Atoi(raw)
|
|
}
|
|
if err != nil || capacity < 1 {
|
|
return nil, fmt.Errorf("%w: primary capacity", ErrInvalidConfig)
|
|
}
|
|
primary := Spec{ID: "primary", Name: "主媒体分片", Mode: mode, ControlAPI: strings.TrimSpace(primaryAPI), Capacity: capacity}
|
|
if err = validateSpec(primary, true); err != nil {
|
|
return nil, err
|
|
}
|
|
result := []Spec{primary}
|
|
path := strings.TrimSpace(os.Getenv("SENSE_MEDIAMTX_SHARDS_FILE"))
|
|
if path == "" {
|
|
return result, nil
|
|
}
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read MediaMTX shards file: %w", err)
|
|
}
|
|
var extra []Spec
|
|
if err = json.Unmarshal(data, &extra); err != nil {
|
|
return nil, fmt.Errorf("%w: shards JSON", ErrInvalidConfig)
|
|
}
|
|
seen := map[string]bool{"primary": true}
|
|
for _, item := range extra {
|
|
item.ID, item.Name, item.Mode, item.ControlAPI = strings.TrimSpace(item.ID), strings.TrimSpace(item.Name), strings.ToLower(strings.TrimSpace(item.Mode)), strings.TrimSpace(item.ControlAPI)
|
|
if item.Mode == "" {
|
|
item.Mode = "external"
|
|
}
|
|
if seen[item.ID] || item.Mode != "external" {
|
|
return nil, fmt.Errorf("%w: duplicate id or non-external extra shard", ErrInvalidConfig)
|
|
}
|
|
if err = validateSpec(item, false); err != nil {
|
|
return nil, err
|
|
}
|
|
seen[item.ID] = true
|
|
result = append(result, item)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func validateSpec(spec Spec, primary bool) error {
|
|
if spec.ID == "" || len(spec.ID) > 64 || spec.Name == "" || len([]rune(spec.Name)) > 128 || spec.Capacity < 1 || spec.Capacity > 100000 {
|
|
return fmt.Errorf("%w: shard identity or capacity", ErrInvalidConfig)
|
|
}
|
|
if primary && spec.Mode != "managed" && spec.Mode != "external" {
|
|
return fmt.Errorf("%w: primary mode", ErrInvalidConfig)
|
|
}
|
|
parsed, err := url.Parse(spec.ControlAPI)
|
|
if err != nil || parsed.Scheme != "http" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" || parsed.Path != "" {
|
|
return fmt.Errorf("%w: control API", ErrInvalidConfig)
|
|
}
|
|
host := parsed.Hostname()
|
|
ip := net.ParseIP(host)
|
|
if !strings.EqualFold(host, "localhost") && (ip == nil || !ip.IsLoopback()) {
|
|
return fmt.Errorf("%w: control API must be loopback", ErrInvalidConfig)
|
|
}
|
|
return nil
|
|
}
|