kopia lustrzana https://codeberg.org/nmkj/audon
add static url
rodzic
073510d4b5
commit
17e07d28a1
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -46,6 +46,7 @@ roomReady:
|
|||
header: "お部屋の用意ができました!"
|
||||
message: "{title} を作りました。参加者に以下の URL を共有してください。"
|
||||
errors:
|
||||
offline: "このユーザーは現在オフラインです。"
|
||||
invalidAddress: "アドレスが有効ではありません"
|
||||
serverNotFound: "サーバーが見つかりません"
|
||||
notFound: "{value} が見つかりません"
|
||||
|
|
|
@ -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" };
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
name: this.$route.name,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
const type = this.$route.params.type;
|
||||
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();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-alert v-if="this.name === 'notfound'" type="error">Page not found</v-alert>
|
||||
</template>
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { useMastodonStore } from "../stores/mastodon";
|
||||
import axios from "axios";
|
||||
import { some } from "lodash-es";
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
|
@ -10,9 +11,16 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
canCreate: true,
|
||||
query: "",
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
const resp = await axios.get("/api/room");
|
||||
if (resp.data.length > 0) {
|
||||
this.canCreate = !some(resp.data, { role: "host" });
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onLogout() {
|
||||
// if (!confirm(this.$t("logoutConfirm"))) return;
|
||||
|
@ -63,9 +71,13 @@ export default {
|
|||
<v-text-field v-mode="query"></v-text-field>
|
||||
</v-col> -->
|
||||
<v-col cols="12">
|
||||
<v-btn block :to="{ name: 'create' }" color="indigo">{{
|
||||
$t("createNewRoom")
|
||||
}}</v-btn>
|
||||
<v-btn
|
||||
:disabled="!canCreate"
|
||||
block
|
||||
:to="{ name: 'create' }"
|
||||
color="indigo"
|
||||
>{{ $t("createNewRoom") }}</v-btn
|
||||
>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</main>
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
<script></script>
|
||||
|
||||
<template>
|
||||
<v-alert type="error">Page not found</v-alert>
|
||||
</template>
|
|
@ -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];
|
||||
|
|
1
auth.go
1
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) {
|
||||
|
|
17
room.go
17
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()
|
||||
|
|
18
schema.go
18
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
|
||||
}
|
||||
|
|
|
@ -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})
|
||||
})
|
||||
|
|
74
user.go
74
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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Ładowanie…
Reference in New Issue