From 05e21aed4e6617cdd927ccdf41850d95fce5f98d Mon Sep 17 00:00:00 2001 From: Joerg Lehmann Date: Sat, 6 Jul 2019 11:36:01 +0200 Subject: [PATCH] scales: fill in metrics --- authentication.go | 5 +- main.go | 39 +++++--- metrics.go | 227 ++++++++++++++++++++++++++++++++++++++++++- persistence.go | 107 ++++++++++++++++---- snippets/scales.html | 78 ++++++++------- 5 files changed, 385 insertions(+), 71 deletions(-) diff --git a/authentication.go b/authentication.go index d3effd7..efc4b70 100644 --- a/authentication.go +++ b/authentication.go @@ -66,13 +66,12 @@ func loginHandler(response http.ResponseWriter, request *http.Request) { name := request.FormValue("email") pass := request.FormValue("password") redirectTarget := "/invalid_login.html" - //if name != "" && pass != "" { + // .. check credentials .. if checkLoginCredentials(name,pass) { - // .. check credentials .. + redirectTarget = "/scales.html" logit(fmt.Sprintf("loginHandler: successful login for User %s",name)) setSession(name, response) updateLoginTime(name) - redirectTarget = "/scales.html" } else { logit(fmt.Sprintf("loginHandler: invalid login for User %s",name)) } diff --git a/main.go b/main.go index 0a6347b..5e8f47a 100644 --- a/main.go +++ b/main.go @@ -8,22 +8,15 @@ import ( "time" ) -type AccountData struct { - Full_name string - Phone string - Address string - Zip string - City string -} - type Scale struct { - Beielipi_id string - Beielipi_alias string - Id string - Alias string - My_sms_number string - Sms_alarm string - Last_alarm string + Deveui string + Alias string + Last_measurement string + Last_temp string + Last_humidity string + Last_weight string + Last_pressure string + Last_accu_level string } func serveTemplate(w http.ResponseWriter, r *http.Request) { @@ -57,12 +50,27 @@ func serveTemplate(w http.ResponseWriter, r *http.Request) { t := time.Now() var datetimestring = t.Format("20060102150405") + var scales = getMyDevs(userName) + var last_metrics []OneMetric + + if r.URL.Path == "/scales.html" { + // wir holen noch die letzten Metriken + for _, v := range scales { + last_metric := getLastMetrics(v) + last_metrics = append(last_metrics, last_metric) + } + } + data := struct { UserName string DateTimeString string + Scales []string + LastMetrics []OneMetric } { userName, datetimestring, + scales, + last_metrics, } if err := tmpl.ExecuteTemplate(w, "layout", &data); err != nil { @@ -85,6 +93,7 @@ func main() { http.HandleFunc("/logout", logoutHandler) http.HandleFunc("/confirm", confirmHandler) http.HandleFunc("/metrics", metricsHandler) + http.HandleFunc("/lastmetrics", lastmetricsHandler) logit("Starting Web Application...") http.ListenAndServe("127.0.0.1:4000", nil) diff --git a/metrics.go b/metrics.go index fcc6e49..4c02530 100644 --- a/metrics.go +++ b/metrics.go @@ -11,13 +11,64 @@ import ( "strings" ) +type OneMetric struct { + Deveui string + Alias string + Timestamp string + Temperature string + Humidity string + Weight string + Pressure string + BatteryPercent string +} + +// Contains tells whether a contains x. +func Contains(a []string, x string) bool { + log.Println("Search for: "+x) + for _, n := range a { + log.Println("Piece of Array: "+n) + if x == n { + return true + } + } + return false +} + + // metrics handler func metricsHandler(response http.ResponseWriter, request *http.Request) { name := getUserName(request) if name != "" { + + property, ok := request.URL.Query()["property"] + if !ok || len(property[0]) < 1 { + log.Println("Url Param 'property' is missing") + fmt.Fprintf(response, "{ \"msg\": \"property must be specified in URL\" }") + return + } + + deveui, ok := request.URL.Query()["deveui"] + + if !ok || len(deveui[0]) < 1 { + log.Println("Url Param 'deveui' is missing") + fmt.Fprintf(response, "{ \"msg\": \"deveui must be specified in URL\" }") + return + } + // Query()["deveui"] will return an array of items, + // we only want the single item. + mydeveui := deveui[0] + + if !(Contains(getMyDevs(name),mydeveui)) { + log.Println("specified 'deveui' does not belong to this user") + fmt.Fprintf(response, "{ \"msg\": \"specified deveui does not belong to this user\" }") + return + } + + log.Println("Url Param 'deveui' is: " + string(mydeveui)) + url := "http://localhost:9999/api/v2/query?org=beieliorg" - data := []byte(`from(bucket:"beielibucket") |> range(start:-24h) |> filter(fn: (r) => r._field == "v") |> filter(fn: (r) => r.devaddr == "084B3824")`) + data := []byte(fmt.Sprintf(`from(bucket:"beielibucket") |> range(start:-24h) |> filter(fn: (r) => r._field == "%s") |> filter(fn: (r) => r.deveui == "%s")`,property[0],mydeveui)) req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) if err != nil { @@ -73,3 +124,177 @@ func metricsHandler(response http.ResponseWriter, request *http.Request) { fmt.Fprintf(response, "{ \"msg\": \"Only available for logged in users\" }") } } + +func lastmetricsHandler(response http.ResponseWriter, request *http.Request) { + name := getUserName(request) + if name != "" { + + deveui, ok := request.URL.Query()["deveui"] + + if !ok || len(deveui[0]) < 1 { + log.Println("Url Param 'deveui' is missing") + fmt.Fprintf(response, "{ \"msg\": \"deveui must be specified in URL\" }") + return + } + // Query()["deveui"] will return an array of items, + // we only want the single item. + mydeveui := deveui[0] + + if !(Contains(getMyDevs(name),mydeveui)) { + log.Println("specified 'deveui' does not belong to this user") + fmt.Fprintf(response, "{ \"msg\": \"specified deveui does not belong to this user\" }") + return + } + + log.Println("Url Param 'deveui' is: " + string(mydeveui)) + + url := "http://localhost:9999/api/v2/query?org=beieliorg" + //data := []byte(fmt.Sprintf(`from(bucket:"beielibucket") |> range(start:-365d) |> filter(fn: (r) => r.deveui == "%s") |> filter(fn: (r) => r._field == "v" or r._field == "t") |> last() |> yield(name: "last")`,mydeveui)) + data := []byte(fmt.Sprintf(`from(bucket:"beielibucket") + |> range(start:-365d) + |> filter(fn: (r) => r.deveui == "%s") + |> filter(fn: (r) => r._field == "t" or r._field == "h" or r._field == "w" or r._field == "p" or r._field == "vp") + |> last() |> yield(name: "last")`,mydeveui)) + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + log.Fatal("Error reading request. ", err) + } + + // Set headers + req.Header.Set("Authorization", "Token xXnq8ADcDygAyM_L0B_10c9yuoOv-cYcUfIXkJunRrIDMhB5ZNmza-Whr1ELcbKNzW8GzUMMYD85QohUHQdAmg==") + req.Header.Set("accept", "application/csv") + req.Header.Set("content-type", "application/vnd.flux") + + // Set client timeout + client := &http.Client{Timeout: time.Second * 10} + + // Send request + resp, err := client.Do(req) + if err != nil { + log.Fatal("Error reading response. ", err) + } + defer resp.Body.Close() + + fmt.Println("response Status:", resp.Status) + fmt.Println("response Headers:", resp.Header) + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal("Error reading body. ", err) + } + fmt.Println("response Body:", string(body)) + + scanner := bufio.NewScanner(strings.NewReader(string(body))) + ts := "" + t := "" + h := "" + w := "" + p := "" + vp := "" + location, err := time.LoadLocation("Europe/Zurich") + for scanner.Scan() { + s := strings.Split(scanner.Text(),",") + if ((len(s) >= 7) && !(strings.HasPrefix(s[5],"_"))) { + mytime,err := time.Parse(time.RFC3339,s[5]) + if err != nil { + continue + } + ts = mytime.In(location).Format("02.01.2006 15:04") + value := s[6] + field := s[7] + if field == "t" { + t = value + } else if field == "h" { + h = value + } else if field == "w" { + w = value + } else if field == "p" { + p = value + } else if field == "vp" { + vp = value + } + } + } + fmt.Fprintf(response,`{ + "ts": "%s", + "t": "%s", + "h": "%s", + "w": "%s", + "p": "%s", + "vp": "%s" +}`,ts,t,h,w,p,vp) + + } else { + fmt.Fprintf(response, "{ \"msg\": \"Only available for logged in users\" }") + } +} + +func getLastMetrics(deveui string) OneMetric { + var res OneMetric + + url := "http://localhost:9999/api/v2/query?org=beieliorg" + data := []byte(fmt.Sprintf(`from(bucket:"beielibucket") + |> range(start:-365d) + |> filter(fn: (r) => r.deveui == "%s") + |> filter(fn: (r) => r._field == "t" or r._field == "h" or r._field == "w" or r._field == "p" or r._field == "vp") + |> last() |> yield(name: "last")`,deveui)) + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) + if err != nil { + log.Fatal("Error reading request. ", err) + } + + // Set headers + req.Header.Set("Authorization", "Token xXnq8ADcDygAyM_L0B_10c9yuoOv-cYcUfIXkJunRrIDMhB5ZNmza-Whr1ELcbKNzW8GzUMMYD85QohUHQdAmg==") + req.Header.Set("accept", "application/csv") + req.Header.Set("content-type", "application/vnd.flux") + + // Set client timeout + client := &http.Client{Timeout: time.Second * 10} + + // Send request + resp, err := client.Do(req) + if err != nil { + log.Fatal("Error reading response. ", err) + } + defer resp.Body.Close() + + fmt.Println("response Status:", resp.Status) + fmt.Println("response Headers:", resp.Header) + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Fatal("Error reading body. ", err) + } + fmt.Println("response Body:", string(body)) + + scanner := bufio.NewScanner(strings.NewReader(string(body))) + location, err := time.LoadLocation("Europe/Zurich") + for scanner.Scan() { + s := strings.Split(scanner.Text(),",") + if ((len(s) >= 7) && !(strings.HasPrefix(s[5],"_"))) { + mytime,err := time.Parse(time.RFC3339,s[5]) + if err != nil { + continue + } + res.Timestamp = mytime.In(location).Format("02.01.2006 15:04") + value := s[6] + field := s[7] + if field == "t" { + res.Temperature = value + } else if field == "h" { + res.Humidity = value + } else if field == "w" { + res.Weight = value + } else if field == "p" { + res.Pressure = value + } else if field == "vp" { + res.BatteryPercent = value + } + } + } + res.Deveui = deveui + res.Alias = getDevAlias(deveui) + return res +} diff --git a/persistence.go b/persistence.go index b11d1a7..a9a2687 100644 --- a/persistence.go +++ b/persistence.go @@ -2,6 +2,8 @@ package main import ( "time" + "strings" + "log" "encoding/json" "crypto/rand" "golang.org/x/crypto/bcrypt" @@ -9,10 +11,9 @@ import ( ) var globalPool *redis.Pool -var globalConn redis.Conn - const userPrefix string = "user:" +const devPrefix string = "dev:" const confirmPrefix string = "confirm:" func newPool() *redis.Pool { @@ -56,6 +57,13 @@ type User struct { NewPassword string `json:"new_password"` ConfirmId string `json:"confirm_id"` LastLogin string `json:"last_login"` + MyDevs string `json:"my_devs"` + +} + +type Dev struct { + Deveui string `json:"deveui"` + Alias string `json:"alias"` } @@ -96,14 +104,14 @@ func initDB() { // newPool returns a pointer to a redis.Pool pool := newPool() - // get a connection from the pool (redis.Conn) + // get a connection from the globalPool (redis.Conn) conn := pool.Get() + defer conn.Close() globalPool = pool - globalConn = conn // wir machen einen Connection Test - ping(globalConn) + ping(conn) // Wir legen einen initialen Admin User an, falls es diesen noch nicht gibt if checkUserAvailable("joerg.lehmann@nbit.ch") { @@ -113,13 +121,15 @@ func initDB() { } func closeDB() { - globalConn.Close() globalPool.Close() } func checkUserAvailable(username string) bool { logit("checkUserAvailable: User: "+username) - _, err := redis.String(globalConn.Do("GET", userPrefix+username)) + conn := globalPool.Get() + defer conn.Close() + + _, err := redis.String(conn.Do("GET", userPrefix+username)) if (err == redis.ErrNil) { logit("User does not exist and is therefore available:"+username) return true @@ -130,6 +140,52 @@ func checkUserAvailable(username string) bool { return false } +func getMyDevs(username string) []string { + res := []string{} + + if username == "" { + return res + } + + + conn := globalPool.Get() + defer conn.Close() + + logit("getMyDevs: User: "+username) + mydevs, err := redis.String(conn.Do("HGET", userPrefix+username, "my_devs")) + if err == nil { + logit("getMyDevs: mydevs: "+mydevs) + res = strings.Split(mydevs, ",") + } else { + log.Print(err) + } + + return res +} + +func getDevAlias(deveui string) string { + res := deveui + + if deveui == "" { + return res + } + + + conn := globalPool.Get() + defer conn.Close() + + logit("getDevAlias: Deveui: "+deveui) + alias, err := redis.String(conn.Do("HGET", devPrefix+deveui, "alias")) + if err == nil { + logit("getDevAlias: alias: "+alias) + res = alias + } else { + log.Print(err) + } + + return res +} + func randString(n int) string { const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" var bytes = make([]byte, n) @@ -141,6 +197,8 @@ func randString(n int) string { } func insertUser(username,password,is_admin string) { + conn := globalPool.Get() + defer conn.Close() logit("insertUser: "+username) pwd := []byte(password) hashedPassword,err := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost) @@ -150,7 +208,7 @@ func insertUser(username,password,is_admin string) { } confirm_id := "" - _, err = globalConn.Do("HMSET", userPrefix+username, "password", string(hashedPassword), "new_password", string(hashedPassword), "confirm_id", confirm_id, "last_login", "") + _, err = conn.Do("HMSET", userPrefix+username, "password", string(hashedPassword), "new_password", string(hashedPassword), "confirm_id", confirm_id, "last_login", "", "my_devs","") if err != nil { logit("insertUser: Error inserting User: "+username) @@ -159,6 +217,8 @@ func insertUser(username,password,is_admin string) { } func updateUser(username,password string) { + conn := globalPool.Get() + defer conn.Close() logit("updateUser: "+username) pwd := []byte(password) hashedPassword,err := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost) @@ -168,12 +228,12 @@ func updateUser(username,password string) { } confirm_id := randString(30) - _, err = globalConn.Do("HMSET", userPrefix+username, "new_password", string(hashedPassword), "confirm_id", confirm_id) + _, err = conn.Do("HMSET", userPrefix+username, "new_password", string(hashedPassword), "confirm_id", confirm_id) if err != nil { logit("updateUser: Error updateing User: "+username) return } - _, err = globalConn.Do("SET", confirmPrefix+confirm_id,username) + _, err = conn.Do("SET", confirmPrefix+confirm_id,username) if err != nil { logit("updateUser: Error inserting confirm_id: "+confirm_id+": "+username) return @@ -183,10 +243,14 @@ func updateUser(username,password string) { } func checkLoginCredentials(username,password string) bool { + conn := globalPool.Get() + defer conn.Close() + logit("checkLoginCredentials: called with username,password: "+username+","+password) - pwd, err := redis.String(globalConn.Do("HGET", userPrefix+username, "password")) + pwd, err := redis.String(conn.Do("HGET", userPrefix+username, "password")) if err == nil { - cid, err := redis.String(globalConn.Do("HGET", userPrefix+username, "confirm_id")) + logit("checkLoginCredentials: pwd: "+pwd+" CMD: HGET "+userPrefix+username+userPrefix+username+" confirm_id") + cid, err := redis.String(conn.Do("HGET", userPrefix+username, "confirm_id")) if err == nil { logit("checkLoginCredentials: cid: "+cid) if !(err != nil && cid != "") { @@ -196,6 +260,9 @@ func checkLoginCredentials(username,password string) bool { } } } + } else { + log.Print(err) + return false } hashedPassword := []byte(pwd) @@ -204,7 +271,10 @@ func checkLoginCredentials(username,password string) bool { } func updateLoginTime(username string) { - _, err := globalConn.Do("HSET", userPrefix+username, "last_login", time.Now().UTC().Format("2006-01-02 15:04:05")) + conn := globalPool.Get() + defer conn.Close() + + _, err := conn.Do("HSET", userPrefix+username, "last_login", time.Now().UTC().Format("2006-01-02 15:04:05")) if err != nil { logit("updateUser: Error updateing User: "+username) return @@ -212,22 +282,25 @@ func updateLoginTime(username string) { } func confirmUser(confirm_id string) { - u, err := redis.String(globalConn.Do("GET", confirmPrefix+confirm_id)) + conn := globalPool.Get() + defer conn.Close() + + u, err := redis.String(conn.Do("GET", confirmPrefix+confirm_id)) if err != nil { logit("confirmUser: Error with searching confirm_id: "+confirm_id) return } - new_password, err := redis.String(globalConn.Do("HGET", userPrefix+u, "new_password")) + new_password, err := redis.String(conn.Do("HGET", userPrefix+u, "new_password")) if err != nil { logit("confirmUser: Error with getting new_password: "+u) return } - _, err = globalConn.Do("HMSET", userPrefix+u, "confirm_id", "", "password", new_password) + _, err = conn.Do("HMSET", userPrefix+u, "confirm_id", "", "password", new_password) if err != nil { logit("confirmUser: Error updateing User: "+u) return } - _, err = globalConn.Do("DEL", confirmPrefix+confirm_id) + _, err = conn.Do("DEL", confirmPrefix+confirm_id) if err != nil { logit("confirmUser: Error deleting confirm_id: "+confirm_id) return diff --git a/snippets/scales.html b/snippets/scales.html index ef3b1a8..143cca5 100644 --- a/snippets/scales.html +++ b/snippets/scales.html @@ -16,44 +16,51 @@ +{{range .LastMetrics}}
-

Waage 1

-

letzte Messung: 1.1.2000 19:20

-
-
-
+
+
+{{end}} + + {{ else }}

Bitte zuerst einloggen

{{end}}