Files
uPortal2/backend/internal/rest/handler_jitsi.go
T
2026-10-21 20:13:27 +03:00

131 lines
2.9 KiB
Go

package rest
import (
"net/http"
"strings"
"time"
)
// sanitizeRoomName trims a room name and collapses any character that is not
// allowed in a room name into a single dash, stripping leading/trailing dashes.
// Case is preserved because Jitsi room names are case-sensitive.
func sanitizeRoomName(value string) string {
var builder strings.Builder
builder.Grow(len(value))
previousDash := false
for _, r := range strings.TrimSpace(value) {
allowed := (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '-' || r == '_'
if allowed {
builder.WriteRune(r)
previousDash = false
continue
}
if !previousDash {
builder.WriteByte('-')
previousDash = true
}
}
return strings.Trim(builder.String(), "-")
}
type joinRoomRequest struct {
Room string `json:"room"`
}
type joinRoomResponse struct {
Room string `json:"room"`
Token string `json:"token"`
Url string `json:"url"`
AppId string `json:"appId"`
AppName string `json:"appName"`
Domain string `json:"domain"`
}
type roomHistoryResponse struct {
RoomName string `json:"roomName"`
LastJoinedAt time.Time `json:"lastJoinedAt"`
}
func (h *Handler) listRecentRooms(w http.ResponseWriter, r *http.Request) {
u := userFromRequest(r)
limit, offset := pagination(r)
rooms, err := h.jitsi.RoomHistory(r.Context(), u, limit, offset)
if err != nil {
h.fail(w, err)
return
}
result := make([]roomHistoryResponse, 0, len(rooms))
for _, room := range rooms {
result = append(result, roomHistoryResponse{
RoomName: room.RoomName,
LastJoinedAt: room.LastJoinedAt,
})
}
writeJson(w, http.StatusOK, result)
}
func (h *Handler) joinRoom(w http.ResponseWriter, r *http.Request) {
u := userFromRequest(r)
var req joinRoomRequest
if err := decodeJson(r, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid request body")
return
}
room := sanitizeRoomName(req.Room)
if room == "" {
writeError(w, http.StatusBadRequest, "room is required")
return
}
// Moderator rights are inherited from the user's admin flag.
moderator := u.IsAdmin
token, err := h.jitsi.GenerateToken(
formatUserId(u.Id),
u.DisplayedName(),
room,
moderator,
h.avatarURL(r, u.Id),
)
if err != nil {
h.fail(w, err)
return
}
if err := h.jitsi.RecordRoomJoin(r.Context(), u, room); err != nil {
h.fail(w, err)
return
}
writeJson(w, http.StatusOK, joinRoomResponse{
Room: room,
Token: token,
Url: h.jitsi.MeetUrl(room, token),
AppId: h.jitsi.AppID,
AppName: h.jitsi.AppName,
Domain: h.jitsi.Domain,
})
}
func (h *Handler) deleteRecentRoom(w http.ResponseWriter, r *http.Request) {
u := userFromRequest(r)
room := sanitizeRoomName(r.PathValue("room"))
if room == "" {
writeError(w, http.StatusBadRequest, "room is required")
return
}
if err := h.jitsi.DeleteRoom(r.Context(), u, room); err != nil {
h.fail(w, err)
return
}
writeNoContent(w)
}