Initial quick and dirty code, TODO: pushover actions, crontab, and cleanliness
This commit is contained in:
parent
445be657f2
commit
ed13a5994f
8 changed files with 667 additions and 0 deletions
7
config/config.go
Normal file
7
config/config.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package config
|
||||
|
||||
// Config for application setup
|
||||
type Config interface {
|
||||
BadgerPath() (string, error)
|
||||
PushoverAPIToken() (string, error)
|
||||
}
|
51
config/env.go
Normal file
51
config/env.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
// BadgerPathEnv name
|
||||
BadgerPathEnv = "BADGER_PATH"
|
||||
// PushoverAPITokenEnv name
|
||||
PushoverAPITokenEnv = "PUSHOVER_API_TOKEN"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEnvVariableNotSet occurs when an environment variable is not set
|
||||
ErrEnvVariableNotSet = errors.New("environment variable is not set")
|
||||
)
|
||||
|
||||
// Env variable Config implementation
|
||||
type Env struct {
|
||||
}
|
||||
|
||||
// BadgerPath for the database directory
|
||||
func (e *Env) BadgerPath() (string, error) {
|
||||
val, ok := os.LookupEnv(BadgerPathEnv)
|
||||
if !ok {
|
||||
return "", fmt.Errorf(
|
||||
"unable to get badger path from env variable %s: %w",
|
||||
BadgerPathEnv,
|
||||
ErrEnvVariableNotSet,
|
||||
)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// PushoverAPIToken getter
|
||||
func (e *Env) PushoverAPIToken() (string, error) {
|
||||
val, ok := os.LookupEnv(PushoverAPITokenEnv)
|
||||
if !ok {
|
||||
return "", fmt.Errorf(
|
||||
"unable to get pushover API token from env variable %s: %w",
|
||||
PushoverAPITokenEnv,
|
||||
ErrEnvVariableNotSet,
|
||||
)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
184
db/badger.go
Normal file
184
db/badger.go
Normal 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
26
db/medication.go
Normal 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
23
db/user.go
Normal 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)...)
|
||||
}
|
9
go.mod
Normal file
9
go.mod
Normal file
|
@ -0,0 +1,9 @@
|
|||
module git.0xdad.com/tblyler/meditime
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/dgraph-io/badger v1.6.2
|
||||
github.com/dgraph-io/ristretto v0.0.4-0.20210122082011-bb5d392ed82d // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
)
|
110
go.sum
Normal file
110
go.sum
Normal file
|
@ -0,0 +1,110 @@
|
|||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/ristretto v0.0.4-0.20210122082011-bb5d392ed82d h1:eQYOG6A4td1tht0NdJB9Ls6DsXRGb2Ft6X9REU/MbbE=
|
||||
github.com/dgraph-io/ristretto v0.0.4-0.20210122082011-bb5d392ed82d/go.mod h1:tv2ec8nA7vRpSYX7/MbP52ihrUMXIHit54CQMq8npXQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mmcloughlin/avo v0.0.0-20201105074841-5d2f697d268f/go.mod h1:6aKT4zZIrpGqB3RpFU14ByCSSyKY6LfJz4J/JJChHfI=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.0/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
257
main.go
Normal file
257
main.go
Normal file
|
@ -0,0 +1,257 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.0xdad.com/tblyler/meditime/config"
|
||||
"git.0xdad.com/tblyler/meditime/db"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func errLog(messages ...interface{}) {
|
||||
fmt.Fprintln(os.Stderr, messages...)
|
||||
}
|
||||
|
||||
func log(messages ...interface{}) {
|
||||
fmt.Println(messages...)
|
||||
}
|
||||
|
||||
func help() {
|
||||
}
|
||||
|
||||
func run(b *db.Badger) error {
|
||||
users, err := b.ListUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
medications, err := b.ListMedicationsForUser(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, medication := range medications {
|
||||
// FIXME do the stuff
|
||||
fmt.Println(user)
|
||||
fmt.Println(medication)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
lenArgs := len(os.Args)
|
||||
if lenArgs <= 1 {
|
||||
help()
|
||||
errLog("must supply at least one argument")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := func() error {
|
||||
inputScanner := bufio.NewScanner(os.Stdin)
|
||||
config := config.Env{}
|
||||
|
||||
badgerPath, err := config.BadgerPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := db.NewBadger(badgerPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer b.Close()
|
||||
|
||||
switch os.Args[1] {
|
||||
case "run":
|
||||
return run(b)
|
||||
|
||||
case "user":
|
||||
if lenArgs < 3 {
|
||||
return errors.New("must supply an argument to the user command")
|
||||
}
|
||||
|
||||
switch os.Args[2] {
|
||||
case "add":
|
||||
fmt.Print("username: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
username := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if username == "" {
|
||||
return fmt.Errorf("failed to get username from STDIN prompt: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
fmt.Print("pushover device token: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
deviceToken := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if deviceToken == "" {
|
||||
return fmt.Errorf("no pushover device token provided: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
id := uuid.New()
|
||||
|
||||
err = b.AddUser(&db.User{
|
||||
ID: id,
|
||||
Name: username,
|
||||
PushoverDeviceTokens: map[string]string{
|
||||
"default": deviceToken,
|
||||
},
|
||||
CreatedAt: time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to insert username %s: %w", username, err)
|
||||
}
|
||||
|
||||
log("created user id", id)
|
||||
|
||||
case "get":
|
||||
fmt.Print("username: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
username := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if username == "" {
|
||||
return fmt.Errorf("failed to get username from STDIN prompt: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
user, err := b.GetUser(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return fmt.Errorf("username %s does not exist", username)
|
||||
}
|
||||
|
||||
log(user)
|
||||
|
||||
case "list":
|
||||
users, err := b.ListUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
fmt.Println(user)
|
||||
}
|
||||
}
|
||||
|
||||
case "medication":
|
||||
if lenArgs < 3 {
|
||||
return errors.New("must supply an argument to the user command")
|
||||
}
|
||||
|
||||
switch os.Args[2] {
|
||||
case "add":
|
||||
fmt.Print("username: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
username := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if username == "" {
|
||||
return fmt.Errorf("failed to get username from STDIN prompt: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
user, err := b.GetUser(username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup username %s: %w", username, err)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return fmt.Errorf("username %s doesn't exist", username)
|
||||
}
|
||||
|
||||
fmt.Print("name: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
name := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if name == "" {
|
||||
return fmt.Errorf("failed to get medication name from STDIN prompt: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
fmt.Print("cron schedule: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
crontab := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if crontab == "" {
|
||||
return fmt.Errorf("failed to get cron schedule from STDIN prompt: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
fmt.Print("interval quantity: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
intervalQuantity, err := strconv.ParseUint(string(bytes.TrimSpace(inputScanner.Bytes())), 10, 64)
|
||||
if intervalQuantity == 0 || err != nil {
|
||||
return fmt.Errorf("failed to get interval quantity from STDIN prompt: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
fmt.Print("interval pushover device token id: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
intervalPushoverDevice := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if _, ok := user.PushoverDeviceTokens[intervalPushoverDevice]; !ok {
|
||||
return fmt.Errorf("the '%s' pushover device token doesn't exist for user %s", intervalPushoverDevice, user.Name)
|
||||
}
|
||||
|
||||
medication := &db.Medication{
|
||||
IDUser: user.ID,
|
||||
ID: uuid.New(),
|
||||
Name: name,
|
||||
IntervalCrontab: crontab,
|
||||
IntervalQuantity: uint(intervalQuantity),
|
||||
IntervalPushoverDevices: []string{intervalPushoverDevice},
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
err = b.AddMedication(medication)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log(medication)
|
||||
|
||||
case "list":
|
||||
fmt.Print("username: ")
|
||||
inputScanner.Scan()
|
||||
|
||||
username := string(bytes.TrimSpace(inputScanner.Bytes()))
|
||||
if username == "" {
|
||||
return fmt.Errorf("failed to get username from STDIN prompt: %w", inputScanner.Err())
|
||||
}
|
||||
|
||||
user, err := b.GetUser(username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup username %s: %w", username, err)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
return fmt.Errorf("username %s doesn't exist", username)
|
||||
}
|
||||
|
||||
medications, err := b.ListMedicationsForUser(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, medication := range medications {
|
||||
log(medication)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
errLog(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue