2025-07-26 23:31:00 +02:00
|
|
|
package common
|
|
|
|
|
|
|
|
|
|
import (
|
2025-08-23 19:14:16 +02:00
|
|
|
"encoding/json"
|
2025-07-26 23:31:00 +02:00
|
|
|
"fmt"
|
2025-08-23 19:14:16 +02:00
|
|
|
"os"
|
2025-07-26 23:31:00 +02:00
|
|
|
"sync"
|
2025-08-23 13:28:48 +02:00
|
|
|
|
|
|
|
|
bolt "go.etcd.io/bbolt"
|
2025-07-26 23:31:00 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type DataStore interface {
|
|
|
|
|
SaveValueToBucket(bucket, key, value string) error
|
|
|
|
|
CreateBucket(bucket string) error
|
|
|
|
|
GetFromBucketByKey(bucket, key string) (string, error)
|
|
|
|
|
GetAllBuckets() ([]string, error)
|
|
|
|
|
GetAllFromBucket(bucket string) (map[string]string, error)
|
|
|
|
|
GetAllKeysFromBucket(bucket string) ([]string, error)
|
|
|
|
|
EmptyBucket(bucket string) error
|
|
|
|
|
Close() error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type BoltStore struct {
|
|
|
|
|
DB *bolt.DB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewBoltStore(path string) (*BoltStore, error) {
|
2025-08-23 19:14:16 +02:00
|
|
|
os.Mkdir("data", 0755)
|
2025-07-26 23:31:00 +02:00
|
|
|
db, err := bolt.Open(path, 0600, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("could not open db: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return &BoltStore{DB: db}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *BoltStore) Close() error {
|
|
|
|
|
return s.DB.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
dataStore *BoltStore
|
|
|
|
|
once sync.Once
|
|
|
|
|
err error
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func GetTodoDataStore() (*BoltStore, error) {
|
|
|
|
|
once.Do(func() {
|
|
|
|
|
// We assign to the outer 'dataStore' and 'err' variables.
|
2025-07-26 23:47:26 +02:00
|
|
|
dataStore, err = NewBoltStore("data/todo.db")
|
2025-07-26 23:31:00 +02:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dataStore, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SaveValueToBucket Save data to bucket
|
|
|
|
|
func (s *BoltStore) SaveValueToBucket(bucket, key, value string) error {
|
|
|
|
|
return s.DB.Update(func(tx *bolt.Tx) error {
|
|
|
|
|
b, err := tx.CreateBucketIfNotExists([]byte(bucket))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return b.Put([]byte(key), []byte(value))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoveValueFromBucket remove value
|
|
|
|
|
func (s *BoltStore) RemoveValueFromBucket(bucket, key string) error {
|
|
|
|
|
return s.DB.Update(func(tx *bolt.Tx) error {
|
|
|
|
|
b, err := tx.CreateBucketIfNotExists([]byte(bucket))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return b.Delete([]byte(key))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreateBucket Create bucket if not exists
|
|
|
|
|
func (s *BoltStore) CreateBucket(bucket string) error {
|
|
|
|
|
return s.DB.Update(func(tx *bolt.Tx) error {
|
|
|
|
|
_, err := tx.CreateBucketIfNotExists([]byte(bucket))
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetFromBucketByKey Returns value from bucket by key or empty string
|
|
|
|
|
func (s *BoltStore) GetFromBucketByKey(bucket, key string) string {
|
|
|
|
|
var value string
|
|
|
|
|
s.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
|
b := tx.Bucket([]byte(bucket))
|
|
|
|
|
if b == nil {
|
|
|
|
|
return nil // Bucket doesn't exist, nothing to do
|
|
|
|
|
}
|
|
|
|
|
// Get returns nil if the key doesn't exist
|
|
|
|
|
valBytes := b.Get([]byte(key))
|
|
|
|
|
if valBytes != nil {
|
|
|
|
|
value = string(valBytes)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAllBuckets Returns map of all buckets
|
|
|
|
|
func (s *BoltStore) GetAllBuckets() []string {
|
|
|
|
|
var a = make([]string, 0)
|
|
|
|
|
s.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
|
return tx.ForEach(func(name []byte, b *bolt.Bucket) error {
|
|
|
|
|
a = append(a, string(name))
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAllFromBucket Returns map of all from bucket
|
|
|
|
|
func (s *BoltStore) GetAllFromBucket(bucket string) map[string]string {
|
|
|
|
|
results := make(map[string]string)
|
|
|
|
|
s.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
|
b := tx.Bucket([]byte(bucket))
|
|
|
|
|
if b == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
// Using a cursor is the recommended way to iterate
|
|
|
|
|
c := b.Cursor()
|
|
|
|
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
|
|
|
|
results[string(k)] = string(v)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return results
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetAllKeysFromBucket Returns keys from bucket
|
|
|
|
|
func (s *BoltStore) GetAllKeysFromBucket(bucket string) []string {
|
|
|
|
|
var keys []string
|
|
|
|
|
s.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
|
b := tx.Bucket([]byte(bucket))
|
|
|
|
|
if b == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return b.ForEach(func(k, v []byte) error {
|
|
|
|
|
keys = append(keys, string(k))
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
return keys
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EmptyBucket Returns value from bucket by key or empty string
|
|
|
|
|
func (s *BoltStore) EmptyBucket(bucket string) error {
|
|
|
|
|
return s.DB.Update(func(tx *bolt.Tx) error {
|
|
|
|
|
if err := tx.DeleteBucket([]byte(bucket)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if _, err := tx.CreateBucket([]byte(bucket)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-08-23 13:28:48 +02:00
|
|
|
|
|
|
|
|
// ExistsByKey returns bool if key already exists
|
|
|
|
|
func (s *BoltStore) ExistsByKey(bucket, key string) (bool, error) {
|
|
|
|
|
var exists bool
|
|
|
|
|
err := s.DB.View(func(tx *bolt.Tx) error {
|
|
|
|
|
b := tx.Bucket([]byte(bucket))
|
|
|
|
|
if b == nil {
|
|
|
|
|
return nil // Bucket doesn't exist, nothing to do
|
|
|
|
|
}
|
|
|
|
|
// Get returns nil if the key doesn't exist
|
|
|
|
|
valBytes := b.Get([]byte(key))
|
|
|
|
|
if valBytes != nil {
|
|
|
|
|
exists = string(valBytes) == key
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return exists, err
|
|
|
|
|
}
|
2025-08-23 19:14:16 +02:00
|
|
|
|
|
|
|
|
func (s *BoltStore) GetTodos(user string) map[string]Todo {
|
|
|
|
|
storedTodoJsons := s.GetAllFromBucket(user)
|
|
|
|
|
|
|
|
|
|
serverTodos := make(map[string]Todo)
|
|
|
|
|
for key, val := range storedTodoJsons {
|
|
|
|
|
var todo Todo
|
|
|
|
|
if json.Unmarshal([]byte(val), &todo) == nil {
|
|
|
|
|
serverTodos[key] = todo
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return serverTodos
|
|
|
|
|
}
|