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
}
--- /dev/null
+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"])
+ }
+}
### 说明
本项目使用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`
"登陆二维码-白":"OO",
"登陆二维码-黑":" ",
"服务器时区-help":"用于正确解析服务器响应中的时间,整数,单位秒,正数是UTC以东,默认0",
- "服务器时区":0
+ "服务器时区":0,
+ "从环境变量覆盖": [
+ {
+ "key": "",
+ "type": "",
+ "env": ""
+ }
+ ]
}