From 01dc8ec7dff504d008bb23c9132e2e6ca7b33b34 Mon Sep 17 00:00:00 2001 From: TheSilentSage Date: Mon, 30 Jun 2025 18:22:49 +0530 Subject: [PATCH] feat: join code for users to join without invitation --- vitty-backend-api/api/serializers/circles.go | 20 ++++-- vitty-backend-api/api/v2/circleHandler.go | 72 +++++++++++++++++++ .../internal/database/initialize.go | 2 +- vitty-backend-api/internal/models/circles.go | 25 +++++-- vitty-backend-api/internal/utils/circles.go | 18 +++++ 5 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 vitty-backend-api/internal/utils/circles.go diff --git a/vitty-backend-api/api/serializers/circles.go b/vitty-backend-api/api/serializers/circles.go index 6df33ba..12d97ff 100644 --- a/vitty-backend-api/api/serializers/circles.go +++ b/vitty-backend-api/api/serializers/circles.go @@ -8,10 +8,12 @@ func CirclesListSerializer(ucj []models.UsersCirclesJoin) []map[string]interface for _, userCircle := range ucj { out := map[string]interface{}{ - "circle_id": userCircle.CID, - "circle_role": userCircle.CircleRole, - "circle_name": userCircle.Circles.CircleName, + "circle_id": userCircle.CID, + "circle_role": userCircle.CircleRole, + "circle_name": userCircle.Circles.CircleName, + "circle_join_code": userCircle.Circles.CircleJoinCode, } + result = append(result, out) } @@ -39,11 +41,15 @@ func UsersListCircleSerializer(users []models.User) []map[string]interface{} { var result []map[string]interface{} for _, user := range users { + + currStatus := user.GetCurrentStatus() + out := map[string]interface{}{ - "username": user.Username, - "name": user.Name, - "picture": user.Picture, - "email": user.Email, + "current_status": currStatus, + "username": user.Username, + "name": user.Name, + "picture": user.Picture, + "email": user.Email, } result = append(result, out) } diff --git a/vitty-backend-api/api/v2/circleHandler.go b/vitty-backend-api/api/v2/circleHandler.go index 76336aa..fc8474b 100644 --- a/vitty-backend-api/api/v2/circleHandler.go +++ b/vitty-backend-api/api/v2/circleHandler.go @@ -22,7 +22,9 @@ func circleHandler(api fiber.Router) { group.Get("/requests/sent", getSentCircleRequests) group.Post("/create/:circleName", createCircle) group.Post("/sendRequest/:circleId/:username", sendCircleRequestToUser) + group.Post("/:circleId/generateJoinCode", generateCircleJoinCode) group.Post("/acceptRequest/:circleId", acceptCircleRequest) + group.Post("/join", joinCircleByCode) group.Post("/declineRequest/:circleId", declineCircleRequest) group.Patch("/", updateCircleName) group.Delete("/:circleId", deleteCircle) @@ -279,6 +281,32 @@ func sendCircleRequestToUser(c *fiber.Ctx) error { }) } +func generateCircleJoinCode(c *fiber.Ctx) error { + var circle models.Circles + + circleId := c.Params("circleId") + + circle.CircleId = circleId + + joinCode := utils.GenerateJoinCode(10) + err := circle.CreateCircleJoinCode(joinCode) + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return c.Status(fiber.StatusBadRequest).JSON(fiber.ErrBadRequest) + } + + log.Println(err) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "detail": "circle share code generation failed", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "detail": "circle code generated", + }) +} + func acceptCircleRequest(c *fiber.Ctx) error { var circleRequest models.CircleRequest @@ -308,6 +336,50 @@ func acceptCircleRequest(c *fiber.Ctx) error { }) } +func joinCircleByCode(c *fiber.Ctx) error { + var circle models.Circles + var userCircle models.UsersCirclesJoin + + joinCode := c.Query("code") + reqUser := c.Locals("user").(models.User).Username + + err := circle.GetCircleByJoinCode(joinCode) + + if err != nil { + + if errors.Is(err, gorm.ErrRecordNotFound) { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "detail": "invalid join code", + }) + } + log.Println(err) + } + + userCircle.CID = circle.CircleId + userCircle.CircleRole = "member" + userCircle.Uname = reqUser + + err = userCircle.AddUserToCircle() + + if err != nil { + log.Println(err) + + if errors.Is(err, gorm.ErrDuplicatedKey) { + return c.Status(fiber.StatusConflict).JSON(fiber.Map{ + "error": "you are already part of the circle", + }) + } + + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "failed to join circle", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "detail": "joined circle successfully", + }) +} + func updateCircleName(c *fiber.Ctx) error { var circle models.Circles diff --git a/vitty-backend-api/internal/database/initialize.go b/vitty-backend-api/internal/database/initialize.go index 3a2c164..4032fd8 100644 --- a/vitty-backend-api/internal/database/initialize.go +++ b/vitty-backend-api/internal/database/initialize.go @@ -20,7 +20,7 @@ func Connect(debug string, dbUrls string) { }) } else { DB, err = gorm.Open(postgres.Open(dbUrls), &gorm.Config{ - Logger: logger.Default.LogMode(logger.Silent), + Logger: logger.Default.LogMode(logger.Silent), TranslateError: true, }) } diff --git a/vitty-backend-api/internal/models/circles.go b/vitty-backend-api/internal/models/circles.go index ff8763f..71683cb 100644 --- a/vitty-backend-api/internal/models/circles.go +++ b/vitty-backend-api/internal/models/circles.go @@ -11,11 +11,12 @@ import ( ) type Circles struct { - CircleId string `json:"circle_id" gorm:"unique"` - CircleName string `json:"circle_name" gorm:"primaryKey"` - Uname string `json:"omitempty" gorm:"primaryKey"` - CircleSlots string - User User `gorm:"foreignKey:Uname;references:Username;"` + CircleId string `json:"circle_id" gorm:"unique"` + CircleName string `json:"circle_name" gorm:"primaryKey"` + Uname string `json:"omitempty" gorm:"primaryKey"` + CircleSlots string + CircleJoinCode string `json:"circle_join_code" gorm:"default:null"` + User User `gorm:"foreignKey:Uname;references:Username;"` } func (c *Circles) CreateCircle() error { @@ -23,8 +24,13 @@ func (c *Circles) CreateCircle() error { return err } -func (c *Circles) GetCircleByCircleId() error { - err := database.DB.Where(c).First(&c).Error +func (c *Circles) GetCircleByCircleId(circleId string) error { + results := database.DB.Where("circle_join_code = ?", circleId).First(&c).Error + return results +} + +func (c *Circles) GetCircleByJoinCode(joinCode string) error { + err := database.DB.Where("circle_join_code = ?", joinCode).First(&c).Error return err } @@ -54,6 +60,11 @@ func (c *Circles) updateCircleSlots(circleSlotMap map[string]string) error { return err } +func (c *Circles) CreateCircleJoinCode(joinCode string) error { + err := database.DB.Model(&Circles{}).Where("circle_id like ?", c.CircleId).Update("circle_join_code", joinCode).Error + return err +} + func (c *Circles) DeleteCircle() error { result := database.DB.Where(&c).Delete(&Circles{}) diff --git a/vitty-backend-api/internal/utils/circles.go b/vitty-backend-api/internal/utils/circles.go new file mode 100644 index 0000000..d493e92 --- /dev/null +++ b/vitty-backend-api/internal/utils/circles.go @@ -0,0 +1,18 @@ +package utils + +import ( + "math/rand" + "time" +) + +const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +func GenerateJoinCode(length int) string { + var rng = rand.New(rand.NewSource(time.Now().UnixNano())) + + b := make([]byte, length) + for i := range b { + b[i] = charset[rng.Intn(len(charset))] + } + return string(b) +}