2024-02-08 22:17:16 +01:00
|
|
|
package client
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Storage
|
|
|
|
|
//
|
|
|
|
|
import (
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"encoding/json"
|
2024-02-09 23:28:43 +01:00
|
|
|
"path/filepath"
|
2026-04-21 15:53:56 +02:00
|
|
|
"sync"
|
2024-02-08 22:17:16 +01:00
|
|
|
|
|
|
|
|
"forge.redroom.link/yves/meowlib"
|
|
|
|
|
"github.com/dgraph-io/badger"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type ServerStorage struct {
|
|
|
|
|
DbFile string `json:"db_file,omitempty"`
|
2026-04-21 15:53:56 +02:00
|
|
|
mu sync.Mutex
|
2024-02-08 22:17:16 +01:00
|
|
|
db *badger.DB
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// open opens the Badger database. Caller must hold mu.
|
2024-02-08 22:17:16 +01:00
|
|
|
func (ss *ServerStorage) open() error {
|
2024-03-29 18:07:06 +01:00
|
|
|
opts := badger.DefaultOptions(filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, ss.DbFile))
|
2024-02-08 22:17:16 +01:00
|
|
|
opts.Logger = nil
|
|
|
|
|
var err error
|
|
|
|
|
ss.db, err = badger.Open(opts)
|
2026-04-21 15:53:56 +02:00
|
|
|
return err
|
2024-02-08 22:17:16 +01:00
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// close closes the Badger database. Caller must hold mu.
|
|
|
|
|
func (ss *ServerStorage) close() {
|
|
|
|
|
ss.db.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StoreServer stores a server in the Badger database with Server.GetUid() as key.
|
2024-02-08 22:17:16 +01:00
|
|
|
func (ss *ServerStorage) StoreServer(sc *Server) error {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
|
|
|
|
return ss.storeServerLocked(sc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// storeServerLocked is StoreServer without acquiring the lock. Caller must hold mu.
|
|
|
|
|
func (ss *ServerStorage) storeServerLocked(sc *Server) error {
|
|
|
|
|
if err := ss.open(); err != nil {
|
2024-02-08 22:17:16 +01:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
|
|
|
|
jsonsrv, err := json.Marshal(sc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-02-02 18:15:57 +01:00
|
|
|
password, err := GetConfig().GetMemPass()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
data, err := meowlib.SymEncrypt(password, jsonsrv)
|
2024-02-08 22:17:16 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
|
|
|
|
key := shakey[:]
|
|
|
|
|
return ss.db.Update(func(txn *badger.Txn) error {
|
|
|
|
|
return txn.Set(key, data)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// ServerExists checks if a server exists in the Badger database.
|
2024-06-05 14:45:01 +02:00
|
|
|
func (ss *ServerStorage) ServerExists(sc *Server) (bool, error) {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
|
|
|
|
return ss.serverExistsLocked(sc)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// serverExistsLocked is ServerExists without acquiring the lock. Caller must hold mu.
|
|
|
|
|
func (ss *ServerStorage) serverExistsLocked(sc *Server) (bool, error) {
|
|
|
|
|
if err := ss.open(); err != nil {
|
2024-06-05 14:45:01 +02:00
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
|
|
|
|
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
|
|
|
|
key := shakey[:]
|
2026-04-21 15:53:56 +02:00
|
|
|
err := ss.db.View(func(txn *badger.Txn) error {
|
2024-06-05 14:45:01 +02:00
|
|
|
_, err := txn.Get(key)
|
|
|
|
|
return err
|
2026-04-21 15:53:56 +02:00
|
|
|
})
|
|
|
|
|
if err != nil {
|
2024-06-05 14:45:01 +02:00
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// StoreServerIfNotExists stores a server only if it is not already present.
|
2024-06-05 14:45:01 +02:00
|
|
|
func (ss *ServerStorage) StoreServerIfNotExists(sc *Server) error {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
|
|
|
|
exists, err := ss.serverExistsLocked(sc)
|
2024-06-05 14:45:01 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !exists {
|
2026-04-21 15:53:56 +02:00
|
|
|
return ss.storeServerLocked(sc)
|
2024-06-05 14:45:01 +02:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// LoadServer loads a Server from the Badger database by uid.
|
2024-02-08 22:17:16 +01:00
|
|
|
func (ss *ServerStorage) LoadServer(uid string) (*Server, error) {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
2024-02-08 22:17:16 +01:00
|
|
|
var sc Server
|
2026-04-21 15:53:56 +02:00
|
|
|
if err := ss.open(); err != nil {
|
2024-02-08 22:17:16 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
2026-02-02 18:15:57 +01:00
|
|
|
password, err := GetConfig().GetMemPass()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-02-08 22:17:16 +01:00
|
|
|
shakey := sha256.Sum256([]byte(uid))
|
|
|
|
|
key := shakey[:]
|
|
|
|
|
err = ss.db.View(func(txn *badger.Txn) error {
|
|
|
|
|
item, err := txn.Get(key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return item.Value(func(val []byte) error {
|
2026-02-02 18:15:57 +01:00
|
|
|
jsonsrv, err := meowlib.SymDecrypt(password, val)
|
2024-02-08 22:17:16 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return json.Unmarshal(jsonsrv, &sc)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
return &sc, err
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// DeleteServer deletes a Server from the Badger database by uid.
|
2024-02-08 22:17:16 +01:00
|
|
|
func (ss *ServerStorage) DeleteServer(uid string) error {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
|
|
|
|
if err := ss.open(); err != nil {
|
2024-02-08 22:17:16 +01:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
|
|
|
|
shakey := sha256.Sum256([]byte(uid))
|
|
|
|
|
key := shakey[:]
|
|
|
|
|
return ss.db.Update(func(txn *badger.Txn) error {
|
|
|
|
|
return txn.Delete(key)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// LoadAllServers loads all Servers from the Badger database.
|
2024-02-08 22:17:16 +01:00
|
|
|
func (ss *ServerStorage) LoadAllServers() ([]*Server, error) {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
2024-02-08 22:17:16 +01:00
|
|
|
var scs []*Server
|
2026-04-21 15:53:56 +02:00
|
|
|
if err := ss.open(); err != nil {
|
2024-02-08 22:17:16 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
2026-02-02 18:15:57 +01:00
|
|
|
password, err := GetConfig().GetMemPass()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-02-08 22:17:16 +01:00
|
|
|
err = ss.db.View(func(txn *badger.Txn) error {
|
|
|
|
|
opts := badger.DefaultIteratorOptions
|
|
|
|
|
opts.PrefetchSize = 10
|
|
|
|
|
it := txn.NewIterator(opts)
|
|
|
|
|
defer it.Close()
|
|
|
|
|
for it.Rewind(); it.Valid(); it.Next() {
|
|
|
|
|
item := it.Item()
|
|
|
|
|
var sc Server
|
|
|
|
|
err := item.Value(func(val []byte) error {
|
2026-02-02 18:15:57 +01:00
|
|
|
jsonsrv, err := meowlib.SymDecrypt(password, val)
|
2024-02-08 22:17:16 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return json.Unmarshal(jsonsrv, &sc)
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
scs = append(scs, &sc)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return scs, err
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// LoadAllServerCards loads all ServerCards from the Badger database.
|
2024-02-09 22:30:00 +01:00
|
|
|
func (ss *ServerStorage) LoadAllServerCards() ([]*meowlib.ServerCard, error) {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
2024-02-09 22:30:00 +01:00
|
|
|
var scs []*meowlib.ServerCard
|
2026-04-21 15:53:56 +02:00
|
|
|
if err := ss.open(); err != nil {
|
2024-02-09 22:30:00 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
2026-02-02 18:15:57 +01:00
|
|
|
password, err := GetConfig().GetMemPass()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-02-09 22:30:00 +01:00
|
|
|
err = ss.db.View(func(txn *badger.Txn) error {
|
|
|
|
|
opts := badger.DefaultIteratorOptions
|
|
|
|
|
opts.PrefetchSize = 10
|
|
|
|
|
it := txn.NewIterator(opts)
|
|
|
|
|
defer it.Close()
|
|
|
|
|
for it.Rewind(); it.Valid(); it.Next() {
|
|
|
|
|
item := it.Item()
|
|
|
|
|
var sc Server
|
|
|
|
|
err := item.Value(func(val []byte) error {
|
2026-02-02 18:15:57 +01:00
|
|
|
jsonsrv, err := meowlib.SymDecrypt(password, val)
|
2024-02-09 22:30:00 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return json.Unmarshal(jsonsrv, &sc)
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
scs = append(scs, sc.GetServerCard())
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return scs, err
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// LoadServersFromUids loads Servers whose UIDs are in the provided slice.
|
2024-02-08 22:17:16 +01:00
|
|
|
func (ss *ServerStorage) LoadServersFromUids(uids []string) ([]*Server, error) {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
2024-02-08 22:17:16 +01:00
|
|
|
var scs []*Server
|
2026-04-21 15:53:56 +02:00
|
|
|
if err := ss.open(); err != nil {
|
2024-02-08 22:17:16 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
2026-02-02 18:15:57 +01:00
|
|
|
password, err := GetConfig().GetMemPass()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-02-08 22:17:16 +01:00
|
|
|
err = ss.db.View(func(txn *badger.Txn) error {
|
|
|
|
|
for _, uid := range uids {
|
|
|
|
|
shakey := sha256.Sum256([]byte(uid))
|
|
|
|
|
key := shakey[:]
|
|
|
|
|
item, err := txn.Get(key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var sc Server
|
|
|
|
|
err = item.Value(func(val []byte) error {
|
2026-02-02 18:15:57 +01:00
|
|
|
jsonsrv, err := meowlib.SymDecrypt(password, val)
|
2024-02-08 22:17:16 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return json.Unmarshal(jsonsrv, &sc)
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
scs = append(scs, &sc)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return scs, err
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 15:53:56 +02:00
|
|
|
// LoadServerCardsFromUids loads ServerCards whose UIDs are in the provided slice.
|
2024-02-08 22:17:16 +01:00
|
|
|
func (ss *ServerStorage) LoadServerCardsFromUids(uids []string) ([]*meowlib.ServerCard, error) {
|
2026-04-21 15:53:56 +02:00
|
|
|
ss.mu.Lock()
|
|
|
|
|
defer ss.mu.Unlock()
|
2024-02-08 22:17:16 +01:00
|
|
|
var scs []*meowlib.ServerCard
|
2026-04-21 15:53:56 +02:00
|
|
|
if err := ss.open(); err != nil {
|
2024-02-08 22:17:16 +01:00
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer ss.close()
|
2026-02-02 18:15:57 +01:00
|
|
|
password, err := GetConfig().GetMemPass()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-02-08 22:17:16 +01:00
|
|
|
err = ss.db.View(func(txn *badger.Txn) error {
|
|
|
|
|
for _, uid := range uids {
|
|
|
|
|
shakey := sha256.Sum256([]byte(uid))
|
|
|
|
|
key := shakey[:]
|
|
|
|
|
item, err := txn.Get(key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var sc Server
|
|
|
|
|
err = item.Value(func(val []byte) error {
|
2026-02-02 18:15:57 +01:00
|
|
|
jsonsrv, err := meowlib.SymDecrypt(password, val)
|
2024-02-08 22:17:16 +01:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return json.Unmarshal(jsonsrv, &sc)
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
scs = append(scs, sc.GetServerCard())
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
return scs, err
|
|
|
|
|
}
|