change timing to create room

peertube
Namekuji 2023-01-26 17:31:53 -05:00
rodzic b21aa42aaa
commit 1a6b838c9c
11 zmienionych plików z 108 dodań i 79 usunięć

Wyświetl plik

@ -20,7 +20,7 @@ webhook:
room:
auto_create: false
empty_timeout: 3600
empty_timeout: 30
max_participants: 0
max_metadata_size: 0

Wyświetl plik

@ -28,6 +28,8 @@ LIVEKIT_API_SECRET=secret
LIVEKIT_HOST=livekit.example.com
# This value will be returned by Audon backend to browsers. Set the same domain as LIVEKIT_HOST if you are not sure.
LIVEKIT_LOCAL_DOMAIN=livekit.example.com
# If this period (seconds) passes, the new room will be automatically closed.
LIVEKIT_EMPTY_ROOM_TIMEOUT=300
### Bot Settings ###
# Leave the following fields empty to disable the notification bot.

Wyświetl plik

@ -26,8 +26,8 @@ comingFuture: "Coming with future update!"
processing: "Processing now...<br />Keep this window open!"
lostWarning: "Unsaved data will be lost if you leave the page, are you sure?"
staticLink:
title: "Your Static Link"
hint: "Other can join to your room via this link while you're hosting."
title: "Your Audon Link"
hint: "Other can join to your room via this personal link while you're hosting."
form:
title: "Title"
titleRequired: "Room title required"
@ -48,6 +48,7 @@ shareRoomMessage: "Join my Audon room!\n{link}\n\nTitle: {title}"
roomReady:
header: "Your room is ready!"
message: "Your room \"{title}\" is now ready. Share the following URL with other participants."
timeout: "The room will be closed automatically if no one joins within {minutes} minutes."
errors:
offline: "This user is not hosting now."
invalidAddress: "Invalid address"

Wyświetl plik

@ -26,8 +26,8 @@ comingFuture: "今後のアップデートで追加予定"
processing: "処理中です。<br />画面を閉じないでください。"
lostWarning: "この画面を閉じると保存前の内容が失われます。構いませんか?"
staticLink:
title: "固定リンク"
hint: "プロフィール欄などに固定しておくと、あなたが部屋をホストしたとき他の人はこのリンクを使って参加できます。"
title: "Audon リンク"
hint: "あなたが部屋をホストしたとき、他の人はこの固定 URL からでも参加できます。"
form:
title: "タイトル"
titleRequired: "部屋の名前を入力してください"
@ -48,6 +48,7 @@ shareRoomMessage: "Audon で部屋を作りました!\n参加用リンク {
roomReady:
header: "お部屋の用意ができました!"
message: "{title} を作りました。参加者に以下の URL を共有してください。"
timeout: "{minutes} 分以内に誰も入室しないと部屋が閉じますのでご注意ください。"
errors:
offline: "このユーザーは現在ホスト中ではありません。"
invalidAddress: "アドレスが有効ではありません"

Wyświetl plik

@ -104,11 +104,13 @@ export default {
if (!donURL) return "";
const url = new URL(donURL);
const texts = [
this.$t("shareRoomMessage", { link: this.roomURL, title: this.title }),
this.$t("shareRoomMessage", {
link: this.donStore.myStaticLink,
title: this.title,
}),
];
if (this.description)
texts.push(truncate("\n" + this.description, { length: 200 }));
texts.push("\n#Audon");
return encodeURI(`${url.origin}/share?text=${texts.join("\n")}`);
},
},
@ -209,7 +211,7 @@ export default {
{{ $t("roomReady.message", { title }) }}
</div>
<div class="my-3">
<h3 style="word-break: break-all">{{ roomURL }}</h3>
<h3 style="word-break: break-all">{{ donStore.myStaticLink }}</h3>
</div>
<div>
<v-btn
@ -221,7 +223,7 @@ export default {
>{{ $t("share") }}</v-btn
>
<v-btn
@click="clipboard.copy(roomURL)"
@click="clipboard.copy(donStore.myStaticLink)"
color="lime"
size="small"
:prepend-icon="
@ -230,7 +232,10 @@ export default {
>{{ clipboard.copied.value ? $t("copied") : $t("copy") }}</v-btn
>
</div>
<div class="text-center mt-10 mb-1">
<v-alert class="mt-5" density="compact" type="warning" variant="tonal">{{
$t("roomReady.timeout", { minutes: 5 })
}}</v-alert>
<div class="text-center mt-5 mb-1">
<v-btn
color="indigo"
:to="{ name: 'room', params: { id: createdRoomID } }"

Wyświetl plik

@ -10,9 +10,8 @@ export default {
if (!type) return;
if (type === "offline") {
const target = new URL(decodeURI(this.$route.query.url));
alert(this.$t("errors.offline"));
window.location.href = target.toString();
this.$router.push({ name: "home" });
}
},
};

Wyświetl plik

@ -6,6 +6,8 @@ import (
"net/url"
"os"
"path/filepath"
"strconv"
"time"
"github.com/joho/godotenv"
)
@ -30,11 +32,12 @@ type (
}
LivekitConfig struct {
APIKey string `validate:"required,ascii"`
APISecret string `validate:"required,ascii"`
Host string `validate:"required,hostname|hostname_port"`
LocalDomain string `validate:"required,hostname|hostname_port"`
URL *url.URL
APIKey string `validate:"required,ascii"`
APISecret string `validate:"required,ascii"`
Host string `validate:"required,hostname|hostname_port"`
LocalDomain string `validate:"required,hostname|hostname_port"`
URL *url.URL
EmptyRoomTimeout time.Duration `validate:"required"`
}
DBConfig struct {
@ -164,11 +167,16 @@ func loadConfig(envname string) (*AppConfig, error) {
appConf.Redis = redisConf
// Setup LiveKit config
timeout, err := strconv.Atoi(os.Getenv("LIVEKIT_EMPTY_ROOM_TIMEOUT"))
if err != nil {
return nil, err
}
lkConf := &LivekitConfig{
APIKey: os.Getenv("LIVEKIT_API_KEY"),
APISecret: os.Getenv("LIVEKIT_API_SECRET"),
Host: os.Getenv("LIVEKIT_HOST"),
LocalDomain: os.Getenv("LIVEKIT_LOCAL_DOMAIN"),
APIKey: os.Getenv("LIVEKIT_API_KEY"),
APISecret: os.Getenv("LIVEKIT_API_SECRET"),
Host: os.Getenv("LIVEKIT_HOST"),
LocalDomain: os.Getenv("LIVEKIT_LOCAL_DOMAIN"),
EmptyRoomTimeout: time.Duration(timeout) * time.Second,
}
if err := mainValidator.Struct(lkConf); err != nil {
return nil, err

94
room.go
Wyświetl plik

@ -99,6 +99,8 @@ func createRoomHandler(c echo.Context) error {
}
room.RoomID = canonic()
room.CreatedAt = now
// if cohosts are already registered, retrieve their data from DB
for i, cohost := range room.CoHosts {
cohostUser, err := findUserByRemote(c.Request().Context(), cohost.RemoteID, cohost.RemoteURL)
@ -112,6 +114,37 @@ func createRoomHandler(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError)
}
// Create livekit room
roomMetadata := &RoomMetadata{Room: room, MastodonAccounts: make(map[string]*MastodonAccount)}
metadata, _ := json.Marshal(roomMetadata)
_, err = lkRoomServiceClient.CreateRoom(c.Request().Context(), &livekit.CreateRoomRequest{
Name: room.RoomID,
Metadata: string(metadata),
})
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusConflict)
}
countdown := time.NewTimer(mainConfig.Livekit.EmptyRoomTimeout)
orphanRooms.Set(room.RoomID, true, ttlcache.DefaultTTL)
go func(r *Room, logger echo.Logger) {
<-countdown.C
if orphaned := orphanRooms.Get(r.RoomID); orphaned == nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if !r.IsAnyomeInLivekitRoom(ctx) {
if err := endRoom(ctx, r); err != nil {
logger.Error(err)
}
}
}(room, c.Logger())
return c.String(http.StatusCreated, room.RoomID)
}
@ -402,50 +435,31 @@ func joinRoomHandler(c echo.Context) (err error) {
c.Logger().Error(err)
}
// Create room in LiveKit if it doesn't exist
if lkRoom == nil {
room.CreatedAt = now
coll := mainDB.Collection(COLLECTION_ROOM)
if _, err := coll.UpdateOne(c.Request().Context(),
bson.D{{Key: "room_id", Value: roomID}},
bson.D{{Key: "$set", Value: bson.D{{Key: "created_at", Value: now}}}}); err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
metadata, _ := json.Marshal(roomMetadata)
_, err = lkRoomServiceClient.CreateRoom(c.Request().Context(), &livekit.CreateRoomRequest{
Name: room.RoomID,
Metadata: string(metadata),
})
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusConflict)
}
} else {
currentMeta, err := getRoomMetadataFromLivekitRoom(lkRoom)
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
currentMeta.MastodonAccounts[user.AudonID] = mastoAccount
newMetadata, err := json.Marshal(currentMeta)
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
_, err = lkRoomServiceClient.UpdateRoomMetadata(c.Request().Context(), &livekit.UpdateRoomMetadataRequest{
Room: roomID,
Metadata: string(newMetadata),
})
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
// Update room metadata
currentMeta, err := getRoomMetadataFromLivekitRoom(lkRoom)
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
currentMeta.MastodonAccounts[user.AudonID] = mastoAccount
newMetadata, err := json.Marshal(currentMeta)
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
_, err = lkRoomServiceClient.UpdateRoomMetadata(c.Request().Context(), &livekit.UpdateRoomMetadataRequest{
Room: roomID,
Metadata: string(newMetadata),
})
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
// Store user's session data in cache
data, _ := getSessionData(c)
roomSessionCache.Set(user.AudonID, data, ttlcache.DefaultTTL)
userSessionCache.Set(user.AudonID, data, ttlcache.DefaultTTL)
orphanRooms.Delete(roomID)
return c.JSON(http.StatusOK, resp)
}

Wyświetl plik

@ -53,8 +53,9 @@ var (
mainConfig *AppConfig
lkRoomServiceClient *lksdk.RoomServiceClient
localeBundle *i18n.Bundle
roomSessionCache *ttlcache.Cache[string, *SessionData]
userSessionCache *ttlcache.Cache[string, *SessionData]
webhookTimerCache *ttlcache.Cache[string, *time.Timer]
orphanRooms *ttlcache.Cache[string, bool]
)
func init() {
@ -154,10 +155,12 @@ func main() {
e.Use(session.Middleware(redisStore))
// Setup caches
roomSessionCache = ttlcache.New(ttlcache.WithTTL[string, *SessionData](24 * time.Hour))
userSessionCache = ttlcache.New(ttlcache.WithTTL[string, *SessionData](24 * time.Hour))
webhookTimerCache = ttlcache.New(ttlcache.WithTTL[string, *time.Timer](60 * time.Second))
go roomSessionCache.Start()
orphanRooms = ttlcache.New(ttlcache.WithTTL[string, bool](24 * time.Hour))
go userSessionCache.Start()
go webhookTimerCache.Start()
go orphanRooms.Start()
e.POST("/app/login", loginHandler)
e.GET("/app/oauth", oauthHandler)
@ -203,8 +206,9 @@ func main() {
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
e.Logger.Print("Attempting graceful shutdown")
defer shutdownCancel()
roomSessionCache.DeleteAll()
userSessionCache.DeleteAll()
webhookTimerCache.DeleteAll()
orphanRooms.DeleteAll()
if err := e.Shutdown(shutdownCtx); err != nil {
e.Logger.Fatalf("Failed shutting down gracefully: %s\n", err.Error())
}

19
user.go
Wyświetl plik

@ -10,6 +10,7 @@ import (
"github.com/labstack/echo/v4"
mastodon "github.com/mattn/go-mastodon"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
type MastodonAccount struct {
@ -77,19 +78,13 @@ func redirectUserHandler(c echo.Context) error {
return ErrUserNotFound
}
rooms, err := user.GetCurrentLivekitRooms(c.Request().Context())
if err != nil {
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
coll := mainDB.Collection(COLLECTION_ROOM)
opts := options.FindOne().SetSort(bson.D{{Key: "created_at", Value: -1}})
var room Room
for _, r := range rooms {
meta, err := getRoomMetadataFromLivekitRoom(r)
if err != nil {
continue
}
if meta.Host.Equal(user) {
return c.Redirect(http.StatusFound, fmt.Sprintf("/r/%s", r.GetName()))
if err := coll.FindOne(c.Request().Context(), bson.D{{Key: "host.audon_id", Value: user.AudonID}}, opts).Decode(&room); err == nil {
if room.ExistsInLivekit(c.Request().Context()) {
return c.Redirect(http.StatusFound, fmt.Sprintf("/r/%s", room.RoomID))
}
}

Wyświetl plik

@ -47,11 +47,10 @@ func livekitWebhookHandler(c echo.Context) error {
}
still, err := user.InLivekit(c.Request().Context())
if !still && err == nil {
data := roomSessionCache.Get(audonID)
data := userSessionCache.Get(audonID)
if data == nil {
return echo.NewHTTPError(http.StatusGone)
}
roomSessionCache.Delete(audonID)
mastoClient := getMastodonClient(data.Value())
if mastoClient == nil {
c.Logger().Errorf("unable to get mastodon client: %v", data.Value().MastodonConfig)
@ -93,6 +92,7 @@ func livekitWebhookHandler(c echo.Context) error {
log.Println(err)
}
nextUser.ClearUserAvatar(ctx)
userSessionCache.Delete(audonID)
} else if err != nil {
log.Println(err)
}