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

7
config/config.go Normal file
View 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
View 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
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)...)
}

9
go.mod Normal file
View 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
View 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
View 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)
}
}