132 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | |
| }
 |