|
|
@ -0,0 +1,131 @@ |
|
|
|
package kvdb |
|
|
|
|
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"errors" |
|
|
|
"fmt" |
|
|
|
"io/ioutil" |
|
|
|
"os" |
|
|
|
"path/filepath" |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
) |
|
|
|
|
|
|
|
type KVDB struct { |
|
|
|
Prefix string |
|
|
|
Ext string |
|
|
|
} |
|
|
|
|
|
|
|
func (kv *KVDB) Load( |
|
|
|
keyif interface{}, |
|
|
|
typ ...interface{}, |
|
|
|
) (value interface{}, ok bool, err error) { |
|
|
|
key, _ := keyif.(string) |
|
|
|
if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#!:| \n") { |
|
|
|
return nil, false, nil |
|
|
|
} |
|
|
|
|
|
|
|
userFile := filepath.Join(kv.Prefix, key+"."+kv.Ext) |
|
|
|
fmt.Println("Debug user file:", userFile) |
|
|
|
b, err := ioutil.ReadFile(userFile) |
|
|
|
if nil != err { |
|
|
|
if os.IsNotExist(err) { |
|
|
|
return nil, false, nil |
|
|
|
} |
|
|
|
fmt.Println("kvdb debug read:", err) |
|
|
|
return nil, false, errors.New("database read failed") |
|
|
|
} |
|
|
|
|
|
|
|
ok = true |
|
|
|
value = b |
|
|
|
if 1 == len(typ) { |
|
|
|
err := json.Unmarshal(b, typ[0]) |
|
|
|
if nil != err { |
|
|
|
return nil, false, err |
|
|
|
} |
|
|
|
value = typ[0] |
|
|
|
} else if len(b) > 0 && '"' == b[0] { |
|
|
|
var str string |
|
|
|
err := json.Unmarshal(b, &str) |
|
|
|
if nil == err { |
|
|
|
value = str |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return value, ok, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (kv *KVDB) Store(keyif interface{}, value interface{}) (err error) { |
|
|
|
key, _ := keyif.(string) |
|
|
|
if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#! \n") { |
|
|
|
return errors.New("invalid key name") |
|
|
|
} |
|
|
|
|
|
|
|
keypath := filepath.Join(kv.Prefix, key+"."+kv.Ext) |
|
|
|
f, err := os.Open(keypath) |
|
|
|
if nil == err { |
|
|
|
s, err := f.Stat() |
|
|
|
if nil != err { |
|
|
|
// if we can open, we should be able to stat
|
|
|
|
return errors.New("database connection failure") |
|
|
|
} |
|
|
|
ts := strconv.FormatInt(s.ModTime().Unix(), 10) |
|
|
|
bakpath := filepath.Join(kv.Prefix, key+"."+ts+"."+kv.Ext) |
|
|
|
if err := os.Rename(keypath, bakpath); nil != err { |
|
|
|
// keep the old record as a backup
|
|
|
|
return errors.New("database write failure") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var b []byte |
|
|
|
switch v := value.(type) { |
|
|
|
case []byte: |
|
|
|
b = v |
|
|
|
case string: |
|
|
|
b, _ = json.Marshal(v) |
|
|
|
default: |
|
|
|
fmt.Println("kvdb: not []byte or string:", v) |
|
|
|
jsonb, err := json.Marshal(v) |
|
|
|
if nil != err { |
|
|
|
return err |
|
|
|
} |
|
|
|
b = jsonb |
|
|
|
} |
|
|
|
|
|
|
|
if err := ioutil.WriteFile( |
|
|
|
keypath, |
|
|
|
b, |
|
|
|
os.FileMode(0600), |
|
|
|
); nil != err { |
|
|
|
fmt.Println("write failure:", err) |
|
|
|
return errors.New("database write failed") |
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (kv *KVDB) Delete(keyif interface{}) (err error) { |
|
|
|
key, _ := keyif.(string) |
|
|
|
if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#! \n") { |
|
|
|
return errors.New("invalid key name") |
|
|
|
} |
|
|
|
|
|
|
|
keypath := filepath.Join(kv.Prefix, key+"."+kv.Ext) |
|
|
|
f, err := os.Open(keypath) |
|
|
|
if nil == err { |
|
|
|
s, err := f.Stat() |
|
|
|
if nil != err { |
|
|
|
return errors.New("database connection failure") |
|
|
|
} |
|
|
|
ts := strconv.FormatInt(s.ModTime().Unix(), 64) |
|
|
|
if err := os.Rename(keypath, filepath.Join(kv.Prefix, key+"."+ts+"."+kv.Ext)); nil != err { |
|
|
|
return errors.New("database connection failure") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
func (kv *KVDB) Vacuum() (err error) { |
|
|
|
return nil |
|
|
|
} |