✨ More channel call operations available
♿ Prevent two user create call in a channel at the same time
			
			
This commit is contained in:
		| @@ -4,12 +4,16 @@ import ( | ||||
| 	"git.solsynth.dev/hydrogen/messaging/pkg/internal/database" | ||||
| 	"git.solsynth.dev/hydrogen/messaging/pkg/internal/gap" | ||||
| 	"git.solsynth.dev/hydrogen/messaging/pkg/internal/models" | ||||
| 	"git.solsynth.dev/hydrogen/messaging/pkg/internal/server/exts" | ||||
| 	"git.solsynth.dev/hydrogen/messaging/pkg/internal/services" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"sync" | ||||
| ) | ||||
|  | ||||
| var callLocks sync.Map | ||||
|  | ||||
| func listCall(c *fiber.Ctx) error { | ||||
| 	take := c.QueryInt("take", 0) | ||||
| 	offset := c.QueryInt("offset", 0) | ||||
| @@ -41,13 +45,16 @@ func getOngoingCall(c *fiber.Ctx) error { | ||||
|  | ||||
| 	if call, err := services.GetOngoingCall(channel); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 	} else if res, err := services.GetCallParticipants(call); err != nil { | ||||
| 		return c.JSON(call) | ||||
| 	} else { | ||||
| 		call.Participants = res | ||||
| 		return c.JSON(call) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func startCall(c *fiber.Ctx) error { | ||||
| 	if err := gap.H.EnsureAuthenticated(c); err != nil { | ||||
| 	if err := gap.H.EnsureGrantedPerm(c, "CreateCalls", true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| @@ -66,10 +73,19 @@ func startCall(c *fiber.Ctx) error { | ||||
| 		AccountID: user.ID, | ||||
| 	}).Find(&membership).Error; err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 	} else if membership.PowerLevel < 0 { | ||||
| 		return fiber.NewError(fiber.StatusForbidden, "you have not enough permission to create a call") | ||||
| 	} | ||||
|  | ||||
| 	if _, ok := callLocks.Load(channel.ID); ok { | ||||
| 		return fiber.NewError(fiber.StatusLocked, "there is already a call in creation progress for this channel") | ||||
| 	} else { | ||||
| 		callLocks.Store(channel.ID, true) | ||||
| 	} | ||||
|  | ||||
| 	call, err := services.NewCall(channel, membership) | ||||
| 	if err != nil { | ||||
| 		callLocks.Delete(channel.ID) | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||
| 	} else { | ||||
| 		_, _ = services.NewEvent(models.Event{ | ||||
| @@ -82,6 +98,7 @@ func startCall(c *fiber.Ctx) error { | ||||
| 			SenderID:  membership.ID, | ||||
| 		}) | ||||
|  | ||||
| 		callLocks.Delete(channel.ID) | ||||
| 		return c.JSON(call) | ||||
| 	} | ||||
| } | ||||
| @@ -111,7 +128,7 @@ func endCall(c *fiber.Ctx) error { | ||||
| 	call, err := services.GetOngoingCall(channel) | ||||
| 	if err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 	} else if call.FounderID != user.ID && membership.PowerLevel < 100 { | ||||
| 	} else if call.FounderID != user.ID && membership.PowerLevel < 50 { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, "only call founder or channel admin can end this call") | ||||
| 	} | ||||
|  | ||||
| @@ -132,6 +149,48 @@ func endCall(c *fiber.Ctx) error { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func kickParticipantInCall(c *fiber.Ctx) error { | ||||
| 	if err := gap.H.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
| 	alias := c.Params("channel") | ||||
|  | ||||
| 	var data struct { | ||||
| 		Username string `json:"username" validate:"required"` | ||||
| 	} | ||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	var channel models.Channel | ||||
| 	if err := database.C.Where(&models.Channel{ | ||||
| 		Alias: alias, | ||||
| 	}).First(&channel).Error; err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	var membership models.ChannelMember | ||||
| 	if err := database.C.Where(&models.ChannelMember{ | ||||
| 		ChannelID: channel.ID, | ||||
| 		AccountID: user.ID, | ||||
| 	}).Find(&membership).Error; err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	call, err := services.GetOngoingCall(channel) | ||||
| 	if err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 	} else if call.FounderID != user.ID && membership.PowerLevel < 50 { | ||||
| 		return fiber.NewError(fiber.StatusBadRequest, "only call founder or channel admin can kick participant in this call") | ||||
| 	} | ||||
|  | ||||
| 	if err = services.KickParticipantInCall(call, data.Username); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusInternalServerError, err.Error()) | ||||
| 	} | ||||
| 	return c.SendStatus(fiber.StatusOK) | ||||
| } | ||||
|  | ||||
| func exchangeCallToken(c *fiber.Ctx) error { | ||||
| 	if err := gap.H.EnsureAuthenticated(c); err != nil { | ||||
| 		return err | ||||
|   | ||||
| @@ -105,7 +105,7 @@ func listAvailableChannel(c *fiber.Ctx) error { | ||||
| } | ||||
|  | ||||
| func createChannel(c *fiber.Ctx) error { | ||||
| 	if err := gap.H.EnsureAuthenticated(c); err != nil { | ||||
| 	if err := gap.H.EnsureGrantedPerm(c, "CreateChannels", true); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	user := c.Locals("user").(models.Account) | ||||
|   | ||||
| @@ -43,6 +43,7 @@ func MapAPIs(app *fiber.App, baseURL string) { | ||||
| 			channels.Get("/:channel/calls/ongoing", getOngoingCall) | ||||
| 			channels.Post("/:channel/calls", startCall) | ||||
| 			channels.Delete("/:channel/calls/ongoing", endCall) | ||||
| 			channels.Delete("/:channel/calls/ongoing/participant", kickParticipantInCall) | ||||
| 			channels.Post("/:channel/calls/ongoing/token", exchangeCallToken) | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user