package main import ( "arbeitszeitmessung/models" "encoding/json" "errors" "fmt" "log" "net/http" "os" "strconv" _ "github.com/lib/pq" ) func main() { var err error models.DB, err = OpenDatabase() if err != nil { log.Fatal(err) } defer models.DB.Close() // handles the different http routes http.HandleFunc("/time/new", timeCreateHandler) http.HandleFunc("/time", timeHandler) http.HandleFunc("/logout", logoutHandler) // starting the http server fmt.Printf("Server is running at http://localhost:8000 exposed to port %s\n", 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, r) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } func autoLogout(w http.ResponseWriter, r *http.Request) { 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", "*") } }