Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Edit card #72

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
315 changes: 313 additions & 2 deletions http.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package main

import (
"bytes"
"net/http"

"context"
"encoding/hex"
"github.com/gorilla/mux"
"net/http"
"time"
)

// GetLoginPage renders the login page to the user.
Expand Down Expand Up @@ -203,6 +205,309 @@ func (k *Kasse) GetLogout(res http.ResponseWriter, req *http.Request) {
}
}

// GetAddCard renders the add card dialog. The rendered template contains an instruction for the browser to connect to AddCardEvent and listen for the card UID on the next swipe
func (k *Kasse) GetAddCard(res http.ResponseWriter, req *http.Request) {
session, err := k.sessions.Get(req, "nnev-kasse")
if err != nil {
http.Redirect(res, req, "/login.html", 302)
return
}
ui, ok := session.Values["user"]
if !ok {
http.Redirect(res, req, "/login.html", 302)
return
}

user := ui.(User)
data := struct {
User *User
Description string
Message string
}{
User: &user,
Description: "",
Message: "",
}

if err := ExecuteTemplate(res, TemplateInput{Title: "ccchd Kasse", Body: "add_card.html", Data: &data}); err != nil {
k.log.Println("Could not render template:", err)
http.Error(res, "Internal error", 500)
return
}
}

// AddCardEvent returns a json containing the next swiped card UID. The UID is obtained using a channel which is written by the HandleCard method
func (k *Kasse) AddCardEvent(res http.ResponseWriter, req *http.Request) {
session, err := k.sessions.Get(req, "nnev-kasse")
if err != nil {
http.Redirect(res, req, "/login.html", 302)
return
}
_, ok := session.Values["user"]
if !ok {
http.Redirect(res, req, "/login.html", 302)
return
}

res.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
res.WriteHeader(http.StatusOK)

// Only one go routine can listen on the next card swipe. Tell the client, when it obtains the lock
k.registration.Lock()
defer k.registration.Unlock()
if _, err := res.Write([]byte("event: lock\ndata: lock\n\n")); err != nil {
k.log.Println("Could not write: ", err)
}
if f, ok := res.(http.Flusher); ok {
f.Flush()
}

k.log.Println("Waiting for Card")

// Read from the channel for one minute. If the timeout is exceeded and the registration window is still open on the client, the browser reconnects anyway
var uid []byte
ctx, cancel := context.WithTimeout(req.Context(), 1*time.Minute)
defer cancel()
select {
case uid = <-k.card:
case <-ctx.Done():
http.Error(res, ctx.Err().Error(), http.StatusRequestTimeout)
return
}

// Send card UID in hexadecimal to client
uidString := hex.EncodeToString(uid)
k.log.Println("Card UID obtained! Card uid is", uidString)

if _, err := res.Write([]byte("event: card\ndata: " + uidString + "\n\n")); err != nil {
k.log.Println("Could not write: ", err)
}

if f, ok := res.(http.Flusher); ok {
f.Flush()
}
}

// PostAddCard creates a new Card for the POSTing user
func (k *Kasse) PostAddCard(res http.ResponseWriter, req *http.Request) {
session, err := k.sessions.Get(req, "nnev-kasse")
if err != nil {
http.Redirect(res, req, "/login.html", 302)
return
}
ui, ok := session.Values["user"]
if !ok {
http.Redirect(res, req, "/login.html", 302)
return
}

user := ui.(User)

description := req.FormValue("description")
uidString := req.FormValue("uid")

renderError := func(message string) {
data := struct {
User *User
Description string
Message string
}{
User: &user,
Description: description,
Message: message,
}

if err := ExecuteTemplate(res, TemplateInput{Title: "ccchd Kasse", Body: "add_card.html", Data: &data}); err != nil {
k.log.Println("Could not render template:", err)
http.Error(res, "Internal error", 500)
return
}
}

if len(uidString) == 0 {
renderError("Please swipe Card to register")
return
}

uid, err := hex.DecodeString(uidString)
if err != nil {
renderError("Hexadecimal UID could not be decoded")
return
}

_, err = k.AddCard(uid, &user, description)
if err != nil {
if err == ErrCardExists {
renderError("Card is already registered")
} else {
renderError("Card could not be added")
}
return
}

http.Redirect(res, req, "/", 302)
}

// PostRemoveCard removes a card for the POSTing user
func (k *Kasse) PostRemoveCard(res http.ResponseWriter, req *http.Request) {
session, err := k.sessions.Get(req, "nnev-kasse")
if err != nil {
http.Redirect(res, req, "/login.html", 302)
return
}
ui, ok := session.Values["user"]
if !ok {
http.Redirect(res, req, "/login.html", 302)
return
}

user := ui.(User)

err = req.ParseForm()
if err != nil {
http.Error(res, "Internal error", http.StatusBadRequest)
}
uidString := req.Form.Get("uid")

uid, err := hex.DecodeString(uidString)
if err != nil {
http.Redirect(res, req, "/", http.StatusNotFound)
return
}

card, err := k.GetCard(uid, user)
if err != nil {
http.Redirect(res, req, "/", http.StatusNotFound)
return
}

err = k.RemoveCard(uid, &user)
if err != nil {
data := struct {
Card *Card
Message string
}{
Card: card,
Message: err.Error(),
}

if err := ExecuteTemplate(res, TemplateInput{Title: "ccchd Kasse", Body: "edit_card.html", Data: &data}); err != nil {
k.log.Println("Could not render template:", err)
http.Error(res, "Internal error", 500)
return
}
} else {
http.Redirect(res, req, "/", http.StatusFound)
return
}

http.Redirect(res, req, "/", 302)
}

// PostEditCard renders an edit dialog for a given card
func (k *Kasse) PostEditCard(res http.ResponseWriter, req *http.Request) {
session, err := k.sessions.Get(req, "nnev-kasse")
if err != nil {
http.Redirect(res, req, "/login.html", 302)
return
}
ui, ok := session.Values["user"]
if !ok {
http.Redirect(res, req, "/login.html", 302)
return
}

user := ui.(User)

uids, ok := req.URL.Query()["uid"]
if !ok || len(uids[0]) < 1 {
http.Error(res, "No uid given", http.StatusBadRequest)
return
}
uidString := uids[0]

uid, err := hex.DecodeString(uidString)
if err != nil {
http.Redirect(res, req, "/", http.StatusNotFound)
return
}

card, err := k.GetCard(uid, user)
if err != nil {
http.Redirect(res, req, "/", http.StatusNotFound)
return
}

data := struct {
Card *Card
Message string
}{
Card: card,
Message: "",
}

if err := ExecuteTemplate(res, TemplateInput{Title: "ccchd Kasse", Body: "edit_card.html", Data: &data}); err != nil {
k.log.Println("Could not render template:", err)
http.Error(res, "Internal error", 500)
return
}
}

// PostUpdateCard saves card changes and redirects to the dashboard
func (k *Kasse) PostUpdateCard(res http.ResponseWriter, req *http.Request) {
session, err := k.sessions.Get(req, "nnev-kasse")
if err != nil {
http.Redirect(res, req, "/login.html", 302)
return
}
ui, ok := session.Values["user"]
if !ok {
http.Redirect(res, req, "/login.html", 302)
return
}

user := ui.(User)

err = req.ParseForm()
if err != nil {
http.Error(res, "Internal error", http.StatusBadRequest)
}
uidString := req.Form.Get("uid")

uid, err := hex.DecodeString(uidString)
if err != nil {
http.Redirect(res, req, "/", http.StatusNotFound)
}

description := req.Form.Get("description")

err = k.UpdateCard(uid, &user, description)
if err != nil {
card, err2 := k.GetCard(uid, user)
message := err.Error()
if err2 != nil {
message = err2.Error()
}

data := struct {
Card *Card
Message string
}{
Card: card,
Message: message,
}

if err := ExecuteTemplate(res, TemplateInput{Title: "ccchd Kasse", Body: "edit_card.html", Data: &data}); err != nil {
k.log.Println("Could not render template:", err)
http.Error(res, "Internal error", 500)
return
}
}

http.Redirect(res, req, "/", http.StatusFound)
return
}

// Handler returns a http.Handler for the webinterface.
func (k *Kasse) Handler() http.Handler {
r := mux.NewRouter()
Expand All @@ -213,5 +518,11 @@ func (k *Kasse) Handler() http.Handler {
r.Methods("GET").Path("/logout.html").HandlerFunc(k.GetLogout)
r.Methods("GET").Path("/create_user.html").HandlerFunc(k.GetNewUserPage)
r.Methods("POST").Path("/create_user.html").HandlerFunc(k.PostNewUserPage)
r.Methods("GET").Path("/add_card.html").HandlerFunc(k.GetAddCard)
r.Methods("POST").Path("/add_card.html").HandlerFunc(k.PostAddCard)
r.Methods("GET").Path("/add_card_event").HandlerFunc(k.AddCardEvent)
r.Methods("POST").Path("/remove_card.html").HandlerFunc(k.PostRemoveCard)
r.Methods("GET").Path("/edit_card.html").HandlerFunc(k.PostEditCard)
r.Methods("POST").Path("/update_card.html").HandlerFunc(k.PostUpdateCard)
return r
}
Loading