]> 127.0.0.1 Git - bili_danmu/.git/commitdiff
Add 环境变量覆盖配置项 (#137)
authorqydysky <qydysky@foxmail.com>
Sun, 29 Dec 2024 13:18:27 +0000 (21:18 +0800)
committerGitHub <noreply@github.com>
Sun, 29 Dec 2024 13:18:27 +0000 (21:18 +0800)
CV/Var.go
CV/Var_test.go [new file with mode: 0644]
README.md
demo/config/config_K_v.json

index ffa9fa33442325054d227d4350cbab8ca9fc061a..fa32cc6247e7a9a5e5f9974bb351e3b4afeb7d85 100644 (file)
--- a/CV/Var.go
+++ b/CV/Var.go
@@ -719,6 +719,110 @@ func (t *Common) loadConf(customConf string) error {
                t.K_v.Store(k, v)
        }
 
+       // 从环境变量获取
+       if v, ok := t.K_v.LoadV("从环境变量覆盖").([]any); ok && len(v) > 0 {
+               for i := 0; i < len(v); i++ {
+                       if vm, ok := v[i].(map[string]any); ok {
+                               if err := dealEnv(&t.K_v, vm); err != nil {
+                                       return err
+                               }
+                       }
+               }
+       }
+
+       return nil
+}
+
+var (
+       ErrDealEnvUnknowType          = errors.New("ErrDealEnvUnknowType")
+       ErrDealEnvEnvValueTypeNoMatch = errors.New("ErrDealEnvEnvValueTypeNoMatch")
+       ErrDealEnvKeyNoArray          = errors.New("ErrDealEnv")
+       ErrDealEnvKeyNoMap            = errors.New("ErrDealEnvKeyNoMap")
+       ErrDealEnvKeyArrayNoUInt      = errors.New("ErrDealEnvKeyArrayNoUInt")
+)
+
+// vm:{"key":"","type":"","env":""}
+func dealEnv(K_v *syncmap.Map, vm map[string]any) error {
+       _key, ok := vm[`key`].(string)
+       if !ok || strings.TrimSpace(_key) == `` {
+               return nil
+       }
+       var val any
+       {
+               _env, ok := vm[`env`].(string)
+               if !ok || strings.TrimSpace(_env) == `` {
+                       return nil
+               }
+               _val := os.Getenv(_env)
+               _type, ok := vm[`type`].(string)
+               if !ok || strings.TrimSpace(_type) == `` {
+                       _type = "string"
+               }
+               switch _type {
+               case `string`:
+                       val = _val
+               case `float64`:
+                       if v, err := strconv.ParseFloat(_val, 64); err != nil {
+                               return ErrDealEnvEnvValueTypeNoMatch
+                       } else {
+                               val = v
+                       }
+               case `bool`:
+                       switch strings.ToLower(_val) {
+                       case `true`:
+                               val = true
+                       case `false`:
+                               val = false
+                       default:
+                               return ErrDealEnvEnvValueTypeNoMatch
+                       }
+               default:
+                       return ErrDealEnvUnknowType
+               }
+       }
+
+       _keys := strings.Split(_key, ".")
+       if len(_keys) == 1 {
+               K_v.Store(_keys[0], val)
+       } else {
+               var key = K_v.LoadV(_keys[0])
+               for i := 1; i < len(_keys)-1; i++ {
+                       if strings.Contains(_keys[i], "[") {
+                               if tmp, ok := key.([]any); !ok {
+                                       return ErrDealEnvKeyNoArray
+                               } else if n, err := strconv.ParseInt(_keys[i][1:len(_keys[i])-1], 0, 64); err != nil {
+                                       return ErrDealEnvKeyArrayNoUInt
+                               } else if int(n) > len(tmp)-1 {
+                                       return nil
+                               } else {
+                                       key = tmp[int(n)]
+                               }
+                       } else {
+                               if tmp, ok := key.(map[string]any); !ok {
+                                       return ErrDealEnvKeyNoMap
+                               } else {
+                                       key = tmp[_keys[i]]
+                               }
+                       }
+               }
+               if strings.Contains(_keys[len(_keys)-1], "[") {
+                       if tmp, ok := key.([]any); !ok {
+                               return ErrDealEnvKeyNoArray
+                       } else if n, err := strconv.ParseInt(_keys[len(_keys)-1][1:len(_keys[len(_keys)-1])-1], 0, 64); err != nil {
+                               return ErrDealEnvKeyArrayNoUInt
+                       } else if int(n) > len(tmp)-1 {
+                               return nil
+                       } else {
+                               tmp[n] = val
+                       }
+               } else {
+                       if tmp, ok := key.(map[string]any); !ok {
+                               return ErrDealEnvKeyNoMap
+                       } else {
+                               tmp[_keys[len(_keys)-1]] = val
+                       }
+               }
+       }
        return nil
 }
 
diff --git a/CV/Var_test.go b/CV/Var_test.go
new file mode 100644 (file)
index 0000000..877dfb9
--- /dev/null
@@ -0,0 +1,84 @@
+package cv
+
+import (
+       _ "embed"
+       "os"
+       "testing"
+
+       _ "github.com/go-sql-driver/mysql"
+       _ "github.com/jackc/pgx/v5/stdlib"
+       syncmap "github.com/qydysky/part/sync"
+       _ "modernc.org/sqlite"
+)
+
+func TestDealEnv(t *testing.T) {
+       os.Setenv("tes", "2")
+       os.Setenv("tes1", "true")
+       os.Setenv("tes2", "true")
+       var m syncmap.Map
+       m.Store("k", float64(1))
+       m.Store("b", false)
+       m.Store("s", "s")
+       if e := dealEnv(&m, map[string]any{`key`: `k`, `type`: `float64`, `env`: `tes`}); e != nil {
+               t.Fatal(e)
+       }
+       if v, ok := m.LoadV("k").(float64); !ok || v != 2 {
+               t.Fatal(v)
+       }
+       if e := dealEnv(&m, map[string]any{`key`: `b`, `type`: `bool`, `env`: `tes1`}); e != nil {
+               t.Fatal(e)
+       }
+       if v, ok := m.LoadV("b").(bool); !ok || !v {
+               t.Fatal(v)
+       }
+       if e := dealEnv(&m, map[string]any{`key`: `s`, `env`: `tes2`}); e != nil {
+               t.Fatal(e)
+       }
+       if v, ok := m.LoadV("s").(string); !ok || v != "true" {
+               t.Fatal(v)
+       }
+}
+
+func TestDealEnv2(t *testing.T) {
+       os.Setenv("tes", "2")
+       var m syncmap.Map
+       m.Store("k", map[string]any{"d": float64(1)})
+       if e := dealEnv(&m, map[string]any{`key`: `k.d`, `type`: `float64`, `env`: `tes`}); e != nil {
+               t.Fatal(e)
+       }
+       if v, ok := m.LoadV("k").(map[string]any); !ok {
+               t.Fatal(v)
+       } else if v[`d`].(float64) != 2 {
+               t.Fatal()
+       }
+}
+
+func TestDealEnv3(t *testing.T) {
+       os.Setenv("tes", "2")
+       var m syncmap.Map
+       m.Store("k", []any{float64(1)})
+       if e := dealEnv(&m, map[string]any{`key`: `k.[0]`, `type`: `float64`, `env`: `tes`}); e != nil {
+               t.Fatal(e)
+       }
+       if v, ok := m.LoadV("k").([]any); !ok {
+               t.Fatal(v)
+       } else if v[0].(float64) != 2 {
+               t.Fatal()
+       }
+}
+
+func TestDealEnv4(t *testing.T) {
+       os.Setenv("tes", "2")
+       var m syncmap.Map
+       m.Store("k", []any{map[string]any{"d": float64(1)}})
+       if e := dealEnv(&m, map[string]any{`key`: `k.[0].d`, `type`: `float64`, `env`: `tes`}); e != nil {
+               t.Fatal(e)
+       }
+       if v, ok := m.LoadV("k").([]any); !ok {
+               t.Fatal(v)
+       } else if q, ok := v[0].(map[string]any); !ok {
+               t.Fatal(q)
+       } else if q["d"].(float64) != 2 {
+               t.Fatal(q["d"])
+       }
+}
index 92d62f1df213c54527f7ecc91f2f035f9d783656..41316ac6a63fd635707d55eb32c83fd4aee0aba3 100644 (file)
--- a/README.md
+++ b/README.md
 ### 说明
 本项目使用github action自动构建,构建过程详见[yml](https://github.com/qydysky/bili_danmu/blob/master/.github/workflows/go.yml)
 
+#### 环境变量覆盖配置项
+添加配置项`从环境变量覆盖`(>v0.14.26)。将在配置文件都加载后,用以配置环境变量覆盖配置项。
+
+- `key`为配置键名(例如:`Web服务地址`),为空时将忽略。
+- `type`为类型,可选`string`,`bool`,`float64`。为空(默认)为`string`。当为`int`等类型时,也填为`float64`
+- `env`为env名(例如:`addr`),为空时将忽略。
+
+例子:
+```json
+{
+  "Web服务地址":"0.0.0.0:20000",
+  "从环境变量覆盖": [
+      {
+          "key": "Web服务地址",
+          "env": "addr"
+      }
+  ]
+}
+```
+配置环境变量:
+```
+export addr=0.0.0.0:22000
+```
+启动后,程序将监听22000端口而非20000端口
+
+注意:
+
+- 当要配置的键为数组时,使用`a.[n]`表示第n个,n为非负整数。当数组长度小于n时,将忽略。
+- 当要配置的键为map时,使用`a.b`表示a下的b键。当b不存在时,将忽略。
+- 注意核对配置的类型是正确的,否则可能会导致配置无效。
+
 #### cookie自定义位置
 配置文件添加配置项`cookie路径`(>v0.14.26),默认为`./cookie.txt`
 
index 3886c908b11bf89b4e9e13c4c63c3d4c2ab9dca4..9ded9bc7ba26c2fd389b193f173052d957ccd383 100644 (file)
     "登陆二维码-白":"OO",
     "登陆二维码-黑":"  ",
     "服务器时区-help":"用于正确解析服务器响应中的时间,整数,单位秒,正数是UTC以东,默认0",
-    "服务器时区":0
+    "服务器时区":0,
+    "从环境变量覆盖": [
+        {
+            "key": "",
+            "type": "",
+            "env": ""
+        }
+    ]
 }