198 lines
3.8 KiB
Go
198 lines
3.8 KiB
Go
|
package config
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/boltdb/bolt"
|
||
|
"github.com/gorilla/mux"
|
||
|
"github.com/rs/zerolog/log"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
bucket = []byte("config")
|
||
|
)
|
||
|
|
||
|
type configValue struct {
|
||
|
Key string `json:"key"`
|
||
|
Value interface{} `json:"value"`
|
||
|
}
|
||
|
|
||
|
func (value configValue) getBytes() ([]byte, error) {
|
||
|
return json.Marshal(value)
|
||
|
}
|
||
|
|
||
|
func decodeConfig(in []byte) (configValue, error) {
|
||
|
var out configValue
|
||
|
err := json.Unmarshal(in, &out)
|
||
|
return out, err
|
||
|
}
|
||
|
|
||
|
type Config struct {
|
||
|
// I'm not completely sure if this mutex is necessary.
|
||
|
// It is left here because I'm opening an closing the db
|
||
|
// for every operation.
|
||
|
sync.Mutex
|
||
|
path string
|
||
|
}
|
||
|
|
||
|
func (c *Config) db() *bolt.DB {
|
||
|
db, err := bolt.Open(c.path, 0666,
|
||
|
&bolt.Options{
|
||
|
Timeout: 1 * time.Second,
|
||
|
})
|
||
|
if err != nil {
|
||
|
log.Fatal().Msgf("Could not open database: %s", err)
|
||
|
}
|
||
|
err = db.Update(func(tx *bolt.Tx) error {
|
||
|
_, err := tx.CreateBucketIfNotExists(bucket)
|
||
|
return err
|
||
|
})
|
||
|
if err != nil {
|
||
|
log.Fatal().Err(err).Msg("Could not open bucket")
|
||
|
}
|
||
|
return db
|
||
|
}
|
||
|
|
||
|
func (c *Config) getAll() (map[string]configValue, error) {
|
||
|
c.Lock()
|
||
|
defer c.Unlock()
|
||
|
db := c.db()
|
||
|
defer db.Close()
|
||
|
vals := map[string]configValue{}
|
||
|
err := db.View(func(tx *bolt.Tx) error {
|
||
|
b := tx.Bucket(bucket)
|
||
|
err := b.ForEach(func(k, v []byte) error {
|
||
|
value, err := decodeConfig(v)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("could not convert value: %w", err)
|
||
|
}
|
||
|
vals[string(k)] = value
|
||
|
return nil
|
||
|
})
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error in forEach: %w", err)
|
||
|
}
|
||
|
return nil
|
||
|
})
|
||
|
return vals, err
|
||
|
}
|
||
|
|
||
|
func (c *Config) get(key string) (configValue, error) {
|
||
|
c.Lock()
|
||
|
defer c.Unlock()
|
||
|
db := c.db()
|
||
|
defer db.Close()
|
||
|
var out configValue
|
||
|
err := db.View(func(tx *bolt.Tx) error {
|
||
|
b := tx.Bucket(bucket)
|
||
|
value, err := decodeConfig(b.Get([]byte(key)))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
out = value
|
||
|
return nil
|
||
|
})
|
||
|
if err != nil {
|
||
|
return configValue{}, err
|
||
|
}
|
||
|
return out, nil
|
||
|
}
|
||
|
|
||
|
func (c *Config) set(key string, value configValue) error {
|
||
|
c.Lock()
|
||
|
defer c.Unlock()
|
||
|
db := c.db()
|
||
|
defer db.Close()
|
||
|
err := db.Update(func(tx *bolt.Tx) error {
|
||
|
b := tx.Bucket(bucket)
|
||
|
v, err := value.getBytes()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
err = b.Put([]byte(key), v)
|
||
|
return err
|
||
|
})
|
||
|
if err != nil {
|
||
|
log.Error().Msgf("Error setting key: %s", err)
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func (c *Config) delete(key string) error {
|
||
|
c.Lock()
|
||
|
defer c.Unlock()
|
||
|
db := c.db()
|
||
|
defer db.Close()
|
||
|
err := db.Update(func(tx *bolt.Tx) error {
|
||
|
b := tx.Bucket(bucket)
|
||
|
return b.Delete([]byte(key))
|
||
|
})
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Public interface
|
||
|
|
||
|
func New(r *mux.Router) *Config {
|
||
|
c := &Config{
|
||
|
path: "lameralert.db",
|
||
|
}
|
||
|
c.setup(r)
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
func (c *Config) GetInt64(key string, defaultValue int64) int64 {
|
||
|
v, err := c.get(key)
|
||
|
if err != nil {
|
||
|
return defaultValue
|
||
|
}
|
||
|
return int64(v.Value.(float64))
|
||
|
}
|
||
|
|
||
|
func (c *Config) GetInt(key string, defaultValue int) int {
|
||
|
v, err := c.get(key)
|
||
|
if err != nil {
|
||
|
return defaultValue
|
||
|
}
|
||
|
return int(v.Value.(float64))
|
||
|
}
|
||
|
|
||
|
func (c *Config) GetStringSlice(key string, defaultValue []string) []string {
|
||
|
v, err := c.get(key)
|
||
|
if err != nil {
|
||
|
log.Error().Msgf("GetStringSlice errored looking for %s: %s", key, err)
|
||
|
return defaultValue
|
||
|
}
|
||
|
return interfaceSliceToStringSlice(v.Value.([]interface{}))
|
||
|
}
|
||
|
|
||
|
func interfaceSliceToStringSlice(in []interface{}) []string {
|
||
|
out := []string{}
|
||
|
for _, it := range in {
|
||
|
out = append(out, it.(string))
|
||
|
}
|
||
|
return out
|
||
|
}
|
||
|
|
||
|
func (c *Config) GetStringMap(key string, defaultValue map[string]string) map[string]string {
|
||
|
v, err := c.get(key)
|
||
|
if err != nil {
|
||
|
return defaultValue
|
||
|
}
|
||
|
return v.Value.(map[string]string)
|
||
|
}
|
||
|
|
||
|
func (c *Config) GetString(key, defaultValue string) string {
|
||
|
v, err := c.get(key)
|
||
|
if err != nil {
|
||
|
return defaultValue
|
||
|
}
|
||
|
return v.Value.(string)
|
||
|
}
|
||
|
|
||
|
func (c *Config) Set(key string, value interface{}) error {
|
||
|
return c.set(key, configValue{key, value})
|
||
|
}
|