diff --git a/Backend/database.go b/Backend/database.go index 80d2d38..bf1c4de 100644 --- a/Backend/database.go +++ b/Backend/database.go @@ -1,16 +1,17 @@ package main import ( + "arbeitszeitmessung/helper" "arbeitszeitmessung/models" "database/sql" "fmt" ) func OpenDatabase() (*sql.DB, error) { - dbHost := getEnv("POSTGRES_HOST", "localhost") - dbName := getEnv("POSTGRES_DB", "arbeitszeitmessung") - dbUser := getEnv("POSTGRES_USER", "arbeit_zeit") - dbPassword := getEnv("POSTGRES_PASS", "password") + dbHost := helper.GetEnv("POSTGRES_HOST", "localhost") + dbName := helper.GetEnv("POSTGRES_DB", "arbeitszeitmessung") + dbUser := helper.GetEnv("POSTGRES_USER", "arbeit_zeit") + dbPassword := helper.GetEnv("POSTGRES_PASS", "password") connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/%s?sslmode=disable", dbUser, dbPassword, dbHost, dbName) return sql.Open("postgres", connStr) diff --git a/Backend/endpoints/logout.go b/Backend/endpoints/logout.go new file mode 100644 index 0000000..9457d3e --- /dev/null +++ b/Backend/endpoints/logout.go @@ -0,0 +1,41 @@ +package endpoints + +import ( + "arbeitszeitmessung/helper" + "arbeitszeitmessung/models" + "encoding/json" + "fmt" + "net/http" +) + +func LogoutHandler(w http.ResponseWriter, r *http.Request) { + helper.SetCors(w) + switch r.Method { + case "GET": + autoLogout(w) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + +func autoLogout(w http.ResponseWriter) { + users, err := (*models.User).GetAll(nil) + var logged_out_users []models.User + if err != nil { + fmt.Printf("Error getting user list %v\n", err) + } + for _, user := range users { + if user.CheckAnwesenheit() { + err = user.Logout() + if err != nil { + fmt.Printf("Error logging out user %v\n", err) + } + logged_out_users = append(logged_out_users, user) + } + + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(logged_out_users) + +} diff --git a/Backend/endpoints/time.go b/Backend/endpoints/time.go new file mode 100644 index 0000000..255ccd9 --- /dev/null +++ b/Backend/endpoints/time.go @@ -0,0 +1,82 @@ +package endpoints + +import ( + "arbeitszeitmessung/helper" + "arbeitszeitmessung/models" + "encoding/json" + "log" + "net/http" + "strconv" +) + +// Frontend relevant backend functionality -> not used by the arduino devices +func TimeHandler(w http.ResponseWriter, r *http.Request) { + helper.SetCors(w) + switch r.Method { + case "GET": + getBookings(w, r) + case "UPDATE": + updateBooking(w, r) + case "OPTIONS": + // just support options header for non GET Requests from SWAGGER + w.WriteHeader(http.StatusOK) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + + +// Returns bookings from DB with similar card uid -> checks for card uid in http query params +func getBookings(w http.ResponseWriter, r *http.Request) { + card_uid := r.URL.Query().Get("card_uid") + if card_uid == "" { + http.Error(w, "Missing cardID query parameter", http.StatusBadRequest) + return + } + bookings, err := (*models.Booking).GetBookingsByCardID(nil, card_uid) + if err != nil { + log.Println("Error getting bookings: ", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(bookings) +} + +// Updates a booking form the given json body +func updateBooking(w http.ResponseWriter, r *http.Request) { + _booking_id := r.URL.Query().Get("counter_id") + if _booking_id == "" { + http.Error(w, "Missing bookingID query parameter", http.StatusBadRequest) + return + } + booking_id, err := strconv.Atoi(_booking_id) + if err != nil { + http.Error(w, "Invalid bookingID query parameter", http.StatusBadRequest) + return + } + _booking, err := (*models.Booking).GetBookingById(nil, booking_id) + if err != nil { + log.Println("Error getting booking: ", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + var booking models.Booking + dec := json.NewDecoder(r.Body) + dec.DisallowUnknownFields() + err = dec.Decode(&booking) + if err != nil { + log.Println("Error parsing booking: ", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + if booking.CounterId != 0 && booking.CounterId != _booking.CounterId { + log.Println("Booking Ids do not match") + http.Error(w, "Booking Ids do not match", http.StatusBadRequest) + return + } + _booking.Update(booking) + _booking.Save() + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(_booking) +} diff --git a/Backend/endpoints/time_create.go b/Backend/endpoints/time_create.go new file mode 100644 index 0000000..a720547 --- /dev/null +++ b/Backend/endpoints/time_create.go @@ -0,0 +1,53 @@ +package endpoints + +import ( + "arbeitszeitmessung/helper" + "arbeitszeitmessung/models" + "encoding/json" + "errors" + "log" + "net/http" +) + +// Relevant for arduino inputs -> creates new Booking from get and put method +// GET only for demo purpose +func TimeCreateHandler(w http.ResponseWriter, r *http.Request) { + helper.SetCors(w) + // switch with request methods + switch r.Method { + case "PUT": + createBooking(w, r) + case "GET": + createBooking(w, r) + case "OPTIONS": + // just support options header for non GET Requests from SWAGGER + w.WriteHeader(http.StatusOK) + default: + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} + + + +// Creates a booking from the http query params -> no body needed +// after that entry wi'll be written to database and the booking is returned as json +func createBooking(w http.ResponseWriter, r *http.Request) { + + booking := (*models.Booking).FromUrlParams(nil, r.URL.Query()) + if booking.Verify() { + err := booking.Insert() + if errors.Is(models.SameBookingError{}, err) { + http.Error(w, "Booking already exists", http.StatusConflict) + return + } + if err != nil { + log.Println("Error inserting booking: ", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusAccepted) + json.NewEncoder(w).Encode(booking) + } + w.WriteHeader(http.StatusBadRequest) +} diff --git a/Backend/helper/system.go b/Backend/helper/system.go new file mode 100644 index 0000000..68446d1 --- /dev/null +++ b/Backend/helper/system.go @@ -0,0 +1,18 @@ +package helper + +import ( + "os" +) + +// Returns env with default fallback value. +// +// Params: +// +// key - enviroment var name +// fallback - default value +func GetEnv(key, fallback string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return fallback +} diff --git a/Backend/helper/web.go b/Backend/helper/web.go new file mode 100644 index 0000000..70a2288 --- /dev/null +++ b/Backend/helper/web.go @@ -0,0 +1,17 @@ +package helper + +import ( + "net/http" + "os" +) + +// setting cors, important for later frontend use +// +// in DEBUG == "true" everything is set to "*" so that no cors errors will be happen +func SetCors(w http.ResponseWriter) { + if os.Getenv("DEBUG") == "true" { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "*") + w.Header().Set("Access-Control-Allow-Headers", "*") + } +} diff --git a/Backend/main.go b/Backend/main.go index b21ed7f..3f3d718 100644 --- a/Backend/main.go +++ b/Backend/main.go @@ -1,14 +1,12 @@ package main import ( + "arbeitszeitmessung/endpoints" + "arbeitszeitmessung/helper" "arbeitszeitmessung/models" - "encoding/json" - "errors" "fmt" "log" "net/http" - "os" - "strconv" _ "github.com/lib/pq" ) @@ -22,178 +20,11 @@ func main() { defer models.DB.Close() // handles the different http routes - http.HandleFunc("/time/new", timeCreateHandler) - http.HandleFunc("/time", timeHandler) - http.HandleFunc("/logout", logoutHandler) + http.HandleFunc("/time/new", endpoints.TimeCreateHandler) + http.HandleFunc("/time", endpoints.TimeHandler) + http.HandleFunc("/logout", endpoints.LogoutHandler) // starting the http server - fmt.Printf("Server is running at http://localhost:8000 exposed to port %s\n", getEnv("EXPOSED_PORT", "8000")) + fmt.Printf("Server is running at http://localhost:8000 exposed to port %s\n", helper.GetEnv("EXPOSED_PORT", "8000")) log.Fatal(http.ListenAndServe(":8080", nil)) } - -// Relevant for arduino inputs -> creates new Booking from get and put method -// GET only for demo purpose -func timeCreateHandler(w http.ResponseWriter, r *http.Request) { - setCors(w) - // switch with request methods - switch r.Method { - case "PUT": - createBooking(w, r) - case "GET": - createBooking(w, r) - case "OPTIONS": - // just support options header for non GET Requests from SWAGGER - w.WriteHeader(http.StatusOK) - default: - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - } -} - -// Frontendrelevant backend functionality -> not used by the arduino devices -func timeHandler(w http.ResponseWriter, r *http.Request) { - setCors(w) - switch r.Method { - case "GET": - getBookings(w, r) - case "PUT": - updateBooking(w, r) - case "OPTIONS": - // just support options header for non GET Requests from SWAGGER - w.WriteHeader(http.StatusOK) - default: - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - } -} - -func logoutHandler(w http.ResponseWriter, r *http.Request) { - setCors(w) - switch r.Method { - case "GET": - autoLogout(w) - default: - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - } -} - -func autoLogout(w http.ResponseWriter) { - users, err := (*models.User).GetAll(nil) - var logged_out_users []models.User - if err != nil { - fmt.Printf("Error getting user list %v\n", err) - } - for _, user := range users { - if user.CheckAnwesenheit() { - err = user.Logout() - if err != nil { - fmt.Printf("Error logging out user %v\n", err) - } - logged_out_users = append(logged_out_users, user) - } - - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(logged_out_users) - -} - -// Creates a booking from the http query params -> no body needed -// after that entry wi'll be written to database and the booking is returned as json -func createBooking(w http.ResponseWriter, r *http.Request) { - - booking := (*models.Booking).FromUrlParams(nil, r.URL.Query()) - if booking.Verify() { - err := booking.Insert() - if errors.Is(models.SameBookingError{}, err) { - http.Error(w, "Booking already exists", http.StatusConflict) - return - } - if err != nil { - log.Println("Error inserting booking: ", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusAccepted) - json.NewEncoder(w).Encode(booking) - } - w.WriteHeader(http.StatusBadRequest) -} - -// Returns bookings from DB with similar card uid -> checks for card uid in http query params -func getBookings(w http.ResponseWriter, r *http.Request) { - card_uid := r.URL.Query().Get("card_uid") - if card_uid == "" { - http.Error(w, "Missing cardID query parameter", http.StatusBadRequest) - return - } - bookings, err := (*models.Booking).GetBookingsByCardID(nil, card_uid) - if err != nil { - log.Println("Error getting bookings: ", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(bookings) -} - -// Updates a booking form the given json body -func updateBooking(w http.ResponseWriter, r *http.Request) { - _booking_id := r.URL.Query().Get("counter_id") - if _booking_id == "" { - http.Error(w, "Missing bookingID query parameter", http.StatusBadRequest) - return - } - booking_id, err := strconv.Atoi(_booking_id) - if err != nil { - http.Error(w, "Invalid bookingID query parameter", http.StatusBadRequest) - return - } - _booking, err := (*models.Booking).GetBookingById(nil, booking_id) - if err != nil { - log.Println("Error getting booking: ", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - var booking models.Booking - dec := json.NewDecoder(r.Body) - dec.DisallowUnknownFields() - err = dec.Decode(&booking) - if err != nil { - log.Println("Error parsing booking: ", err) - http.Error(w, "Internal Server Error", http.StatusInternalServerError) - return - } - if booking.CounterId != 0 && booking.CounterId != _booking.CounterId { - log.Println("Booking Ids do not match") - http.Error(w, "Booking Ids do not match", http.StatusBadRequest) - return - } - _booking.Update(booking) - _booking.Save() - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(_booking) -} - -// Returns env with default fallback value. -// -// Params: -// -// key - enviroment var name -// fallback - default value -func getEnv(key, fallback string) string { - if value, ok := os.LookupEnv(key); ok { - return value - } - return fallback -} - -// setting cors, important for later frontend use -// in DEBUG == "true" everything is set to "*" so that no cors errors will be happen -func setCors(w http.ResponseWriter) { - if os.Getenv("DEBUG") == "true" { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "*") - w.Header().Set("Access-Control-Allow-Headers", "*") - } -} diff --git a/Docker/dev.docker-compose.yml b/Docker/docker-compose.dev.yml similarity index 100% rename from Docker/dev.docker-compose.yml rename to Docker/docker-compose.dev.yml