From e1d10c75dfc52561d3b26bea63a5f4774762c2f2 Mon Sep 17 00:00:00 2001 From: konsalex Date: Thu, 21 May 2026 22:56:05 +0200 Subject: [PATCH] fix: room join from another team --- backend/api-files/openapi.yaml | 23 ++++++++ backend/internal/handlers/handlers.go | 6 +- tauri/src/openapi.d.ts | 30 ++++++++++ web-app/src/openapi.d.ts | 30 ++++++++++ web-app/src/pages/Room.tsx | 80 ++++++++++++++++++++++----- 5 files changed, 153 insertions(+), 16 deletions(-) diff --git a/backend/api-files/openapi.yaml b/backend/api-files/openapi.yaml index 67d1265e..5cf81995 100644 --- a/backend/api-files/openapi.yaml +++ b/backend/api-files/openapi.yaml @@ -810,6 +810,29 @@ paths: application/json: schema: $ref: "#/components/schemas/SessionTokensResponse" + "402": + description: Trial or subscription expired + content: + application/json: + schema: + type: object + properties: + error: + type: string + example: trial-ended + "403": + description: User does not belong to the room's team + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: Room not found + content: + text/plain: + schema: + type: string + example: Room not found put: summary: Update room details diff --git a/backend/internal/handlers/handlers.go b/backend/internal/handlers/handlers.go index e73f8434..7dc1eebd 100644 --- a/backend/internal/handlers/handlers.go +++ b/backend/internal/handlers/handlers.go @@ -1019,9 +1019,9 @@ func (h *AuthHandler) GetRoom(c echo.Context) error { return c.String(http.StatusNotFound, "Room not found") } - // Check if user can access the room - if user.Team != room.Team { - return c.String(http.StatusUnauthorized, "Unauthorized request") + // Check if user can access the room (same team) + if room.TeamID == nil || user.TeamID == nil || *room.TeamID != *user.TeamID { + return echo.NewHTTPError(http.StatusForbidden, "You don't have access to this room") } // Check if caller has access (paid or active trial) diff --git a/tauri/src/openapi.d.ts b/tauri/src/openapi.d.ts index eed539cd..90f84aba 100644 --- a/tauri/src/openapi.d.ts +++ b/tauri/src/openapi.d.ts @@ -1077,6 +1077,36 @@ export interface paths { "application/json": components["schemas"]["SessionTokensResponse"]; }; }; + /** @description Trial or subscription expired */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example trial-ended */ + error?: string; + }; + }; + }; + /** @description User does not belong to the room's team */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Room not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; }; }; /** Update room details */ diff --git a/web-app/src/openapi.d.ts b/web-app/src/openapi.d.ts index eed539cd..90f84aba 100644 --- a/web-app/src/openapi.d.ts +++ b/web-app/src/openapi.d.ts @@ -1077,6 +1077,36 @@ export interface paths { "application/json": components["schemas"]["SessionTokensResponse"]; }; }; + /** @description Trial or subscription expired */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @example trial-ended */ + error?: string; + }; + }; + }; + /** @description User does not belong to the room's team */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Room not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; }; }; /** Update room details */ diff --git a/web-app/src/pages/Room.tsx b/web-app/src/pages/Room.tsx index 46368c13..493322b3 100644 --- a/web-app/src/pages/Room.tsx +++ b/web-app/src/pages/Room.tsx @@ -23,8 +23,10 @@ import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select"; import clsx from "clsx"; import { VideoPresets, Track, LocalTrack, Participant, ParticipantEvent, LocalTrackPublication } from "livekit-client"; -import { useAPI } from "@/hooks/useQueryClients"; +import { useAPI, isFetchError } from "@/hooks/useQueryClients"; import { useHoppStore } from "@/store/store"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { AlertTriangle } from "lucide-react"; // HACK: Import shared components from tauri app for cursor rendering // These files use relative imports so they work across projects @@ -70,6 +72,68 @@ function getGridCols(count: number): string { return "grid-cols-4"; } +function RoomJoinError({ error, onGoToDashboard }: { error: unknown; onGoToDashboard: () => void }) { + if (isFetchError(error)) { + const status = error.response.status; + + if (status === 403) { + return ( +
+
+
🔒
+

Access Denied

+

You don't have access to this room.

+ + + Why can't I join? + + This room belongs to a different team. Ask a member of that team to invite you, so you can join. + + + +
+
+ ); + } + + if (status === 404) { + return ( +
+
+

Room Not Found

+

This room doesn't exist or may have been deleted.

+ +
+
+ ); + } + + if (status === 402) { + return ( +
+
+

Trial Expired

+

Your team's trial has ended. Contact us if you want to extend it.

+ +
+
+ ); + } + } + + return ( +
+
+

Unable to Join Room

+

+ {error ? "You don't have access to this room or the room doesn't exist." : "Failed to get room access."} +

+ +
+
+ ); +} + export function Room() { const { roomId } = useParams<{ roomId: string }>(); const navigate = useNavigate(); @@ -125,17 +189,7 @@ export function Room() { } if (error || !roomTokens || !livekitServer) { - return ( -
-
-

Unable to Join Room

-

- {error ? "You don't have access to this room or the room doesn't exist." : "Failed to get room access."} -

- -
-
- ); + return navigate("/dashboard")} />; } return ( @@ -502,7 +556,7 @@ function ScreenShareView({ screenShareTrack, ownerName }: { screenShareTrack: Tr {/* Screen share label */}
- {ownerName}'s screen + {ownerName}'s screen