From f9c3dbe6aed8fddcd23d80019f7e6ce2c80e2dca Mon Sep 17 00:00:00 2001 From: Peter Stuifzand Date: Sat, 29 Sep 2018 16:28:43 +0200 Subject: [PATCH] Add session and small improvements --- main.go | 123 +++++++++++++++++++++++++++++++++-------------------- session.go | 87 +++++++++++++++++++++++++++++++++++++ util.go | 15 +++++++ 3 files changed, 180 insertions(+), 45 deletions(-) create mode 100644 session.go create mode 100644 util.go diff --git a/main.go b/main.go index 54f39bd..7ef7bcd 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "fmt" "html/template" "log" + "math" "net/http" "time" @@ -13,6 +14,7 @@ import ( ) const BucketKeyMoments = "moments" +const DBFilename = "./moments.db" var indexPageTemplate = ` @@ -33,7 +35,8 @@ var indexPageTemplate = `
-
+
@@ -46,18 +49,19 @@ var indexPageTemplate = ` {{ if .Moments }}
- {{ range .Moments }} -
-
- {{ (.Time.Format "15:04") }} - {{ .Memo }} -
-
- {{ end }} + + {{ range .Moments }} + + + + + + {{ end }} +
{{ (.Time.Format "15:04") }}{{ if ne .Diff 0 }}{{ .Diff }}m{{ else }}start{{ end }} {{ .Memo }}
{{ end }} @@ -113,13 +117,15 @@ function notifyMe(text) { 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") - path := "./moments.db" + path := DBFilename db, err := bolt.Open(path, 0666, nil) if err != nil { log.Println(err) @@ -128,6 +134,13 @@ func main() { defer db.Close() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + sess, err := NewSession(w, r) + if err != nil { + log.Printf("Error loading session: %s", err) + return + } + defer sess.Flush() + moments, err := loadMoments(db, time.Now().Format("2006-01-02")) if err != nil { log.Println(err) @@ -142,8 +155,8 @@ func main() { if len(moments) > 0 { a := moments - for i := len(a)/2-1; i >= 0; i-- { - opp := len(a)-1-i + for i := len(a)/2 - 1; i >= 0; i-- { + opp := len(a) - 1 - i a[i], a[opp] = a[opp], a[i] } lastMoment := moments[0] @@ -162,9 +175,14 @@ func main() { return } }) - http.HandleFunc("/moment", func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() + sess, err := NewSession(w, r) + if err != nil{ + log.Println(err) + return + } + defer sess.Flush() if r.Method == http.MethodGet { moments, err := loadMoments(db, "") @@ -195,10 +213,10 @@ func main() { return } }) - 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 @@ -214,12 +232,25 @@ func loadMoments(db *bolt.DB, today string) ([]Moment, error) { 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) } @@ -230,6 +261,8 @@ func loadMoments(db *bolt.DB, today string) ([]Moment, error) { 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)) diff --git a/session.go b/session.go new file mode 100644 index 0000000..ce1a0c4 --- /dev/null +++ b/session.go @@ -0,0 +1,87 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "time" +) + +type Session struct { + ID string +} + +func NewSession(w http.ResponseWriter, r *http.Request) (*Session, error) { + sessionID, err := getSessionCookie(w, r) + if err != nil { + return nil, err + } + session := &Session{ID: sessionID} + err = loadSession(session) + if err != nil { + return nil, err + } + return session, nil +} + +func (sess *Session) Flush() error { + return saveSession(sess) +} + +func saveSession(sess *Session) error { + filename := generateFilename(sess.ID) + err := os.Mkdir("session", 0755) + f, err := os.Create(filename) + if err != nil { + return err + } + defer f.Close() + err = json.NewEncoder(f).Encode(sess) + return err +} + +func loadSession(sess *Session) error { + filename := generateFilename(sess.ID) + err := os.Mkdir("session", 0755) + f, err := os.Open(filename) + if err != nil { + if os.IsNotExist(err) { + // add defaults to session? + return nil + } + return err + } + defer f.Close() + err = json.NewDecoder(f).Decode(sess) + return err +} + +func generateFilename(id string) string { + return fmt.Sprintf("session/%s.json", id) +} + +func getSessionCookie(w http.ResponseWriter, r *http.Request) (string, error) { + c, err := r.Cookie("session") + var sessionVar string + + if err != nil { + if err == http.ErrNoCookie { + sessionVar = RandStringBytes(16) + newCookie := &http.Cookie{ + Name: "session", + Value: sessionVar, + Expires: time.Now().Add(24 * time.Hour), + } + + http.SetCookie(w, newCookie) + return sessionVar, nil + } + + return "", err + } else { + sessionVar = c.Value + } + + return sessionVar, nil +} diff --git a/util.go b/util.go new file mode 100644 index 0000000..450d9b8 --- /dev/null +++ b/util.go @@ -0,0 +1,15 @@ +package main + +import ( + "math/rand" +) + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +func RandStringBytes(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return string(b) +} \ No newline at end of file