diff options
| author | Leonardo Bishop <me@leonardobishop.com> | 2025-01-17 13:21:24 +0000 |
|---|---|---|
| committer | Leonardo Bishop <me@leonardobishop.com> | 2025-01-17 13:21:24 +0000 |
| commit | c00b690bd6f600554a1404e692bd9e4373325d27 (patch) | |
| tree | 4488b625e1c24af52fced6f60ac1b3ddff1383bc /api/handlers | |
Initial commit
Diffstat (limited to 'api/handlers')
| -rw-r--r-- | api/handlers/favourites.go | 112 | ||||
| -rw-r--r-- | api/handlers/schedule.go | 25 | ||||
| -rw-r--r-- | api/handlers/users.go | 112 | ||||
| -rw-r--r-- | api/handlers/util.go | 28 |
4 files changed, 277 insertions, 0 deletions
diff --git a/api/handlers/favourites.go b/api/handlers/favourites.go new file mode 100644 index 0000000..862d366 --- /dev/null +++ b/api/handlers/favourites.go @@ -0,0 +1,112 @@ +package handlers + +import ( + "github.com/LMBishop/confplanner/api/dto" + "github.com/LMBishop/confplanner/pkg/favourites" + "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v5/pgtype" +) + +func CreateFavourite(service favourites.Service) fiber.Handler { + return func(c *fiber.Ctx) error { + var request dto.CreateFavouritesRequest + if err := readBody(c, &request); err != nil { + return err + } + + if request.GUID == nil && request.ID == nil { + return &dto.ErrorResponse{ + Code: fiber.StatusBadRequest, + Message: "One of event GUID or event ID must be specified", + } + } + + uid := c.Locals("uid").(int32) + var uuid pgtype.UUID + if request.GUID != nil { + if err := uuid.Scan(*request.GUID); err != nil { + return &dto.ErrorResponse{ + Code: fiber.StatusBadRequest, + Message: "Bad event GUID", + } + } + } + + createdFavourite, err := service.CreateFavouriteForUser(uid, uuid, request.ID) + if err != nil { + return err + } + + return &dto.OkResponse{ + Code: fiber.StatusCreated, + Data: &dto.CreateFavouritesResponse{ + ID: createdFavourite.ID, + }, + } + } +} + +func GetFavourites(service favourites.Service) fiber.Handler { + return func(c *fiber.Ctx) error { + uid := c.Locals("uid").(int32) + + favourites, err := service.GetFavouritesForUser(uid) + if err != nil { + return err + } + + favouritesResponse := make([]dto.GetFavouritesResponse, 0) + for _, favourite := range *favourites { + var favouriteResponse dto.GetFavouritesResponse + favouriteResponse.Scan(favourite) + + favouritesResponse = append(favouritesResponse, favouriteResponse) + } + + return &dto.OkResponse{ + Code: fiber.StatusOK, + Data: favouritesResponse, + } + } +} + +func DeleteFavourite(service favourites.Service) fiber.Handler { + return func(c *fiber.Ctx) error { + var request dto.DeleteFavouritesRequest + if err := readBody(c, &request); err != nil { + return err + } + + if request.GUID == nil && request.ID == nil { + return &dto.ErrorResponse{ + Code: fiber.StatusBadRequest, + Message: "One of event GUID or event ID must be specified", + } + } + + uid := c.Locals("uid").(int32) + var err error + var uuid pgtype.UUID + if err := uuid.Scan(*request.GUID); err != nil { + return &dto.ErrorResponse{ + Code: fiber.StatusBadRequest, + Message: "Bad event GUID", + } + } + + err = service.DeleteFavouriteForUserByEventDetails(uid, uuid, request.ID) + if err != nil { + if err == favourites.ErrNotFound { + return &dto.ErrorResponse{ + Code: fiber.StatusNotFound, + Message: "Favourite not found", + } + } + return err + } + + return &dto.OkResponse{ + Code: fiber.StatusOK, + } + } +} diff --git a/api/handlers/schedule.go b/api/handlers/schedule.go new file mode 100644 index 0000000..fd3a183 --- /dev/null +++ b/api/handlers/schedule.go @@ -0,0 +1,25 @@ +package handlers + +import ( + "github.com/LMBishop/confplanner/api/dto" + "github.com/LMBishop/confplanner/pkg/schedule" + "github.com/gofiber/fiber/v2" + "github.com/golang-cz/nilslice" +) + +func GetSchedule(service schedule.Service) fiber.Handler { + return func(c *fiber.Ctx) error { + schedule, lastUpdated, err := service.GetSchedule() + if err != nil { + return err + } + + return &dto.OkResponse{ + Code: fiber.StatusOK, + Data: &dto.GetScheduleResponse{ + Schedule: nilslice.Initialize(*schedule), + LastUpdated: *lastUpdated, + }, + } + } +} diff --git a/api/handlers/users.go b/api/handlers/users.go new file mode 100644 index 0000000..deda9ca --- /dev/null +++ b/api/handlers/users.go @@ -0,0 +1,112 @@ +package handlers + +import ( + "errors" + "time" + + "github.com/LMBishop/confplanner/api/dto" + "github.com/LMBishop/confplanner/pkg/user" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/session" +) + +func Register(service user.Service) fiber.Handler { + return func(c *fiber.Ctx) error { + var request dto.RegisterRequest + if err := readBody(c, &request); err != nil { + return err + } + + createdUser, err := service.CreateUser(request.Username, request.Password) + if err != nil { + if errors.Is(err, user.ErrUserExists) { + return &dto.ErrorResponse{ + Code: fiber.StatusConflict, + Message: "User with that username already exists", + } + } else if errors.Is(err, user.ErrNotAcceptingRegistrations) { + return &dto.ErrorResponse{ + Code: fiber.StatusForbidden, + Message: "This service is not currently accepting registrations", + } + } + + return err + } + + return &dto.OkResponse{ + Code: fiber.StatusCreated, + Data: &dto.RegisterResponse{ + ID: createdUser.ID, + }, + } + } +} + +func Login(service user.Service, store *session.Store) fiber.Handler { + return func(c *fiber.Ctx) error { + var request dto.LoginRequest + if err := readBody(c, &request); err != nil { + return err + } + + user, err := service.Authenticate(request.Username, request.Password) + if err != nil { + return err + } + + if user == nil { + return &dto.ErrorResponse{ + Code: fiber.StatusBadRequest, + Message: "Username and password combination not found", + } + } + + s, err := store.Get(c) + if err != nil { + return err + } + + if s.Fresh() { + uid := user.ID + sid := s.ID() + + s.Set("uid", uid) + s.Set("sid", sid) + s.Set("ip", c.Context().RemoteIP().String()) + s.Set("login", time.Unix(time.Now().Unix(), 0).UTC().String()) + s.Set("ua", string(c.Request().Header.UserAgent())) + + err = s.Save() + if err != nil { + return err + } + } + + return &dto.OkResponse{ + Code: fiber.StatusOK, + Data: &dto.LoginResponse{ + ID: user.ID, + Username: user.Username, + }, + } + } +} + +func Logout(store *session.Store) fiber.Handler { + return func(c *fiber.Ctx) error { + s, err := store.Get(c) + if err != nil { + return err + } + + err = s.Destroy() + if err != nil { + return err + } + + return &dto.OkResponse{ + Code: fiber.StatusNoContent, + } + } +} diff --git a/api/handlers/util.go b/api/handlers/util.go new file mode 100644 index 0000000..b0cf344 --- /dev/null +++ b/api/handlers/util.go @@ -0,0 +1,28 @@ +package handlers + +import ( + "fmt" + + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" +) + +var validate = validator.New(validator.WithRequiredStructEnabled()) + +func readBody(c *fiber.Ctx, request interface{}) error { + if err := c.BodyParser(request); err != nil { + return &fiber.Error{ + Code: fiber.StatusBadRequest, + Message: fmt.Errorf("Invalid request (%w)", err).Error(), + } + } + + if err := validate.Struct(request); err != nil { + return &fiber.Error{ + Code: fiber.StatusBadRequest, + Message: err.Error(), + } + } + + return nil +} |
