92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package media
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var ErrUnsafeControlAPI = errors.New("MediaMTX Control API 必须使用本机回环地址")
|
|
|
|
type RuntimeConfig struct {
|
|
Binary string
|
|
ConfigPath string
|
|
APIBase string
|
|
PollInterval time.Duration
|
|
StartTimeout time.Duration
|
|
}
|
|
|
|
func ConfigFromEnvironment() (RuntimeConfig, error) {
|
|
c := RuntimeConfig{
|
|
Binary: strings.TrimSpace(os.Getenv("SENSE_MEDIAMTX_BINARY")), ConfigPath: strings.TrimSpace(os.Getenv("SENSE_MEDIAMTX_CONFIG")),
|
|
APIBase: strings.TrimSpace(os.Getenv("SENSE_MEDIAMTX_API")), PollInterval: 5 * time.Second, StartTimeout: 8 * time.Second,
|
|
}
|
|
if c.APIBase == "" {
|
|
c.APIBase = "http://127.0.0.1:9997"
|
|
}
|
|
if err := validateControlAPI(c.APIBase); err != nil {
|
|
return RuntimeConfig{}, err
|
|
}
|
|
if c.Binary != "" && c.ConfigPath == "" {
|
|
return RuntimeConfig{}, errors.New("SENSE_MEDIAMTX_CONFIG is required when SENSE_MEDIAMTX_BINARY is configured")
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func validateControlAPI(value string) error {
|
|
u, err := url.Parse(value)
|
|
if err != nil || u.Scheme != "http" || u.User != nil || u.RawQuery != "" || u.Fragment != "" || u.Path != "" {
|
|
return ErrUnsafeControlAPI
|
|
}
|
|
host := u.Hostname()
|
|
ip := net.ParseIP(host)
|
|
if host != "localhost" && (ip == nil || !ip.IsLoopback()) {
|
|
return ErrUnsafeControlAPI
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func RenderBaseConfig(w io.Writer, apiBase string) error {
|
|
if err := validateControlAPI(apiBase); err != nil {
|
|
return err
|
|
}
|
|
u, _ := url.Parse(apiBase)
|
|
_, err := fmt.Fprintf(w, "logLevel: info\napi: true\napiAddress: %s\nmetrics: false\npaths: {}\n", u.Host)
|
|
return err
|
|
}
|
|
|
|
func EnsureBaseConfig(path, apiBase string) error {
|
|
if path == "" {
|
|
return errors.New("MediaMTX config path is empty")
|
|
}
|
|
if _, err := os.Stat(path); err == nil {
|
|
return nil
|
|
} else if !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
|
|
return err
|
|
}
|
|
temporary := path + ".tmp"
|
|
f, err := os.OpenFile(temporary, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
writeErr := RenderBaseConfig(f, apiBase)
|
|
closeErr := f.Close()
|
|
if writeErr != nil || closeErr != nil {
|
|
_ = os.Remove(temporary)
|
|
return errors.Join(writeErr, closeErr)
|
|
}
|
|
if err = os.Rename(temporary, path); err != nil {
|
|
_ = os.Remove(temporary)
|
|
}
|
|
return err
|
|
}
|