💩 Switch to use livekit (Non-tested)

This commit is contained in:
2024-04-27 00:04:01 +08:00
parent a8008c2c8c
commit 88d3fbcf5c
10 changed files with 433 additions and 48 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"git.solsynth.dev/hydrogen/messaging/pkg/external"
"git.solsynth.dev/hydrogen/messaging/pkg/services"
"github.com/robfig/cron/v3"
"os"
@ -42,11 +43,10 @@ func main() {
}
// Connect other services
go func() {
if err := grpc.ConnectPassport(); err != nil {
log.Fatal().Err(err).Msg("An error occurred when connecting to identity grpc endpoint...")
}
}()
external.SetupLiveKit()
if err := grpc.ConnectPassport(); err != nil {
log.Fatal().Err(err).Msg("An error occurred when connecting to identity grpc endpoint...")
}
// Server
server.NewServer()

18
pkg/external/livekit.go vendored Normal file
View File

@ -0,0 +1,18 @@
package external
import (
lksdk "github.com/livekit/server-sdk-go"
"github.com/spf13/viper"
)
var Lk *lksdk.RoomServiceClient
func SetupLiveKit() {
host := "https://" + viper.GetString("calling.endpoint")
Lk = lksdk.NewRoomServiceClient(
host,
viper.GetString("calling.api_key"),
viper.GetString("calling.api_secret"),
)
}

View File

@ -2,17 +2,10 @@ package models
import "time"
type CallProvider = string
const (
CallProviderJitsi = "jitsi"
)
type Call struct {
BaseModel
Provider string `json:"provider"`
EndedAt *time.Time `json:"ended_at"`
EndedAt *time.Time `json:"ended_at"`
ExternalID string `json:"external_id"`
FounderID uint `json:"founder_id"`

View File

@ -136,10 +136,10 @@ func exchangeCallToken(c *fiber.Ctx) error {
} else {
return c.JSON(fiber.Map{
"token": tk,
"endpoint": viper.GetString("meeting.endpoint"),
"endpoint": viper.GetString("calling.endpoint"),
"full_url": fmt.Sprintf(
"%s/%s?jwt=%s",
viper.GetString("meeting.endpoint"),
viper.GetString("calling.endpoint"),
call.ExternalID,
url.QueryEscape(tk),
),

View File

@ -53,13 +53,15 @@ func createChannel(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
var data struct {
Alias string `json:"alias" validate:"required,min=4,max=32"`
Alias string `json:"alias" validate:"required,lowercase,min=4,max=32"`
Name string `json:"name" validate:"required"`
Description string `json:"description"`
}
if err := BindAndValidate(c, &data); err != nil {
return err
} else if err = services.GetChannelAliasAvailability(data.Alias); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
channel, err := services.NewChannel(user, data.Alias, data.Name, data.Description)

View File

@ -1,11 +1,14 @@
package services
import (
"context"
"errors"
"fmt"
"git.solsynth.dev/hydrogen/messaging/pkg/database"
"git.solsynth.dev/hydrogen/messaging/pkg/external"
"git.solsynth.dev/hydrogen/messaging/pkg/models"
"github.com/golang-jwt/jwt/v5"
"github.com/livekit/protocol/auth"
"github.com/livekit/protocol/livekit"
"github.com/rs/zerolog/log"
"github.com/samber/lo"
"github.com/spf13/viper"
@ -65,8 +68,7 @@ func GetOngoingCall(channel models.Channel) (models.Call, error) {
func NewCall(channel models.Channel, founder models.ChannelMember) (models.Call, error) {
call := models.Call{
Provider: models.CallProviderJitsi,
ExternalID: channel.Name,
ExternalID: channel.Alias,
FounderID: founder.ID,
ChannelID: channel.ID,
Founder: founder,
@ -77,6 +79,15 @@ func NewCall(channel models.Channel, founder models.ChannelMember) (models.Call,
return call, fmt.Errorf("this channel already has an ongoing call")
}
_, err := external.Lk.CreateRoom(context.Background(), &livekit.CreateRoomRequest{
Name: call.ExternalID,
EmptyTimeout: viper.GetUint32("calling.empty_timeout_duration"),
MaxParticipants: viper.GetUint32("calling.max_participants"),
})
if err != nil {
return call, fmt.Errorf("remote livekit error: %v", err)
}
var members []models.ChannelMember
if err := database.C.Save(&call).Error; err != nil {
return call, err
@ -111,6 +122,12 @@ func NewCall(channel models.Channel, founder models.ChannelMember) (models.Call,
func EndCall(call models.Call) (models.Call, error) {
call.EndedAt = lo.ToPtr(time.Now())
if _, err := external.Lk.DeleteRoom(context.Background(), &livekit.DeleteRoomRequest{
Room: call.ExternalID,
}); err != nil {
log.Error().Err(err).Msg("Unable to delete room at livekit side")
}
var members []models.ChannelMember
if err := database.C.Save(&call).Error; err != nil {
return call, err
@ -129,20 +146,22 @@ func EndCall(call models.Call) (models.Call, error) {
return call, nil
}
func EncodeCallToken(user models.Account) (string, error) {
// Jitsi requires HS256 as algorithm, so we cannot use HS512
tk := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"context": jwt.MapClaims{
"user": jwt.MapClaims{
"avatar": user.Avatar,
"name": user.Name,
},
},
"aud": viper.GetString("meeting.client_id"),
"iss": viper.GetString("domain"),
"sub": "meet.jitsi",
"room": "*",
})
func EncodeCallToken(user models.Account, call models.Call) (string, error) {
isAdmin := false
if user.ID == call.FounderID || user.ID == call.Channel.AccountID {
isAdmin = true
}
return tk.SignedString([]byte(viper.GetString("meeting.client_secret")))
identity := fmt.Sprintf("%d", user.ID)
grant := &auth.VideoGrant{
Room: call.ExternalID,
RoomJoin: true,
RoomAdmin: isAdmin,
}
duration := time.Second * time.Duration(viper.GetInt("calling.token_duration"))
tk := auth.NewAccessToken(viper.GetString("calling.api_key"), viper.GetString("calling.api_secret"))
tk.AddGrant(grant).SetIdentity(identity).SetValidFor(duration)
return tk.ToJWT()
}

View File

@ -2,12 +2,20 @@ package services
import (
"fmt"
"regexp"
"git.solsynth.dev/hydrogen/messaging/pkg/database"
"git.solsynth.dev/hydrogen/messaging/pkg/models"
"github.com/samber/lo"
)
func GetChannelAliasAvailability(alias string) error {
if !regexp.MustCompile("^[a-z0-9-]+$").MatchString(alias) {
return fmt.Errorf("channel alias should only contains lowercase letters, numbers, and hyphens")
}
return nil
}
func GetChannel(id uint) (models.Channel, error) {
var channel models.Channel
if err := database.C.Where(models.Channel{