audon/room.go

271 wiersze
6.8 KiB
Go
Czysty Zwykły widok Historia

2022-12-03 03:20:49 +00:00
package main
import (
2022-12-05 01:11:44 +00:00
"context"
2022-12-06 01:48:03 +00:00
"encoding/json"
2022-12-05 01:11:44 +00:00
"errors"
2022-12-03 03:20:49 +00:00
"net/http"
"time"
"github.com/jaevor/go-nanoid"
"github.com/labstack/echo/v4"
"github.com/livekit/protocol/auth"
2022-12-05 02:38:34 +00:00
"github.com/livekit/protocol/livekit"
2022-12-05 01:11:44 +00:00
"go.mongodb.org/mongo-driver/bson"
2022-12-03 03:20:49 +00:00
"go.mongodb.org/mongo-driver/mongo"
)
2022-12-05 08:33:11 +00:00
type (
TokenResponse struct {
RtcURL string `json:"rtc"`
Token string `json:"token"`
}
)
2022-12-05 07:45:51 +00:00
2022-12-04 19:50:55 +00:00
// handler for POST to /api/room
2022-12-05 01:11:44 +00:00
func createRoomHandler(c echo.Context) error {
2022-12-03 03:20:49 +00:00
room := new(Room)
2022-12-05 01:11:44 +00:00
if err := c.Bind(room); err != nil {
2022-12-03 03:20:49 +00:00
return ErrInvalidRequestFormat
}
2022-12-05 01:11:44 +00:00
if err := mainValidator.StructExcept(room, "RoomID"); err != nil { // New RoomID will be created, so one in request doesn't matter
2022-12-03 03:20:49 +00:00
return wrapValidationError(err)
}
canonic, err := nanoid.Standard(16)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
room.RoomID = canonic()
2022-12-05 01:11:44 +00:00
host := c.Get("user").(*AudonUser)
2022-12-03 03:20:49 +00:00
room.Host = host
2022-12-04 17:52:44 +00:00
now := time.Now().UTC()
2022-12-05 01:11:44 +00:00
if now.After(room.ScheduledAt) {
2022-12-04 17:52:44 +00:00
room.ScheduledAt = now
}
2022-12-05 01:11:44 +00:00
// if cohosts are already registered, retrieve their data from DB
2022-12-04 05:19:41 +00:00
for i, cohost := range room.CoHost {
2022-12-03 03:20:49 +00:00
cohostUser, err := findUserByRemote(c.Request().Context(), cohost.RemoteID, cohost.RemoteURL)
if err == nil {
2022-12-04 17:52:44 +00:00
room.CoHost[i] = cohostUser
2022-12-03 03:20:49 +00:00
}
}
2022-12-04 05:19:41 +00:00
2022-12-04 17:52:44 +00:00
room.CreatedAt = now
coll := mainDB.Collection(COLLECTION_ROOM)
if _, insertErr := coll.InsertOne(c.Request().Context(), room); insertErr != nil {
c.Logger().Error(insertErr)
2022-12-04 05:19:41 +00:00
return echo.NewHTTPError(http.StatusInternalServerError)
}
2022-12-04 17:52:44 +00:00
return c.String(http.StatusCreated, room.RoomID)
2022-12-03 03:20:49 +00:00
}
2022-12-05 02:38:34 +00:00
func joinRoomHandler(c echo.Context) (err error) {
2022-12-05 01:11:44 +00:00
roomID := c.Param("id")
if err := mainValidator.Var(&roomID, "required,printascii"); err != nil {
return wrapValidationError(err)
}
user := c.Get("user").(*AudonUser)
room, err := findRoomByID(c.Request().Context(), roomID)
if err != nil {
return echo.NewHTTPError(http.StatusNotFound)
}
now := time.Now().UTC()
// check if room is not yet started
if room.ScheduledAt.After(now) {
return echo.NewHTTPError(http.StatusConflict, "not_yet_started")
}
// check if room has already ended
if !room.EndedAt.IsZero() && room.EndedAt.Before(now) {
return echo.NewHTTPError(http.StatusGone, "already_ended")
}
// return 403 if one has been kicked
for _, kicked := range room.Kicked {
if kicked.Equal(user) {
return echo.NewHTTPError(http.StatusForbidden)
}
}
2022-12-06 01:48:03 +00:00
token, err := getRoomToken(room, user, room.IsHost(user) || room.IsCoHost(user)) // host and cohost can talk from the beginning
2022-12-05 01:11:44 +00:00
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
2022-12-05 08:33:11 +00:00
resp := &TokenResponse{
2022-12-05 07:45:51 +00:00
RtcURL: mainConfig.Livekit.URL.String(),
Token: token,
}
2022-12-06 01:48:03 +00:00
// Create room in LiveKit if it doesn't exist
metadata, _ := json.Marshal(room)
2022-12-05 07:45:51 +00:00
_, err = lkRoomServiceClient.CreateRoom(c.Request().Context(), &livekit.CreateRoomRequest{
2022-12-06 01:48:03 +00:00
Name: room.RoomID,
Metadata: string(metadata),
2022-12-05 07:45:51 +00:00
})
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusConflict)
}
2022-12-05 02:38:34 +00:00
2022-12-05 07:45:51 +00:00
return c.JSON(http.StatusOK, resp)
2022-12-05 01:11:44 +00:00
}
// intended to be called by room's host
func closeRoomHandler(c echo.Context) error {
roomID := c.Param("id")
if err := mainValidator.Var(&roomID, "required,printascii"); err != nil {
return wrapValidationError(err)
}
// retrieve room info from the given room ID
room, err := findRoomByID(c.Request().Context(), roomID)
if err == mongo.ErrNoDocuments {
2022-12-06 01:48:03 +00:00
return ErrRoomNotFound
2022-12-05 01:11:44 +00:00
} else if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
// only host can close the room
user := c.Get("user").(*AudonUser)
if !room.IsHost(user) {
2022-12-06 01:48:03 +00:00
return ErrOperationNotPermitted
2022-12-05 01:11:44 +00:00
}
if err := endRoom(c.Request().Context(), room); err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
return c.NoContent(http.StatusOK)
}
2022-12-05 08:33:11 +00:00
func updatePermissionHandler(c echo.Context) error {
2022-12-06 01:48:03 +00:00
roomID := c.Param("room")
// look up room in livekit
room, exists := getRoomInLivekit(c.Request().Context(), roomID)
if !exists {
return ErrRoomNotFound
}
audonRoom := new(Room)
err := json.Unmarshal([]byte(room.Metadata), audonRoom)
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
iam := c.Get("user").(*AudonUser)
if !(audonRoom.IsHost(iam) || audonRoom.IsCoHost(iam)) {
return ErrOperationNotPermitted
}
tgtAudonID := c.Param("user")
if !audonRoom.IsUserInLivekitRoom(c.Request().Context(), tgtAudonID) {
return ErrUserNotFound
}
2022-12-05 08:33:11 +00:00
2022-12-06 01:48:03 +00:00
permission := new(livekit.ParticipantPermission)
if err := c.Bind(permission); err != nil {
return ErrInvalidRequestFormat
}
info, err := lkRoomServiceClient.UpdateParticipant(c.Request().Context(), &livekit.UpdateParticipantRequest{
Room: roomID,
Identity: tgtAudonID,
Permission: permission,
})
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
return c.JSON(http.StatusOK, info)
2022-12-05 08:33:11 +00:00
}
2022-12-06 01:48:03 +00:00
func getRoomToken(room *Room, user *AudonUser, canTalk bool) (string, error) {
2022-12-04 05:19:41 +00:00
at := auth.NewAccessToken(mainConfig.Livekit.APIKey, mainConfig.Livekit.APISecret)
2022-12-06 01:48:03 +00:00
canPublishData := true
2022-12-03 03:20:49 +00:00
grant := &auth.VideoGrant{
2022-12-06 01:48:03 +00:00
Room: room.RoomID,
RoomJoin: true,
RoomCreate: false,
CanPublish: &canTalk,
CanPublishData: &canPublishData,
}
metadata, _ := json.Marshal(map[string]string{
"remote_id": user.RemoteID,
"remote_url": user.RemoteURL,
})
at.AddGrant(grant).SetIdentity(user.AudonID).SetValidFor(10 * time.Minute).SetMetadata(string(metadata))
2022-12-03 03:20:49 +00:00
return at.ToJWT()
}
2022-12-05 01:11:44 +00:00
2022-12-06 01:48:03 +00:00
func getRoomInLivekit(ctx context.Context, roomID string) (*livekit.Room, bool) {
rooms, _ := lkRoomServiceClient.ListRooms(ctx, &livekit.ListRoomsRequest{Names: []string{roomID}})
if len(rooms.GetRooms()) != 0 {
return nil, false
}
return rooms.GetRooms()[0], true
}
2022-12-05 01:11:44 +00:00
func findRoomByID(ctx context.Context, roomID string) (*Room, error) {
var room Room
collRoom := mainDB.Collection(COLLECTION_ROOM)
if err := collRoom.FindOne(ctx, bson.D{{Key: "room_id", Value: roomID}}).Decode(&room); err != nil {
return nil, err
}
return &room, nil
}
func endRoom(ctx context.Context, room *Room) error {
if room == nil {
return errors.New("room cannot be nil")
}
if !room.EndedAt.IsZero() {
return nil
}
now := time.Now().UTC()
collRoom := mainDB.Collection(COLLECTION_ROOM)
if _, err := collRoom.UpdateOne(ctx,
bson.D{{Key: "room_id", Value: room.RoomID}},
bson.D{
{Key: "$set", Value: bson.D{{Key: "ended_at", Value: now}}},
}); err != nil {
return err
}
2022-12-05 02:38:34 +00:00
rooms, err := lkRoomServiceClient.ListRooms(ctx, &livekit.ListRoomsRequest{Names: []string{room.RoomID}})
if err == nil && len(rooms.Rooms) != 0 {
_, err := lkRoomServiceClient.DeleteRoom(ctx, &livekit.DeleteRoomRequest{Room: room.RoomID})
if err != nil {
return err
}
} else if err != nil {
return err
}
2022-12-05 01:11:44 +00:00
return nil
}