Compare commits

..
Author SHA1 Message Date
jlehmann 6fe448e3e2 make expiration text more precise 2022-08-01 11:06:17 +02:00
jlehmann e306a17ebd use same logig for date calculation as mini-beieli-web 2022-08-01 10:58:09 +02:00
jlehmann 5a2204876d only send email 30, 10 and 0 days before expiration 2022-07-22 19:32:21 +02:00
jlehmann 9aaf966728 send bcc to info@wo-bisch.ch 2022-07-18 19:35:45 +02:00
jlehmann 02cc375133 access control with header for checknodes 2022-07-14 20:20:34 +02:00
jlehmann 736fc6b058 integrate check_nodes in wo-bisch-web 2022-07-14 19:38:33 +02:00
jlehmann b4814970ce refactor email send functions 2022-07-13 19:22:43 +02:00
jlehmann 667051907b install tzdata because of dependency in go program 2022-07-05 19:11:04 +02:00
jlehmann cd79d3c852 make deployment container-ready 2022-07-05 18:15:04 +02:00
jlehmann 56fa4ab05f make deployment container-ready 2022-07-02 10:45:51 +02:00
jlehmann 695df00ebd price change 2022-06-04 19:22:48 +02:00
jlehmann 8232be9c01 allow spaces in greenzone 2022-06-03 18:13:36 +02:00
jlehmann 8e465698ad replace list on main page with ul 2022-04-16 19:50:51 +02:00
jlehmann e0b5b2498b fix timestamp - 2 2021-12-17 20:49:06 +01:00
jlehmann 8e4bc7a02b fix timestamp 2021-12-17 20:48:10 +01:00
jlehmann c678fbe5cd last vbat value not respecting invalid GPS positions 2021-12-17 20:45:05 +01:00
jlehmann fbcebb980a include vbat percent in download data 2021-12-17 19:37:26 +01:00
jlehmann 34fe1b107b do not filter unknown coordinates in main query 2021-12-17 19:16:19 +01:00
jlehmann e127b48642 fix getting last value 2021-08-23 14:04:23 +02:00
jlehmann 1c1cc71eff revert last change 2021-08-23 13:51:11 +02:00
jlehmann 260a9b1640 fix time problem 2021-08-23 13:47:32 +02:00
jlehmann a505fc5709 field order has changed with influxdb 2.0.8 2021-08-23 13:35:35 +02:00
jlehmann 61f61c474f AGB, Version 1.1 2021-07-05 20:03:57 +02:00
jlehmann 9784f44cb9 incl. Umhaengeband 2021-07-05 19:36:07 +02:00
jlehmann 4168f0f0ad change product images, no magnetic cable adapter 2021-07-05 19:29:03 +02:00
jlehmann eb257fea7d slight change on landing page 2021-06-29 19:49:17 +02:00
jlehmann 1a30cf7e84 modify landing page 2021-06-26 19:54:54 +02:00
jlehmann 5163a70261 typo 2021-06-09 20:02:44 +02:00
jlehmann e4a935d565 harmonize battery colors 2021-06-09 13:07:06 +02:00
jlehmann 40cf8cfabf harmonize padding 2021-06-09 10:07:40 +02:00
jlehmann 2830a648a5 typo 2021-06-08 15:27:19 +02:00
jlehmann 088af52bc9 cosmetic change 2021-06-08 15:26:01 +02:00
jlehmann 7db46d3f78 cosmetics... 2021-06-08 11:01:13 +02:00
jlehmann 8c3c75ae19 cosmetic change 2021-06-08 10:49:57 +02:00
jlehmann 6a451aae99 make more than one email receipient possible 2021-06-08 09:49:24 +02:00
jlehmann 19b52e4971 small text change 2021-06-08 09:04:19 +02:00
jlehmann c173fb653f place red marker above the orange ones 2021-06-07 20:48:06 +02:00
jlehmann 9db4ae08f3 not necessary to set height of daterangepicker 2021-06-07 20:41:35 +02:00
jlehmann 65c2fec4ac flags added 2021-06-07 19:43:49 +02:00
jlehmann 2cc02925a3 zuerst Laengengrad, dann Breitengrad 2021-06-07 13:13:34 +02:00
jlehmann 97a8e6d894 change text 2021-06-07 13:08:26 +02:00
jlehmann 9a717a5c5a activate Abo-Alert 2021-06-07 11:34:48 +02:00
jlehmann 6179b81f71 no-cache for templates, tune map position 2021-06-07 11:19:10 +02:00
jlehmann 992fb3d4f1 because of bootom margin on mobile browsers/ios 2021-06-05 20:35:57 +02:00
21 changed files with 372 additions and 537 deletions
+14
View File
@@ -0,0 +1,14 @@
FROM golang:alpine AS builder
WORKDIR /build
ADD go.mod .
COPY . .
RUN go build
FROM alpine
WORKDIR /build
RUN apk add --no-cache tzdata
COPY --from=builder /build/wo-bisch-web /build/wo-bisch-web
COPY snippets snippets
COPY templates templates
COPY static static
CMD ["./wo-bisch-web"]
EXPOSE 4000
+131
View File
@@ -0,0 +1,131 @@
package main
import (
"fmt"
"net/http"
"strconv"
"strings"
)
func sendEmailAccu(username string, alias string, deveui string, accu_percent string, threshold int, level string) {
fmt.Printf("SEND EMAIL ACCU (%s) - %s:%s\n", level, username, deveui)
mail_message := "To: " + username + `
From: info@wo-bisch.ch
Subject: ` + level + ` - wo-bisch.ch: Akku Ladezustand (` + alias + `)
Lieber Benutzer von wo-bisch.ch
Der Akku von "` + alias + `" (DevEUI: ` + deveui + `) ist noch zu ` + accu_percent + ` Prozent geladen.
Bitte rechtzeitig wieder laden! Bei Unterschreitung von 5% Ladung erscheint die letzte Warnung.
Mit freundlichen Grüssen
--
wo-bisch.ch`
sendEmail(username, "mail@wo-bisch.ch", mail_message)
}
func sendEmailAbo(username string, alias string, deveui string, days_left int, level string) {
var ablauftext string
if days_left == 0 {
fmt.Printf("SEND EMAIL ABO (%s) - %s:%s\n", level, username, deveui)
ablauftext = "Das Abo von \"" + alias + "\" (DevEUI: " + deveui + ") laeuft heute ab."
} else if days_left > 0 {
fmt.Printf("SEND EMAIL ABO (%s) - %s:%s\n", level, username, deveui)
ablauftext = "Das Abo von \"" + alias + "\" (DevEUI: " + deveui + ") laeuft in " + strconv.Itoa(days_left) + " Tagen ab."
}
mail_message := "To: " + username + `
From: info@wo-bisch.ch
Subject: ` + level + ` - wo-bisch.ch: Abo verlaengern (` + alias + `)
Lieber Benutzer von wo-bisch.ch
` + ablauftext + `
Bitte Abo verlaengern auf https://wo-bisch.ch
Mit freundlichen Grüssen
--
wo-bisch.ch`
sendEmail(username, "mail@wo-bisch.ch", mail_message)
}
func CheckThreshold(d string, vp int, u2 string, last_metric OneMetric, info_threshold int, warning_threshold int, alert_threshold int) bool {
var alias string
if vp <= info_threshold {
alias = getDevAlias(d)
}
if vp <= alert_threshold {
if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, alert_threshold) {
sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "ALARM")
InsertAlert("alarm_sent_accu:", d, u2, alert_threshold)
}
return false
}
if vp <= warning_threshold {
if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, warning_threshold) {
sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, warning_threshold, "WARNING")
InsertAlert("alarm_sent_accu:", d, u2, warning_threshold)
}
return false
}
if vp <= info_threshold {
if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, info_threshold) {
sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "INFO")
InsertAlert("alarm_sent_accu:", d, u2, info_threshold)
}
return false
}
return true
}
func sendReminder(days int) bool {
return (days == 0 || days == 5 || days == 10)
}
func checkNodes() {
logit("Starting check_battery...")
users := getUsers()
for _, u := range users {
//fmt.Println(u)
u2 := u[5:]
my_devs := getMyDevs(u2)
for _, d := range my_devs {
fmt.Printf("%s:%s\n", u2, d)
if !strings.HasPrefix(d, "@") {
last_metric := getLastMetrics(d)
// Zuerst der Batteriealarm
if last_metric.BatteryPercent != "" {
fmt.Printf("%s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated)
vp, _ := strconv.Atoi(last_metric.BatteryPercent)
if CheckThreshold(d, vp, u2, last_metric, 20, 10, 5) {
DeleteAlert("alarm_sent_accu:", d, u2)
}
}
// Jetzt der Alarm wegen der Abodauer
if sendReminder(last_metric.DaysUntilDeactivated) {
fmt.Printf("SEND EMAIL %s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated)
alias := getDevAlias(d)
sendEmailAbo(u2, alias, d, last_metric.DaysUntilDeactivated, "INFO")
}
}
}
}
logit("Done with check_battery...")
}
func checkNodesHandler(w http.ResponseWriter, req *http.Request) {
headers := req.Header
val, ok := headers["X-Checknodes"]
if ok {
fmt.Printf("X-Checknodes header is present with value %s\n", val)
checkNodes()
} else {
fmt.Println("X-Checknodes header is not present")
}
w.WriteHeader(http.StatusOK)
}
-419
View File
@@ -1,419 +0,0 @@
package main
import (
"bufio"
"bytes"
"fmt"
"github.com/gomodule/redigo/redis"
"github.com/jordan-wright/email"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func sendEmailAccu(username string, alias string, deveui string, accu_percent string, threshold int, level string) {
fmt.Printf("SEND EMAIL ACCU (%s) - %s:%s\n", level, username, deveui)
mail_message := `Lieber Benutzer von wo-bisch.ch
Der Akku von "` + alias + `" (DevEUI: ` + deveui + `) ist noch zu ` + accu_percent + ` Prozent geladen.
Bitte rechtzeitig wieder laden! Bei Unterschreitung von 5% Ladung erscheint die letzte Warnung.
Mit freundlichen Grüssen
--
wo-bisch.ch`
e := email.NewEmail()
e.From = "wo-bisch.ch <info@wo-bisch.ch>"
e.To = []string{username}
e.Bcc = []string{"joerg.lehmann@nbit.ch"}
e.Subject = level + " - wo-bisch.ch: Akku Ladezustand (" + alias + ")"
e.Text = []byte(mail_message)
e.Send("127.0.0.1:25", nil)
}
func sendEmailAbo(username string, alias string, deveui string, days_left int, level string) {
fmt.Printf("SEND EMAIL ABO (%s) - %s:%s\n", level, username, deveui)
mail_message := `Lieber Benutzer von wo-bisch.ch
Das Abo von "` + alias + `" (DevEUI: ` + deveui + `) laeuft in ` + strconv.Itoa(days_left) + ` Tagen ab.
Bitte Abo verlaengern auf https://wo-bisch.ch
Mit freundlichen Grüssen
--
wo-bisch.ch`
e := email.NewEmail()
e.From = "wo-bisch.ch <info@wo-bisch.ch>"
e.To = []string{username}
e.Bcc = []string{"joerg.lehmann@nbit.ch"}
e.Subject = level + " - wo-bisch.ch: Abo laeuft ab (" + alias + ")"
e.Text = []byte(mail_message)
e.Send("127.0.0.1:25", nil)
}
var globalPool *redis.Pool
const userPrefix string = "user:"
const devPrefix string = "dev:"
const confirmPrefix string = "confirm:"
func newPool() *redis.Pool {
return &redis.Pool{
// Maximum number of idle connections in the pool.
MaxIdle: 80,
// max number of connections
MaxActive: 12000,
// Dial is an application supplied function for creating and
// configuring a connection.
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", ":6379")
if err != nil {
panic(err.Error())
}
return c, err
},
}
}
// ping tests connectivity for redis (PONG should be returned)
func ping(c redis.Conn) error {
// Send PING command to Redis
// PING command returns a Redis "Simple String"
// Use redis.String to convert the interface type to string
s, err := redis.String(c.Do("PING"))
if err != nil {
return err
}
logit("PING Response = " + s)
// Output: PONG
return nil
}
type Dev struct {
Deveui string
Alias string
Alarmactive string
Smsnumber string
ActiveUntil string // Abo bezahlt bis TT.MM.YYYY
}
func initDB() {
// newPool returns a pointer to a redis.Pool
pool := newPool()
// get a connection from the globalPool (redis.Conn)
conn := pool.Get()
defer conn.Close()
globalPool = pool
// wir machen einen Connection Test
ping(conn)
}
func closeDB() {
globalPool.Close()
}
func getUsers() []string {
res := []string{}
conn := globalPool.Get()
defer conn.Close()
//logit("getUsers")
users, err := redis.Strings(conn.Do("KEYS", userPrefix+"*"))
if err == nil {
//logit("getUsers successful!")
res = users
} else {
log.Print(err)
}
return res
}
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 getActiveUntil(deveui string) string {
res := ""
if deveui == "" {
return res
}
conn := globalPool.Get()
defer conn.Close()
//logit("getActiveUntil: Deveui: " + deveui)
activeuntil, err := redis.String(conn.Do("HGET", devPrefix+deveui, "active_until"))
if err == nil {
//logit("getActiveUntil: activeuntil: " + activeuntil)
res = activeuntil
} else {
log.Print(err)
}
return res
}
func InsertAlert(prefix string, deveui string, email string, threshold int) {
conn := globalPool.Get()
defer conn.Close()
_, err := conn.Do("SET", prefix+deveui+":"+email, threshold)
if err != nil {
logit("InsertAlert: Error inserting: " + prefix + deveui + ":" + email)
}
}
func DeleteAlert(prefix string, deveui string, email string) {
conn := globalPool.Get()
defer conn.Close()
exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email))
if exists {
_, err := conn.Do("DEL", prefix+deveui+":"+email)
if err != nil {
logit("DeleteAlert: Error deleting: " + prefix + deveui + ":" + email)
}
}
}
func AlarmNotAlreadySent(prefix string, deveui string, email string, threshold int) bool {
conn := globalPool.Get()
defer conn.Close()
exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email))
if !exists {
return true
}
alarm_threshold, _ := redis.Int(conn.Do("GET", prefix+deveui+":"+email))
return threshold != alarm_threshold
}
type OneMetric struct {
Deveui string
Alias string
Timestamp string
BatteryPercent string
ActiveUntil string
DaysUntilDeactivated int // berechneter Wert
}
func CalcDaysUntil(mydate string) int {
var days int
layout := "02.01.2006"
t, err := time.Parse(layout, mydate)
if err != nil {
days = 0
}
days = int(t.Sub(time.Now()).Hours() / 24)
return days
}
func vbat2percent(vbat string) int {
i, err := strconv.Atoi(vbat)
res := 0
if err == nil {
res = int(float64(i-3400) / 6.0)
fmt.Printf("vbat2percent Result in Percent: %d\n", res)
if res < 0 {
res = 0
} else if res > 100 {
res = 100
}
}
return res
}
func getLastMetrics(deveui string) OneMetric {
var res OneMetric
res.BatteryPercent = "999"
url := "http://localhost:8086/api/v2/query?org=wobischorg"
data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
|> range(start:-5d)
|> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
|> filter(fn: (r) => r._field == "vbat")
|> last(column: "_time")`, deveui))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
log.Fatal("Error reading request. ", err)
}
// Set headers
var INFLUX_RO_TOKEN = os.Getenv("INFLUX_RO_TOKEN")
req.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN)
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(), ",")
//fmt.Printf("s: %q\n", s)
if (len(s) >= 7) && !(strings.HasPrefix(s[3], "_")) {
t, err := time.Parse(time.RFC3339, s[5])
if err != nil {
fmt.Printf("error converting time: %s\n", s[5])
continue
}
res.Timestamp = t.In(location).Format("02.01.2006 15:04")
res.BatteryPercent = strconv.Itoa(vbat2percent(s[6]))
fmt.Printf("vbat: %s\n", s[6])
}
}
res.Deveui = deveui
res.Alias = getDevAlias(deveui)
res.ActiveUntil = getActiveUntil(deveui)
res.DaysUntilDeactivated = CalcDaysUntil(res.ActiveUntil)
return res
}
func CheckThreshold(d string, vp int, u2 string, last_metric OneMetric, info_threshold int, warning_threshold int, alert_threshold int) bool {
var alias string
if vp <= info_threshold {
alias = getDevAlias(d)
}
if vp <= alert_threshold {
if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, alert_threshold) {
sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "ALARM")
InsertAlert("alarm_sent_accu:", d, u2, alert_threshold)
}
return false
}
if vp <= warning_threshold {
if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, warning_threshold) {
sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, warning_threshold, "WARNING")
InsertAlert("alarm_sent_accu:", d, u2, warning_threshold)
}
return false
}
if vp <= info_threshold {
if AlarmNotAlreadySent("alarm_sent_accu:", d, u2, info_threshold) {
sendEmailAccu(u2, alias, d, last_metric.BatteryPercent, alert_threshold, "INFO")
InsertAlert("alarm_sent_accu:", d, u2, info_threshold)
}
return false
}
return true
}
func logit(log_message string) {
log.Println(log_message)
}
func main() {
logit("Starting check_battery...")
initDB()
defer closeDB()
users := getUsers()
for _, u := range users {
//fmt.Println(u)
u2 := u[5:]
my_devs := getMyDevs(u2)
for _, d := range my_devs {
fmt.Printf("%s:%s\n", u2, d)
if !strings.HasPrefix(d, "@") {
last_metric := getLastMetrics(d)
// Zuerst der Batteriealarm
if last_metric.BatteryPercent != "" {
fmt.Printf("%s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated)
vp, _ := strconv.Atoi(last_metric.BatteryPercent)
if CheckThreshold(d, vp, u2, last_metric, 20, 10, 5) {
DeleteAlert("alarm_sent_accu:", d, u2)
}
}
// Jetzt der Alarm wegen der Abodauer
if last_metric.DaysUntilDeactivated < 30 {
fmt.Printf("SEND EMAIL %s:%s:%s Percent:%s:%d\n", u2, d, last_metric.BatteryPercent, last_metric.ActiveUntil, last_metric.DaysUntilDeactivated)
alias := getDevAlias(d)
sendEmailAbo("joerg.lehmann@nbit.ch", alias, d, last_metric.DaysUntilDeactivated, "INFO")
}
}
}
}
logit("Done with check_battery...")
}
+9
View File
@@ -2,6 +2,7 @@ package main
import (
"log"
"os"
)
// Contains tells whether a contains x.
@@ -18,3 +19,11 @@ func Contains(a []string, x string) bool {
}
return false
}
func getenv(key, fallback string) string {
value := os.Getenv(key)
if len(value) == 0 {
return fallback
}
return value
}
+30 -53
View File
@@ -1,27 +1,39 @@
package main
import (
"bytes"
"log"
"net/smtp"
)
func sendEmail(username, confirm_id string) {
c, err := smtp.Dial("127.0.0.1:25")
func sendEmail(mail_to, mail_default_authuser, mail_message string) {
var auth smtp.Auth
if getenv("MAILSERVER_USER", "") != "" {
// Set up authentication information.
auth = smtp.PlainAuth(
"",
getenv("MAILSERVER_USER", ""),
getenv("MAILSERVER_PASSWORD", ""),
getenv("MAILSERVER_HOST", "127.0.0.1"),
)
}
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
err := smtp.SendMail(
getenv("MAILSERVER_HOST", "127.0.0.1")+":"+getenv("MAILSERVER_PORT", "25"),
auth,
getenv("MAILSERVER_USER", mail_default_authuser),
[]string{mail_to, "info@wo-bisch.ch"},
[]byte(mail_message),
)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Set the sender and recipient.
c.Mail("register@wo-bisch.ch")
c.Rcpt(username)
// Send the email body.
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
defer wc.Close()
}
func sendEmailConfirm(username, confirm_id string) {
mail_message := "To: " + username + `
From: register@wo-bisch.ch
Subject: Passwortaenderung auf https://wo-bisch.ch, bitte bestaetigen
Lieber Benutzer von wo-bisch.ch
@@ -37,28 +49,12 @@ Mit freundlichen Grüssen
--
wo-bisch.ch`
buf := bytes.NewBufferString(mail_message)
if _, err = buf.WriteTo(wc); err != nil {
log.Fatal(err)
}
sendEmail(username, "mail@wo-bisch.ch", mail_message)
}
func sendPaymentConfirmationEmail(username, charge_data string, amount int64) {
c, err := smtp.Dial("127.0.0.1:25")
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Set the sender and recipient.
c.Mail("info@wo-bisch.ch")
c.Rcpt(username)
// Send the email body.
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
defer wc.Close()
mail_message := "To: " + username + `
From: info@wo-bisch.ch
Subject: Zahlungsbestaetigung wo-bisch.ch
Lieber Benutzer von wo-bisch.ch
@@ -71,28 +67,12 @@ Mit freundlichen Grüssen
--
wo-bisch.ch`
buf := bytes.NewBufferString(mail_message)
if _, err = buf.WriteTo(wc); err != nil {
log.Fatal(err)
}
sendEmail(username, "mail@wo-bisch.ch", mail_message)
}
func sendOrderEmail(username, body string) {
c, err := smtp.Dial("127.0.0.1:25")
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Set the sender and recipient.
c.Mail("info@wo-bisch.ch")
c.Rcpt(username)
// Send the email body.
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
defer wc.Close()
mail_message := "To: " + username + `
From: info@wo-bisch.ch
Subject: Bestelleingang auf https://wo-bisch.ch
Lieber Administrator
@@ -105,8 +85,5 @@ Mit freundlichen Grüssen
--
wo-bisch.ch`
buf := bytes.NewBufferString(mail_message)
if _, err = buf.WriteTo(wc); err != nil {
log.Fatal(err)
}
sendEmail(username, "mail@wo-bisch.ch", mail_message)
}
+32 -1
View File
@@ -9,6 +9,24 @@ import (
"time"
)
var epoch = time.Unix(0, 0).Format(time.RFC1123)
var noCacheHeaders = map[string]string{
"Expires": epoch,
"Cache-Control": "no-cache, private, max-age=0",
"Pragma": "no-cache",
"X-Accel-Expires": "0",
}
var etagHeaders = []string{
"ETag",
"If-Modified-Since",
"If-Match",
"If-None-Match",
"If-Range",
"If-Unmodified-Since",
}
func serveTemplate(w http.ResponseWriter, r *http.Request) {
logit("Called URL: " + r.URL.Path)
// wennn kein File angegeben ist: index.html
@@ -77,6 +95,18 @@ func serveTemplate(w http.ResponseWriter, r *http.Request) {
// because of caching, we need to set the Last-Modified header
w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))
// Delete any ETag headers that may have been set
for _, v := range etagHeaders {
if r.Header.Get(v) != "" {
r.Header.Del(v)
}
}
// Set our NoCache headers
for k, v := range noCacheHeaders {
w.Header().Set(k, v)
}
if err := tmpl.ExecuteTemplate(w, "layout", &data); err != nil {
logit(err.Error())
http.Error(w, http.StatusText(500), 500)
@@ -104,8 +134,9 @@ func main() {
http.HandleFunc("/save_tracker_greenzone", save_tracker_greenzoneHandler)
http.HandleFunc("/getstripepaymentintent", getstripepaymentintentHandler)
http.HandleFunc("/stripewebhook", stripeWebhookHandler)
http.HandleFunc("/checknodes", checkNodesHandler)
logit("Starting Web Application...")
http.ListenAndServe("127.0.0.1:4000", nil)
http.ListenAndServe(":4000", nil)
logit("Terminating Web Application...")
}
+90 -28
View File
@@ -112,7 +112,7 @@ func metricsHandler(response http.ResponseWriter, request *http.Request) {
mystart = start[0]
}
url := "http://localhost:8086/api/v2/query?org=wobischorg"
url := getenv("INFLUX_URL", "http://localhost:8086/api/v2/query?org=wobischorg")
data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
|> range(start: %s, stop: %s)
|> filter(fn: (r) => r._measurement == "measurement")
@@ -163,16 +163,16 @@ func metricsHandler(response http.ResponseWriter, request *http.Request) {
fmt.Fprintf(response, " \"data\": [\n")
for scanner.Scan() {
s := strings.Split(scanner.Text(), ",")
fmt.Printf("Scanned Line: %v\n", s)
if (len(s) >= 12) && !(strings.HasPrefix(s[3], "_")) {
t, err := time.Parse(time.RFC3339, s[3])
fmt.Printf("Scanned Line: %v, %d elements\n", s, len(s))
if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) {
t, err := time.Parse(time.RFC3339, s[5])
if err != nil {
fmt.Printf("error converting time: %s\n", s[3])
fmt.Printf("error converting time: %s\n", s[5])
continue
}
a := t.Unix()
lat = string2float64(s[9])
lon = string2float64(s[10])
lat = string2float64(s[len(s)-3])
lon = string2float64(s[len(s)-2])
if (lat == 0) || (lon == 0) {
fmt.Println("skip 0 value for lon/lat")
} else {
@@ -195,7 +195,7 @@ func metricsHandler(response http.ResponseWriter, request *http.Request) {
fmt.Fprintf(response, " ")
}
fmt.Fprintf(response, "[%d, %s, %s, %s, %d]\n", a, s[9], s[10], s[11], vbat2percent(s[11]))
fmt.Fprintf(response, "[%d, %s, %s, %s, %d]\n", a, s[len(s)-3], s[len(s)-2], s[len(s)-1], vbat2percent(s[len(s)-1]))
}
@@ -274,12 +274,12 @@ func downloadmetricsHandler(response http.ResponseWriter, request *http.Request)
mystart = start[0]
}
url := "http://localhost:8086/api/v2/query?org=wobischorg"
url := getenv("INFLUX_URL", "http://localhost:8086/api/v2/query?org=wobischorg")
data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
|> range(start: %s, stop: %s)
|> filter(fn: (r) => r._measurement == "measurement")
|> filter(fn: (r) => r.deveui == "%s")
|> filter(fn: (r) => r._field == "lon" or r._field == "lat")
|> filter(fn: (r) => r._field == "lon" or r._field == "lat" or r._field == "flags" or r._field == "vbat")
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")`, mystart, mystop, mydeveui))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
@@ -316,15 +316,34 @@ func downloadmetricsHandler(response http.ResponseWriter, request *http.Request)
for scanner.Scan() {
s := strings.Split(scanner.Text(), ",")
fmt.Printf("Scanned Line: %v\n", s)
if (len(s) >= 10) && !(strings.HasPrefix(s[3], "_")) {
t, err := time.Parse(time.RFC3339, s[3])
fmt.Printf("Scanned Line: %v, elements: %d\n", s, len(s))
if (len(s) >= 11) && !(strings.HasPrefix(s[5], "_")) {
s_flags := ""
lat := s[len(s)-3]
lon := s[len(s)-2]
if lat == "0" {
s_flags = "NOGPS"
} else {
s_flags = "GPS"
}
if len(s) == 12 {
flags, _ := strconv.Atoi(s[len(s)-4])
if flags&1 == 1 {
s_flags = s_flags + "+ALARM_BUTTON"
}
if flags&2 == 2 && lat != "0" {
s_flags = s_flags + "+OUTSIDE_GREENZONE"
}
}
t, err := time.Parse(time.RFC3339, s[5])
if err != nil {
fmt.Printf("error converting time: %s\n", s[3])
fmt.Printf("error converting time: %s\n", s[5])
continue
}
mytime := t.In(location).Format("02.01.2006 15:04")
fmt.Fprintf(response, "\"%s\",%s,%s\n", mytime, s[8], s[9])
vbat := s[len(s)-1]
percent := vbat2percent(vbat)
fmt.Fprintf(response, "\"%s\",%s,%s,%d,%s\n", mytime, lon, lat, percent, s_flags)
}
}
@@ -360,7 +379,7 @@ func vbat2html(vbat string) string {
icon_color = "has-background-danger-dark has-text-white pl-2 pr-2"
} else if percent <= 40 {
fa_battery_string = "fa-battery-quarter"
icon_color = "has-background-danger-light has-text-danger pl-2 pr-2"
icon_color = "has-text-success pl-2 pr-2"
} else if percent <= 60 {
fa_battery_string = "fa-battery-half"
icon_color = "has-text-success pl-2 pr-2"
@@ -383,7 +402,7 @@ func CalcDaysUntil(mydate string) int {
if err != nil {
days = 0
}
days = int(t.Sub(time.Now()).Hours() / 24)
days = int(t.Sub(time.Now().Truncate(24*time.Hour)).Hours() / 24)
return days
}
@@ -391,12 +410,14 @@ func CalcDaysUntil(mydate string) int {
func getLastMetrics(deveui string) OneMetric {
var res OneMetric
url := "http://localhost:8086/api/v2/query?org=wobischorg"
url := getenv("INFLUX_URL", "http://localhost:8086/api/v2/query?org=wobischorg")
data := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
|> range(start:-365d)
|> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
|> filter(fn: (r) => r.lat != 0)
|> filter(fn: (r) => r.lat != 0)
|> sort(columns: ["_time"], desc: false)
|> last(column: "_time")`, deveui))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
@@ -432,27 +453,68 @@ func getLastMetrics(deveui string) OneMetric {
location, err := time.LoadLocation("Europe/Zurich")
for scanner.Scan() {
s := strings.Split(scanner.Text(), ",")
fmt.Printf("BlaBla: %v\n", s)
if (len(s) >= 12) && !(strings.HasPrefix(s[3], "_")) {
t, err := time.Parse(time.RFC3339, s[3])
fmt.Printf("BlaBla: %v, elements: %d\n", s, len(s))
if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) {
t, err := time.Parse(time.RFC3339, s[5])
if err != nil {
fmt.Printf("error converting time: %s\n", s[3])
fmt.Printf("error converting time: %s\n", s[5])
continue
}
res.Fw = s[8]
res.Fw = s[len(s)-4]
res.Timestamp = t.In(location).Format("02.01.2006 15:04")
if s[9] != "0" {
res.Lat = s[9]
res.Lat = s[len(s)-3]
res.PosTimestamp = t.In(location).Format("02.01.2006 15:04")
}
if s[10] != "0" {
res.Lon = s[10]
res.Lon = s[len(s)-2]
res.PosTimestamp = t.In(location).Format("02.01.2006 15:04")
}
res.BatteryPercent = strconv.Itoa(vbat2percent(s[11]))
res.BatteryPercentHTML = template.HTML(vbat2html(s[11]))
}
}
data2 := []byte(fmt.Sprintf(`from(bucket:"wobischbucket")
|> range(start:-365d)
|> filter(fn: (r) => r._measurement == "measurement" and r.deveui == "%s")
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
|> sort(columns: ["_time"], desc: false)
|> last(column: "_time")`, deveui))
req2, err := http.NewRequest("POST", url, bytes.NewBuffer(data2))
if err != nil {
log.Fatal("Error reading request. ", err)
}
// Set headers
req2.Header.Set("Authorization", "Token "+INFLUX_RO_TOKEN)
req2.Header.Set("accept", "application/csv")
req2.Header.Set("content-type", "application/vnd.flux")
// Send request
resp2, err := client.Do(req2)
if err != nil {
log.Fatal("Error reading response. ", err)
}
defer resp2.Body.Close()
fmt.Println("response Status:", resp2.Status)
fmt.Println("response Headers:", resp2.Header)
body2, err := ioutil.ReadAll(resp2.Body)
if err != nil {
log.Fatal("Error reading body. ", err)
}
fmt.Println("response Body 2:", string(body2))
scanner2 := bufio.NewScanner(strings.NewReader(string(body2)))
for scanner2.Scan() {
s := strings.Split(scanner2.Text(), ",")
fmt.Printf("BlaBla: %v, elements: %d\n", s, len(s))
if (len(s) >= 12) && !(strings.HasPrefix(s[5], "_")) {
res.BatteryPercent = strconv.Itoa(vbat2percent(s[len(s)-1]))
res.BatteryPercentHTML = template.HTML(vbat2html(s[len(s)-1]))
}
}
+38 -2
View File
@@ -26,7 +26,7 @@ func newPool() *redis.Pool {
// Dial is an application supplied function for creating and
// configuring a connection.
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", ":6379")
c, err := redis.Dial("tcp", getenv("REDIS_CONNECTION_STRING", ":6379"))
if err != nil {
panic(err.Error())
}
@@ -452,7 +452,7 @@ func updateUser(username, password string) {
return
}
sendEmail(username, confirm_id)
sendEmailConfirm(username, confirm_id)
}
func checkLoginCredentials(username, password string) bool {
@@ -521,3 +521,39 @@ func confirmUser(confirm_id string) bool {
return true
}
func InsertAlert(prefix string, deveui string, email string, threshold int) {
conn := globalPool.Get()
defer conn.Close()
_, err := conn.Do("SET", prefix+deveui+":"+email, threshold)
if err != nil {
logit("InsertAlert: Error inserting: " + prefix + deveui + ":" + email)
}
}
func DeleteAlert(prefix string, deveui string, email string) {
conn := globalPool.Get()
defer conn.Close()
exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email))
if exists {
_, err := conn.Do("DEL", prefix+deveui+":"+email)
if err != nil {
logit("DeleteAlert: Error deleting: " + prefix + deveui + ":" + email)
}
}
}
func AlarmNotAlreadySent(prefix string, deveui string, email string, threshold int) bool {
conn := globalPool.Get()
defer conn.Close()
exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email))
if !exists {
return true
}
alarm_threshold, _ := redis.Int(conn.Do("GET", prefix+deveui+":"+email))
return threshold != alarm_threshold
}
+9 -17
View File
@@ -3,24 +3,16 @@
{{define "body_content"}}
<p class="title is-4">Willkommen bei wo-bisch.ch</p>
<p>
Hier sind Sie am richtigen Ort, wenn Sie einen kleinen, sparsamen <a href="/product.html"><strong>GPS-Tracker</strong></a> suchen, der seinen Standort periodisch &uuml;bermittelt.
</p>
<p class="pl-4 pt-2">
<span class="icon"><i class="fa fa-arrow-right"></i></span>
einfacher Zugang auf Positionsdaten der letzten 365 Tage per Webbrowser
</p>
<p class="pl-4">
<span class="icon"><i class="fa fa-arrow-right"></i></span>
ohne SIM-Karte (verwendet LoraWAN Netzwerk der Swisscom)
</p>
<p class="pl-4">
<span class="icon"><i class="fa fa-arrow-right"></i></span>
Einsatz ganze Schweiz (Abdeckung: 95% der Schweizer Bev&ouml;lkerung)
</p>
<p class="pl-4 pb-2">
<span class="icon"><i class="fa fa-arrow-right"></i></span>
hohe Datensicherheit
Hier sind Sie am richtigen Ort, wenn Sie einen kleinen, sparsamen <a href="/product.html"><strong>GPS-Tracker</strong></a> suchen.
</p>
<div class="content">
<ul>
<li>einfacher Zugang auf Positionsdaten der letzten 365 Tage per Webbrowser</li>
<li>SMS und/oder E-Mail Alarm bei Druck des Knopfs oder Verlassen einer benutzerdefinierten "gr&uuml;nen Zone"</li>
<li>ohne Handy-Abo (verwendet LoraWAN Netzwerk der Swisscom)</li>
<li>Einsatz ganze Schweiz (Abdeckung: 95% der Schweizer Bev&ouml;lkerung)</li>
</ul>
</div>
<p class="pb-4">
Hier geht's zur <a href="/product.html">Produktbeschreibung</a>
</p>
+1 -1
View File
@@ -5,7 +5,7 @@
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""></script>
<script type="text/javascript" src="/static/js/Map.SelectArea.min.js"></script>
<script src="/static/js/Map.SelectArea.min.js"></script>
{{end}}
{{define "body_content"}}
{{ if ne .UserName "" }}
+2 -1
View File
@@ -27,7 +27,7 @@
</figure>
</div>
<div class="column is-three-quarters">
<p class="is-size-2 has-text-weight-bold">CHF 150.00</p>
<p class="is-size-2 has-text-weight-bold">CHF 120.00</p>
<p class="is-size-7 neg-margin-1">pro Stück, inkl. MwSt.</p>
</div>
</div>
@@ -36,6 +36,7 @@
<ul>
<li>GPS Tracker mit integriertem Akku</li>
<li>USB-Netzadapter mit Ladekabel USB-Typ-A</li>
<li>Umh&auml;ngeband</li>
<li>2 Dual Lock Klettpad 25 x 25mm zum optionalen einfachen Befestigen</li>
<li>12 Monate Abo, <span class="has-text-weight-bold">anschliessend CHF 5.00 pro Monat</span></li>
</ul>
+2 -1
View File
@@ -23,7 +23,7 @@
</div>
</div>
<div class="content">
<p>Durch Dr&uuml;cken auf den roten Knopf f&uuml;r mehr als 3 Sekunden (bis die LED blinkt) wird der Alarmmodus ausgel&ouml;st. Damit wird anschliessend w&auml;hrend einer Stunde jede Minute der Standort &uuml;bermittelt. Die Alarmmeldungkann an eine SMS-Adresse und/oder E-Mail Adresse weitergeleitet werden. Die Alarm&uuml;bermittlung h&ouml;rt auf, sobald der GPS Tracker eine Best&auml;tigungsmeldung empfangen hat.</p>
<p>Durch Dr&uuml;cken auf den roten Knopf f&uuml;r mehr als 3 Sekunden (bis die LED blinkt) wird der Alarmmodus ausgel&ouml;st. Damit wird anschliessend w&auml;hrend einer Stunde jede Minute der Standort &uuml;bermittelt. Die Alarmmeldung kann an eine SMS-Adresse und/oder E-Mail Adresse weitergeleitet werden. Die Alarm&uuml;bermittlung h&ouml;rt auf, sobald der GPS Tracker eine Best&auml;tigungsmeldung empfangen hat.</p>
<p>Der GPS Tracker enth&auml;lt einen Akku, der per mitgeliefertem USB-Kabel aufgeladen werden kann. Der Akku-Ladezustand wird &uuml;berwacht und es werden Erinnerungsmails versendet, wenn der GPS Tracker wieder aufgeladen werden sollte (jeweils bei Unterschreiten von 20%, 10% und 5% Ladung).</p>
<p>Dieser GPS Tracker funktioniert ohne Mobilfunkabo/SIM-Karte. Daten werden &uuml;ber ein f&uuml;r solche Zwecke geschaffenes Funknetz (LoraWAN) &uuml;bermittelt: damit ist der geringe Strombedarf und auch die sehr geringe Strahlung (nur w&auml;hrend der &Uuml;bermittlung) begr&uuml;ndet. Das verwendete LoraWAN Netzwerk (Swisscom) deckt mehr als 95% der Schweizer Bev&ouml;kerung ab. Der Einsatz ist auf die Schweiz beschr&auml;nkt.</p>
<div class="content pb-4">
@@ -57,6 +57,7 @@
<li>durch Klick auf "alle Positionen anzeigen" (Symbol oben rechts) werden alle Messpunkte dargestellt</li>
<li>Positionen können in ein CSV-Datei exportiert werden</li>
<li>der Kartenausschnitt wird automatisch so gezoomt, dass alle Positionen der gw&auml;hlten Periode darauf dargestellt werden k&ouml;nnen</li>
<li>durch gleichzeitiges Dr&uuml;cken der Ctrl-Taste kann mit der Maus eine "gr&uuml;ne Zone" selektiert werden</li>
</ul>
</p>
<p class="pt-4">Mit einem Doppelklick auf die Karte wird der automatische Zoom reaktiviert.</p>
+3 -3
View File
@@ -58,9 +58,9 @@
</div>
<div class="field">
<label class="label">Alarm E-Mail (SMS)</label>
<label class="label">Alarm E-Mail(s) (mehrere durch Komma getrennt)</label>
<div class="control has-icons-right">
<input id="email" class="input" type="text" maxlength="50"> <span id="email_exclamation" class="icon is-small is-right">
<input id="email" class="input" type="text" maxlength="100"> <span id="email_exclamation" class="icon is-small is-right">
<i class="fas fa-exclamation-triangle"></i>
</span>
</div>
@@ -152,7 +152,7 @@ Sie erhalten eine E-Mail, sobald die Zahlung erfolgreich abgeschlossen ist.
<p id="emailalarmactive_{{.Deveui}}" hidden>{{.Emailalarmactive}}</p>
<p id="email_{{.Deveui}}" hidden>{{.Email}}</p>
<p id="greenzone_{{.Deveui}}" hidden>{{.Greenzone}}</p>
<p id="lastmeasurement_{{.Deveui}}" class="has-text-centered">letzte &uuml;bermittelte Position: {{.Timestamp}}</p>
<p id="lastmeasurement_{{.Deveui}}" class="has-text-centered">letzte Position: {{.Timestamp}}</p>
<p id="batpercent_{{.Deveui}}" class="has-text-centered">{{.BatteryPercentHTML}}</p>
<div id="{{.Deveui}}" class="columns mt-3">
<p id="lat" hidden>{{.Lat}}</p>
+2 -2
View File
@@ -3,7 +3,7 @@ html,body {
}
.section,.container {
height: 90%;
height: calc(100% - 24px);
}
hr {
@@ -80,7 +80,7 @@ input:focus,
}
.mapbig {
height: 100%;
height: calc(100% - 100px);
}
.slidecontainer {
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 35 KiB

+4 -5
View File
@@ -34,7 +34,7 @@ function vbat2icon(percent) {
icon_color = "has-background-danger-dark has-text-white pl-2 pr-2";
} else if (percent <= 40) {
fa_battery_string = "fa-battery-quarter";
icon_color = "has-background-danger-light has-text-danger pl-2 pr-2";
icon_color = "has-text-success pl-2 pr-2";
} else if (percent <= 60) {
fa_battery_string = "fa-battery-half";
icon_color = "has-text-success pl-2 pr-2";
@@ -111,7 +111,7 @@ function refreshDatapoints(deveui, start, stop) {
for (let i of datapoints) {
unix_timestamp = i[0];
date = new Date(unix_timestamp * 1000);
markers.push(L.marker([i[1], i[2]], { icon: woBischIcon2, zIndexOffset: 1000 }).bindPopup(moment(date).format('DD.MM.YYYY HH:mm')));
markers.push(L.marker([i[1], i[2]], { icon: woBischIcon2, zIndexOffset: 500 }).bindPopup(moment(date).format('DD.MM.YYYY HH:mm')));
lat = i[1];
lon = i[2];
}
@@ -128,14 +128,14 @@ function refreshDatapoints(deveui, start, stop) {
lcontrol = L.control.layers({}).addTo(map);
lcontrol.addOverlay(allmarkers, 'alle Positionen anzeigen');
bounds = new L.LatLngBounds([[mydata['max_lat'], mydata['max_lon']], [mydata['min_lat'], mydata['min_lon']]]);
map.fitBounds(bounds, { padding: [15, 15] });
map.fitBounds(bounds, { padding: [20, 20] });
marker.bindPopup(myhtml).openPopup();
} else {
$('#map').hide();
$('#datetimeslider').hide();
$('#chart').html(`<article class="message is-danger">
<div class="message-body">
Keine Messpunkte f&uuml;r diese Zeitperiode
Keine GPS-Messpunkte f&uuml;r diese Zeitperiode.<br /><br />Hinweis: heruntergeladene Daten enthalten alle &Uuml;bermittlungen, auch die ohne GPS-Messpunkt (L&auml;ngen- und Breitengrade haben den Wert 0).
</div>
</article>`);
$('#chart').show();
@@ -247,7 +247,6 @@ $(document).ready(function () {
var tilelayer = new L.tileLayer(url);
map.attributionControl.setPrefix('Source: Swiss Federal Office of Topography')
map.addLayer(tilelayer);
//marker = L.marker([lat, lon], { icon: woBischIcon, zIndexOffset: 1000 }).addTo(map);
$('#reportrange span').html(moment(s_start).locale('de').format('D. MMM YYYY') + ' - ' + moment(s_stop).locale('de').format('D. MMM YYYY'));
+2 -2
View File
@@ -8,9 +8,9 @@ function validate(what, text) {
} else if (what == 'smsnumber') {
var re = /^\+[0-9]{11,11}$/;
} else if (what == 'email') {
var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
var re = /^(([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+([,.](([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+)*$/;
} else if (what == 'greenzone') {
var re = /^$|^[0-9]+\.[0-9]+,[0-9]+\.[0-9]+,[0-9]+\.[0-9]+,[0-9]+\.[0-9]+$/;
var re = /^ *$|^[0-9]+\.[0-9]+,[0-9]+\.[0-9]+,[0-9]+\.[0-9]+,[0-9]+\.[0-9]+$/;
}
return re.test(text);
+3 -2
View File
@@ -5,6 +5,7 @@ import (
"log"
"net/http"
"regexp"
"strings"
)
// tracker handler
@@ -110,7 +111,7 @@ func save_tracker_settingsHandler(response http.ResponseWriter, request *http.Re
}
myemail := email[0]
match2, _ := regexp.MatchString(`^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`, myemail)
match2, _ := regexp.MatchString(`^(([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+([,.](([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5}){1,25})+)*$`, myemail)
if !(match2) {
log.Println("Url Param 'email' is not valid")
fmt.Fprintf(response, "{ \"rc\": 15, \"msg\": \"email is not valid, must be in in format max.mustermann@example.com\" }")
@@ -124,7 +125,7 @@ func save_tracker_settingsHandler(response http.ResponseWriter, request *http.Re
fmt.Fprintf(response, "{ \"rc\": 16, \"msg\": \"greenzone must be specified in URL\" }")
return
}
mygreenzone := greenzone[0]
mygreenzone := strings.ReplaceAll(greenzone[0], " ", "")
match3, _ := regexp.MatchString(`^$|^[0-9]+\.[0-9]+,[0-9]+\.[0-9]+,[0-9]+\.[0-9]+,[0-9]+\.[0-9]+$`, mygreenzone)
if !(match3) {