diff --git a/.air.toml b/.air.toml
deleted file mode 100644
index c546c22..0000000
--- a/.air.toml
+++ /dev/null
@@ -1,4 +0,0 @@
-[build]
-bin = "tmp/cafe-plaetschwiesle"
-cmd = "go build -o tmp/cafe-plaetschwiesle cmd/cafe-plaetschwiesle/cafe-plaetschwiesle.go"
-exclude_dir = [".gitlab", "docker", "scripts", "web", "storage", "tmp", "docs"]
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 670887b..a2660bc 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,17 +1,9 @@
stages:
- - test
- build
- deploy
include:
- local: .gitlab/_common.gitlab-ci.yml
- local: .gitlab/_rules.gitlab-ci.yml
- - local: /.gitlab/test.gitlab-ci.yml
- local: /.gitlab/build.gitlab-ci.yml
- local: /.gitlab/deploy.gitlab-ci.yml
- - template: Jobs/Secret-Detection.gitlab-ci.yml
- - template: Security/SAST.gitlab-ci.yml
-
-secret_detection:
- rules: !reference [.rules:default, rules]
- stage: test
diff --git a/.gitlab/_common.gitlab-ci.yml b/.gitlab/_common.gitlab-ci.yml
index 2cd2c34..fb6d21b 100644
--- a/.gitlab/_common.gitlab-ci.yml
+++ b/.gitlab/_common.gitlab-ci.yml
@@ -12,13 +12,3 @@ image: docker:$DOCKER_VERSION-git
.login_registry:
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
-
-.go-cache:
- variables:
- GOPATH: $CI_PROJECT_DIR/.go
- before_script:
- - mkdir -p .go
- - export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
- cache:
- paths:
- - .go/pkg/mod/
diff --git a/.gitlab/build.gitlab-ci.yml b/.gitlab/build.gitlab-ci.yml
index 35d56e0..d34188e 100644
--- a/.gitlab/build.gitlab-ci.yml
+++ b/.gitlab/build.gitlab-ci.yml
@@ -15,7 +15,7 @@ build_release:
--build-arg GOLANG_VERSION=$GOLANG_VERSION
--build-arg NODE_VERSION=$NODE_VERSION
--build-arg ALPINE_VERSION=$ALPINE_VERSION
- --build-arg VERSION=$CI_COMMIT_TAG
+ --build-arg APP_VERSION=$CI_COMMIT_TAG
--build-arg BUILD_TIME=$CI_JOB_STARTED_AT
--tag $CURRENT_IMAGE
--tag $LATEST_IMAGE
diff --git a/.gitlab/test.gitlab-ci.yml b/.gitlab/test.gitlab-ci.yml
deleted file mode 100644
index 41c6216..0000000
--- a/.gitlab/test.gitlab-ci.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-unit_tests:
- rules: !reference [.rules:default, rules]
- stage: test
- image: golang:$GOLANG_VERSION-alpine
- extends:
- - .go-cache
- script:
- - ./scripts/swagger.sh install
- - ./scripts/swagger.sh init
- - go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname -- ./... -coverprofile=profile.cov
- - go tool cover -func profile.cov
- coverage: '/\(statements\)(?:\s+)?(\d+(?:\.\d+)?%)/'
- artifacts:
- when: always
- reports:
- junit: report.xml
diff --git a/README.md b/README.md
index 218b990..1d55a17 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,76 @@
# Café Plätschwiesle
-
-
-
+
+
+
-[](https://gitlab.unjx.de/flohoss/cafe-plaetschwiesle/-/commits/main)
-[](https://gitlab.unjx.de/flohoss/cafe-plaetschwiesle/-/commits/main)
+## docker-compose example
+
+```yaml
+services:
+ cafe:
+ image: ghcr.io/flohoss/cafe-plaetschwiesle:latest
+ container_name: cafe
+ restart: unless-stopped
+ environment:
+ - PUID=1000
+ - PGID=1000
+ - ALLOWED_HOSTS=http://localhost:5000,https://home.example.com
+ - SWAGGER=true
+ - LOG_LEVEL=info # trace,debug,info,warn,error,fatal,panic
+ volumes:
+ - ./storage:/app/storage
+ ports:
+ - '127.0.0.1:5000:5000'
+```
+
+## docker-compose example with MariaDB as database
+
+```yaml
+networks:
+ net:
+ external: false
+
+services:
+ cafe-db:
+ image: lscr.io/linuxserver/mariadb:latest
+ container_name: cafe-db
+ restart: unless-stopped
+ environment:
+ - PUID=1000
+ - PGID=1000
+ - MYSQL_ROOT_PASSWORD=root
+ - TZ=Europe/Berlin
+ - MYSQL_DATABASE=db
+ - MYSQL_USER=user
+ - MYSQL_PASSWORD=password
+ volumes:
+ - ./db:/config
+ expose:
+ - 3306
+ networks:
+ - net
+
+ cafe:
+ image: ghcr.io/flohoss/cafe-plaetschwiesle:latest
+ container_name: cafe
+ restart: unless-stopped
+ depends_on:
+ - cafe-db
+ environment:
+ - PUID=1000
+ - PGID=1000
+ - ALLOWED_HOSTS=http://localhost:5000,https://home.example.com
+ - SWAGGER=true
+ - LOG_LEVEL=info # trace,debug,info,warn,error,fatal,panic
+ - MYSQL_URL=cafe-db:3306
+ - MYSQL_USER=user
+ - MYSQL_PASSWORD=password
+ - MYSQL_DATABASE=db
+ volumes:
+ - ./storage:/app/storage
+ ports:
+ - '127.0.0.1:5000:5000'
+ networks:
+ - net
+```
diff --git a/api/middlwares.go b/api/middlwares.go
new file mode 100644
index 0000000..9cedeca
--- /dev/null
+++ b/api/middlwares.go
@@ -0,0 +1,52 @@
+package api
+
+import (
+ "cafe/config"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/gin-contrib/cors"
+ "github.com/gin-gonic/gin"
+ "github.com/sirupsen/logrus"
+)
+
+func myLogger() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ if logrus.GetLevel() != logrus.TraceLevel {
+ return
+ }
+ reqUri := c.Request.RequestURI
+ if strings.Contains(reqUri, "/storage") {
+ return
+ }
+ startTime := time.Now()
+ c.Next()
+ endTime := time.Now()
+ latencyTime := endTime.Sub(startTime)
+ logrus.WithFields(logrus.Fields{
+ "status": http.StatusText(c.Writer.Status()),
+ "latency": latencyTime,
+ "client": c.ClientIP(),
+ "method": c.Request.Method,
+ }).Trace(reqUri)
+ }
+}
+
+func authHeader() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ c.Writer.Header().Set("Remote-Groups", c.Request.Header.Get("Remote-Groups"))
+ c.Writer.Header().Set("Remote-Name", c.Request.Header.Get("Remote-Name"))
+ }
+}
+
+func (a *Api) SetMiddlewares() {
+ a.Router.Use(myLogger())
+ a.Router.Use(gin.Recovery())
+ a.Router.Use(cors.Default())
+ _ = a.Router.SetTrustedProxies(nil)
+ a.Router.MaxMultipartMemory = 8 << 20 // 8 MiB
+ logrus.WithFields(logrus.Fields{
+ "allowedOrigins": config.Cafe.AllowedHosts,
+ }).Debug("Middlewares set")
+}
diff --git a/api/router.go b/api/router.go
new file mode 100644
index 0000000..7b06266
--- /dev/null
+++ b/api/router.go
@@ -0,0 +1,62 @@
+package api
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+ "github.com/sirupsen/logrus"
+)
+
+func (a *Api) SetupRouter() {
+ api := a.Router.Group("/api")
+ {
+ tableGroup := api.Group("/tables")
+ {
+ tableGroup.GET("", a.getTables)
+ tableGroup.POST("", a.createTable)
+ tableGroup.DELETE("", a.deleteTable)
+ }
+ orderGroup := api.Group("/orders")
+ {
+ orderGroup.GET("", a.getOrders)
+ orderGroup.POST("", a.createOrder)
+ orderGroup.DELETE("", a.deleteOrder)
+ orderGroup.PUT("", a.updateOrder)
+ orderGroup.GET("/ws", a.serveWs)
+ orderItemGroup := orderGroup.Group("/items")
+ {
+ orderItemGroup.GET("", a.getOrderItems)
+ orderItemGroup.POST("", a.createOrderItem)
+ orderItemGroup.PUT("", a.updateOrderItem)
+ orderItemGroup.DELETE("/:id", a.deleteOrderItem)
+ }
+ }
+ billGroup := api.Group("/bills")
+ {
+ billGroup.GET("", a.getBills)
+ billGroup.POST("", a.createBill)
+ billGroup.DELETE("/:id", a.deleteBill)
+ billItemGroup := billGroup.Group("/items")
+ {
+ billItemGroup.GET("", a.getBillItems)
+ }
+ }
+ userGroup := api.Group("/users")
+ {
+ userGroup.GET("/:username", a.getUser)
+ userGroup.PUT("", a.updateUser)
+ }
+ health := api.Group("/health")
+ {
+ health.Use(authHeader())
+ health.GET("", func(c *gin.Context) {
+ c.Status(http.StatusOK)
+ })
+ }
+ }
+
+ a.Router.NoRoute(func(c *gin.Context) {
+ c.HTML(http.StatusOK, "index.html", nil)
+ })
+ logrus.WithField("amount", len(a.Router.Routes())).Debug("Routes initialized")
+}
diff --git a/api/routesBill.go b/api/routesBill.go
new file mode 100644
index 0000000..38d0453
--- /dev/null
+++ b/api/routesBill.go
@@ -0,0 +1,112 @@
+package api
+
+import (
+ "cafe/service"
+ "cafe/types"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/gin-gonic/gin"
+)
+
+// @Schemes
+// @Summary get all bills
+// @Description gets all bills as array
+// @Tags bills
+// @Produce json
+// @Param year query int true "year"
+// @Param month query int true "month (1-12)"
+// @Param day query int true "day (1-31)"
+// @Success 200 {array} service.Bill
+// @Router /bills [get]
+func (a *Api) getBills(c *gin.Context) {
+ year, presentYear := c.GetQuery("year")
+ month, presentMonth := c.GetQuery("month")
+ day, presentDay := c.GetQuery("day")
+ if !presentYear || !presentMonth || !presentDay {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ bills, err := service.GetAllBills(year, month, day)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusOK, bills)
+ }
+}
+
+// @Schemes
+// @Summary get all billItems
+// @Description gets all billItems for bill
+// @Tags bills
+// @Produce json
+// @Param bill query int true "Bill ID"
+// @Success 200 {array} service.BillItem
+// @Router /bills/items [get]
+func (a *Api) getBillItems(c *gin.Context) {
+ bill, err := service.DoesBillExist(c.Query("bill"))
+ if err != nil {
+ c.JSON(http.StatusNotFound, errorResponse{err.Error()})
+ return
+ }
+ billItems, err := service.GetAllBillItems(bill.ID)
+ if err != nil {
+ c.JSON(http.StatusNotFound, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusOK, billItems)
+ }
+}
+
+// @Schemes
+// @Summary create new bill
+// @Description creates a new bill and returns it
+// @Tags bills
+// @Produce json
+// @Param table query int true "Table ID"
+// @Param filter query string false "filter"
+// @Success 201 {object} service.Bill
+// @Failure 404 "Not Found"
+// @Failure 500 {object} errorResponse
+// @Router /bills [post]
+func (a *Api) createBill(c *gin.Context) {
+ table, tableErr := strconv.ParseUint(c.Query("table"), 10, 64)
+ if tableErr != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ stringFiler, filterPresent := c.GetQuery("filter")
+ var filter []string
+ if filterPresent {
+ filter = strings.Split(stringFiler, ",")
+ }
+ bill, err := service.CreateBill(service.GetOrderOptions{TableId: table, Grouped: true, Filter: filter})
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ }
+ c.JSON(http.StatusCreated, bill)
+}
+
+// @Schemes
+// @Summary delete a bill
+// @Description deletes a bill
+// @Tags bills
+// @Produce json
+// @Param id path int true "Bill ID"
+// @Success 200 "OK"
+// @Failure 404 "Not Found"
+// @Failure 500 {object} errorResponse
+// @Router /bills/{id} [delete]
+func (a *Api) deleteBill(c *gin.Context) {
+ id := c.Param("id")
+ bill, err := service.DoesBillExist(id)
+ if err != nil {
+ c.JSON(http.StatusNotFound, errorResponse{err.Error()})
+ return
+ }
+ err = service.DeleteBill(&bill)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ }
+ c.Status(http.StatusOK)
+}
diff --git a/api/routesOrder.go b/api/routesOrder.go
new file mode 100644
index 0000000..80da9bc
--- /dev/null
+++ b/api/routesOrder.go
@@ -0,0 +1,258 @@
+package api
+
+import (
+ "cafe/hub"
+ "cafe/service"
+ "cafe/types"
+ ws "cafe/websocket"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/gin-gonic/gin"
+ "github.com/gorilla/websocket"
+ "github.com/sirupsen/logrus"
+)
+
+// @Schemes
+// @Summary get all orders
+// @Description gets all orders as array
+// @Tags orders
+// @Produce json
+// @Param table query int false "Table ID"
+// @Param grouping query bool false "grouping"
+// @Param filter query string false "filter"
+// @Success 200 {array} service.Order
+// @Router /orders [get]
+func (a *Api) getOrders(c *gin.Context) {
+ table, _ := strconv.ParseUint(c.Query("table"), 10, 64)
+ grouping, _ := strconv.ParseBool(c.Query("grouping"))
+ stringFiler, filterPresent := c.GetQuery("filter")
+ var filter []string
+ if filterPresent {
+ filter = strings.Split(stringFiler, ",")
+ }
+ options := service.GetOrderOptions{TableId: table, Grouped: grouping, Filter: filter}
+ var orders []service.Order
+ if options.TableId == 0 {
+ orders = service.GetAllActiveOrders()
+ } else {
+ orders = service.GetAllOrdersForTable(options)
+ }
+ c.JSON(http.StatusOK, orders)
+}
+
+// @Schemes
+// @Summary create new order
+// @Description creates a new order and returns it
+// @Tags orders
+// @Accept json
+// @Produce json
+// @Param item query int true "OrderItem ID"
+// @Param table query int true "Table ID"
+// @Success 201 {object} service.Order
+// @Failure 400 {object} errorResponse
+// @Failure 500 {object} errorResponse
+// @Router /orders [post]
+func (a *Api) createOrder(c *gin.Context) {
+ table, err1 := strconv.ParseUint(c.Query("table"), 10, 64)
+ item, err2 := strconv.ParseUint(c.Query("item"), 10, 64)
+ if err1 != nil || err2 != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ order := service.Order{TableID: table, OrderItemID: item, IsServed: false}
+ err := service.CreateOrder(&order)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusCreated, order)
+ }
+}
+
+// @Schemes
+// @Summary delete an order
+// @Description deletes an order from the database
+// @Tags orders
+// @Produce json
+// @Param item query int true "OrderItem ID"
+// @Param table query int true "Table ID"
+// @Success 200 "OK"
+// @Failure 400 {object} errorResponse
+// @Failure 500 {object} errorResponse
+// @Router /orders [delete]
+func (a *Api) deleteOrder(c *gin.Context) {
+ item := c.Query("item")
+ table := c.Query("table")
+ if table == "" || item == "" {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ err := service.DeleteOrder(table, item)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.Status(http.StatusOK)
+ }
+}
+
+// @Schemes
+// @Summary update an order
+// @Description updates an order with provided information
+// @Tags orders
+// @Accept json
+// @Produce json
+// @Param order body service.Order true "updated Order"
+// @Success 200 {object} service.Order
+// @Failure 400 {object} errorResponse
+// @Failure 404 "Not Found" errorResponse
+// @Failure 500 {object} errorResponse
+// @Router /orders [put]
+func (a *Api) updateOrder(c *gin.Context) {
+ var newOrder service.Order
+ err := c.ShouldBindJSON(&newOrder)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ oldOrder, err := service.DoesOrderExist(strconv.Itoa(int(newOrder.ID)))
+ if err != nil {
+ c.JSON(http.StatusNotFound, errorResponse{err.Error()})
+ return
+ }
+ err = service.UpdateOrder(&oldOrder, &newOrder)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusOK, newOrder)
+ }
+}
+
+func (a *Api) serveWs(c *gin.Context) {
+ conn, err := ws.Upgrader.Upgrade(c.Writer, c.Request, nil)
+ if err != nil {
+ logrus.WithField("error", err).Warning("Cannot upgrade websocket")
+ return
+ }
+ messageChan := make(hub.NotifierChan)
+ a.Hub.NewClients <- messageChan
+ defer func() {
+ a.Hub.ClosingClients <- messageChan
+ conn.Close()
+ }()
+ go ws.ReadPump(conn)
+ for {
+ msg, ok := <-messageChan
+ if !ok {
+ err := conn.WriteMessage(websocket.CloseMessage, []byte{})
+ if err != nil {
+ return
+ }
+ return
+ }
+ err := conn.WriteJSON(msg)
+ if err != nil {
+ return
+ }
+ }
+}
+
+// @Schemes
+// @Summary get all orderItems
+// @Description gets all orderItems as array
+// @Tags orderItems
+// @Produce json
+// @Param type query int true "ItemType"
+// @Success 200 {array} service.OrderItem
+// @Router /orders/items [get]
+func (a *Api) getOrderItems(c *gin.Context) {
+ orderType := c.Query("type")
+ if orderType == "" {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ } else {
+ c.JSON(http.StatusOK, service.GetOrderItemsForType(orderType))
+ }
+}
+
+// @Schemes
+// @Summary create new orderItem
+// @Description creates a new orderItem and returns it
+// @Tags orderItems
+// @Accept json
+// @Produce json
+// @Param order body service.OrderItem true "OrderItem ID"
+// @Success 201 {object} service.OrderItem
+// @Failure 400 {object} errorResponse
+// @Failure 500 {object} errorResponse
+// @Router /orders/items [post]
+func (a *Api) createOrderItem(c *gin.Context) {
+ var orderItem service.OrderItem
+ err := c.ShouldBindJSON(&orderItem)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ err = service.CreateOrderItem(&orderItem)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusCreated, orderItem)
+ }
+}
+
+// @Schemes
+// @Summary update a orderItem
+// @Description updates a orderItem with provided information
+// @Tags orderItems
+// @Accept json
+// @Produce json
+// @Param orderItem body service.OrderItem true "updated OrderItem"
+// @Success 200 {object} service.OrderItem
+// @Failure 400 {object} errorResponse
+// @Failure 404 "Not Found" errorResponse
+// @Failure 500 {object} errorResponse
+// @Router /orders/items [put]
+func (a *Api) updateOrderItem(c *gin.Context) {
+ var newOrderItem service.OrderItem
+ err := c.ShouldBindJSON(&newOrderItem)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ oldOrderItem, err := service.DoesOrderItemExist(strconv.Itoa(int(newOrderItem.ID)))
+ if err != nil {
+ c.JSON(http.StatusNotFound, errorResponse{err.Error()})
+ return
+ }
+ err = service.UpdateOrderItem(&oldOrderItem, &newOrderItem)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusOK, newOrderItem)
+ }
+}
+
+// @Schemes
+// @Summary delete an orderItem
+// @Description deletes an orderItem from the database
+// @Tags orderItems
+// @Produce json
+// @Param id path int true "OrderItem ID"
+// @Success 200 "OK"
+// @Failure 404 "Not Found"
+// @Failure 500 {object} errorResponse
+// @Router /orders/items/{id} [delete]
+func (a *Api) deleteOrderItem(c *gin.Context) {
+ id := c.Param("id")
+ orderItem, err := service.DoesOrderItemExist(id)
+ if err != nil {
+ c.JSON(http.StatusNotFound, errorResponse{err.Error()})
+ return
+ }
+ err = service.DeleteOrderItem(&orderItem)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.Status(http.StatusOK)
+ }
+}
diff --git a/api/routesTable.go b/api/routesTable.go
new file mode 100644
index 0000000..6706dcf
--- /dev/null
+++ b/api/routesTable.go
@@ -0,0 +1,54 @@
+package api
+
+import (
+ "cafe/service"
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+)
+
+// @Schemes
+// @Summary get all active tables
+// @Description gets all active tables as array
+// @Tags tables
+// @Produce json
+// @Success 200 {array} service.Table
+// @Router /tables [get]
+func (a *Api) getTables(c *gin.Context) {
+ c.JSON(http.StatusOK, service.GetAllTables())
+}
+
+// @Schemes
+// @Summary create new table
+// @Description creates a new table and returns it
+// @Tags tables
+// @Accept json
+// @Produce json
+// @Success 201 {object} service.Table "Table has been created"
+// @Failure 500 {object} errorResponse "Cannot create table"
+// @Router /tables [post]
+func (a *Api) createTable(c *gin.Context) {
+ table, err := service.CreateNewTable()
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusCreated, table)
+ }
+}
+
+// @Schemes
+// @Summary delete the latest table
+// @Description deletes the latest table from the database
+// @Tags tables
+// @Produce json
+// @Success 200 "OK"
+// @Failure 500 {object} errorResponse "Cannot delete table"
+// @Router /tables [delete]
+func (a *Api) deleteTable(c *gin.Context) {
+ err := service.DeleteLatestTable()
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.Status(http.StatusOK)
+ }
+}
diff --git a/api/routesUser.go b/api/routesUser.go
new file mode 100644
index 0000000..79265a4
--- /dev/null
+++ b/api/routesUser.go
@@ -0,0 +1,60 @@
+package api
+
+import (
+ "cafe/types"
+ "cafe/user"
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+)
+
+// @Schemes
+// @Summary get a user
+// @Description gets a user
+// @Tags users
+// @Produce json
+// @Param username path string true "Username"
+// @Success 200 {object} user.User
+// @Failure 500 {object} errorResponse
+// @Router /users/{username} [get]
+func (a *Api) getUser(c *gin.Context) {
+ username := c.Param("username")
+ u, err := user.GetUserOrCreate(username)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusOK, u)
+ }
+}
+
+// @Schemes
+// @Summary update a user
+// @Description updates a user with provided information
+// @Tags users
+// @Accept json
+// @Produce json
+// @Param user body user.User true "updated User"
+// @Success 200 {object} user.User
+// @Failure 400 {object} errorResponse
+// @Failure 404 "Not Found" errorResponse
+// @Failure 500 {object} errorResponse
+// @Router /users [put]
+func (a *Api) updateUser(c *gin.Context) {
+ var newUser user.User
+ err := c.ShouldBindJSON(&newUser)
+ if err != nil {
+ c.JSON(http.StatusBadRequest, errorResponse{types.MissingInformation.String()})
+ return
+ }
+ oldUser, err := user.DoesUserExist(newUser.Username)
+ if err != nil {
+ c.JSON(http.StatusNotFound, errorResponse{err.Error()})
+ return
+ }
+ err = user.UpdateUser(&oldUser, &newUser)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, errorResponse{err.Error()})
+ } else {
+ c.JSON(http.StatusOK, newUser)
+ }
+}
diff --git a/api/static.go b/api/static.go
new file mode 100644
index 0000000..0e4ba35
--- /dev/null
+++ b/api/static.go
@@ -0,0 +1,26 @@
+package api
+
+import (
+ "cafe/config"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/gin-contrib/static"
+ "github.com/sirupsen/logrus"
+)
+
+func (a *Api) HandleStaticFiles() {
+ a.Router.LoadHTMLFiles(config.TemplatesDir + "index.html")
+ a.serveFoldersInTemplates()
+}
+
+func (a *Api) serveFoldersInTemplates() {
+ _ = filepath.WalkDir(config.TemplatesDir, func(path string, info os.DirEntry, err error) error {
+ if info.IsDir() && info.Name() != strings.TrimSuffix(config.TemplatesDir, "/") {
+ a.Router.Use(static.Serve("/"+info.Name(), static.LocalFile(config.TemplatesDir+info.Name(), false)))
+ logrus.WithField("folder", info.Name()).Debug("Serve static folder")
+ }
+ return err
+ })
+}
diff --git a/api/swagger.go b/api/swagger.go
new file mode 100644
index 0000000..947416d
--- /dev/null
+++ b/api/swagger.go
@@ -0,0 +1,31 @@
+package api
+
+import (
+ "cafe/config"
+ "cafe/docs"
+ "net/http"
+ "net/url"
+ "os"
+
+ "github.com/gin-gonic/gin"
+ "github.com/sirupsen/logrus"
+ swaggerFiles "github.com/swaggo/files"
+ ginSwagger "github.com/swaggo/gin-swagger"
+)
+
+func (a *Api) SetupSwagger() {
+ if config.Cafe.Swagger {
+ docs.SwaggerInfo.Title = "Cafe"
+ docs.SwaggerInfo.Description = "This is the backend of a cafe"
+ docs.SwaggerInfo.Version = os.Getenv("VERSION")
+ docs.SwaggerInfo.BasePath = "/api"
+ parsed, _ := url.Parse(config.Cafe.AllowedHosts[0])
+ docs.SwaggerInfo.Host = parsed.Host
+
+ a.Router.GET("/swagger", func(c *gin.Context) {
+ c.Redirect(http.StatusMovedPermanently, "/swagger/index.html")
+ })
+ a.Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
+ logrus.WithField("url", config.Cafe.AllowedHosts[0]+"/swagger").Info("Swagger running")
+ }
+}
diff --git a/api/types.go b/api/types.go
new file mode 100644
index 0000000..616b354
--- /dev/null
+++ b/api/types.go
@@ -0,0 +1,16 @@
+package api
+
+import (
+ "cafe/hub"
+
+ "github.com/gin-gonic/gin"
+)
+
+type Api struct {
+ Router *gin.Engine
+ Hub hub.Hub
+}
+
+type errorResponse struct {
+ Error string `json:"error" validate:"required"`
+}
diff --git a/cmd/cafe-plaetschwiesle/cafe-plaetschwiesle.go b/cmd/cafe-plaetschwiesle/cafe-plaetschwiesle.go
deleted file mode 100644
index 21752a5..0000000
--- a/cmd/cafe-plaetschwiesle/cafe-plaetschwiesle.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package main
-
-import (
- "fmt"
- "log"
- "net/http"
-
- "gitlab.unjx.de/flohoss/cafe-plaetschwiesle/internal/controller"
- "gitlab.unjx.de/flohoss/cafe-plaetschwiesle/internal/env"
- "gitlab.unjx.de/flohoss/cafe-plaetschwiesle/internal/logging"
- "gitlab.unjx.de/flohoss/cafe-plaetschwiesle/internal/router"
- "go.uber.org/zap"
-)
-
-func main() {
- env, err := env.Parse()
- if err != nil {
- log.Fatal(err)
- }
- zap.ReplaceGlobals(logging.CreateLogger(env.LogLevel))
-
- r := router.InitRouter()
- c := controller.NewController(env)
- router.SetupRoutes(r, c, env)
-
- zap.L().Info("starting server", zap.String("url", fmt.Sprintf("http://localhost:%d", env.Port)), zap.String("version", env.Version))
- if err := r.Start(fmt.Sprintf(":%d", env.Port)); err != http.ErrServerClosed {
- zap.L().Fatal("cannot start server", zap.Error(err))
- }
-}
diff --git a/config.toml b/config.toml
new file mode 100644
index 0000000..570fb9e
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,10 @@
+ALLOWED_HOSTS = "https://cafe.test"
+LOG_LEVEL = "info"
+PORT = 5000
+SWAGGER = true
+
+[MYSQL]
+DATABASE = ""
+PASSWORD = ""
+URL = ""
+USER = ""
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..390bf58
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,72 @@
+package config
+
+import (
+ "cafe/database"
+ "strings"
+
+ "github.com/mitchellh/mapstructure"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/viper"
+ "github.com/unjx-de/go-folder"
+)
+
+const StorageDir = "storage/"
+const TemplatesDir = "templates/"
+
+type Config struct {
+ Port int
+ AllowedHosts []string `mapstructure:"ALLOWED_HOSTS"`
+ Swagger bool
+ Bookmarks bool
+ LogLevel string `mapstructure:"LOG_LEVEL"`
+ Database database.MySQL `mapstructure:"MYSQL"`
+}
+
+var Cafe = Config{}
+
+func configLogger() {
+ logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: "2006/01/02 15:04:05", FullTimestamp: true})
+}
+
+func readConfig() {
+ viper.AddConfigPath(".")
+ viper.SetConfigName("config")
+ viper.SetConfigType("toml")
+ err := viper.ReadInConfig()
+ if err != nil {
+ logrus.WithField("error", err).Fatal("Failed opening config file")
+ }
+ viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
+ viper.AutomaticEnv()
+ err = viper.Unmarshal(&Cafe, viper.DecodeHook(mapstructure.StringToSliceHookFunc(",")))
+ if err != nil {
+ logrus.WithField("error", err).Fatal("Failed reading environment variables")
+ }
+ logrus.WithField("file", viper.ConfigFileUsed()).Info("Initializing configuration")
+}
+
+func setLogLevel() {
+ logLevel, err := logrus.ParseLevel(Cafe.LogLevel)
+ if err != nil {
+ logrus.SetLevel(logrus.InfoLevel)
+ } else {
+ logrus.SetLevel(logLevel)
+ }
+ logrus.WithField("logLevel", logLevel.String()).Debug("Log level set")
+}
+
+func createFolderStructure() {
+ folders := []string{StorageDir, TemplatesDir}
+ err := folder.CreateFolders(folders, 0755)
+ if err != nil {
+ logrus.WithField("error", err).Fatal("Failed creating folders")
+ }
+ logrus.WithField("folders", folders).Debug("Folders created")
+}
+
+func init() {
+ configLogger()
+ readConfig()
+ setLogLevel()
+ createFolderStructure()
+}
diff --git a/database/database.go b/database/database.go
new file mode 100644
index 0000000..fe9bae0
--- /dev/null
+++ b/database/database.go
@@ -0,0 +1,80 @@
+package database
+
+import (
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/sirupsen/logrus"
+ "gorm.io/driver/mysql"
+ "gorm.io/driver/sqlite"
+ "gorm.io/gorm"
+)
+
+type MySQL struct {
+ Url string
+ User string
+ Password string
+ Database string
+ ORM *gorm.DB
+}
+
+func (config *MySQL) MigrateHelper(i interface{}, name string) {
+ err := config.ORM.AutoMigrate(i)
+ if err != nil {
+ logrus.WithField("error", err).Fatalf("Failed to migrate %s", name)
+ }
+}
+
+func (config *MySQL) tryDbConnection() {
+ i := 1
+ total := 20
+ for i <= total {
+ ln, err := net.DialTimeout("tcp", config.Url, 1*time.Second)
+ if err != nil {
+ if i == total {
+ logrus.WithField("attempt", i).Fatal("Failed connecting to database")
+ }
+ logrus.WithField("attempt", i).Warning("Connecting to database")
+ time.Sleep(2 * time.Second)
+ i++
+ } else {
+ _ = ln.Close()
+ logrus.WithField("attempt", i).Info("Connected to database")
+ i = total + 1
+ }
+ }
+}
+
+func (config *MySQL) initializeMySql() {
+ var err error
+ config.tryDbConnection()
+ dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
+ config.User,
+ config.Password,
+ config.Url,
+ config.Database,
+ )
+ config.ORM, err = gorm.Open(mysql.Open(dsn), &gorm.Config{PrepareStmt: true})
+ if err != nil {
+ logrus.WithField("error", err).Fatal("Failed to open database")
+ }
+}
+
+func (config *MySQL) initializeSqLite(storageDir string) {
+ var err error
+ absPath := storageDir + "db.sqlite"
+ config.ORM, err = gorm.Open(sqlite.Open(absPath), &gorm.Config{PrepareStmt: true})
+ if err != nil {
+ logrus.WithField("error", err).Fatal("Failed to open database")
+ }
+}
+
+func (config *MySQL) Initialize(storageDir string) {
+ if config.Url == "" {
+ config.initializeSqLite(storageDir)
+ } else {
+ config.initializeMySql()
+ }
+ logrus.WithField("dialect", config.ORM.Dialector.Name()).Debug("Database initialized")
+}
diff --git a/docker-compose.yml b/docker-compose.yml
index 962ba90..a2ec2e3 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -102,7 +102,7 @@ services:
networks:
- net
volumes:
- - ./web:/app/
+ - ./frontend:/app/
cafe-backend:
build:
@@ -111,11 +111,10 @@ services:
args:
- GOLANG_VERSION=${GOLANG_VERSION}
container_name: cafe-backend
- command: air -c .air.toml
+ entrypoint: air --build.exclude_dir "node_modules,frontend,static,docs,storage,tmp,dist"
environment:
- PUID=1000
- PGID=1000
- - SWAGGER_HOST=https://cafe.test
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.backend.rule=Host(`cafe.test`) && PathPrefix(`/api`)'
@@ -123,7 +122,7 @@ services:
- 'traefik.http.routers.backend.tls=true'
- 'traefik.http.routers.backend.middlewares=authelia@docker'
expose:
- - 8080
+ - 5000
networks:
- net
volumes:
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 74adbbc..830235b 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -14,17 +14,17 @@ RUN go mod download
COPY . .
RUN ./swagger.sh init
-RUN go build -ldflags="-s -w" cmd/cafe-plaetschwiesle/cafe-plaetschwiesle.go
+RUN go build -ldflags="-s -w"
FROM node:${NODE_VERSION}-alpine AS nodeBuilder
WORKDIR /app
-COPY ./web/package.json .
-COPY ./web/yarn.lock .
+COPY ./frontend/package.json .
+COPY ./frontend/yarn.lock .
RUN yarn install --frozen-lockfile
COPY --from=goBuilder /app/docs/swagger.json ../docs/swagger.json
-COPY ./web/ .
+COPY ./frontend/ .
RUN yarn run types:openapi
RUN yarn run build
@@ -39,8 +39,9 @@ WORKDIR /app
COPY ./scripts/entrypoint.sh .
COPY --from=logo /logo.txt .
-COPY --from=nodeBuilder /app/dist/ ./web/
-COPY --from=goBuilder /app/cafe-plaetschwiesle .
+COPY --from=nodeBuilder /app/dist/ ./templates/
+COPY --from=goBuilder /app/cafe .
+COPY config.toml .
ARG VERSION
ENV VERSION=$VERSION
diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev
index e2d1a6e..5aa8461 100644
--- a/docker/Dockerfile.dev
+++ b/docker/Dockerfile.dev
@@ -8,7 +8,8 @@ COPY ./go.sum .
RUN go mod download
RUN go install github.com/cosmtrek/air@latest
-COPY ./.air.toml .
ENV VERSION=v0.0.0-DEV
ENV BUILD_TIME=2023-06-01T08:07:43.454Z
+
+CMD ["air"]
diff --git a/web/.dockerignore b/frontend/.dockerignore
similarity index 100%
rename from web/.dockerignore
rename to frontend/.dockerignore
diff --git a/web/.gitignore b/frontend/.gitignore
similarity index 100%
rename from web/.gitignore
rename to frontend/.gitignore
diff --git a/web/README.md b/frontend/README.md
similarity index 96%
rename from web/README.md
rename to frontend/README.md
index 8fe4eab..fb1f6eb 100644
--- a/web/README.md
+++ b/frontend/README.md
@@ -1,35 +1,29 @@
-# web
+# frontend
## Project setup
-
```
npm install
```
### Compiles and hot-reloads for development
-
```
npm run serve
```
### Compiles and minifies for production
-
```
npm run build
```
### Run your unit tests
-
```
npm run test:unit
```
### Lints and fixes files
-
```
npm run lint
```
### Customize configuration
-
See [Configuration Reference](https://cli.vuejs.org/config/).
diff --git a/web/babel.config.js b/frontend/babel.config.js
similarity index 100%
rename from web/babel.config.js
rename to frontend/babel.config.js
diff --git a/web/package.json b/frontend/package.json
similarity index 99%
rename from web/package.json
rename to frontend/package.json
index 35106ea..7be8523 100644
--- a/web/package.json
+++ b/frontend/package.json
@@ -1,5 +1,5 @@
{
- "name": "web",
+ "name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
diff --git a/web/public/favicon/android-chrome-192x192.png b/frontend/public/favicon/android-chrome-192x192.png
similarity index 100%
rename from web/public/favicon/android-chrome-192x192.png
rename to frontend/public/favicon/android-chrome-192x192.png
diff --git a/web/public/favicon/android-chrome-512x512.png b/frontend/public/favicon/android-chrome-512x512.png
similarity index 100%
rename from web/public/favicon/android-chrome-512x512.png
rename to frontend/public/favicon/android-chrome-512x512.png
diff --git a/web/public/favicon/apple-touch-icon.png b/frontend/public/favicon/apple-touch-icon.png
similarity index 100%
rename from web/public/favicon/apple-touch-icon.png
rename to frontend/public/favicon/apple-touch-icon.png
diff --git a/web/public/favicon/browserconfig.xml b/frontend/public/favicon/browserconfig.xml
similarity index 100%
rename from web/public/favicon/browserconfig.xml
rename to frontend/public/favicon/browserconfig.xml
diff --git a/web/public/favicon/favicon-16x16.png b/frontend/public/favicon/favicon-16x16.png
similarity index 100%
rename from web/public/favicon/favicon-16x16.png
rename to frontend/public/favicon/favicon-16x16.png
diff --git a/web/public/favicon/favicon-32x32.png b/frontend/public/favicon/favicon-32x32.png
similarity index 100%
rename from web/public/favicon/favicon-32x32.png
rename to frontend/public/favicon/favicon-32x32.png
diff --git a/web/public/favicon/favicon.ico b/frontend/public/favicon/favicon.ico
similarity index 100%
rename from web/public/favicon/favicon.ico
rename to frontend/public/favicon/favicon.ico
diff --git a/web/public/favicon/mstile-150x150.png b/frontend/public/favicon/mstile-150x150.png
similarity index 100%
rename from web/public/favicon/mstile-150x150.png
rename to frontend/public/favicon/mstile-150x150.png
diff --git a/web/public/favicon/safari-pinned-tab.svg b/frontend/public/favicon/safari-pinned-tab.svg
similarity index 100%
rename from web/public/favicon/safari-pinned-tab.svg
rename to frontend/public/favicon/safari-pinned-tab.svg
diff --git a/web/public/favicon/site.webmanifest b/frontend/public/favicon/site.webmanifest
similarity index 100%
rename from web/public/favicon/site.webmanifest
rename to frontend/public/favicon/site.webmanifest
diff --git a/web/public/index.html b/frontend/public/index.html
similarity index 100%
rename from web/public/index.html
rename to frontend/public/index.html
diff --git a/web/src/App.vue b/frontend/src/App.vue
similarity index 100%
rename from web/src/App.vue
rename to frontend/src/App.vue
diff --git a/web/src/assets/fonts/roboto.ttf b/frontend/src/assets/fonts/roboto.ttf
similarity index 100%
rename from web/src/assets/fonts/roboto.ttf
rename to frontend/src/assets/fonts/roboto.ttf
diff --git a/web/src/assets/logos/logo.png b/frontend/src/assets/logos/logo.png
similarity index 100%
rename from web/src/assets/logos/logo.png
rename to frontend/src/assets/logos/logo.png
diff --git a/web/src/assets/logos/logo_white.png b/frontend/src/assets/logos/logo_white.png
similarity index 100%
rename from web/src/assets/logos/logo_white.png
rename to frontend/src/assets/logos/logo_white.png
diff --git a/web/src/components/Bills/BillModal.vue b/frontend/src/components/Bills/BillModal.vue
similarity index 93%
rename from web/src/components/Bills/BillModal.vue
rename to frontend/src/components/Bills/BillModal.vue
index 4c53a61..8a8d388 100644
--- a/web/src/components/Bills/BillModal.vue
+++ b/frontend/src/components/Bills/BillModal.vue
@@ -31,7 +31,7 @@
diff --git a/web/src/components/Tables/TableOverview.vue b/frontend/src/components/Tables/TableOverview.vue
similarity index 87%
rename from web/src/components/Tables/TableOverview.vue
rename to frontend/src/components/Tables/TableOverview.vue
index 9eeec8b..d2febf0 100644
--- a/web/src/components/Tables/TableOverview.vue
+++ b/frontend/src/components/Tables/TableOverview.vue
@@ -3,13 +3,8 @@
-
addBeverage(t)" />
- addBeverage(t)"
- />
+ addBeverage(t)" />
+ addBeverage(t)" />
@@ -61,7 +56,7 @@
import { computed, defineComponent, provide, ref } from "vue";
import BaseCard from "@/components/UI/BaseCard.vue";
import { useStore } from "vuex";
-import { OrdersService, controller_Order, controller_OrderItem, controller_ItemType } from "@/services/openapi";
+import { OrdersService, service_Order, service_OrderItem, types_ItemType } from "@/services/openapi";
import BottomNavigation from "@/components/UI/BottomNavigation.vue";
import Button from "primevue/button";
import { convertToEur } from "@/utils";
@@ -87,7 +82,7 @@ export default defineComponent({
const total = ref(0);
const orderItems = computed(() => store.getters.getOrderItems);
const options = ref();
- const orders = ref([]);
+ const orders = ref([]);
store.dispatch("getAllOrderItems");
@@ -115,13 +110,13 @@ export default defineComponent({
total.value = temp;
}
- function addBeverage(itemType: controller_ItemType[]) {
+ function addBeverage(itemType: types_ItemType[]) {
newOrderModal.value = true;
options.value = [];
itemType.forEach((type) => {
options.value = options.value.concat(orderItems.value.get(type));
});
- options.value.sort((a: controller_OrderItem, b: controller_OrderItem) => {
+ options.value.sort((a: service_OrderItem, b: service_OrderItem) => {
const x = a.description.toLowerCase();
const y = b.description.toLowerCase();
if (x < y) return -1;
@@ -147,7 +142,7 @@ export default defineComponent({
total,
convertToEur,
addBeverage,
- controller_ItemType,
+ types_ItemType,
postOrder,
orders,
getData,
diff --git a/web/src/components/UI/BaseCard.vue b/frontend/src/components/UI/BaseCard.vue
similarity index 100%
rename from web/src/components/UI/BaseCard.vue
rename to frontend/src/components/UI/BaseCard.vue
diff --git a/web/src/components/UI/BaseItem.vue b/frontend/src/components/UI/BaseItem.vue
similarity index 100%
rename from web/src/components/UI/BaseItem.vue
rename to frontend/src/components/UI/BaseItem.vue
diff --git a/web/src/components/UI/BaseToolbar.vue b/frontend/src/components/UI/BaseToolbar.vue
similarity index 100%
rename from web/src/components/UI/BaseToolbar.vue
rename to frontend/src/components/UI/BaseToolbar.vue
diff --git a/web/src/components/UI/BottomNavigation.vue b/frontend/src/components/UI/BottomNavigation.vue
similarity index 100%
rename from web/src/components/UI/BottomNavigation.vue
rename to frontend/src/components/UI/BottomNavigation.vue
diff --git a/web/src/components/UI/SmallCard.vue b/frontend/src/components/UI/SmallCard.vue
similarity index 100%
rename from web/src/components/UI/SmallCard.vue
rename to frontend/src/components/UI/SmallCard.vue
diff --git a/web/src/components/UI/TheBadge.vue b/frontend/src/components/UI/TheBadge.vue
similarity index 100%
rename from web/src/components/UI/TheBadge.vue
rename to frontend/src/components/UI/TheBadge.vue
diff --git a/web/src/components/UI/TheNavigation.vue b/frontend/src/components/UI/TheNavigation.vue
similarity index 86%
rename from web/src/components/UI/TheNavigation.vue
rename to frontend/src/components/UI/TheNavigation.vue
index 0cc63fb..476d0c0 100644
--- a/web/src/components/UI/TheNavigation.vue
+++ b/frontend/src/components/UI/TheNavigation.vue
@@ -24,7 +24,7 @@ import Menubar from "primevue/menubar";
import { useStore } from "vuex";
import Button from "primevue/button";
import { useRoute } from "vue-router";
-import { TablesService, controller_ItemType } from "@/services/openapi";
+import { TablesService, types_ItemType } from "@/services/openapi";
import { detailedItemTypeString, errorToast } from "@/utils";
import { useToast } from "primevue/usetoast";
import { visible } from "@/keys";
@@ -84,9 +84,9 @@ export default defineComponent({
label: "Artikel",
icon: "pi pi-fw pi-shopping-cart",
items: [
- { label: detailedItemTypeString(controller_ItemType.Food), icon: "pi pi-fw pi-shopping-cart", to: "/items/" + controller_ItemType.Food },
- { label: detailedItemTypeString(controller_ItemType.ColdDrink), icon: "pi pi-fw pi-shopping-cart", to: "/items/" + controller_ItemType.ColdDrink },
- { label: detailedItemTypeString(controller_ItemType.HotDrink), icon: "pi pi-fw pi-shopping-cart", to: "/items/" + controller_ItemType.HotDrink },
+ { label: detailedItemTypeString(types_ItemType.Food), icon: "pi pi-fw pi-shopping-cart", to: "/items/" + types_ItemType.Food },
+ { label: detailedItemTypeString(types_ItemType.ColdDrink), icon: "pi pi-fw pi-shopping-cart", to: "/items/" + types_ItemType.ColdDrink },
+ { label: detailedItemTypeString(types_ItemType.HotDrink), icon: "pi pi-fw pi-shopping-cart", to: "/items/" + types_ItemType.HotDrink },
],
visible: () => editor.value,
},
@@ -103,7 +103,6 @@ export default defineComponent({
visible: () => maker.value,
},
{ label: "Abmelden", icon: "pi pi-fw pi-power-off", command: () => emit("logout") },
- { label: store.getters.getVersion, icon: "pi pi-fw pi-github", disabled: true },
],
},
]);
diff --git a/web/src/components/UI/WaveSpinner.vue b/frontend/src/components/UI/WaveSpinner.vue
similarity index 100%
rename from web/src/components/UI/WaveSpinner.vue
rename to frontend/src/components/UI/WaveSpinner.vue
diff --git a/web/src/components/User/UserSettings.vue b/frontend/src/components/User/UserSettings.vue
similarity index 91%
rename from web/src/components/User/UserSettings.vue
rename to frontend/src/components/User/UserSettings.vue
index 7b4ab66..a22bd9b 100644
--- a/web/src/components/User/UserSettings.vue
+++ b/frontend/src/components/User/UserSettings.vue
@@ -16,7 +16,7 @@
import { computed, defineComponent, inject, ref } from "vue";
import Sidebar from "primevue/sidebar";
import { visible } from "@/keys";
-import { controller_User, UsersService } from "@/services/openapi";
+import { user_User, UsersService } from "@/services/openapi";
import InputSwitch from "primevue/inputswitch";
import { useStore } from "vuex";
import { errorToast } from "@/utils";
@@ -30,7 +30,7 @@ export default defineComponent({
const toast = useToast();
const isLoading = ref(false);
const isVisible = inject(visible, ref(false));
- const user = computed(() => store.getters.getUser);
+ const user = computed(() => store.getters.getUser);
function updateUser() {
isLoading.value = true;
diff --git a/web/src/keys.ts b/frontend/src/keys.ts
similarity index 100%
rename from web/src/keys.ts
rename to frontend/src/keys.ts
diff --git a/web/src/main.ts b/frontend/src/main.ts
similarity index 97%
rename from web/src/main.ts
rename to frontend/src/main.ts
index bb9db5c..0020657 100644
--- a/web/src/main.ts
+++ b/frontend/src/main.ts
@@ -1,4 +1,4 @@
-import { createApp, version } from "vue";
+import { createApp } from "vue";
import { OpenAPI, UsersService } from "@/services/openapi";
import App from "./App.vue";
import router from "./router";
@@ -18,6 +18,7 @@ import "primeicons/primeicons.css";
import "primeflex/primeflex.css";
export const API_ENDPOINT_URL = window.origin + "/api";
+export const WEBSOCKET_ENDPOINT_URL = API_ENDPOINT_URL.replace("http", "ws") + "/orders/ws";
OpenAPI.BASE = API_ENDPOINT_URL;
async function getHealth() {
@@ -26,7 +27,6 @@ async function getHealth() {
store.commit("setGroups", groups);
const user = await UsersService.getUsers(response.headers.get("remote-name") || "Benutzer");
store.commit("setUser", user);
- store.commit("setVersion", await response.text());
}
getHealth().then(() => {
diff --git a/web/src/router/index.ts b/frontend/src/router/index.ts
similarity index 100%
rename from web/src/router/index.ts
rename to frontend/src/router/index.ts
diff --git a/web/src/shims-vue.d.ts b/frontend/src/shims-vue.d.ts
similarity index 100%
rename from web/src/shims-vue.d.ts
rename to frontend/src/shims-vue.d.ts
diff --git a/web/src/store/index.ts b/frontend/src/store/index.ts
similarity index 69%
rename from web/src/store/index.ts
rename to frontend/src/store/index.ts
index 861806e..08f2cd8 100644
--- a/web/src/store/index.ts
+++ b/frontend/src/store/index.ts
@@ -1,12 +1,11 @@
import { createStore } from "vuex";
import tableStore from "@/store/tables";
import orderItemStore from "@/store/orderItems";
-import { controller_User } from "@/services/openapi";
+import { user_User } from "@/services/openapi";
interface AppStateModel {
- user: controller_User;
+ user: user_User;
groups: string[];
- version: string;
}
export default createStore({
state: {
@@ -16,7 +15,6 @@ export default createStore({
show_hot_drinks: true,
},
groups: [""],
- version: "",
},
getters: {
getUser(state: AppStateModel) {
@@ -28,20 +26,14 @@ export default createStore({
getUsername(state: AppStateModel) {
return state.user.username;
},
- getVersion(state: AppStateModel) {
- return state.version;
- },
},
mutations: {
- setUser(state: AppStateModel, _user: controller_User) {
+ setUser(state: AppStateModel, _user: user_User) {
state.user = _user;
},
setGroups(state: AppStateModel, groups: string[]) {
state.groups = groups;
},
- setVersion(state: AppStateModel, version: string) {
- state.version = version;
- },
},
actions: {},
modules: {
diff --git a/web/src/store/orderItems/index.ts b/frontend/src/store/orderItems/index.ts
similarity index 52%
rename from web/src/store/orderItems/index.ts
rename to frontend/src/store/orderItems/index.ts
index 0cfa31c..f46fefb 100644
--- a/web/src/store/orderItems/index.ts
+++ b/frontend/src/store/orderItems/index.ts
@@ -1,17 +1,17 @@
-import { OrderItemsService, controller_OrderItem, controller_ItemType } from "@/services/openapi";
+import { OrderItemsService, service_OrderItem, types_ItemType } from "@/services/openapi";
interface AppStateModel {
- orderItems: Map;
+ orderItems: Map;
}
interface mutationOrderItems {
- orderItems: controller_OrderItem[];
- orderType: controller_ItemType;
+ orderItems: service_OrderItem[];
+ orderType: types_ItemType;
}
const orderItemStore = {
state: () => ({
- orderItems: new Map(),
+ orderItems: new Map(),
}),
getters: {
getOrderItems(state: AppStateModel) {
@@ -22,52 +22,52 @@ const orderItemStore = {
setOrderItems(state: AppStateModel, payload: mutationOrderItems) {
state.orderItems.set(payload.orderType, payload.orderItems);
},
- pushOrderItem(state: AppStateModel, orderItem: controller_OrderItem) {
+ pushOrderItem(state: AppStateModel, orderItem: service_OrderItem) {
const tempOrderItems = state.orderItems.get(orderItem.item_type);
tempOrderItems && tempOrderItems.push(orderItem);
},
- filterOrderItem(state: AppStateModel, orderItem: controller_OrderItem) {
+ filterOrderItem(state: AppStateModel, orderItem: service_OrderItem) {
const tempOrderItems = state.orderItems.get(orderItem.item_type);
tempOrderItems &&
state.orderItems.set(
orderItem.item_type,
- tempOrderItems.filter((origItem: controller_OrderItem) => origItem.id !== orderItem.id)
+ tempOrderItems.filter((origItem: service_OrderItem) => origItem.id !== orderItem.id)
);
},
- putOrderItem(state: AppStateModel, orderItem: controller_OrderItem) {
+ putOrderItem(state: AppStateModel, orderItem: service_OrderItem) {
const tempOrderItems = state.orderItems.get(orderItem.item_type);
tempOrderItems &&
state.orderItems.set(
orderItem.item_type,
- tempOrderItems.map((origItem: controller_OrderItem) => (origItem.id === orderItem.id ? orderItem : origItem))
+ tempOrderItems.map((origItem: service_OrderItem) => (origItem.id === orderItem.id ? orderItem : origItem))
);
},
},
actions: {
// eslint-disable-next-line
async getAllOrderItems(context: any) {
- await context.dispatch("getOrderItems", controller_ItemType.Food);
- await context.dispatch("getOrderItems", controller_ItemType.ColdDrink);
- await context.dispatch("getOrderItems", controller_ItemType.HotDrink);
+ await context.dispatch("getOrderItems", types_ItemType.Food);
+ await context.dispatch("getOrderItems", types_ItemType.ColdDrink);
+ await context.dispatch("getOrderItems", types_ItemType.HotDrink);
},
// eslint-disable-next-line
- async getOrderItems(context: any, orderType: controller_ItemType) {
+ async getOrderItems(context: any, orderType: types_ItemType) {
const orderTypeArray = context.getters.getOrderItems;
if (!orderTypeArray.get(orderType)) {
- const orderItems: controller_OrderItem[] | null = await OrderItemsService.getOrdersItems(orderType);
+ const orderItems: service_OrderItem[] | null = await OrderItemsService.getOrdersItems(orderType);
context.commit("setOrderItems", { orderItems, orderType });
}
},
// eslint-disable-next-line
- addOrderItem(context: any, orderItem: controller_OrderItem) {
+ addOrderItem(context: any, orderItem: service_OrderItem) {
context.commit("pushOrderItem", orderItem);
},
// eslint-disable-next-line
- deleteOrderItem(context: any, orderItem: controller_OrderItem) {
+ deleteOrderItem(context: any, orderItem: service_OrderItem) {
context.commit("filterOrderItem", orderItem);
},
// eslint-disable-next-line
- updateOrderItem(context: any, orderItem: controller_OrderItem) {
+ updateOrderItem(context: any, orderItem: service_OrderItem) {
context.commit("putOrderItem", orderItem);
},
},
diff --git a/web/src/store/tables/index.ts b/frontend/src/store/tables/index.ts
similarity index 75%
rename from web/src/store/tables/index.ts
rename to frontend/src/store/tables/index.ts
index b3fcd00..031d243 100644
--- a/web/src/store/tables/index.ts
+++ b/frontend/src/store/tables/index.ts
@@ -1,7 +1,7 @@
-import { controller_Table, TablesService } from "@/services/openapi";
+import { service_Table, TablesService } from "@/services/openapi";
interface AppStateModel {
- tables: controller_Table[] | null;
+ tables: service_Table[] | null;
}
const tableStore = {
@@ -17,13 +17,13 @@ const tableStore = {
},
},
mutations: {
- setTables(state: AppStateModel, tables: controller_Table[]) {
+ setTables(state: AppStateModel, tables: service_Table[]) {
state.tables = tables;
},
popTables(state: AppStateModel) {
state.tables && state.tables.pop();
},
- pushTable(state: AppStateModel, table: controller_Table) {
+ pushTable(state: AppStateModel, table: service_Table) {
state.tables && state.tables.push(table);
},
},
@@ -38,7 +38,7 @@ const tableStore = {
context.commit("popTables");
},
// eslint-disable-next-line
- addTable(context: any, table: controller_Table) {
+ addTable(context: any, table: service_Table) {
context.commit("pushTable", table);
},
},
diff --git a/web/src/utils.ts b/frontend/src/utils.ts
similarity index 63%
rename from web/src/utils.ts
rename to frontend/src/utils.ts
index a500c10..7c38943 100644
--- a/web/src/utils.ts
+++ b/frontend/src/utils.ts
@@ -1,44 +1,44 @@
-import { controller_Bill, controller_Order, controller_ItemType } from "@/services/openapi";
+import { service_Bill, service_Order, types_ItemType } from "@/services/openapi";
export function convertToEur(value: number | undefined) {
const temp: number = value ? value : 0;
return temp.toLocaleString("de-DE", { style: "currency", currency: "EUR" });
}
-export function detailedItemTypeString(type: controller_ItemType | undefined) {
+export function detailedItemTypeString(type: types_ItemType | undefined) {
switch (type) {
- case controller_ItemType.Food:
+ case types_ItemType.Food:
return "Speisen";
- case controller_ItemType.ColdDrink:
+ case types_ItemType.ColdDrink:
return "Kaltgetränke";
default:
return "Heiß/Eiskaffee";
}
}
-export function generalItemTypeString(type: controller_ItemType[]) {
- if (type.includes(controller_ItemType.Food)) {
+export function generalItemTypeString(type: types_ItemType[]) {
+ if (type.includes(types_ItemType.Food)) {
return "Speisen";
} else {
return "Getränke";
}
}
-export function detailedItemTypeIcon(type: controller_ItemType | undefined) {
+export function detailedItemTypeIcon(type: types_ItemType | undefined) {
switch (type) {
- case controller_ItemType.Food:
+ case types_ItemType.Food:
return "fa-cheese";
- case controller_ItemType.ColdDrink:
+ case types_ItemType.ColdDrink:
return "fa-champagne-glasses";
- case controller_ItemType.HotDrink:
+ case types_ItemType.HotDrink:
return "fa-mug-hot";
default:
return "";
}
}
-export function generalItemTypeIcon(type: controller_ItemType[]) {
- if (type.includes(controller_ItemType.Food)) {
+export function generalItemTypeIcon(type: types_ItemType[]) {
+ if (type.includes(types_ItemType.Food)) {
return "fa-cheese";
} else {
return "fa-champagne-glasses";
@@ -47,7 +47,7 @@ export function generalItemTypeIcon(type: controller_ItemType[]) {
export interface WebSocketMsg {
type: NotifierType;
- payload: controller_Order[];
+ payload: service_Order[];
}
export enum NotifierType {
@@ -75,4 +75,4 @@ export function lessThan15SecondsAgo(updated_at: number | undefined) {
return moment().diff(updated, "seconds") < 15;
}
-export const emptyBill: controller_Bill = { table_id: 0, total: 0 };
+export const emptyBill: service_Bill = { table_id: 0, total: 0 };
diff --git a/web/src/views/Bills.vue b/frontend/src/views/Bills.vue
similarity index 95%
rename from web/src/views/Bills.vue
rename to frontend/src/views/Bills.vue
index 26dad42..f0152e2 100644
--- a/web/src/views/Bills.vue
+++ b/frontend/src/views/Bills.vue
@@ -59,7 +59,7 @@
import { defineComponent, ref, watch } from "vue";
import BaseCard from "@/components/UI/BaseCard.vue";
import Calendar from "primevue/calendar";
-import { BillsService, controller_Bill } from "@/services/openapi";
+import { BillsService, service_Bill } from "@/services/openapi";
import Sidebar from "primevue/sidebar";
import BillModal from "@/components/Bills/BillModal.vue";
import { convertToEur, emptyBill, errorToast } from "@/utils";
@@ -80,11 +80,11 @@ export default defineComponent({
const confirm = useConfirm();
const toast = useToast();
const today = ref(new Date());
- const bills = ref([]);
+ const bills = ref([]);
const isLoading = ref(false);
const isDisabled = ref(false);
const billModal = ref(false);
- const bill = ref({ ...emptyBill });
+ const bill = ref({ ...emptyBill });
const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
});
@@ -104,7 +104,7 @@ export default defineComponent({
function openBill(billId: number) {
if (isDisabled.value) return;
- const temp: controller_Bill | undefined = bills.value.find((bill) => bill.id === billId);
+ const temp: service_Bill | undefined = bills.value.find((bill) => bill.id === billId);
temp && (bill.value = temp);
billModal.value = true;
}
diff --git a/web/src/views/Checkout.vue b/frontend/src/views/Checkout.vue
similarity index 96%
rename from web/src/views/Checkout.vue
rename to frontend/src/views/Checkout.vue
index 81d2cb0..6b09703 100644
--- a/web/src/views/Checkout.vue
+++ b/frontend/src/views/Checkout.vue
@@ -55,7 +55,7 @@