From 17e07d28a1acfb5c114a67372c6ff6f2ec050e62 Mon Sep 17 00:00:00 2001 From: Namekuji Date: Thu, 26 Jan 2023 13:28:25 -0500 Subject: [PATCH] add static url --- .devcontainer/Caddyfile | 2 +- audon-fe/package-lock.json | 54 ++++++++------------- audon-fe/src/locales/en.yaml | 1 + audon-fe/src/locales/ja.yaml | 1 + audon-fe/src/router/index.js | 34 ++++++++++++- audon-fe/src/views/CreateView.vue | 10 +++- audon-fe/src/views/ErrorView.vue | 23 +++++++++ audon-fe/src/views/HomeView.vue | 18 +++++-- audon-fe/src/views/NotFoundView.vue | 5 -- audon-fe/src/views/RoomView.vue | 6 +-- auth.go | 1 - room.go | 17 +++++++ schema.go | 18 +++++++ server.go | 1 + user.go | 74 ++++++++++++++++++++++++++--- webhooks.go | 4 +- 16 files changed, 209 insertions(+), 60 deletions(-) create mode 100644 audon-fe/src/views/ErrorView.vue delete mode 100644 audon-fe/src/views/NotFoundView.vue diff --git a/.devcontainer/Caddyfile b/.devcontainer/Caddyfile index adaaeca..f66d147 100644 --- a/.devcontainer/Caddyfile +++ b/.devcontainer/Caddyfile @@ -2,7 +2,7 @@ audon.localhost { tls /etc/caddy/certs/cert.pem /etc/caddy/certs/key.pem encode zstd gzip @backend { - path /app/* /api/* /storage/* + path /app/* /api/* /storage/* /u/* } handle @backend { reverse_proxy devcontainer:8100 diff --git a/audon-fe/package-lock.json b/audon-fe/package-lock.json index 6aca956..2e7289f 100644 --- a/audon-fe/package-lock.json +++ b/audon-fe/package-lock.json @@ -1,12 +1,12 @@ { "name": "audon-fe", - "version": "0.2.0", + "version": "0.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "audon-fe", - "version": "0.2.0", + "version": "0.2.3", "dependencies": { "@intlify/unplugin-vue-i18n": "^0.8.1", "@picmo/popup-picker": "^5.7.2", @@ -809,9 +809,9 @@ } }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", "bin": { "acorn": "bin/acorn" }, @@ -895,9 +895,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.3.tgz", - "integrity": "sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.5.tgz", + "integrity": "sha512-9pU/8mmjSSOb4CXVsvGIevN+MlO/t9OWtKadTaLuN85Gge3HGorUckgp8A/2FH4V4hJ7JuQ3LIeI7KAV9ITZrQ==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -2342,9 +2342,9 @@ } }, "node_modules/livekit-client": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-1.6.2.tgz", - "integrity": "sha512-OJIcBY8fzS8tf4vvBL/LNjmEHMFRLivnRjBDjbSiJ8nqV7UB7iSN40RMAejp/OPjhB5ys1u8tt6m3RIXrG2TJA==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-1.6.3.tgz", + "integrity": "sha512-hnVN/rQ9pVWK0L1lemLSOzBI6IXoJepgHP/USqmGJ6eucaoLMx1jK0iMWpR5T2/pg97GmZaUaRSUcXlELNSsoA==", "dependencies": { "async-await-queue": "^1.2.1", "events": "^3.3.0", @@ -2465,9 +2465,9 @@ } }, "node_modules/masto": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/masto/-/masto-5.6.0.tgz", - "integrity": "sha512-+5t6bSt/V0HyL3hDowxabVszfLjkrKVunhgWXC4LrCBMjMKIOF4vrFJriXSPKOprB6VCZztvGGuy02zSy7oRcQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/masto/-/masto-5.6.1.tgz", + "integrity": "sha512-mQxuOLCWP0TSswZEmmrbHbV2+Olt2N+lr1V2C3FO0QCrPGAhx3KLAXRLArajeGtcVtu66/k8gtTWKksYfkQu5Q==", "dependencies": { "@mastojs/ponyfills": "^1.0.4", "change-case": "^4.1.2", @@ -2963,9 +2963,9 @@ } }, "node_modules/protobufjs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.2.tgz", - "integrity": "sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.0.tgz", + "integrity": "sha512-hYCqTDuII4iJ4stZqiuGCSU8xxWl5JeXYpwARGtn/tWcKCAro6h3WQz+xpsNbXW0UYqpmTQFEyFWO0G0Kjt64g==", "hasInstallScript": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", @@ -3365,24 +3365,10 @@ "rxjs": "*" } }, - "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, "node_modules/ua-parser-js": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.32.tgz", - "integrity": "sha512-dXVsz3M4j+5tTiovFVyVqssXBu5HM47//YSOeZ9fQkdDKkfzv2v3PP1jmH6FUyPW+yCSn7aBVK1fGGKNhowdDA==", + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz", + "integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==", "funding": [ { "type": "opencollective", diff --git a/audon-fe/src/locales/en.yaml b/audon-fe/src/locales/en.yaml index 825494b..1f5442c 100644 --- a/audon-fe/src/locales/en.yaml +++ b/audon-fe/src/locales/en.yaml @@ -46,6 +46,7 @@ roomReady: header: "Your room is ready!" message: "Your room \"{title}\" is now ready. Share the following URL with other participants." errors: + offline: "This user is offline now." invalidAddress: "Invalid address" serverNotFound: "Instance not found" notFound: "{value} not found" diff --git a/audon-fe/src/locales/ja.yaml b/audon-fe/src/locales/ja.yaml index 8f8cf65..49820f7 100644 --- a/audon-fe/src/locales/ja.yaml +++ b/audon-fe/src/locales/ja.yaml @@ -46,6 +46,7 @@ roomReady: header: "お部屋の用意ができました!" message: "{title} を作りました。参加者に以下の URL を共有してください。" errors: + offline: "このユーザーは現在オフラインです。" invalidAddress: "アドレスが有効ではありません" serverNotFound: "サーバーが見つかりません" notFound: "{value} が見つかりません" diff --git a/audon-fe/src/router/index.js b/audon-fe/src/router/index.js index dbd890f..c72d103 100644 --- a/audon-fe/src/router/index.js +++ b/audon-fe/src/router/index.js @@ -1,7 +1,8 @@ import { createRouter, createWebHistory } from "vue-router"; import LoginView from "../views/LoginView.vue"; import RoomView from "../views/RoomView.vue"; -import NotFoundView from "../views/NotFoundView.vue"; +import ErrorView from "../views/ErrorView.vue"; +import axios from "axios"; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), @@ -12,7 +13,15 @@ const router = createRouter({ meta: { noauth: true, }, - component: NotFoundView, + component: ErrorView, + }, + { + path: "/error/:type", + name: "error", + meta: { + noauth: true, + }, + component: ErrorView, }, { path: "/", @@ -40,6 +49,27 @@ const router = createRouter({ }, component: RoomView, }, + { + path: "/u/:webfinger", + name: "currentHosting", + meta: { + noauth: true, + }, + redirect: async (to) => { + try { + const resp = await axios.get(`/u/${to.params.webfinger}`); + if (resp.status === 302) { + return { + name: "room", + params: { id: resp.headers.location }, + }; + } + } catch (error) { + console.log(error); + } + return { name: "notfound" }; + }, + }, ], }); diff --git a/audon-fe/src/views/CreateView.vue b/audon-fe/src/views/CreateView.vue index f4b07e0..e862efb 100644 --- a/audon-fe/src/views/CreateView.vue +++ b/audon-fe/src/views/CreateView.vue @@ -32,7 +32,15 @@ export default { clipboard: useClipboard(), }; }, - created() { + async created() { + const resp = await axios.get("/api/room"); + if (resp.data.length > 0) { + const canCreate = !some(resp.data, { role: "host" }); + if (!canCreate) { + alert(this.$t("errors.alreadyAdded")); + this.$router.replace({ name: "home" }); + } + } this.cohostSearch = debounce(this.search, 1000); }, unmounted() { diff --git a/audon-fe/src/views/ErrorView.vue b/audon-fe/src/views/ErrorView.vue new file mode 100644 index 0000000..c9110f8 --- /dev/null +++ b/audon-fe/src/views/ErrorView.vue @@ -0,0 +1,23 @@ + + + diff --git a/audon-fe/src/views/HomeView.vue b/audon-fe/src/views/HomeView.vue index 3515f5e..f734f1f 100644 --- a/audon-fe/src/views/HomeView.vue +++ b/audon-fe/src/views/HomeView.vue @@ -1,6 +1,7 @@ - - diff --git a/audon-fe/src/views/RoomView.vue b/audon-fe/src/views/RoomView.vue index dc97940..6883c86 100644 --- a/audon-fe/src/views/RoomView.vue +++ b/audon-fe/src/views/RoomView.vue @@ -557,11 +557,7 @@ export default { return metadata; }, async fetchMastoData(identity) { - if ( - this.cachedMastoData[identity] !== undefined || - this.roomInfo.accounts[identity] === undefined - ) - return; + if (this.roomInfo.accounts[identity] === undefined) return; try { const resp = await axios.get(`/app/user/${identity}`); const account = this.roomInfo.accounts[identity]; diff --git a/auth.go b/auth.go index f81d864..9bba66f 100644 --- a/auth.go +++ b/auth.go @@ -174,7 +174,6 @@ func oauthHandler(c echo.Context) (err error) { } return c.Redirect(http.StatusFound, req.Redirect) - // return c.Redirect(http.StatusFound, "http://localhost:5173") } func getUserTokenHandler(c echo.Context) (err error) { diff --git a/room.go b/room.go index 5f5015c..b3ab47a 100644 --- a/room.go +++ b/room.go @@ -35,6 +35,23 @@ func createRoomHandler(c echo.Context) error { host := c.Get("user").(*AudonUser) room.Host = host + // check if user is already hosting + lkRooms, err := host.GetCurrentLivekitRooms(c.Request().Context()) + if err != nil { + c.Logger().Error(err) + return echo.NewHTTPError(http.StatusInternalServerError) + } + for _, r := range lkRooms { + meta, err := getRoomMetadataFromLivekitRoom(r) + if err != nil { + c.Logger().Error(err) + return echo.NewHTTPError(http.StatusInternalServerError) + } + if meta.Host.Equal(host) { + return ErrOperationNotPermitted + } + } + coll := mainDB.Collection(COLLECTION_ROOM) now := time.Now().UTC() diff --git a/schema.go b/schema.go index ccfea10..77e929d 100644 --- a/schema.go +++ b/schema.go @@ -133,6 +133,15 @@ func (r *Room) IsHost(u *AudonUser) bool { return r != nil && r.Host.Equal(u) } +func (r *RoomMetadata) IsSpeaker(u *AudonUser) bool { + for _, s := range r.Speakers { + if s.Equal(u) { + return true + } + } + return false +} + func getRoomMetadataFromLivekitRoom(lkRoom *livekit.Room) (*RoomMetadata, error) { metadata := new(RoomMetadata) if err := json.Unmarshal([]byte(lkRoom.GetMetadata()), metadata); err != nil { @@ -240,3 +249,12 @@ func findUserByID(ctx context.Context, audonID string) (*AudonUser, error) { } return &result, nil } + +func findUserByWebfinger(ctx context.Context, webfinger string) (*AudonUser, error) { + var result AudonUser + coll := mainDB.Collection(COLLECTION_USER) + if err := coll.FindOne(ctx, bson.D{{Key: "webfinger", Value: webfinger}}).Decode(&result); err != nil { + return nil, err + } + return &result, nil +} diff --git a/server.go b/server.go index 1b236aa..2e0e4df 100644 --- a/server.go +++ b/server.go @@ -182,6 +182,7 @@ func main() { e.Static("/static", "audon-fe/dist/static") e.Static("/storage", mainConfig.StorageDir) e.GET("/r/:id", renderRoomHandler) + e.GET("/u/:webfinger", redirectUserHandler) e.GET("/*", func(c echo.Context) error { return c.Render(http.StatusOK, "tmpl", &TemplateData{Config: &mainConfig.AppConfigBase}) }) diff --git a/user.go b/user.go index e89e432..4489572 100644 --- a/user.go +++ b/user.go @@ -2,7 +2,9 @@ package main import ( "context" + "fmt" "net/http" + "net/url" "time" "github.com/labstack/echo/v4" @@ -51,13 +53,56 @@ func getUserHandler(c echo.Context) error { func getStatusHandler(c echo.Context) error { u := c.Get("user").(*AudonUser) - ids, err := u.GetCurrentRoomIDs(c.Request().Context()) + status, err := u.GetCurrentRoomStatus(c.Request().Context()) if err != nil { c.Logger().Error(err) return echo.NewHTTPError(http.StatusInternalServerError) } - return c.JSON(http.StatusOK, ids) + return c.JSON(http.StatusOK, status) +} + +func redirectUserHandler(c echo.Context) error { + input := c.Param("webfinger") + if err := mainValidator.Var(&input, "required,startswith=@,min=4"); err != nil { + return wrapValidationError(err) + } + webfinger := input[1:] + if err := mainValidator.Var(&webfinger, "email"); err != nil { + return wrapValidationError(err) + } + + user, err := findUserByWebfinger(c.Request().Context(), webfinger) + if err != nil || user == nil { + return ErrUserNotFound + } + + rooms, err := user.GetCurrentLivekitRooms(c.Request().Context()) + if err != nil { + c.Logger().Error(err) + return echo.NewHTTPError(http.StatusInternalServerError) + } + + 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())) + } + } + + query := make(url.Values) + query.Add("url", user.RemoteURL) + result := url.URL{ + Path: "/error/offline", + ForceQuery: true, + OmitHost: true, + RawQuery: query.Encode(), + } + + return c.Redirect(http.StatusFound, result.String()) } func (a *AudonUser) Equal(u *AudonUser) bool { @@ -90,14 +135,31 @@ func (a *AudonUser) ClearUserAvatar(ctx context.Context) error { return err } -func (a *AudonUser) GetCurrentRoomIDs(ctx context.Context) ([]string, error) { +type UserStatus struct { + RoomID string `json:"roomID"` + Role string `json:"role"` +} + +func (a *AudonUser) GetCurrentRoomStatus(ctx context.Context) ([]UserStatus, error) { rooms, err := a.GetCurrentLivekitRooms(ctx) if err != nil { return nil, err } - roomIDs := make([]string, len(rooms)) + roomList := make([]UserStatus, len(rooms)) for i, r := range rooms { - roomIDs[i] = r.GetName() + meta, _ := getRoomMetadataFromLivekitRoom(r) + role := "listener" + if meta.Room.IsHost(a) { + role = "host" + } else if meta.Room.IsCoHost(a) { + role = "cohost" + } else if meta.IsSpeaker(a) { + role = "speaker" + } + roomList[i] = UserStatus{ + RoomID: r.GetName(), + Role: role, + } } - return roomIDs, nil + return roomList, nil } diff --git a/webhooks.go b/webhooks.go index c7717bd..35848b9 100644 --- a/webhooks.go +++ b/webhooks.go @@ -64,13 +64,13 @@ func livekitWebhookHandler(c echo.Context) error { <-oldTimer.C } } - countdown := time.NewTimer(10 * time.Second) + countdown := time.NewTimer(60 * time.Second) webhookTimerCache.Set(audonID, countdown, ttlcache.DefaultTTL) go func() { <-countdown.C webhookTimerCache.Delete(audonID) - ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() stillAgain, err := user.InLivekit(ctx)