Skip to content

Commit

Permalink
Merge pull request #31 from Ndeta100/ndeta-0
Browse files Browse the repository at this point in the history
Ndeta 0
  • Loading branch information
Ndeta100 authored Sep 5, 2024
2 parents 095c642 + 552b461 commit 8f6fc8e
Show file tree
Hide file tree
Showing 14 changed files with 373 additions and 69 deletions.
15 changes: 11 additions & 4 deletions api/auth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/Ndeta100/CamHotelConnect/types"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"log"
"net/http"
Expand Down Expand Up @@ -49,9 +50,9 @@ func invalidCredentials(c *fiber.Ctx) error {
// HandleAuth ----------------------GENERAL OVERVIEW
// HandleAuth Handler should only do:
// - Serialization of the incoming request(JSON)
// - do some data fetching from db
// - call some business logic
// - return data back to user
// - do some data fetching from db
// - call some business logic
// - return data back to user
func (h *AuthHandler) HandleAuth(c *fiber.Ctx) error {
var authParams AuthParams
if err := c.BodyParser(&authParams); err != nil {
Expand Down Expand Up @@ -123,7 +124,13 @@ func (h *AuthHandler) HandleValidateToken(c *fiber.Ctx) error {
}
// If the token is valid, optionally return user details or just a success message
userID := claims["id"].(string)
user, err := h.userStore.GetUserByID(c.Context(), userID)
// Convert userID to ObjectID
objectID, err := primitive.ObjectIDFromHex(userID)
if err != nil {
fmt.Println("Invalid user ID format in token:", userID)
return ErrUnauthorized()
}
user, err := h.userStore.GetUserByID(c.Context(), objectID)
if err != nil {
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{
"error": "User not found",
Expand Down
36 changes: 36 additions & 0 deletions api/booking_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"github.com/Ndeta100/CamHotelConnect/db"
_ "github.com/Ndeta100/CamHotelConnect/types"
"github.com/Ndeta100/CamHotelConnect/utils"
"github.com/gofiber/fiber/v2"
"go.mongodb.org/mongo-driver/bson"
Expand All @@ -18,6 +19,16 @@ func NewBookingHandler(store *db.Store) *BookingHandler {
}

// HandleGetBookings TODO: This needs to be admin authorized
//
// @Summary Get all bookings
// @Description Get a list of all bookings.
// @Tags bookings
// @Accept json
// @Produce json
// @Success 200 {array} types.Booking "List of bookings retrieved successfully"
// @Failure 404 {object} GenericResp "Bookings not found"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /bookings [get]
func (h *BookingHandler) HandleGetBookings(c *fiber.Ctx) error {
bookings, err := h.store.Booking.GetBookings(c.Context(), bson.M{})
if err != nil {
Expand All @@ -27,6 +38,18 @@ func (h *BookingHandler) HandleGetBookings(c *fiber.Ctx) error {
}

// HandleGetBooking TODO: This needs to be user authorized
//
// @Summary Get a booking
// @Description Get details of a booking by its ID. Only the user who made the booking and hotel owner can retrieve it.
// @Tags bookings
// @Accept json
// @Produce json
// @Param id path string true "Booking ID"
// @Success 200 {object} types.Booking "Booking details retrieved successfully"
// @Failure 404 {object} GenericResp "Booking not found"
// @Failure 401 {object} GenericResp "Unauthorized"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /bookings/{id} [get]
func (h *BookingHandler) HandleGetBooking(c *fiber.Ctx) error {
id := c.Params("id")
booking, err := h.store.Booking.GetBookingByID(c.Context(), id)
Expand All @@ -43,6 +66,19 @@ func (h *BookingHandler) HandleGetBooking(c *fiber.Ctx) error {
return c.JSON(booking)
}

// HandleCancelBooking
//
// @Summary Cancel a booking
// @Description Cancel a booking by its ID. Only the user who made the booking can cancel it.
// @Tags bookings
// @Accept json
// @Produce json
// @Param id path string true "Booking ID"
// @Success 200 {object} GenericResp "Booking canceled successfully"
// @Failure 404 {object} GenericResp "Booking not found"
// @Failure 401 {object} GenericResp "Unauthorized"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /bookings/{id}/cancel [put]
func (h *BookingHandler) HandleCancelBooking(c *fiber.Ctx) error {
id := c.Params("id")
booking, err := h.store.Booking.GetBookingByID(c.Context(), id)
Expand Down
101 changes: 97 additions & 4 deletions api/hotel_handler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"fmt"
"github.com/Ndeta100/CamHotelConnect/db"
"github.com/Ndeta100/CamHotelConnect/types"
"github.com/Ndeta100/CamHotelConnect/utils"
Expand Down Expand Up @@ -30,6 +31,21 @@ func NewHotelHandler(store *db.Store, imageUploader utils.ImageUploader) *HotelH
}
}

// HandleGetHotels TODO
//
// @Summary Get all hotels
// @Description Get a list of all hotels with optional filtering and pagination.
// @Tags hotels
// @Accept json
// @Produce json
// @Param rating query int false "Filter hotels by rating"
// @Param page query int false "Page number for pagination" default(1)
// @Param limit query int false "Number of hotels per page" default(10)
// @Success 200 {object} ResourceResp "List of hotels retrieved successfully"
// @Failure 400 {object} GenericResp "Bad Request"
// @Failure 404 {object} GenericResp "Hotels not found"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /hotels [get]
func (h *HotelHandler) HandleGetHotels(c *fiber.Ctx) error {
var queryFilter db.HotelFilter
if err := c.QueryParser(&queryFilter); err != nil {
Expand All @@ -56,6 +72,18 @@ func (h *HotelHandler) HandleGetHotels(c *fiber.Ctx) error {
return c.JSON(resp)
}

// HandleGetRooms TODO
//
// @Summary Get all rooms in a hotel
// @Description Get a list of all rooms for a specific hotel.
// @Tags hotels
// @Produce json
// @Param id path string true "Hotel ID"
// @Success 200 {array} types.Room "List of rooms retrieved successfully"
// @Failure 400 {object} GenericResp "Invalid Hotel ID"
// @Failure 404 {object} GenericResp "Hotel not found"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /hotels/{id}/rooms [get]
func (h *HotelHandler) HandleGetRooms(c *fiber.Ctx) error {
id := c.Params("id")
oid, err := primitive.ObjectIDFromHex(id)
Expand All @@ -70,20 +98,69 @@ func (h *HotelHandler) HandleGetRooms(c *fiber.Ctx) error {
return c.JSON(rooms)
}

// HandleGetHotel TODO:
//
// @Summary Get a specific hotel
// @Description Get details of a specific hotel by its ID.
// @Tags hotels
// @Produce json
// @Param id path string true "Hotel ID"
// @Success 200 {object} types.Hotel "Hotel details retrieved successfully"
// @Failure 400 {object} GenericResp "Invalid Hotel ID"
// @Failure 404 {object} GenericResp "Hotel not found"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /hotels/{id} [get]
func (h *HotelHandler) HandleGetHotel(c *fiber.Ctx) error {
id := c.Params("id")
oid, err := primitive.ObjectIDFromHex(id)
hotel, err := h.store.Hotel.GetHotelByID(c.Context(), oid)
hotelID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return ErrResourceNotFound("hotel")
}
userInterface := c.Locals("user")
fmt.Println("ID", userInterface)
user, ok := userInterface.(*types.User)
if !ok || user == nil {
fmt.Printf("Invalid user type or nil user in context: %v\n", userInterface)
return ErrUnauthorized()
}
if !userIsAdmin(user) {
return fmt.Errorf("user is not admin")
}
hotel, err := h.store.Hotel.GetHotelByID(c.Context(), user.ID, hotelID)
if err != nil {
//Check if hotel not found
if err.Error() == "hotel not found" {
// TODO: This error occurs when user is trying to get a hotel they did not add
return c.Status(fiber.StatusConflict).JSON("hotel does not exists")
}
return c.Status(fiber.StatusInternalServerError).JSON(GenericResp{Msg: "Internal Server Error"})
}
// Check if the user is authorized (either the owner or an admin)
if hotel.UserId != user.ID {
return c.Status(fiber.StatusForbidden).JSON(GenericResp{Msg: "You do not have permission to access this resource"})
}
return c.JSON(hotel)
}

// HandleAddHotel CreateHotel handles the creation of a new hotel
//
// @Summary Add a new hotel
// @Description Add a new hotel. Only users with the "ADMIN" role can create hotels.
// @Tags hotels
// @Accept multipart/form-data
// @Produce json
// @Param user body types.Hotel true "Hotel data"
// @Param images formData file true "Hotel images"
// @Success 201 {object} types.Hotel "Hotel created successfully"
// @Failure 400 {object} GenericResp "Bad Request"
// @Failure 401 {object} GenericResp "Unauthorized - Missing or invalid credentials"
// @Failure 403 {object} GenericResp "Forbidden - User does not have the required role"
// @Failure 409 {object} GenericResp "Conflict - Hotel already exists"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /hotels [post]
func (h *HotelHandler) HandleAddHotel(c *fiber.Ctx) error {
log.Println("Sending request to add hotel")
// Assuming you have a way to get the current authenticated user
//get the current authenticated user
user := c.Locals("user")
if user == nil {
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{
Expand All @@ -105,20 +182,36 @@ func (h *HotelHandler) HandleAddHotel(c *fiber.Ctx) error {
})
}
var hotel types.Hotel
if err := c.BodyParser(&hotel); err != nil {
log.Printf("Error parsing hotel json: %v\n", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"err": "Invalid input"})
}
//Get form data with images
form, err := c.MultipartForm()
if err != nil {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "failed to parse form",
})
}

//get image files from the form data
files := form.File["images"]
if len(files) == 0 {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "no files provided to upload",
})
}
exists, err := h.store.Hotel.HotelExists(c.Context(), &hotel)
if err != nil {
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
"error": "failed to check if hotel exists",
})
}
if exists {
return c.Status(http.StatusBadRequest).JSON(fiber.Map{
"error": "hotel already exists",
})
}
//upload image and get urls
imageUrls, err := h.ImageUploader.UploadImages(c.Context(), files)
if err != nil {
Expand All @@ -135,7 +228,7 @@ func (h *HotelHandler) HandleAddHotel(c *fiber.Ctx) error {
})
}

insertedHotel, err := h.store.Hotel.InsertHotel(c.Context(), &hotel)
insertedHotel, err := h.store.Hotel.InsertHotel(c.Context(), currentUser.ID, &hotel)
if err != nil {
if err.Error() == "hotel already exists" {
return c.Status(http.StatusConflict).JSON(fiber.Map{
Expand Down
21 changes: 17 additions & 4 deletions api/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/Ndeta100/CamHotelConnect/db"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
"go.mongodb.org/mongo-driver/bson/primitive"
"log"
"net/http"
"os"
Expand All @@ -15,9 +16,10 @@ func JWTAuthentication(userStore db.UserStore) fiber.Handler {
return func(c *fiber.Ctx) error {
token, ok := c.GetReqHeaders()["X-Api-Token"]
if !ok {
fmt.Println("token not present in header")
fmt.Println("X-Api-Token not present in header")
return ErrUnauthorized()
}
fmt.Println("Token received:", token[0])
claims, err := validateToken(token[0])
if err != nil {
return err
Expand All @@ -26,17 +28,28 @@ func JWTAuthentication(userStore db.UserStore) fiber.Handler {
expires := int64(expiresFloat)
//check token expiration
if time.Now().Unix() > expires {
//Todo create refreshToken!
// TODO createRefreshToken
log.Println("Token expired")
return NewError(http.StatusUnauthorized, "Expired token")
}
userID := claims["id"].(string)
user, err := userStore.GetUserByID(c.Context(), userID)
// Convert userID to ObjectID if necessary
objectID, err := primitive.ObjectIDFromHex(userID)
if err != nil {
fmt.Println("Invalid user ID format in token:", objectID)
return ErrUnauthorized()
}
user, err := userStore.GetUserByID(c.Context(), objectID)
if err != nil {
return ErrUnauthorized()
}
// Ensure user is not nil
if user == nil {
fmt.Println("User not found in database")
return ErrUnauthorized()
}
//set authenticated user to the context
c.Context().SetUserValue("user", user)
//c.Context().SetUserValue("user", user)
c.Locals("user", user)
return c.Next()
}
Expand Down
22 changes: 22 additions & 0 deletions api/room_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ func NewRoomHandler(store *db.Store) *RoomHandler {
}
}

// HandleBookRoom TODO:
//
// @Summary Book a room
// @Description Book a room with the specified parameters. The room must be available.
// @Tags rooms
// @Accept json
// @Produce json
// @Param id path string true "Room ID"
// @Param booking body BookRoomParams true "Booking parameters"
// @Success 200 {object} types.Booking "Room booked successfully"
// @Failure 400 {object} GenericResp "Bad Request - Room already booked or invalid parameters"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /rooms/{id}/book [post]
func (h *RoomHandler) HandleBookRoom(c *fiber.Ctx) error {
var params BookRoomParams
if err := c.BodyParser(&params); err != nil {
Expand Down Expand Up @@ -80,6 +93,15 @@ func (h *RoomHandler) HandleBookRoom(c *fiber.Ctx) error {
return c.JSON(inserted)
}

// HandleGetRooms TODO
//
// @Summary Get all rooms
// @Description Get a list of all rooms available.
// @Tags rooms
// @Produce json
// @Success 200 {array} types.Room "List of rooms retrieved successfully"
// @Failure 500 {object} GenericResp "Internal Server Error"
// @Router /rooms [get]
func (h *RoomHandler) HandleGetRooms(c *fiber.Ctx) error {
rooms, err := h.store.Room.GetRooms(c.Context(), bson.M{})
if err != nil {
Expand Down
Loading

0 comments on commit 8f6fc8e

Please sign in to comment.