github.com/rpdict/ponzu@v0.10.1-0.20190226054626-477f29d6bf5e/system/db/config.go (about) 1 package db 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "net/url" 8 "strings" 9 "sync" 10 11 "github.com/rpdict/ponzu/system/admin/config" 12 13 "github.com/boltdb/bolt" 14 "github.com/gorilla/schema" 15 ) 16 17 const ( 18 // DefaultMaxAge provides a 2592000 second (30-day) cache max-age setting 19 DefaultMaxAge = int64(60 * 60 * 24 * 30) 20 ) 21 22 var mu = &sync.Mutex{} 23 var configCache map[string]interface{} 24 25 func init() { 26 configCache = make(map[string]interface{}) 27 } 28 29 // SetConfig sets key:value pairs in the db for configuration settings 30 func SetConfig(data url.Values) error { 31 var j []byte 32 err := store.Update(func(tx *bolt.Tx) error { 33 b := tx.Bucket([]byte("__config")) 34 35 // check for any multi-value fields (ex. checkbox fields) 36 // and correctly format for db storage. Essentially, we need 37 // fieldX.0: value1, fieldX.1: value2 => fieldX: []string{value1, value2} 38 fieldOrderValue := make(map[string]map[string][]string) 39 for k, v := range data { 40 if strings.Contains(k, ".") { 41 fo := strings.Split(k, ".") 42 43 // put the order and the field value into map 44 field := string(fo[0]) 45 order := string(fo[1]) 46 if len(fieldOrderValue[field]) == 0 { 47 fieldOrderValue[field] = make(map[string][]string) 48 } 49 50 // orderValue is 0:[?type=Thing&id=1] 51 orderValue := fieldOrderValue[field] 52 orderValue[order] = v 53 fieldOrderValue[field] = orderValue 54 55 // discard the post form value with name.N 56 data.Del(k) 57 } 58 59 } 60 61 // add/set the key & value to the post form in order 62 for f, ov := range fieldOrderValue { 63 for i := 0; i < len(ov); i++ { 64 position := fmt.Sprintf("%d", i) 65 fieldValue := ov[position] 66 67 if data.Get(f) == "" { 68 for i, fv := range fieldValue { 69 if i == 0 { 70 data.Set(f, fv) 71 } else { 72 data.Add(f, fv) 73 } 74 } 75 } else { 76 for _, fv := range fieldValue { 77 data.Add(f, fv) 78 } 79 } 80 } 81 } 82 83 cfg := &config.Config{} 84 dec := schema.NewDecoder() 85 dec.SetAliasTag("json") // allows simpler struct tagging when creating a content type 86 dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct 87 err := dec.Decode(cfg, data) 88 if err != nil { 89 return err 90 } 91 92 // check for "invalidate" value to reset the Etag 93 if len(cfg.CacheInvalidate) > 0 && cfg.CacheInvalidate[0] == "invalidate" { 94 cfg.Etag = NewEtag() 95 cfg.CacheInvalidate = []string{} 96 } 97 98 j, err = json.Marshal(cfg) 99 if err != nil { 100 return err 101 } 102 103 err = b.Put([]byte("settings"), j) 104 if err != nil { 105 return err 106 } 107 108 return nil 109 }) 110 if err != nil { 111 return err 112 } 113 114 // convert json => map[string]interface{} 115 var kv map[string]interface{} 116 err = json.Unmarshal(j, &kv) 117 if err != nil { 118 return err 119 } 120 121 mu.Lock() 122 configCache = kv 123 mu.Unlock() 124 125 return nil 126 } 127 128 // Config gets the value of a key in the configuration from the db 129 func Config(key string) ([]byte, error) { 130 kv := make(map[string]interface{}) 131 132 cfg, err := ConfigAll() 133 if err != nil { 134 return nil, err 135 } 136 137 if len(cfg) < 1 { 138 return nil, nil 139 } 140 141 err = json.Unmarshal(cfg, &kv) 142 if err != nil { 143 return nil, err 144 } 145 146 return []byte(kv[key].(string)), nil 147 } 148 149 // ConfigAll gets the configuration from the db 150 func ConfigAll() ([]byte, error) { 151 val := &bytes.Buffer{} 152 err := store.View(func(tx *bolt.Tx) error { 153 b := tx.Bucket([]byte("__config")) 154 if b == nil { 155 return fmt.Errorf("Error finding bucket: %s", "__config") 156 } 157 _, err := val.Write(b.Get([]byte("settings"))) 158 if err != nil { 159 return err 160 } 161 162 return nil 163 }) 164 if err != nil { 165 return nil, err 166 } 167 168 return val.Bytes(), nil 169 } 170 171 // PutConfig updates a single k/v in the config 172 func PutConfig(key string, value interface{}) error { 173 kv := make(map[string]interface{}) 174 175 c, err := ConfigAll() 176 if err != nil { 177 return err 178 } 179 180 if c == nil { 181 c, err = emptyConfig() 182 if err != nil { 183 return err 184 } 185 } 186 187 err = json.Unmarshal(c, &kv) 188 if err != nil { 189 return err 190 } 191 192 // set k/v from params to decoded map 193 kv[key] = value 194 195 data := make(url.Values) 196 for k, v := range kv { 197 switch v.(type) { 198 case string: 199 data.Set(k, v.(string)) 200 201 case []string: 202 vv := v.([]string) 203 for i := range vv { 204 data.Add(k, vv[i]) 205 } 206 207 default: 208 data.Set(k, fmt.Sprintf("%v", v)) 209 } 210 } 211 212 err = SetConfig(data) 213 if err != nil { 214 return err 215 } 216 217 return nil 218 } 219 220 // ConfigCache is a in-memory cache of the Configs for quicker lookups 221 // 'key' is the JSON tag associated with the config field 222 func ConfigCache(key string) interface{} { 223 mu.Lock() 224 val := configCache[key] 225 mu.Unlock() 226 227 return val 228 } 229 230 // LoadCacheConfig loads the config into a cache to be accessed by ConfigCache() 231 func LoadCacheConfig() error { 232 c, err := ConfigAll() 233 if err != nil { 234 return err 235 } 236 237 if c == nil { 238 c, err = emptyConfig() 239 if err != nil { 240 return err 241 } 242 } 243 244 // convert json => map[string]interface{} 245 var kv map[string]interface{} 246 err = json.Unmarshal(c, &kv) 247 if err != nil { 248 return err 249 } 250 251 mu.Lock() 252 configCache = kv 253 mu.Unlock() 254 255 return nil 256 } 257 258 func emptyConfig() ([]byte, error) { 259 cfg := &config.Config{} 260 261 data, err := json.Marshal(cfg) 262 if err != nil { 263 return nil, err 264 } 265 266 return data, nil 267 }