Compare commits
57 Commits
feature-gr
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
7f639bca49 | |
|
|
483d10799d | |
|
|
8e3721da89 | |
|
|
a297b127e1 | |
|
|
8838103f2d | |
|
|
0931ecb0f4 | |
|
|
40134ceba9 | |
|
|
6364bedb29 | |
|
|
97d2bb2593 | |
|
|
570de60338 | |
|
|
c6f98415e3 | |
|
|
0520b1eb02 | |
|
|
1f1e34e213 | |
|
|
6fe448e3e2 | |
|
|
e306a17ebd | |
|
|
5a2204876d | |
|
|
9aaf966728 | |
|
|
02cc375133 | |
|
|
736fc6b058 | |
|
|
b4814970ce | |
|
|
667051907b | |
|
|
cd79d3c852 | |
|
|
56fa4ab05f | |
|
|
695df00ebd | |
|
|
8232be9c01 | |
|
|
8e465698ad | |
|
|
e0b5b2498b | |
|
|
8e4bc7a02b | |
|
|
c678fbe5cd | |
|
|
fbcebb980a | |
|
|
34fe1b107b | |
|
|
e127b48642 | |
|
|
1c1cc71eff | |
|
|
260a9b1640 | |
|
|
a505fc5709 | |
|
|
61f61c474f | |
|
|
9784f44cb9 | |
|
|
4168f0f0ad | |
|
|
eb257fea7d | |
|
|
1a30cf7e84 | |
|
|
5163a70261 | |
|
|
e4a935d565 | |
|
|
40cf8cfabf | |
|
|
2830a648a5 | |
|
|
088af52bc9 | |
|
|
7db46d3f78 | |
|
|
8c3c75ae19 | |
|
|
6a451aae99 | |
|
|
19b52e4971 | |
|
|
c173fb653f | |
|
|
9db4ae08f3 | |
|
|
65c2fec4ac | |
|
|
2cc02925a3 | |
|
|
97a8e6d894 | |
|
|
9a717a5c5a | |
|
|
6179b81f71 | |
|
|
992fb3d4f1 |
|
|
@ -0,0 +1,16 @@
|
|||
FROM golang:alpine AS builder
|
||||
WORKDIR /build
|
||||
ADD go.mod .
|
||||
COPY . .
|
||||
RUN go build
|
||||
FROM alpine
|
||||
RUN apk add --no-cache tzdata
|
||||
ENV TZ=Europe/Zurich
|
||||
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
|
||||
20
README.md
20
README.md
|
|
@ -45,3 +45,23 @@ show range of last values:
|
|||
Autor: Joerg Lehmann, nbit Informatik GmbH
|
||||
|
||||
|
||||
### Influxdb Commands
|
||||
|
||||
```
|
||||
Show tokens
|
||||
# influx auth ls
|
||||
|
||||
Clone Admin Token in Webgui if not known anymore and use this to get all tokens:
|
||||
|
||||
Create Default Config:
|
||||
# influx config create --config-name default --token XXXXXXXXXXX --host-url http://127.0.0.1:8086 -a
|
||||
|
||||
Update Token in Default Config:
|
||||
# influx config update --config-name default --token XXXXXXXXXXX
|
||||
|
||||
Backup Influxdb:
|
||||
# influx backup /root/joergs-backup -t XXXXXadmintokenXXXXX
|
||||
|
||||
Restore Influxdb:
|
||||
# influx restore /var/lib/influxdb2/joergs-backup/ --full
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,133 @@
|
|||
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 + `)
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
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 + `)
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
Lieber Benutzer von wo-bisch.ch
|
||||
|
||||
` + ablauftext + `
|
||||
|
||||
Bitte Abo verlängern 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)
|
||||
}
|
||||
|
|
@ -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...")
|
||||
}
|
||||
5
go.mod
5
go.mod
|
|
@ -1,11 +1,10 @@
|
|||
module nbit.ch/wo-bisch-web/v2
|
||||
|
||||
go 1.14
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/gomodule/redigo v1.8.4
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/stripe/stripe-go/v72 v72.41.0
|
||||
github.com/stripe/stripe-go/v74 v74.7.0
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||
)
|
||||
|
|
|
|||
17
go.sum
17
go.sum
|
|
@ -1,29 +1,32 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg=
|
||||
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/gomodule/redigo/redis v0.0.0-do-not-use h1:J7XIp6Kau0WoyT4JtXHT3Ei0gA1KkSc6bc87j9v9WIo=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stripe/stripe-go v1.0.3 h1:RHgK2FUKawVNBPJ15pNM4IWkEVVCg5Ju3xK5QZNhTwY=
|
||||
github.com/stripe/stripe-go v70.15.0+incompatible h1:hNML7M1zx8RgtepEMlxyu/FpVPrP7KZm1gPFQquJQvM=
|
||||
github.com/stripe/stripe-go/v72 v72.41.0 h1:HkyJew+GkD/ClBT306+5vKLjBE4PRCJDiZ1enQyxeGQ=
|
||||
github.com/stripe/stripe-go/v72 v72.41.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stripe/stripe-go/v74 v74.7.0 h1:KHlyslQj9YOv62b1sycQ31LFj7KlqR+seHsSowAWrjc=
|
||||
github.com/stripe/stripe-go/v74 v74.7.0/go.mod h1:5PoXNp30AJ3tGq57ZcFuaMylzNi8KpwlrYAFmO1fHZw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
95
mail.go
95
mail.go
|
|
@ -1,32 +1,45 @@
|
|||
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
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
Lieber Benutzer von wo-bisch.ch
|
||||
|
||||
Sie haben soeben eine Passwortaenderung veranlasst. Bitte klicken Sie folgenden Link,
|
||||
Sie haben soeben eine Passwortänderung veranlasst. Bitte klicken Sie folgenden Link,
|
||||
um das neue Passwort zu aktivieren:
|
||||
|
||||
https://wo-bisch.ch/confirm?id=` + confirm_id + `
|
||||
|
|
@ -37,30 +50,16 @@ 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
|
||||
|
||||
MIME-version: 1.0;
|
||||
Content-Type: text/html; charset="UTF-8";
|
||||
<pre>
|
||||
Lieber Benutzer von wo-bisch.ch
|
||||
|
||||
Sie haben soeben erfolgreich folgende Abo-Verlaengerungen bezahlt:
|
||||
|
|
@ -69,35 +68,20 @@ Sie haben soeben erfolgreich folgende Abo-Verlaengerungen bezahlt:
|
|||
|
||||
Mit freundlichen Grüssen
|
||||
--
|
||||
wo-bisch.ch`
|
||||
wo-bisch.ch</pre>`
|
||||
|
||||
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
|
||||
Content-Type: text/plain; charset="UTF-8"
|
||||
|
||||
Lieber Administrator
|
||||
|
||||
Soeben ist eine Bestellung eingegangen, bitte pruefen:
|
||||
Soeben ist eine Bestellung eingegangen, bitte prüfen:
|
||||
|
||||
` + body + `
|
||||
|
||||
|
|
@ -105,8 +89,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)
|
||||
}
|
||||
|
|
|
|||
33
main.go
33
main.go
|
|
@ -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...")
|
||||
}
|
||||
|
|
|
|||
118
metrics.go
118
metrics.go
|
|
@ -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]))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -383,11 +383,7 @@ func prolongActivation(deveui string, years int) (string, error) {
|
|||
fmt.Println(t.Unix())
|
||||
|
||||
var t_new time.Time
|
||||
if t.Before(time.Now()) {
|
||||
t_new = time.Now().AddDate(years, 0, 0)
|
||||
} else {
|
||||
t_new = t.AddDate(years, 0, 0)
|
||||
}
|
||||
t_new = t.AddDate(years, 0, 0)
|
||||
active_until_new := t_new.Format(layout)
|
||||
|
||||
// SET object
|
||||
|
|
@ -452,7 +448,7 @@ func updateUser(username, password string) {
|
|||
return
|
||||
}
|
||||
|
||||
sendEmail(username, confirm_id)
|
||||
sendEmailConfirm(username, confirm_id)
|
||||
}
|
||||
|
||||
func checkLoginCredentials(username, password string) bool {
|
||||
|
|
@ -521,3 +517,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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{{define "body_content"}}
|
||||
<p class="title is-4">Kontakt</p>
|
||||
<p>nbit Informatik GmbH<br />
|
||||
Kirchweg 2<br />
|
||||
3510 Konolfingen<br />
|
||||
Untere Stockteile 16<br />
|
||||
3806 Bönigen b. Interlaken<br />
|
||||
<br />
|
||||
+41 31 792 00 40<br />
|
||||
<a href='mailto:i%6Efo@%6Ebit.%63h'>info@nbit.ch</a>
|
||||
|
|
|
|||
|
|
@ -3,46 +3,17 @@
|
|||
{{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 ü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ö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ünen Zone"</li>
|
||||
<li>ohne Handy-Abo (verwendet LoraWAN Netzwerk der Swisscom)</li>
|
||||
<li>Einsatz ganze Schweiz (Abdeckung: 95% der Schweizer Bevölkerung)</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p class="pb-4">
|
||||
Hier geht's zur <a href="/product.html">Produktbeschreibung</a>
|
||||
</p>
|
||||
{{ if ne .UserName "" }}
|
||||
<p>
|
||||
<strong>Ich will weitere bestellen!</strong>
|
||||
<span class="icon"><i class="fa fa-arrow-right"></i></span>
|
||||
Hier geht's zur <a href="/order.html">Bestellung</a>
|
||||
</p>
|
||||
{{ else }}
|
||||
<div>
|
||||
<p>
|
||||
<strong>Ich will auch einen!</strong>
|
||||
<span class="icon"><i class="fa fa-arrow-right"></i></span>
|
||||
Hier geht's zur <a href="/order.html">Bestellung</a>
|
||||
</p>
|
||||
<p> </p>
|
||||
<p class="pb-4">
|
||||
<strong>Ich habe bereits einen (oder mehrere)</strong>
|
||||
<span class="icon"><i class="fa fa-arrow-right"></i></span>
|
||||
Hier geht's zum <a href="/login.html">Login</a>
|
||||
</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
|||
|
|
@ -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 "" }}
|
||||
|
|
|
|||
|
|
@ -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ä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>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>Durch Drücken auf den roten Knopf für mehr als 3 Sekunden (bis die LED blinkt) wird der Alarmmodus ausgelöst. Damit wird anschliessend während einer Stunde jede Minute der Standort übermittelt. Die Alarmmeldungkann an eine SMS-Adresse und/oder E-Mail Adresse weitergeleitet werden. Die Alarmübermittlung hört auf, sobald der GPS Tracker eine Bestätigungsmeldung empfangen hat.</p>
|
||||
<p>Durch Drücken auf den roten Knopf für mehr als 3 Sekunden (bis die LED blinkt) wird der Alarmmodus ausgelöst. Damit wird anschliessend während einer Stunde jede Minute der Standort übermittelt. Die Alarmmeldung kann an eine SMS-Adresse und/oder E-Mail Adresse weitergeleitet werden. Die Alarmübermittlung hört auf, sobald der GPS Tracker eine Bestätigungsmeldung empfangen hat.</p>
|
||||
<p>Der GPS Tracker enthält einen Akku, der per mitgeliefertem USB-Kabel aufgeladen werden kann. Der Akku-Ladezustand wird ü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 über ein für solche Zwecke geschaffenes Funknetz (LoraWAN) übermittelt: damit ist der geringe Strombedarf und auch die sehr geringe Strahlung (nur während der Übermittlung) begründet. Das verwendete LoraWAN Netzwerk (Swisscom) deckt mehr als 95% der Schweizer Bevökerung ab. Der Einsatz ist auf die Schweiz beschrä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ählten Periode darauf dargestellt werden können</li>
|
||||
<li>durch gleichzeitiges Drücken der Ctrl-Taste kann mit der Maus eine "grüne Zone" selektiert werden</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p class="pt-4">Mit einem Doppelklick auf die Karte wird der automatische Zoom reaktiviert.</p>
|
||||
|
|
|
|||
|
|
@ -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 ü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>
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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ür diese Zeitperiode
|
||||
Keine GPS-Messpunkte für diese Zeitperiode.<br /><br />Hinweis: heruntergeladene Daten enthalten alle Übermittlungen, auch die ohne GPS-Messpunkt (Lä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'));
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
44
stripe.go
44
stripe.go
|
|
@ -3,8 +3,9 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/stripe/stripe-go/v72"
|
||||
"github.com/stripe/stripe-go/v72/paymentintent"
|
||||
"github.com/stripe/stripe-go/v74"
|
||||
"github.com/stripe/stripe-go/v74/customer"
|
||||
"github.com/stripe/stripe-go/v74/paymentintent"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
|
|
@ -21,6 +22,33 @@ func getStripePK() string {
|
|||
return os.Getenv("STRIPE_PK")
|
||||
}
|
||||
|
||||
func getCustomerid(name string) string {
|
||||
// if customer does not already exist, create it...
|
||||
var cid = ""
|
||||
params := &stripe.CustomerSearchParams{}
|
||||
params.Query = *stripe.String("name:'" + name + "'")
|
||||
customers := customer.Search(params)
|
||||
for customers.Next() {
|
||||
fmt.Printf("%s\n", customers.Current().(*stripe.Customer).ID)
|
||||
cid = customers.Current().(*stripe.Customer).ID
|
||||
}
|
||||
|
||||
if cid == "" {
|
||||
// create new customer
|
||||
paramsc := &stripe.CustomerParams{
|
||||
Name: stripe.String(name),
|
||||
}
|
||||
|
||||
customer, err := customer.New(paramsc)
|
||||
if err != nil {
|
||||
log.Println("Error Creating Customer: " + name)
|
||||
}
|
||||
cid = customer.ID
|
||||
}
|
||||
|
||||
return cid
|
||||
}
|
||||
|
||||
func getstripepaymentintentHandler(response http.ResponseWriter, request *http.Request) {
|
||||
name := getUserName(request)
|
||||
if name != "" {
|
||||
|
|
@ -51,10 +79,14 @@ func getstripepaymentintentHandler(response http.ResponseWriter, request *http.R
|
|||
|
||||
stripe.Key = getStripeKey()
|
||||
|
||||
// if customer does not already exist, create it...
|
||||
customerid := getCustomerid(name)
|
||||
|
||||
// define payment
|
||||
params := &stripe.PaymentIntentParams{
|
||||
Amount: stripe.Int64(abo_amount),
|
||||
Currency: stripe.String(string(stripe.CurrencyCHF)),
|
||||
ReceiptEmail: stripe.String(name),
|
||||
Customer: stripe.String(customerid),
|
||||
Amount: stripe.Int64(abo_amount),
|
||||
Currency: stripe.String(string(stripe.CurrencyCHF)),
|
||||
}
|
||||
params.AddMetadata("charge_data", charge_data[0])
|
||||
params.AddMetadata("login_user", name)
|
||||
|
|
@ -121,7 +153,7 @@ func stripeWebhookHandler(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
fmt.Printf("PaymentIntent was successful (charge_data: %s, amount: %d)!\n", paymentIntent.Metadata["charge_data"], paymentIntent.Amount)
|
||||
HandlePayment(paymentIntent.ReceiptEmail, paymentIntent.Metadata["charge_data"], paymentIntent.Amount)
|
||||
HandlePayment(paymentIntent.Metadata["login_user"], paymentIntent.Metadata["charge_data"], paymentIntent.Amount)
|
||||
// ... handle other event types
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unexpected event type: %s\n", event.Type)
|
||||
|
|
|
|||
|
|
@ -46,12 +46,6 @@
|
|||
<span>Kontakt</span>
|
||||
</div>
|
||||
</a>
|
||||
<a class="navbar-item" href="/order.html">
|
||||
<div style="position:relative">
|
||||
<span class="icon"><i class="fa fa-shopping-cart"></i></span>
|
||||
<span>Bestellen</span>
|
||||
</div>
|
||||
</a>
|
||||
{{ if ne .UserName "" }}
|
||||
<a class="navbar-item" href="/tracker.html">
|
||||
<div style="position:relative">
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue