package main import ( "bytes" "encoding/json" "fmt" "log" "math" "net/http" "time" bolt "go.etcd.io/bbolt" ) const BucketKeyMoments = "moments" const DBFilename = "./moments.db" // Moment is the main information this servers remembers type Moment struct { Key string Time time.Time Diff int64 Memo string } // main is the main function func main() { fmt.Println("Starting tracking backend server") db, err := bolt.Open(DBFilename, 0666, nil) if err != nil { log.Println(err) return } defer db.Close() http.Handle("/auth/", http.StripPrefix("/auth/", &IndieAuthHandler{})) http.Handle("/", &indexHandler{DB: db}) http.Handle("/moment", &momentHandler{DB: db}) log.Fatal(http.ListenAndServe(":8096", nil)) } // loadMoments loads the moments with today as the prefix of the key from the database func loadMoments(db *bolt.DB, today string) ([]Moment, error) { var moments []Moment err := db.View(func(tx *bolt.Tx) error { // Assume bucket exists and has keys b := tx.Bucket([]byte(BucketKeyMoments)) if b == nil { // no bucket found, so moments should be empty return nil } c := b.Cursor() prefix := []byte(today) var prevTime time.Time for k, v := c.Seek(prefix); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() { var moment Moment err := json.Unmarshal(v, &moment) if err != nil { return err } if prevTime.IsZero() { moment.Diff = 0 } else { d := float64(moment.Time.Sub(prevTime)) / 1000000000.0 / 60.0 moment.Diff = int64(math.Ceil(d)) } prevTime = moment.Time moments = append(moments, moment) } // TODO(peter): if there are no moments, here we can add a moment for the start of the day return nil }) return moments, err } // saveMemo saves one memo to the database, it automatically generates a key // based on the timestamp func saveMemo(db *bolt.DB, timestamp time.Time, memo string) error { err := db.Update(func(tx *bolt.Tx) error { bucket, err := tx.CreateBucketIfNotExists([]byte(BucketKeyMoments)) if err != nil { return err } key := timestamp.Format(time.RFC3339) m := Moment{ Key: key, Memo: memo, Time: timestamp, } buf, err := json.Marshal(m) if err != nil { return err } return bucket.Put([]byte(m.Key), buf) }) return err }