@@ -46,18 +49,19 @@ var indexPageTemplate = `
{{ if .Moments }}
- {{ range .Moments }}
-
- {{ end }}
+
+ {{ range .Moments }}
+
+ {{ (.Time.Format "15:04") }} |
+ {{ if ne .Diff 0 }}{{ .Diff }}m{{ else }}start{{ end }} |
+ {{ .Memo }} |
+
+ {{ end }}
+
{{ 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