Initial commit
This commit is contained in:
commit
10993b9209
|
|
@ -0,0 +1,3 @@
|
||||||
|
mini-beieli-web
|
||||||
|
nohup.out
|
||||||
|
database/
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# mini-beieli-web - Bienenstock Ueberwachung
|
||||||
|
|
||||||
|
Webapplikation, geschrieben in Golang.
|
||||||
|
|
||||||
|
Autor: Joerg Lehmann, nbit Informatik GmbH
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/securecookie"
|
||||||
|
"net/http"
|
||||||
|
"fmt"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cookie handling
|
||||||
|
|
||||||
|
var cookieHandler = securecookie.New(
|
||||||
|
securecookie.GenerateRandomKey(64),
|
||||||
|
securecookie.GenerateRandomKey(32))
|
||||||
|
|
||||||
|
func getUserName(request *http.Request) (userName string) {
|
||||||
|
if cookie, err := request.Cookie("session"); err == nil {
|
||||||
|
cookieValue := make(map[string]string)
|
||||||
|
if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
|
||||||
|
userName = cookieValue["name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return userName
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserNameHash(request *http.Request) (userName string) {
|
||||||
|
if cookie, err := request.Cookie("session"); err == nil {
|
||||||
|
cookieValue := make(map[string]string)
|
||||||
|
if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil {
|
||||||
|
userName = cookieValue["name"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasher := md5.New()
|
||||||
|
hasher.Write([]byte(userName))
|
||||||
|
return hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSession(userName string, response http.ResponseWriter) {
|
||||||
|
value := map[string]string{
|
||||||
|
"name": userName,
|
||||||
|
}
|
||||||
|
if encoded, err := cookieHandler.Encode("session", value); err == nil {
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: "session",
|
||||||
|
Value: encoded,
|
||||||
|
Path: "/",
|
||||||
|
}
|
||||||
|
http.SetCookie(response, cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearSession(response http.ResponseWriter) {
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: "session",
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
MaxAge: -1,
|
||||||
|
}
|
||||||
|
http.SetCookie(response, cookie)
|
||||||
|
}
|
||||||
|
|
||||||
|
// login handler
|
||||||
|
|
||||||
|
func loginHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
name := request.FormValue("email")
|
||||||
|
pass := request.FormValue("password")
|
||||||
|
redirectTarget := "/invalid_login.html"
|
||||||
|
//if name != "" && pass != "" {
|
||||||
|
if checkLoginCredentials(name,pass) {
|
||||||
|
// .. check credentials ..
|
||||||
|
logit(fmt.Sprintf("loginHandler: successful login for User %s",name))
|
||||||
|
setSession(name, response)
|
||||||
|
updateLoginTime(name)
|
||||||
|
redirectTarget = "/scales.html"
|
||||||
|
} else {
|
||||||
|
logit(fmt.Sprintf("loginHandler: invalid login for User %s",name))
|
||||||
|
}
|
||||||
|
http.Redirect(response, request, redirectTarget, 302)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// resetPassword handler
|
||||||
|
|
||||||
|
func resetPasswordHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
name := request.FormValue("email")
|
||||||
|
pass := request.FormValue("password")
|
||||||
|
redirectTarget := "/"
|
||||||
|
logit(fmt.Sprintf("resetPasswordHandler: request for User %s",name))
|
||||||
|
if name != "" && pass != "" {
|
||||||
|
if checkUserAvailable(name) {
|
||||||
|
http.Redirect(response, request, "/user_does_not_exist.html", 302)
|
||||||
|
} else {
|
||||||
|
updateUser(name,pass)
|
||||||
|
http.Redirect(response, request, redirectTarget, 302)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.Redirect(response, request, "/error_reset_password.html",302)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPassword handler
|
||||||
|
|
||||||
|
func setPasswordHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
name := getUserName(request)
|
||||||
|
pass := request.FormValue("password")
|
||||||
|
if name != "" && pass != "" {
|
||||||
|
if checkUserAvailable(name) {
|
||||||
|
http.Redirect(response, request, "/user_does_not_exist.html", 302)
|
||||||
|
} else {
|
||||||
|
updateUser(name,pass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// logout handler
|
||||||
|
|
||||||
|
func logoutHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
clearSession(response)
|
||||||
|
http.Redirect(response, request, "/", 302)
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirm handler
|
||||||
|
|
||||||
|
func confirmHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
confirm_id := request.URL.Query().Get("id")
|
||||||
|
logit(fmt.Sprintf("Confirm ID: %s\n",confirm_id))
|
||||||
|
confirmUser(confirm_id)
|
||||||
|
http.Redirect(response, request, "/", 302)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func logit(log_message string) {
|
||||||
|
log.Println(log_message)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"bytes"
|
||||||
|
"net/smtp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sendEmail(username,confirm_id 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("register@mini-beieli.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 + `
|
||||||
|
Subject: Passwortaenderung auf https://mini-beieli.ch, bitte bestaetigen
|
||||||
|
|
||||||
|
Lieber Benutzer von mini-beieli.ch
|
||||||
|
|
||||||
|
Sie haben soeben eine Passwortaenderung veranlasst. Bitte klicken Sie folgenden Link,
|
||||||
|
um die Registration abzuschliessen:
|
||||||
|
|
||||||
|
https://mini-beieli.ch/confirm?id=` + confirm_id + `
|
||||||
|
|
||||||
|
Bitte ignorieren Sie diese Meldung, falls die Aenderung nicht von Ihnen angefordert wurde!
|
||||||
|
|
||||||
|
Mit freundlichen Grüssen
|
||||||
|
--
|
||||||
|
mini-beieli.ch`
|
||||||
|
|
||||||
|
buf := bytes.NewBufferString(mail_message)
|
||||||
|
if _, err = buf.WriteTo(wc); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountData struct {
|
||||||
|
Full_name string
|
||||||
|
Phone string
|
||||||
|
Address string
|
||||||
|
Zip string
|
||||||
|
City string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scale struct {
|
||||||
|
Beielipi_id string
|
||||||
|
Beielipi_alias string
|
||||||
|
Id string
|
||||||
|
Alias string
|
||||||
|
My_sms_number string
|
||||||
|
Sms_alarm string
|
||||||
|
Last_alarm string
|
||||||
|
}
|
||||||
|
|
||||||
|
func serveTemplate(w http.ResponseWriter, r *http.Request) {
|
||||||
|
logit("Called URL: "+r.URL.Path)
|
||||||
|
// wennn kein File angegeben ist: index.html
|
||||||
|
if r.URL.Path == "/" {
|
||||||
|
r.URL.Path = "/index.html"
|
||||||
|
}
|
||||||
|
lp := path.Join("templates", "layout.html")
|
||||||
|
fp := path.Join("snippets", r.URL.Path)
|
||||||
|
|
||||||
|
// Return a 404 if the template doesn't exist
|
||||||
|
_, err := os.Stat(fp)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
logit("URL not found: " + fp)
|
||||||
|
fp = path.Join("snippets", "404.html")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.ParseFiles(lp, fp)
|
||||||
|
if err != nil {
|
||||||
|
// Log the detailed error
|
||||||
|
logit(err.Error())
|
||||||
|
// Return a generic "Internal Server Error" message
|
||||||
|
http.Error(w, http.StatusText(500), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var userName = getUserName(r)
|
||||||
|
|
||||||
|
t := time.Now()
|
||||||
|
var datetimestring = t.Format("20060102150405")
|
||||||
|
data := struct {
|
||||||
|
UserName string
|
||||||
|
DateTimeString string
|
||||||
|
} {
|
||||||
|
userName,
|
||||||
|
datetimestring,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tmpl.ExecuteTemplate(w, "layout", &data); err != nil {
|
||||||
|
logit(err.Error())
|
||||||
|
http.Error(w, http.StatusText(500), 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
initDB()
|
||||||
|
defer closeDB()
|
||||||
|
fs := http.FileServer(http.Dir("static"))
|
||||||
|
http.Handle("/static/", http.StripPrefix("/static/", fs))
|
||||||
|
http.Handle("/favicon.ico", fs)
|
||||||
|
http.HandleFunc("/", serveTemplate)
|
||||||
|
|
||||||
|
http.HandleFunc("/login", loginHandler)
|
||||||
|
http.HandleFunc("/reset_password", resetPasswordHandler)
|
||||||
|
http.HandleFunc("/set_password", setPasswordHandler)
|
||||||
|
http.HandleFunc("/logout", logoutHandler)
|
||||||
|
http.HandleFunc("/confirm", confirmHandler)
|
||||||
|
|
||||||
|
logit("Starting Web Application...")
|
||||||
|
http.ListenAndServe("127.0.0.1:4000", nil)
|
||||||
|
logit("Terminating Web Application...")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,235 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
"encoding/json"
|
||||||
|
"crypto/rand"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
"github.com/gomodule/redigo/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalPool *redis.Pool
|
||||||
|
var globalConn redis.Conn
|
||||||
|
|
||||||
|
|
||||||
|
const userPrefix string = "user:"
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// User is a simple user struct for this example
|
||||||
|
type User struct {
|
||||||
|
Email string `json:"email"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
NewPassword string `json:"new_password"`
|
||||||
|
ConfirmId string `json:"confirm_id"`
|
||||||
|
LastLogin string `json:"last_login"`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func setStruct(c redis.Conn, usr User) error {
|
||||||
|
|
||||||
|
// serialize User object to JSON
|
||||||
|
json, err := json.Marshal(usr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SET object
|
||||||
|
_, err = c.Do("SET", userPrefix+usr.Email, json)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStruct(c redis.Conn, username string) (User, error) {
|
||||||
|
|
||||||
|
usr := User{}
|
||||||
|
s, err := redis.String(c.Do("GET", userPrefix+username))
|
||||||
|
if err == redis.ErrNil {
|
||||||
|
logit("User does not exist:"+username)
|
||||||
|
} else if err != nil {
|
||||||
|
return usr, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(s), &usr)
|
||||||
|
|
||||||
|
return usr, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDB() {
|
||||||
|
|
||||||
|
// newPool returns a pointer to a redis.Pool
|
||||||
|
pool := newPool()
|
||||||
|
// get a connection from the pool (redis.Conn)
|
||||||
|
conn := pool.Get()
|
||||||
|
|
||||||
|
globalPool = pool
|
||||||
|
globalConn = conn
|
||||||
|
|
||||||
|
// wir machen einen Connection Test
|
||||||
|
ping(globalConn)
|
||||||
|
|
||||||
|
// Wir legen einen initialen Admin User an, falls es diesen noch nicht gibt
|
||||||
|
if checkUserAvailable("joerg.lehmann@nbit.ch") {
|
||||||
|
insertUser("joerg.lehmann@nbit.ch","changeme123","Y");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeDB() {
|
||||||
|
globalConn.Close()
|
||||||
|
globalPool.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUserAvailable(username string) bool {
|
||||||
|
logit("checkUserAvailable: User: "+username)
|
||||||
|
_, err := redis.String(globalConn.Do("GET", userPrefix+username))
|
||||||
|
if (err == redis.ErrNil) {
|
||||||
|
logit("User does not exist and is therefore available:"+username)
|
||||||
|
return true
|
||||||
|
} else if err != nil {
|
||||||
|
logit("checkUserAvailable: Error to query Key Value Store")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func randString(n int) string {
|
||||||
|
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
var bytes = make([]byte, n)
|
||||||
|
rand.Read(bytes)
|
||||||
|
for i, b := range bytes {
|
||||||
|
bytes[i] = alphanum[b % byte(len(alphanum))]
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertUser(username,password,is_admin string) {
|
||||||
|
logit("insertUser: "+username)
|
||||||
|
pwd := []byte(password)
|
||||||
|
hashedPassword,err := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
logit("insertUser: Error with bcrypt.GenerateFromPassword, User: "+username)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
confirm_id := ""
|
||||||
|
|
||||||
|
_, err = globalConn.Do("HMSET", userPrefix+username, "password", string(hashedPassword), "new_password", string(hashedPassword), "confirm_id", confirm_id, "last_login", "")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logit("insertUser: Error inserting User: "+username)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUser(username,password string) {
|
||||||
|
logit("updateUser: "+username)
|
||||||
|
pwd := []byte(password)
|
||||||
|
hashedPassword,err := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost)
|
||||||
|
if err != nil {
|
||||||
|
logit("updateUser: Error with bcrypt.GenerateFromPassword, User: "+username)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
confirm_id := randString(30)
|
||||||
|
|
||||||
|
_, err = globalConn.Do("HMSET", userPrefix+username, "new_password", string(hashedPassword), "confirm_id", confirm_id)
|
||||||
|
if err != nil {
|
||||||
|
logit("updateUser: Error updateing User: "+username)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = globalConn.Do("SET", confirmPrefix+confirm_id,username)
|
||||||
|
if err != nil {
|
||||||
|
logit("updateUser: Error inserting confirm_id: "+confirm_id+": "+username)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEmail(username,confirm_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLoginCredentials(username,password string) bool {
|
||||||
|
logit("checkLoginCredentials: called with username,password: "+username+","+password)
|
||||||
|
pwd, err := redis.String(globalConn.Do("HGET", userPrefix+username, "password"))
|
||||||
|
if err == nil {
|
||||||
|
cid, err := redis.String(globalConn.Do("HGET", userPrefix+username, "confirm_id"))
|
||||||
|
if err == nil {
|
||||||
|
logit("checkLoginCredentials: cid: "+cid)
|
||||||
|
if !(err != nil && cid != "") {
|
||||||
|
logit("checkLoginCredentials: pwd: "+pwd)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashedPassword := []byte(pwd)
|
||||||
|
err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateLoginTime(username string) {
|
||||||
|
_, err := globalConn.Do("HSET", userPrefix+username, "last_login", time.Now().UTC().Format("2006-01-02 15:04:05"))
|
||||||
|
if err != nil {
|
||||||
|
logit("updateUser: Error updateing User: "+username)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirmUser(confirm_id string) {
|
||||||
|
u, err := redis.String(globalConn.Do("GET", confirmPrefix+confirm_id))
|
||||||
|
if err != nil {
|
||||||
|
logit("confirmUser: Error with searching confirm_id: "+confirm_id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
new_password, err := redis.String(globalConn.Do("HGET", userPrefix+u, "new_password"))
|
||||||
|
if err != nil {
|
||||||
|
logit("confirmUser: Error with getting new_password: "+u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = globalConn.Do("HMSET", userPrefix+u, "confirm_id", "", "password", new_password)
|
||||||
|
if err != nil {
|
||||||
|
logit("confirmUser: Error updateing User: "+u)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = globalConn.Do("DEL", confirmPrefix+confirm_id)
|
||||||
|
if err != nil {
|
||||||
|
logit("confirmUser: Error deleting confirm_id: "+confirm_id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
<div class="notification is-danger">
|
||||||
|
<strong>Diese Seite existiert nicht</strong>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
<p class="title is-4">Kontakt</p>
|
||||||
|
<p>nbit Informatik GmbH<br />
|
||||||
|
Kirchweg 2<br />
|
||||||
|
3510 Konolfingen<br />
|
||||||
|
<br />
|
||||||
|
+41 31 792 00 40<br />
|
||||||
|
<a href='mailto:i%6Efo@%6Ebit.%63h'>info@nbit.ch</a>
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
{{define "header_additions"}}
|
||||||
|
{{end}}
|
||||||
|
{{define "body_content"}}
|
||||||
|
<p class="title is-4">Die etwas andere Bienenstockwaage...</p>
|
||||||
|
<article class="message is-danger">
|
||||||
|
<div class="message-body">
|
||||||
|
Aktuell noch in Entwicklung, kommen Sie später noch einmal vorbei...
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{ if ne .UserName "" }}
|
||||||
|
<p>
|
||||||
|
<strong>Ich will weitere bestellen!</strong>
|
||||||
|
<span class="icon"><i class="fa fa-arrow-right"></i></span>
|
||||||
|
Hier geht's zum <a href="/order.html">Bestellformular</a>
|
||||||
|
</p>
|
||||||
|
{{ else }}
|
||||||
|
<p>
|
||||||
|
<strong>Ich will auch eine!</strong>
|
||||||
|
<span class="icon"><i class="fa fa-arrow-right"></i></span>
|
||||||
|
Hier geht's zum <a href="/order.html">Bestellformular</a>
|
||||||
|
</p>
|
||||||
|
<p> </p>
|
||||||
|
<p>
|
||||||
|
<strong>Ich habe bereits eine (oder mehrere)</strong>
|
||||||
|
<span class="icon"><i class="fa fa-arrow-right"></i></span>
|
||||||
|
Hier geht's zum <a href="/login.html">Login</a>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
<div class="notification is-danger">
|
||||||
|
<strong>Ungültiges Login!</strong>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
{{ if ne .UserName "" }}
|
||||||
|
Sie sind bereits eingeloggt!
|
||||||
|
{{ else }}
|
||||||
|
<form class="form-signin" id="login-form" action="/login">
|
||||||
|
<div class="column is-auto">
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-4">
|
||||||
|
<h1 class="title">Login</h1>
|
||||||
|
<div class="field">
|
||||||
|
<p class="control has-icons-left has-icons-right">
|
||||||
|
<input id="email" name="email" class="input is-success is-focused" type="text" placeholder="Email">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fa fa-envelope"></i>
|
||||||
|
</span>
|
||||||
|
<span class="icon is-small is-right">
|
||||||
|
<i class="fa fa-check"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<p class="control has-icons-left has-icons-right">
|
||||||
|
<input id="password" name="password" class="input" type="password" placeholder="Password">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fa fa-lock"></i>
|
||||||
|
</span>
|
||||||
|
<span class="icon is-small is-right">
|
||||||
|
<i class="fa fa-check"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="has-text-centered">
|
||||||
|
<a href="/reset_password.html">Passwort vergessen?</a><br/>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div id="errorbox" class="notification is-danger is-size-7-mobile" style="display: none;">
|
||||||
|
</div>
|
||||||
|
<div class="has-text-centered">
|
||||||
|
<input id="login-button" name="login-button" type="submit" class="form-button button is-primary is-centered" value="Anmelden"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
<p class="title is-4">Bestellformular</p>
|
||||||
|
<article class="message is-danger">
|
||||||
|
<div class="message-body">
|
||||||
|
Aktuell noch in Entwicklung, kommen Sie später noch einmal vorbei...
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
{{ if ne .UserName "" }}
|
||||||
|
Sie sind bereits eingeloggt!
|
||||||
|
{{ else }}
|
||||||
|
<p class="title is-4">Passwort zurücksetzen</p>
|
||||||
|
<div class="notification is-info">
|
||||||
|
<p>Hier können Sie ein neues Passwort setzen. Sie erhalten anschliessend eine Meldung zur Bestätigung zugesendet. Das neue Passwort wird erst gültig, wenn Sie die Bestätigung durchgeführt haben.</p>
|
||||||
|
</div>
|
||||||
|
<form class="form-signin" id="reset-password-form" action="/reset_password">
|
||||||
|
<div class="column is-auto">
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-4">
|
||||||
|
<div class="field">
|
||||||
|
<p class="control has-icons-left has-icons-right">
|
||||||
|
<input id="email" name="email" class="input is-success is-focused" type="text" placeholder="Email">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fa fa-envelope"></i>
|
||||||
|
</span>
|
||||||
|
<span class="icon is-small is-right">
|
||||||
|
<i class="fa fa-check"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<p class="control has-icons-left has-icons-right">
|
||||||
|
<input id="password" name="password" class="input" type="password" placeholder="Password">
|
||||||
|
<span class="icon is-small is-left">
|
||||||
|
<i class="fa fa-lock"></i>
|
||||||
|
</span>
|
||||||
|
<span class="icon is-small is-right">
|
||||||
|
<i class="fa fa-check"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div id="errorbox" class="notification is-danger is-size-7-mobile" style="display: none;">
|
||||||
|
</div>
|
||||||
|
<div class="has-text-centered">
|
||||||
|
<input id="login-button" name="login-button" type="submit" class="form-button button is-primary is-centered" value="Passwort zurücksetzen"></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
{{ if ne .UserName "" }}
|
||||||
|
<p>Datenauswertung</p>
|
||||||
|
{{ else }}
|
||||||
|
<h4>Bitte zuerst <a href="login.html">einloggen</a></h4>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{define "body_content"}}
|
||||||
|
<div class="notification is-danger">
|
||||||
|
<strong>Benutzer existiert nicht!</strong>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,29 @@
|
||||||
|
.image.is-10by1 img, .image.is-20by3 img {
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image.is-10by1 {
|
||||||
|
padding-top: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image.is-20by3 {
|
||||||
|
padding-top: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 0px 0 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signup-box {
|
||||||
|
margin: auto;
|
||||||
|
width: 300px;
|
||||||
|
background: rgba(255,255,255,0.05);
|
||||||
|
border: 1px solid rgba(255,255,255,0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,70 @@
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
$("#email").focus();
|
||||||
|
|
||||||
|
// Check for click events on the navbar burger icon
|
||||||
|
$(".navbar-burger").click(function() {
|
||||||
|
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||||
|
$(".navbar-burger").toggleClass("is-active");
|
||||||
|
$(".navbar-menu").toggleClass("is-active");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Login Button
|
||||||
|
$("#login-button").click(function(e){
|
||||||
|
//alert(true);
|
||||||
|
$("#login-form").submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Login Form
|
||||||
|
$("#login-form-blabla").submit(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
var formData = {
|
||||||
|
next: $("#email").val(),
|
||||||
|
email: $("#email").val(),
|
||||||
|
password: $("#password").val(),
|
||||||
|
csrf_token: $("#csrf_token").val(),
|
||||||
|
next: $("#next").val()
|
||||||
|
};
|
||||||
|
|
||||||
|
//console.log(formData);
|
||||||
|
// send ajax
|
||||||
|
$.ajax({
|
||||||
|
url: '/login', // url where to submit the request
|
||||||
|
type : "POST", // type of action POST || GET
|
||||||
|
dataType : 'json', // data type
|
||||||
|
contentType: 'application/json',
|
||||||
|
data : JSON.stringify(formData), // post data || get data
|
||||||
|
success : function(result) {
|
||||||
|
// you can see the result from the console
|
||||||
|
// tab of the developer tools
|
||||||
|
console.log('SUCCESS');
|
||||||
|
console.log(result);
|
||||||
|
window.location.replace("/");
|
||||||
|
},
|
||||||
|
error: function(result) {
|
||||||
|
//console.log(xhr, resp, text);
|
||||||
|
console.log('ERROR');
|
||||||
|
console.log(result);
|
||||||
|
var errortext = '<ul style="list-style-type:disc">';
|
||||||
|
a = result.responseJSON.response.errors.email;
|
||||||
|
if (a != undefined) {
|
||||||
|
for (i=0; i < a.length; ++i) {
|
||||||
|
errortext = errortext + "<li>" + a[i] + "</li>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a = result.responseJSON.response.errors.password;
|
||||||
|
if (a != undefined) {
|
||||||
|
for (i=0; i < a.length; ++i) {
|
||||||
|
errortext = errortext + "<li>" + a[i] + "</li>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errortext = errortext + "</ul>";
|
||||||
|
$('#errorbox').html(errortext);
|
||||||
|
$('#errorbox').show();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
[Unit]
|
||||||
|
Description=mini-beieli web service
|
||||||
|
After=syslog.target
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=beieli
|
||||||
|
Group=beieli
|
||||||
|
WorkingDirectory=/home/beieli/mini-beieli-web
|
||||||
|
ExecStart=/home/beieli/mini-beieli-web/mini-beieli-web
|
||||||
|
Restart=always
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
{{define "header_additions"}}{{end}}
|
||||||
|
{{define "layout"}}<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>mini-beieli.ch - die besondere Bienenstockwaage</title>
|
||||||
|
<link rel="stylesheet" href="/static/css/bulma-0.7.4/bulma.min.css">
|
||||||
|
<link rel="stylesheet" href="static/css/mini-beieli-web.css">
|
||||||
|
|
||||||
|
<script defer src="/static/js/fontawesome-5.1.0/all.js"></script>
|
||||||
|
<script src="/static/js/jquery-3.3.1/jquery.min.js"></script>
|
||||||
|
{{template "header_additions" . }}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<figure class="image is-hidden-mobile is-20by3">
|
||||||
|
<img src="/static/images/bees-banner-1000x150.jpg" alt="mini-beieli Banner" >
|
||||||
|
</figure>
|
||||||
|
<figure class="image is-hidden-tablet is-3by1">
|
||||||
|
<img src="/static/images/bees-banner-450x150.jpg" alt="mini-beieli Banner" >
|
||||||
|
</figure>
|
||||||
|
<nav class="navbar" role="navigation" aria-label="main navigation">
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<a class="navbar-item" href="/">
|
||||||
|
<img src="/static/images/bee-logo-28.png" alt="mini-beieli Logo" width="28" height="28">
|
||||||
|
</a>
|
||||||
|
<a role="button" class="navbar-burger" data-target="navMenu" aria-label="menu" aria-expanded="false">
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu" id="navMenu">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<a class="navbar-item" href="/">
|
||||||
|
<div style="position:relative">
|
||||||
|
<span class="icon"><i class="fa fa-home"></i></span>
|
||||||
|
<span>Home</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a class="navbar-item" href="/contact.html">
|
||||||
|
<div style="position:relative">
|
||||||
|
<span class="icon"><i class="fa fa-address-card"></i></span>
|
||||||
|
<span>Kontakt</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{ if ne .UserName "" }}
|
||||||
|
<a class="navbar-item" href="/scales.html">
|
||||||
|
<div style="position:relative">
|
||||||
|
<span class="icon"><i class="fa fa-balance-scale"></i></span>
|
||||||
|
<span>Meine Waagen</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
{{ if ne .UserName "" }}
|
||||||
|
<a class="navbar-item" href="/logout">
|
||||||
|
<div style="position:relative">
|
||||||
|
<span class="icon"><i class="fa fa-sign-out-alt"></i></span>
|
||||||
|
<span>Logout {{ .UserName }}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{ else }}
|
||||||
|
<a class="navbar-item" href="/login.html">
|
||||||
|
<div style="position:relative">
|
||||||
|
<span class="icon"><i class="fa fa-sign-in-alt"></i></span>
|
||||||
|
<span>Login</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<hr />
|
||||||
|
{{template "body_content" . }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script src="/static/js/mini-beieli-web.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{end}}
|
||||||
Loading…
Reference in New Issue