174 lines
5.6 KiB
Go
174 lines
5.6 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type Config struct {
|
|
Agent AgentConfig `json:"agent"`
|
|
}
|
|
|
|
type AgentConfig struct {
|
|
Listen string `json:"listen"`
|
|
Token string `json:"token"`
|
|
RequireTokenForRead bool `json:"require_token_for_read"`
|
|
DiscoveryEnable bool `json:"discovery_enable"`
|
|
DiscoveryPort int `json:"discovery_port"`
|
|
DeviceName string `json:"device_name"`
|
|
DeviceIDPath string `json:"device_id_path"`
|
|
ModelsDir string `json:"models_dir"`
|
|
ResourcesDir string `json:"resources_dir"`
|
|
MaxUploadMB int `json:"max_upload_mb"`
|
|
ConfigPath string `json:"config_path"`
|
|
MediaServerProcess MediaServerProcessConfig `json:"media_server_process"`
|
|
MediaServerBaseURL string `json:"media_server_base_url"`
|
|
MediaServerTimeout int `json:"media_server_timeout_ms"`
|
|
MediaServerRetry RetryConfig `json:"media_server_retry"`
|
|
}
|
|
|
|
type MediaServerProcessConfig struct {
|
|
Enable bool `json:"enable"`
|
|
ExecPath string `json:"exec_path"`
|
|
WorkDir string `json:"work_dir"`
|
|
ConfigsDir string `json:"configs_dir"`
|
|
PidFile string `json:"pid_file"`
|
|
GracefulTimeoutMS int `json:"graceful_timeout_ms"`
|
|
}
|
|
|
|
type RetryConfig struct {
|
|
MaxAttempts int `json:"max_attempts"`
|
|
BackoffMS []int `json:"backoff_ms"`
|
|
}
|
|
|
|
func Default() Config {
|
|
return Config{
|
|
Agent: AgentConfig{
|
|
Listen: "0.0.0.0:9100",
|
|
RequireTokenForRead: false,
|
|
DiscoveryEnable: true,
|
|
DiscoveryPort: 35688,
|
|
DeviceIDPath: "/var/lib/rk3588-agent/device_id",
|
|
ModelsDir: "/opt/rk3588sys/models",
|
|
ResourcesDir: "/opt/rk3588sys/resources",
|
|
MaxUploadMB: 200,
|
|
ConfigPath: "/etc/rk3588sys/config.json",
|
|
MediaServerProcess: MediaServerProcessConfig{
|
|
Enable: false,
|
|
PidFile: "/var/run/rk3588sys-media-server.pid",
|
|
GracefulTimeoutMS: 5000,
|
|
},
|
|
MediaServerBaseURL: "http://127.0.0.1:9000",
|
|
MediaServerTimeout: 3000,
|
|
MediaServerRetry: RetryConfig{
|
|
MaxAttempts: 3,
|
|
BackoffMS: []int{200, 500},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func Load(path string) (Config, error) {
|
|
if strings.TrimSpace(path) == "" {
|
|
return Config{}, errors.New("config path is empty")
|
|
}
|
|
b, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return Config{}, fmt.Errorf("read config: %w", err)
|
|
}
|
|
cfg := Default()
|
|
if err := json.Unmarshal(b, &cfg); err != nil {
|
|
return Config{}, fmt.Errorf("parse config json: %w", err)
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
return Config{}, err
|
|
}
|
|
return cfg, nil
|
|
}
|
|
|
|
func (c Config) Validate() error {
|
|
a := c.Agent
|
|
if strings.TrimSpace(a.Listen) == "" {
|
|
return errors.New("agent.listen is required")
|
|
}
|
|
if strings.TrimSpace(a.Token) == "" {
|
|
return errors.New("agent.token is required")
|
|
}
|
|
if a.DiscoveryPort <= 0 || a.DiscoveryPort > 65535 {
|
|
return fmt.Errorf("agent.discovery_port invalid: %d", a.DiscoveryPort)
|
|
}
|
|
if a.MaxUploadMB <= 0 {
|
|
return fmt.Errorf("agent.max_upload_mb invalid: %d", a.MaxUploadMB)
|
|
}
|
|
if strings.TrimSpace(a.ModelsDir) == "" {
|
|
return errors.New("agent.models_dir is required")
|
|
}
|
|
if strings.TrimSpace(a.ResourcesDir) == "" {
|
|
return errors.New("agent.resources_dir is required")
|
|
}
|
|
if strings.TrimSpace(a.ConfigPath) == "" {
|
|
return errors.New("agent.config_path is required")
|
|
}
|
|
if a.MediaServerProcess.Enable {
|
|
p := a.MediaServerProcess
|
|
if strings.TrimSpace(p.ExecPath) == "" {
|
|
return errors.New("agent.media_server_process.exec_path is required")
|
|
}
|
|
if strings.TrimSpace(p.WorkDir) == "" {
|
|
return errors.New("agent.media_server_process.work_dir is required")
|
|
}
|
|
if strings.TrimSpace(p.ConfigsDir) == "" {
|
|
return errors.New("agent.media_server_process.configs_dir is required")
|
|
}
|
|
if strings.TrimSpace(p.PidFile) == "" {
|
|
return errors.New("agent.media_server_process.pid_file is required")
|
|
}
|
|
if p.GracefulTimeoutMS <= 0 {
|
|
return fmt.Errorf("agent.media_server_process.graceful_timeout_ms invalid: %d", p.GracefulTimeoutMS)
|
|
}
|
|
}
|
|
if strings.TrimSpace(a.DeviceIDPath) == "" {
|
|
return errors.New("agent.device_id_path is required")
|
|
}
|
|
if strings.TrimSpace(a.MediaServerBaseURL) == "" {
|
|
return errors.New("agent.media_server_base_url is required")
|
|
}
|
|
u, err := url.Parse(a.MediaServerBaseURL)
|
|
if err != nil {
|
|
return fmt.Errorf("agent.media_server_base_url invalid: %w", err)
|
|
}
|
|
if u.Scheme != "http" && u.Scheme != "https" {
|
|
return fmt.Errorf("agent.media_server_base_url scheme must be http/https: %q", u.Scheme)
|
|
}
|
|
host := u.Hostname()
|
|
if host != "127.0.0.1" && host != "localhost" {
|
|
return fmt.Errorf("agent.media_server_base_url must use 127.0.0.1/localhost, got host=%q", host)
|
|
}
|
|
if p := u.Port(); p != "" {
|
|
port, err := net.LookupPort("tcp", p)
|
|
if err != nil || port <= 0 || port > 65535 {
|
|
return fmt.Errorf("agent.media_server_base_url port invalid: %q", p)
|
|
}
|
|
}
|
|
if a.MediaServerTimeout <= 0 {
|
|
return fmt.Errorf("agent.media_server_timeout_ms invalid: %d", a.MediaServerTimeout)
|
|
}
|
|
if a.MediaServerRetry.MaxAttempts <= 0 {
|
|
return fmt.Errorf("agent.media_server_retry.max_attempts invalid: %d", a.MediaServerRetry.MaxAttempts)
|
|
}
|
|
if len(a.MediaServerRetry.BackoffMS) == 0 {
|
|
return errors.New("agent.media_server_retry.backoff_ms is required")
|
|
}
|
|
for i, v := range a.MediaServerRetry.BackoffMS {
|
|
if v < 0 {
|
|
return fmt.Errorf("agent.media_server_retry.backoff_ms[%d] invalid: %d", i, v)
|
|
}
|
|
}
|
|
return nil
|
|
}
|