Initial quick and dirty code, TODO: pushover actions, crontab, and cleanliness

This commit is contained in:
Tony Blyler 2021-05-18 01:25:18 -04:00
parent 445be657f2
commit ed13a5994f
Signed by: tblyler
GPG key ID: 7F13D9A60C0D678E
8 changed files with 667 additions and 0 deletions

184
db/badger.go Normal file
View file

@ -0,0 +1,184 @@
package db
import (
"context"
"encoding/json"
"fmt"
"sync"
"time"
"github.com/dgraph-io/badger"
)
// Badger db implementation
type Badger struct {
db *badger.DB
cancelGC func()
wg sync.WaitGroup
}
// NewBadger creates a new badger instance for the given path
func NewBadger(dbPath string) (*Badger, error) {
db, err := badger.Open(badger.DefaultOptions(dbPath).WithLogger(nil))
if err != nil {
return nil, fmt.Errorf("failed to open badger db at path %s: %w", dbPath, err)
}
ctx, cancel := context.WithCancel(context.Background())
b := &Badger{
db: db,
cancelGC: cancel,
}
b.wg.Add(1)
go func() {
defer b.wg.Done()
ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
for {
select {
case <-ticker.C:
for b.db.RunValueLogGC(0.5) == nil && ctx.Err() == nil {
}
case <-ctx.Done():
return
}
}
}()
return b, nil
}
// Close the database
func (b *Badger) Close() error {
b.cancelGC()
b.wg.Wait()
return b.db.Close()
}
// AddUser to the database
func (b *Badger) AddUser(user *User) error {
return b.db.Update(func(tx *badger.Txn) error {
data, err := json.Marshal(user)
if err != nil {
return fmt.Errorf("failed to JSON marshal user: %w", err)
}
key := user.badgerKey()
if _, err = tx.Get(key); err == nil {
return fmt.Errorf("user %s already exists", user.Name)
}
return tx.Set(key, data)
})
}
// GetUser from the database
func (b *Badger) GetUser(username string) (user *User, err error) {
err = b.db.View(func(tx *badger.Txn) error {
item, err := tx.Get(badgerKeyForUsername(username))
if err != nil {
return fmt.Errorf("failed to get user value for username %s: %w", username, err)
}
user = &User{}
return item.Value(func(val []byte) error {
err = json.Unmarshal(val, user)
if err != nil {
return fmt.Errorf("failed to unmarshal user value for username %s: %w", username, err)
}
return nil
})
})
return
}
// ListUsers from the database
func (b *Badger) ListUsers() (users []*User, err error) {
err = b.db.View(func(tx *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.Prefix = []byte("user:")
it := tx.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
err := item.Value(func(val []byte) error {
user := &User{}
err := json.Unmarshal(val, user)
if err != nil {
return fmt.Errorf("failed to unmarshal user value for user key %s: %w", string(item.Key()), err)
}
users = append(users, user)
return nil
})
if err != nil {
return err
}
}
return nil
})
return
}
// AddMedication to the database
func (b *Badger) AddMedication(medication *Medication) error {
return b.db.Update(func(tx *badger.Txn) error {
data, err := json.Marshal(medication)
if err != nil {
return fmt.Errorf("failed to JSON marshal medication: %w", err)
}
return tx.Set(medication.badgerKey(), data)
})
}
// ListMedicationsForUser from the database
func (b *Badger) ListMedicationsForUser(user *User) (medications []*Medication, err error) {
err = b.db.View(func(tx *badger.Txn) error {
opts := badger.DefaultIteratorOptions
opts.Prefix = badgerPrefixKeyForMedicationUser(user)
it := tx.NewIterator(opts)
defer it.Close()
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
err := item.Value(func(val []byte) error {
medication := &Medication{}
err := json.Unmarshal(val, medication)
if err != nil {
return fmt.Errorf("failed to unmarshal medication value for medication key %s: %w", string(item.Key()), err)
}
medications = append(medications, medication)
return nil
})
if err != nil {
return err
}
}
return nil
})
return
}

26
db/medication.go Normal file
View file

@ -0,0 +1,26 @@
package db
import (
"time"
"github.com/google/uuid"
)
// Medication information for a user
type Medication struct {
IDUser uuid.UUID `json:"id_user"`
ID uuid.UUID `json:"id"`
Name string `json:"name"`
IntervalCrontab string `json:"interval_crontab"`
IntervalQuantity uint `json:"interval_quantity"`
IntervalPushoverDevices []string `json:"interval_pushover_devices"`
CreatedAt time.Time `json:"created_at"`
}
func (m *Medication) badgerKey() []byte {
return append(append([]byte("medication:"), m.IDUser[:]...), m.ID[:]...)
}
func badgerPrefixKeyForMedicationUser(user *User) []byte {
return append([]byte("medication:"), user.ID[:]...)
}

23
db/user.go Normal file
View file

@ -0,0 +1,23 @@
package db
import (
"time"
"github.com/google/uuid"
)
// User information
type User struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
PushoverDeviceTokens map[string]string `json:"pushover_device_tokens"`
CreatedAt time.Time `json:"created_at"`
}
func (u *User) badgerKey() []byte {
return badgerKeyForUsername(u.Name)
}
func badgerKeyForUsername(username string) []byte {
return append([]byte("user:"), []byte(username)...)
}