From e7d62180fe00a7e198782abafa14650a0bd1dfb0 Mon Sep 17 00:00:00 2001 From: palo Date: Sat, 28 Oct 2023 00:44:36 +0200 Subject: [PATCH] Added main pages, print and order_pizzas, that interact with the API --- README.md | 17 ++++++ api.go | 106 ++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ main.go | 126 +++++++++++++++++++++++++++++++++++++++++++ templates/index.html | 21 ++++++++ templates/pizza.html | 34 ++++++++++++ 6 files changed, 307 insertions(+) create mode 100644 api.go create mode 100644 go.mod create mode 100644 main.go create mode 100644 templates/index.html create mode 100644 templates/pizza.html diff --git a/README.md b/README.md index 36844fb..075c9c7 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ # Scontrini Web Client + +Golang simple http server that interfaces with Scontrini-Daemon + +## Usage + +Compiling + +```sh +$ go build +$ chmod +x ./Scontrini-Web-Client +``` + +Executing + +```sh +$ ./Scontrini-Web-Client +``` diff --git a/api.go b/api.go new file mode 100644 index 0000000..16c30c0 --- /dev/null +++ b/api.go @@ -0,0 +1,106 @@ +package main + +import ( + "encoding/binary" + "encoding/json" + "fmt" + "net" +) + +var ( + printer_server_IP = net.ParseIP("127.0.0.1") + printer_server_port = 4444 +) + +// Commands to give to the API +type JSONCommand interface { + PrintCommand | PizzaCommand | GetPizzasCommand +} + +type PrintCommand struct { + Command string `json:"command"` + Text string `json:"text"` +} + +type PizzaCommand struct { + Command string `json:"command"` + Pizzas []Pizza `json:"pizzas"` +} + +type GetPizzasCommand struct { + Command string `json:"command"` +} + +// What to receive from the API +type JSONReceived interface { + ReceivePizzas +} + +type ReceivePizzas struct { + Pizzas []Pizza `json:"pizzas"` +} + +/** +* All messages from and to the API must have the first 4 bytes set to represent +* the length of the message, followed by the message itself + */ +func sendJSONToServer[T JSONCommand](socket *net.TCPConn, json_command T) (*net.TCPConn, error) { + json_data, err := json.Marshal(json_command) + + if err != nil { + return nil, err + } + + // in case a socket doesn't already exist create a new one + if socket == nil { + socket, err = net.DialTCP("tcp4", nil, &net.TCPAddr{printer_server_IP, printer_server_port, ""}) + } + + if err != nil { + return nil, err + } + + // the first 4 bytes must be the length of the message + to_send := make([]byte, 4) + binary.LittleEndian.PutUint32(to_send, uint32(len(json_data))) + + to_send = append(to_send, json_data...) + socket.Write([]byte(to_send)) + + return socket, nil +} + +func receiveJSONFromServer[T JSONReceived](socket *net.TCPConn, json_received *T) (*net.TCPConn, error) { + var err error + + // in case a socket doesn't already exist create a new one + if socket == nil { + fmt.Println("nil") + socket, err = net.DialTCP("tcp4", nil, &net.TCPAddr{printer_server_IP, printer_server_port, ""}) + } + + if err != nil { + return nil, err + } + + // the first 4 bytes are the length of the message + b := make([]byte, 4) + _, err = socket.Read(b[0:]) + + if err != nil { + return nil, err + } + + body_length := binary.LittleEndian.Uint32(b) + + body := make([]byte, body_length) + _, err = socket.Read(body[0:]) + + err = json.Unmarshal(body, json_received) + + if err != nil { + return nil, err + } + + return socket, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b1babe3 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module Scontrini-Web-Client + +go 1.19 diff --git a/main.go b/main.go new file mode 100644 index 0000000..6549e82 --- /dev/null +++ b/main.go @@ -0,0 +1,126 @@ +package main + +import ( + "errors" + "fmt" + "html/template" + "net/http" + "os" + "strconv" +) + +var ( + templates = template.Must(template.ParseFiles("templates/index.html", "templates/pizza.html")) +) + +type Pizza struct { + Name string `json:"name"` + Price int `json:"price"` + Quantity int `json:"quantity"` +} + +/** +* Handler function for "/" + */ +func indexHandler(w http.ResponseWriter, r *http.Request) { + templates.ExecuteTemplate(w, "index.html", nil) +} + +/** +* Handler function for "/print", only accepts POST requests +* +* Tells the API to print the text specified in its Form + */ +func printHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + text := r.PostFormValue("text") + + json_command := PrintCommand{Command: "print", Text: text} + _, err := sendJSONToServer(nil, json_command) + + if err != nil { + fmt.Fprintf(w, "Error while connecting to the printer server") + return + } + + http.Redirect(w, r, "/", http.StatusSeeOther) +} + +/** +* Handler function for "/pizza", only accepts POST requests +* +* Tells the API to print the pizza order specified in its Form + */ +func pizzaHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + pizzas := make([]Pizza, 0) + + r.ParseForm() + for key, _ := range r.Form { + // only put in the pizzas that have a quantity bigger than 0 + if key != "submit" && r.Form[key][0] != "0" { + quantity, _ := strconv.ParseInt(r.Form[key][0], 10, 0) + pizzas = append(pizzas, Pizza{key, 0, int(quantity)}) + } + } + + json_command := PizzaCommand{Command: "pizza", Pizzas: pizzas} + _, err := sendJSONToServer(nil, json_command) + + if err != nil { + fmt.Fprintf(w, "Error while connecting to the printer server") + return + } + + http.Redirect(w, r, "/order_pizzas", http.StatusSeeOther) +} + +/** +* Handler function for "/order_pizzas" +* +* Asks the API for the list of all pizzas then returns a formatted HTML page + */ +func orderPizzasHandler(w http.ResponseWriter, r *http.Request) { + json_command := GetPizzasCommand{Command: "get-pizzas"} + socket, err := sendJSONToServer(nil, json_command) + + if err != nil { + fmt.Fprintf(w, "Error while connecting to the printer server") + return + } + + pizzas := ReceivePizzas{} + _, err = receiveJSONFromServer(socket, &pizzas) + + if err != nil { + fmt.Fprintf(w, "Error while connecting to the printer server") + return + } + + templates.ExecuteTemplate(w, "pizza.html", pizzas.Pizzas) +} + +func main() { + mux := http.NewServeMux() + + mux.HandleFunc("/", indexHandler) + mux.HandleFunc("/print", printHandler) + mux.HandleFunc("/pizza", pizzaHandler) + mux.HandleFunc("/order_pizzas", orderPizzasHandler) + + err := http.ListenAndServe(":8080", mux) + if errors.Is(err, http.ErrServerClosed) { + fmt.Println("Server closed") + } else if err != nil { + fmt.Printf("Error starting server: %s\n", err) + os.Exit(1) + } +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..452f745 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,21 @@ + + + + + Scontrini + + + + +

SCONTRINI

+ +

Cosa stampare:

+
+ + +
+ + + + + diff --git a/templates/pizza.html b/templates/pizza.html new file mode 100644 index 0000000..6f1c538 --- /dev/null +++ b/templates/pizza.html @@ -0,0 +1,34 @@ + + + + + Scontrini + + + + +

PIZZE

+ +
+ + {{ range . }} + + + + + + {{ end }} +
+ {{ .Name }} + + {{ .Price }} + + +
+ +
+ + + + +