refactor
This commit is contained in:
+1
-4
@@ -26,14 +26,11 @@ const usage = `Usage of go2rtc:
|
|||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
var config flagConfig
|
var config flagConfig
|
||||||
var secret string
|
|
||||||
var daemon bool
|
var daemon bool
|
||||||
var version bool
|
var version bool
|
||||||
|
|
||||||
flag.Var(&config, "config", "")
|
flag.Var(&config, "config", "")
|
||||||
flag.Var(&config, "c", "")
|
flag.Var(&config, "c", "")
|
||||||
flag.StringVar(&secret, "secret", "go2rtc.secret", "")
|
|
||||||
flag.StringVar(&secret, "s", "go2rtc.secret", "")
|
|
||||||
flag.BoolVar(&daemon, "daemon", false, "")
|
flag.BoolVar(&daemon, "daemon", false, "")
|
||||||
flag.BoolVar(&daemon, "d", false, "")
|
flag.BoolVar(&daemon, "d", false, "")
|
||||||
flag.BoolVar(&version, "version", false, "")
|
flag.BoolVar(&version, "version", false, "")
|
||||||
@@ -71,7 +68,7 @@ func Init() {
|
|||||||
Info["revision"] = revision
|
Info["revision"] = revision
|
||||||
|
|
||||||
initConfig(config)
|
initConfig(config)
|
||||||
initSecret(secret)
|
initSecrets()
|
||||||
initLogger()
|
initLogger()
|
||||||
|
|
||||||
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
|
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
|||||||
+134
-61
@@ -1,33 +1,97 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/AlexxIT/go2rtc/pkg/yaml"
|
"github.com/AlexxIT/go2rtc/pkg/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
var secrets [][]byte
|
var secrets = make(map[string]*Secret)
|
||||||
|
var secretsMu sync.Mutex
|
||||||
|
|
||||||
var templateRegex = regexp.MustCompile(`\{\{\s*([^\}]+)\s*\}\}`)
|
var templateRegex = regexp.MustCompile(`\{\{\s*([^\}]+)\s*\}\}`)
|
||||||
|
|
||||||
func ResolveSecrets(template string) string {
|
type Secrets interface {
|
||||||
|
Get(key string) any
|
||||||
|
Set(key string, value any)
|
||||||
|
Parse(template string) string
|
||||||
|
Marshal(v any) ([]byte, error)
|
||||||
|
Unmarshal(v any) error
|
||||||
|
Save() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Secret struct {
|
||||||
|
Secrets
|
||||||
|
|
||||||
|
Name string
|
||||||
|
Values map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSecret(name string, values interface{}) *Secret {
|
||||||
|
secretsMu.Lock()
|
||||||
|
defer secretsMu.Unlock()
|
||||||
|
|
||||||
|
if s, exists := secrets[name]; exists {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &Secret{Name: name, Values: make(map[string]any)}
|
||||||
|
|
||||||
|
switch v := values.(type) {
|
||||||
|
case map[string]any:
|
||||||
|
s.Values = v
|
||||||
|
default:
|
||||||
|
data, err := yaml.Encode(values, 2)
|
||||||
|
if err == nil {
|
||||||
|
var mapValues map[string]any
|
||||||
|
if err := yaml.Unmarshal(data, &mapValues); err == nil {
|
||||||
|
s.Values = mapValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secrets[name] = s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Secret) Get(key string) any {
|
||||||
|
secretsMu.Lock()
|
||||||
|
defer secretsMu.Unlock()
|
||||||
|
|
||||||
|
return s.Values[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Secret) Set(key string, value any) {
|
||||||
|
secretsMu.Lock()
|
||||||
|
defer secretsMu.Unlock()
|
||||||
|
|
||||||
|
if s.Values == nil {
|
||||||
|
s.Values = make(map[string]any)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Values[key] = value
|
||||||
|
secrets[s.Name] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Secret) Parse(template string) string {
|
||||||
if !templateRegex.MatchString(template) {
|
if !templateRegex.MatchString(template) {
|
||||||
return template
|
return template
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretsMap map[string]interface{}
|
secretsMu.Lock()
|
||||||
LoadSecret(&secretsMap)
|
defer secretsMu.Unlock()
|
||||||
|
|
||||||
|
if _, exists := secrets[s.Name]; !exists {
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
|
||||||
// ex template: rtsp://{{ my_camera.username }}:{{ my_camera.password }}@192.168.178.1:554/stream
|
|
||||||
result := templateRegex.ReplaceAllStringFunc(template, func(match string) string {
|
result := templateRegex.ReplaceAllStringFunc(template, func(match string) string {
|
||||||
varName := strings.TrimSpace(templateRegex.FindStringSubmatch(match)[1])
|
varName := strings.TrimSpace(templateRegex.FindStringSubmatch(match)[1])
|
||||||
pathParts := strings.Split(varName, ".")
|
pathParts := strings.Split(varName, ".")
|
||||||
value := getNestedValue(secretsMap, pathParts)
|
value := getNestedValue(s.Values, pathParts)
|
||||||
|
|
||||||
if value != nil {
|
if value != nil {
|
||||||
return stringify(value)
|
return stringify(value)
|
||||||
@@ -39,71 +103,80 @@ func ResolveSecrets(template string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadSecret(v any) {
|
func (s *Secret) Marshal(v any) ([]byte, error) {
|
||||||
for _, data := range secrets {
|
secretsMu.Lock()
|
||||||
var tempData map[string]interface{}
|
defer secretsMu.Unlock()
|
||||||
|
|
||||||
if err := yaml.Unmarshal(data, &tempData); err != nil {
|
if s.Values == nil {
|
||||||
Logger.Warn().Err(err).Send()
|
return nil, fmt.Errorf("no values in secret %s", s.Name)
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if secretData, exists := tempData["secret"]; exists {
|
|
||||||
secretBytes, err := yaml.Encode(secretData, 2)
|
|
||||||
if err != nil {
|
|
||||||
Logger.Warn().Err(err).Send()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := yaml.Unmarshal(secretBytes, v); err != nil {
|
|
||||||
Logger.Warn().Err(err).Send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PatchSecret(path []string, value any) error {
|
|
||||||
if SecretPath == "" {
|
|
||||||
return errors.New("secret file disabled")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// empty config is OK
|
data, err := yaml.Encode(s.Values, 2)
|
||||||
b, _ := os.ReadFile(SecretPath)
|
|
||||||
|
|
||||||
b, err := yaml.Patch(b, append([]string{"secret"}, path...), value)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, fmt.Errorf("error encoding secret values: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(SecretPath, b, 0644); err == nil {
|
return data, nil
|
||||||
secrets = [][]byte{b}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initSecret(secret string) {
|
func (s *Secret) Unmarshal(v any) error {
|
||||||
if secret == "" {
|
secretsMu.Lock()
|
||||||
secret = "go2rtc.secrets"
|
defer secretsMu.Unlock()
|
||||||
|
|
||||||
|
if s.Values == nil {
|
||||||
|
return fmt.Errorf("no values in secret %s", s.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
SecretPath = secret
|
data, err := yaml.Encode(s.Values, 2)
|
||||||
|
if err != nil {
|
||||||
if SecretPath != "" {
|
return fmt.Errorf("error encoding secret values: %w", err)
|
||||||
if !filepath.IsAbs(SecretPath) {
|
|
||||||
if cwd, err := os.Getwd(); err == nil {
|
|
||||||
SecretPath = filepath.Join(cwd, SecretPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Info["secret_path"] = SecretPath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if data, err := os.ReadFile(SecretPath); err == nil {
|
if err := yaml.Unmarshal(data, v); err != nil {
|
||||||
secrets = append(secrets, data)
|
return fmt.Errorf("error unmarshaling secret values: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Secret) Save() error {
|
||||||
|
secretsMu.Lock()
|
||||||
|
defer secretsMu.Unlock()
|
||||||
|
return saveSecret(s.Name, s.Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSecrets() {
|
||||||
|
var cfg struct {
|
||||||
|
Secrets map[string]map[string]any `yaml:"secrets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example config:
|
||||||
|
secrets:
|
||||||
|
test_camera:
|
||||||
|
username: test
|
||||||
|
password: test
|
||||||
|
*/
|
||||||
|
|
||||||
|
LoadConfig(&cfg)
|
||||||
|
|
||||||
|
if cfg.Secrets == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsMu.Lock()
|
||||||
|
defer secretsMu.Unlock()
|
||||||
|
|
||||||
|
for name, values := range cfg.Secrets {
|
||||||
|
secrets[name] = &Secret{Name: name, Values: values}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNestedValue(m map[string]interface{}, path []string) interface{} {
|
func saveSecret(name string, secret map[string]any) error {
|
||||||
|
return PatchConfig([]string{"secrets", name}, secret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNestedValue(m map[string]any, path []string) interface{} {
|
||||||
if len(path) == 0 || m == nil {
|
if len(path) == 0 || m == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -120,10 +193,10 @@ func getNestedValue(m map[string]interface{}, path []string) interface{} {
|
|||||||
|
|
||||||
// Check nested maps
|
// Check nested maps
|
||||||
switch nextMap := value.(type) {
|
switch nextMap := value.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]any:
|
||||||
return getNestedValue(nextMap, path[1:])
|
return getNestedValue(nextMap, path[1:])
|
||||||
case map[interface{}]interface{}:
|
case map[interface{}]interface{}:
|
||||||
stringMap := make(map[string]interface{})
|
stringMap := make(map[string]any)
|
||||||
for k, v := range nextMap {
|
for k, v := range nextMap {
|
||||||
if keyStr, ok := k.(string); ok {
|
if keyStr, ok := k.(string); ok {
|
||||||
stringMap[keyStr] = v
|
stringMap[keyStr] = v
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ func (l *Listener) Fire(msg any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Listener) ParseSource(url string) string {
|
func (l *Listener) NewSecret(name string, defaultValues interface{}) *app.Secret {
|
||||||
return app.ResolveSecrets(url)
|
return app.NewSecret(name, defaultValues)
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) SaveSource(path []string, value any) error {
|
|
||||||
return app.PatchSecret(path, value)
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user