feat: implement CORS proxy for Matrix homeservers during development and refactor call handling logic
This commit is contained in:
@@ -328,6 +328,7 @@ export class CallService {
|
|||||||
// Step 2: Try fetching LiveKit JWT from homeservers in priority order
|
// Step 2: Try fetching LiveKit JWT from homeservers in priority order
|
||||||
const result = await fetchLiveKitJWTFromServers(
|
const result = await fetchLiveKitJWTFromServers(
|
||||||
homeserverPriority,
|
homeserverPriority,
|
||||||
|
this.config.homeserverBaseUrl,
|
||||||
roomId,
|
roomId,
|
||||||
this.config.userId,
|
this.config.userId,
|
||||||
this.config.deviceId,
|
this.config.deviceId,
|
||||||
|
|||||||
@@ -152,7 +152,8 @@ export interface CallEvent {
|
|||||||
* Call service configuration
|
* Call service configuration
|
||||||
*/
|
*/
|
||||||
export interface CallServiceConfig {
|
export interface CallServiceConfig {
|
||||||
livekitServiceUrl: string;
|
/** @deprecated No longer used - JWT fetched dynamically from multiple homeservers */
|
||||||
|
livekitServiceUrl?: string;
|
||||||
homeserverBaseUrl: string;
|
homeserverBaseUrl: string;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|||||||
@@ -82,26 +82,12 @@ export function useCallService(): CallContextValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get LiveKit service URL from Matrix homeserver well-known
|
// Initialize CallService without checking user's homeserver
|
||||||
const wellKnown = await fetchWellKnownWithRTC(baseUrl);
|
// Call support will be determined per-room based on available homeservers
|
||||||
// eslint-disable-next-line no-console
|
console.log('Call service init - Initializing (call support determined per-room)');
|
||||||
console.log('Call service init - well-known:', wellKnown);
|
|
||||||
const livekitFocus = getLiveKitFocus(wellKnown);
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('Call service init - LiveKit focus:', livekitFocus);
|
|
||||||
const livekitServiceUrl = livekitFocus?.livekit_service_url;
|
|
||||||
|
|
||||||
if (!livekitServiceUrl) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('Call service init - No LiveKit config in well-known, calls not supported');
|
|
||||||
setCallSupported(false);
|
|
||||||
setCallSupportLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
service = new CallService(
|
service = new CallService(
|
||||||
{
|
{
|
||||||
livekitServiceUrl,
|
|
||||||
homeserverBaseUrl: baseUrl,
|
homeserverBaseUrl: baseUrl,
|
||||||
accessToken,
|
accessToken,
|
||||||
userId,
|
userId,
|
||||||
@@ -112,10 +98,10 @@ export function useCallService(): CallContextValue {
|
|||||||
|
|
||||||
setCallService(service);
|
setCallService(service);
|
||||||
setGlobalCallService(service); // Make available globally for Paarrot API
|
setGlobalCallService(service); // Make available globally for Paarrot API
|
||||||
setCallSupported(true);
|
setCallSupported(true); // Global flag - actual support checked per-room
|
||||||
setCallSupportLoading(false);
|
setCallSupportLoading(false);
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('Call service init - Success! Calls are supported');
|
console.log('Call service init - Success!');
|
||||||
|
|
||||||
unsubscribe = service.on((event: CallEvent) => {
|
unsubscribe = service.on((event: CallEvent) => {
|
||||||
// Update state for any event that might change the call state
|
// Update state for any event that might change the call state
|
||||||
@@ -245,8 +231,62 @@ export function useCallService(): CallContextValue {
|
|||||||
* Hook to check if a call is active in a specific room
|
* Hook to check if a call is active in a specific room
|
||||||
*/
|
*/
|
||||||
export function useRoomCall(roomId: string) {
|
export function useRoomCall(roomId: string) {
|
||||||
const { activeCall, startCall, endCall, callSupported, callSupportLoading, callService } = useCall();
|
const { activeCall, startCall, endCall, callSupported: globalCallSupported, callSupportLoading: globalCallSupportLoading, callService } = useCall();
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
const [roomCallSupported, setRoomCallSupported] = useState(false);
|
||||||
|
const [roomCallSupportLoading, setRoomCallSupportLoading] = useState(true);
|
||||||
|
|
||||||
|
// Check if calls are supported for this specific room by checking all priority homeservers
|
||||||
|
useEffect(() => {
|
||||||
|
const checkRoomCallSupport = async () => {
|
||||||
|
setRoomCallSupportLoading(true);
|
||||||
|
|
||||||
|
const room = mx.getRoom(roomId);
|
||||||
|
if (!room) {
|
||||||
|
setRoomCallSupported(false);
|
||||||
|
setRoomCallSupportLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get homeserver priority list for this room
|
||||||
|
const homeserverPriority = getLiveKitHomeserverPriority(
|
||||||
|
room,
|
||||||
|
mx.getHomeserverUrl(),
|
||||||
|
mx.getSafeUserId()
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Checking call support for room ${roomId}, homeserver priority:`, homeserverPriority);
|
||||||
|
|
||||||
|
// Check each homeserver in priority order
|
||||||
|
for (const homeserver of homeserverPriority) {
|
||||||
|
try {
|
||||||
|
console.log(`Fetching well-known from ${homeserver}...`);
|
||||||
|
const wellKnown = await fetchWellKnownWithRTC(homeserver);
|
||||||
|
console.log(`Well-known response from ${homeserver}:`, wellKnown);
|
||||||
|
|
||||||
|
const livekitFocus = getLiveKitFocus(wellKnown);
|
||||||
|
console.log(`LiveKit focus from ${homeserver}:`, livekitFocus);
|
||||||
|
|
||||||
|
if (livekitFocus) {
|
||||||
|
console.log(`Call support found for room ${roomId} on homeserver: ${homeserver}`);
|
||||||
|
setRoomCallSupported(true);
|
||||||
|
setRoomCallSupportLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to check call support on ${homeserver}:`, error);
|
||||||
|
// Continue to next homeserver
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No homeserver supports calls
|
||||||
|
console.log(`No call support found for room ${roomId}`);
|
||||||
|
setRoomCallSupported(false);
|
||||||
|
setRoomCallSupportLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkRoomCallSupport();
|
||||||
|
}, [roomId, mx]);
|
||||||
|
|
||||||
const isInCall = activeCall?.roomId === roomId;
|
const isInCall = activeCall?.roomId === roomId;
|
||||||
const callState = isInCall ? activeCall.state : CallState.Idle;
|
const callState = isInCall ? activeCall.state : CallState.Idle;
|
||||||
@@ -287,7 +327,7 @@ export function useRoomCall(roomId: string) {
|
|||||||
startVoiceCall,
|
startVoiceCall,
|
||||||
startVideoCall,
|
startVideoCall,
|
||||||
endCall,
|
endCall,
|
||||||
callSupported,
|
callSupported: roomCallSupported,
|
||||||
callSupportLoading,
|
callSupportLoading: roomCallSupportLoading,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import { getActiveCallMembers } from './useRoomCallMembers';
|
|||||||
* Get room server from room ID (format: !roomid:server.com)
|
* Get room server from room ID (format: !roomid:server.com)
|
||||||
*/
|
*/
|
||||||
function getRoomServer(roomId: string): string | undefined {
|
function getRoomServer(roomId: string): string | undefined {
|
||||||
return getMxIdServer(roomId);
|
const server = getMxIdServer(roomId);
|
||||||
|
console.log(`[getRoomServer] Room ID: ${roomId} -> Server: ${server}`);
|
||||||
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,13 +32,20 @@ export function getLiveKitHomeserverPriority(
|
|||||||
const memberCount = room.getMembers().length;
|
const memberCount = room.getMembers().length;
|
||||||
const isDM = memberCount <= 2;
|
const isDM = memberCount <= 2;
|
||||||
|
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Room: ${room.roomId}`);
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Member count: ${memberCount}, isDM: ${isDM}`);
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] User homeserver: ${userHomeserver}`);
|
||||||
|
|
||||||
// Check for active call members
|
// Check for active call members
|
||||||
const activeCallMembers = getActiveCallMembers(room);
|
const activeCallMembers = getActiveCallMembers(room);
|
||||||
const hasActiveCall = activeCallMembers.length > 0;
|
const hasActiveCall = activeCallMembers.length > 0;
|
||||||
|
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Active call members: ${activeCallMembers.length}, hasActiveCall: ${hasActiveCall}`);
|
||||||
|
|
||||||
if (hasActiveCall) {
|
if (hasActiveCall) {
|
||||||
// Joining existing call - prioritize room's homeserver (where call is hosted)
|
// Joining existing call - prioritize room's homeserver (where call is hosted)
|
||||||
const roomServer = getRoomServer(room.roomId);
|
const roomServer = getRoomServer(room.roomId);
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Room server (active call): ${roomServer}`);
|
||||||
if (roomServer) {
|
if (roomServer) {
|
||||||
servers.push(`https://${roomServer}`);
|
servers.push(`https://${roomServer}`);
|
||||||
}
|
}
|
||||||
@@ -60,32 +69,39 @@ export function getLiveKitHomeserverPriority(
|
|||||||
} else {
|
} else {
|
||||||
// Starting new call - use original priority logic
|
// Starting new call - use original priority logic
|
||||||
if (isDM) {
|
if (isDM) {
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] DM path - user homeserver first`);
|
||||||
// For DMs: user's homeserver first, then other member's homeserver
|
// For DMs: user's homeserver first, then other member's homeserver
|
||||||
servers.push(userHomeserver);
|
servers.push(userHomeserver);
|
||||||
|
|
||||||
const otherUserId = guessDmRoomUserId(room, myUserId);
|
const otherUserId = guessDmRoomUserId(room, myUserId);
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Other user ID: ${otherUserId}`);
|
||||||
if (otherUserId && otherUserId !== myUserId) {
|
if (otherUserId && otherUserId !== myUserId) {
|
||||||
const otherServer = getMxIdServer(otherUserId);
|
const otherServer = getMxIdServer(otherUserId);
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Other user server: ${otherServer}`);
|
||||||
const userDomain = userHomeserver.replace('https://', '').replace('http://', '');
|
const userDomain = userHomeserver.replace('https://', '').replace('http://', '');
|
||||||
if (otherServer && otherServer !== userDomain) {
|
if (otherServer && otherServer !== userDomain) {
|
||||||
servers.push(`https://${otherServer}`);
|
servers.push(`https://${otherServer}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Channel path - room homeserver first`);
|
||||||
// For channels: room's homeserver first, then user's homeserver
|
// For channels: room's homeserver first, then user's homeserver
|
||||||
const roomServer = getRoomServer(room.roomId);
|
const roomServer = getRoomServer(room.roomId);
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Room server from ID: ${roomServer}`);
|
||||||
if (roomServer) {
|
if (roomServer) {
|
||||||
servers.push(`https://${roomServer}`);
|
servers.push(`https://${roomServer}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to user's homeserver if different
|
// Fallback to user's homeserver if different
|
||||||
const userDomain = userHomeserver.replace('https://', '').replace('http://', '');
|
const userDomain = userHomeserver.replace('https://', '').replace('http://', '');
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] User domain: ${userDomain}, room server: ${roomServer}`);
|
||||||
if (!roomServer || roomServer !== userDomain) {
|
if (!roomServer || roomServer !== userDomain) {
|
||||||
servers.push(userHomeserver);
|
servers.push(userHomeserver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[getLiveKitHomeserverPriority] Final server list:`, servers);
|
||||||
return servers;
|
return servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,11 +112,16 @@ export function getLiveKitHomeserverPriority(
|
|||||||
*/
|
*/
|
||||||
export function getLiveKitFocus(wellKnown: WellKnownWithRTC): LiveKitFocus | undefined {
|
export function getLiveKitFocus(wellKnown: WellKnownWithRTC): LiveKitFocus | undefined {
|
||||||
const rtcFoci = wellKnown['org.matrix.msc4143.rtc_foci'];
|
const rtcFoci = wellKnown['org.matrix.msc4143.rtc_foci'];
|
||||||
|
console.log(`[getLiveKitFocus] rtcFoci from well-known:`, rtcFoci);
|
||||||
|
|
||||||
if (!rtcFoci || !Array.isArray(rtcFoci)) {
|
if (!rtcFoci || !Array.isArray(rtcFoci)) {
|
||||||
|
console.log(`[getLiveKitFocus] No rtc_foci found or not an array`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rtcFoci.find((focus): focus is LiveKitFocus => focus.type === 'livekit');
|
const livekitFocus = rtcFoci.find((focus): focus is LiveKitFocus => focus.type === 'livekit');
|
||||||
|
console.log(`[getLiveKitFocus] Found LiveKit focus:`, livekitFocus);
|
||||||
|
return livekitFocus;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,14 +130,28 @@ export function getLiveKitFocus(wellKnown: WellKnownWithRTC): LiveKitFocus | und
|
|||||||
* @returns The well-known response with RTC configuration
|
* @returns The well-known response with RTC configuration
|
||||||
*/
|
*/
|
||||||
export async function fetchWellKnownWithRTC(baseUrl: string): Promise<WellKnownWithRTC> {
|
export async function fetchWellKnownWithRTC(baseUrl: string): Promise<WellKnownWithRTC> {
|
||||||
const wellKnownUrl = `${baseUrl}/.well-known/matrix/client`;
|
// In development mode (localhost), use CORS proxy to avoid CORS issues
|
||||||
|
const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
||||||
|
|
||||||
|
let wellKnownUrl: string;
|
||||||
|
if (isDev) {
|
||||||
|
// Extract hostname from baseUrl for proxy
|
||||||
|
const hostname = baseUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
|
||||||
|
wellKnownUrl = `/_matrix-cors-proxy/${hostname}/.well-known/matrix/client`;
|
||||||
|
} else {
|
||||||
|
wellKnownUrl = `${baseUrl}/.well-known/matrix/client`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[fetchWellKnownWithRTC] Fetching from: ${wellKnownUrl}`);
|
||||||
|
|
||||||
const response = await fetch(wellKnownUrl);
|
const response = await fetch(wellKnownUrl);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Failed to fetch well-known: ${response.status}`);
|
throw new Error(`Failed to fetch well-known: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
const data = await response.json();
|
||||||
|
console.log(`[fetchWellKnownWithRTC] Response data:`, data);
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,7 +167,16 @@ export async function requestOpenIdToken(
|
|||||||
userId: string,
|
userId: string,
|
||||||
accessToken: string
|
accessToken: string
|
||||||
): Promise<OpenIdToken> {
|
): Promise<OpenIdToken> {
|
||||||
const url = `${homeserverBaseUrl}/_matrix/client/v3/user/${encodeURIComponent(userId)}/openid/request_token`;
|
// In development mode (localhost), use CORS proxy to avoid CORS issues
|
||||||
|
const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
||||||
|
|
||||||
|
let url: string;
|
||||||
|
if (isDev) {
|
||||||
|
const hostname = homeserverBaseUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
|
||||||
|
url = `/_matrix-cors-proxy/${hostname}/_matrix/client/v3/user/${encodeURIComponent(userId)}/openid/request_token`;
|
||||||
|
} else {
|
||||||
|
url = `${homeserverBaseUrl}/_matrix/client/v3/user/${encodeURIComponent(userId)}/openid/request_token`;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -175,7 +219,21 @@ export async function fetchLiveKitJWT(
|
|||||||
// to create the LiveKit room name.
|
// to create the LiveKit room name.
|
||||||
const slotId = 'm.call';
|
const slotId = 'm.call';
|
||||||
|
|
||||||
const response = await fetch(livekitServiceUrl, {
|
// In development mode (localhost), use CORS proxy to avoid CORS issues
|
||||||
|
const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
|
||||||
|
|
||||||
|
let url: string;
|
||||||
|
if (isDev) {
|
||||||
|
// Extract hostname and path from livekitServiceUrl
|
||||||
|
const urlObj = new URL(livekitServiceUrl);
|
||||||
|
const hostname = urlObj.hostname;
|
||||||
|
const pathname = urlObj.pathname;
|
||||||
|
url = `/_matrix-cors-proxy/${hostname}${pathname}`;
|
||||||
|
} else {
|
||||||
|
url = livekitServiceUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -202,7 +260,8 @@ export async function fetchLiveKitJWT(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Try fetching LiveKit JWT from multiple homeservers in priority order
|
* Try fetching LiveKit JWT from multiple homeservers in priority order
|
||||||
* @param homeservers - Array of homeserver base URLs to try
|
* @param homeservers - Array of homeserver base URLs to check for LiveKit config
|
||||||
|
* @param userHomeserver - The user's own homeserver URL (for OpenID token)
|
||||||
* @param roomId - The Matrix room ID
|
* @param roomId - The Matrix room ID
|
||||||
* @param userId - The Matrix user ID
|
* @param userId - The Matrix user ID
|
||||||
* @param deviceId - The Matrix device ID
|
* @param deviceId - The Matrix device ID
|
||||||
@@ -211,6 +270,7 @@ export async function fetchLiveKitJWT(
|
|||||||
*/
|
*/
|
||||||
export async function fetchLiveKitJWTFromServers(
|
export async function fetchLiveKitJWTFromServers(
|
||||||
homeservers: string[],
|
homeservers: string[],
|
||||||
|
userHomeserver: string,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
userId: string,
|
userId: string,
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
@@ -229,8 +289,9 @@ export async function fetchLiveKitJWTFromServers(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get OpenID token from this homeserver
|
// Always get OpenID token from user's own homeserver
|
||||||
const openIdToken = await requestOpenIdToken(homeserver, userId, accessToken);
|
// (Can't use access token on remote homeserver)
|
||||||
|
const openIdToken = await requestOpenIdToken(userHomeserver, userId, accessToken);
|
||||||
|
|
||||||
// Exchange for LiveKit JWT
|
// Exchange for LiveKit JWT
|
||||||
const jwt = await fetchLiveKitJWT(
|
const jwt = await fetchLiveKitJWT(
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ import { useRoomCreators } from '../../hooks/useRoomCreators';
|
|||||||
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
|
import { useRoomPermissions } from '../../hooks/useRoomPermissions';
|
||||||
import { InviteUserPrompt } from '../../components/invite-user-prompt';
|
import { InviteUserPrompt } from '../../components/invite-user-prompt';
|
||||||
import { CallParticipantsIndicator } from './CallParticipantsIndicator';
|
import { CallParticipantsIndicator } from './CallParticipantsIndicator';
|
||||||
import { useCall } from '../call/useCall';
|
import { useCall, useRoomCall } from '../call/useCall';
|
||||||
import { CallType } from '../call/types';
|
import { CallType } from '../call/types';
|
||||||
import { roomToParentsAtom } from '../../state/room/roomToParents';
|
import { roomToParentsAtom } from '../../state/room/roomToParents';
|
||||||
import { StateEvent } from '../../../types/matrix/room';
|
import { StateEvent } from '../../../types/matrix/room';
|
||||||
@@ -75,7 +75,8 @@ const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
|
|||||||
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
const unread = useRoomUnread(room.roomId, roomToUnreadAtom);
|
||||||
const powerLevels = usePowerLevels(room);
|
const powerLevels = usePowerLevels(room);
|
||||||
const creators = useRoomCreators(room);
|
const creators = useRoomCreators(room);
|
||||||
const { startCall, endCall, activeCall, callSupported } = useCall();
|
const { startCall: startCallGlobal, endCall, activeCall } = useCall();
|
||||||
|
const { callSupported } = useRoomCall(room.roomId);
|
||||||
const markAsRead = useMarkAsRead(mx);
|
const markAsRead = useMarkAsRead(mx);
|
||||||
|
|
||||||
const permissions = useRoomPermissions(creators, powerLevels);
|
const permissions = useRoomPermissions(creators, powerLevels);
|
||||||
@@ -119,7 +120,7 @@ const RoomNavItemMenu = forwardRef<HTMLDivElement, RoomNavItemMenuProps>(
|
|||||||
if (activeCall) {
|
if (activeCall) {
|
||||||
await endCall();
|
await endCall();
|
||||||
}
|
}
|
||||||
await startCall(room.roomId, CallType.Voice);
|
await startCallGlobal(room.roomId, CallType.Voice);
|
||||||
requestClose();
|
requestClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to join voice call:', error);
|
console.error('Failed to join voice call:', error);
|
||||||
|
|||||||
@@ -450,12 +450,7 @@ function RoomCallButtons({ roomId }: RoomCallButtonsProps) {
|
|||||||
{isConnecting ? (
|
{isConnecting ? (
|
||||||
<Spinner size="100" variant="Secondary" />
|
<Spinner size="100" variant="Secondary" />
|
||||||
) : (
|
) : (
|
||||||
<Box direction="Row" gap="100" alignItems="Center">
|
<Icon size="400" src={Icons.Phone} filled={isActive && activeCall?.callType === CallType.Voice} />
|
||||||
<Icon size="400" src={Icons.Phone} filled={isActive && activeCall?.callType === CallType.Voice} />
|
|
||||||
{isUsingRemoteHomeserver && (
|
|
||||||
<Icon size="50" src={Icons.HashGlobe} style={{ opacity: 0.7 }} />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
@@ -481,12 +476,7 @@ function RoomCallButtons({ roomId }: RoomCallButtonsProps) {
|
|||||||
{isConnecting ? (
|
{isConnecting ? (
|
||||||
<Spinner size="100" variant="Secondary" />
|
<Spinner size="100" variant="Secondary" />
|
||||||
) : (
|
) : (
|
||||||
<Box direction="Row" gap="100" alignItems="Center">
|
<Icon size="400" src={Icons.VideoCamera} filled={isActive && activeCall?.callType === CallType.Video} />
|
||||||
<Icon size="400" src={Icons.VideoCamera} filled={isActive && activeCall?.callType === CallType.Video} />
|
|
||||||
{isUsingRemoteHomeserver && (
|
|
||||||
<Icon size="50" src={Icons.HashGlobe} style={{ opacity: 0.7 }} />
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const DOMAIN_REGEX = /\b(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}\b/;
|
|||||||
|
|
||||||
export const isServerName = (serverName: string): boolean => DOMAIN_REGEX.test(serverName);
|
export const isServerName = (serverName: string): boolean => DOMAIN_REGEX.test(serverName);
|
||||||
|
|
||||||
const matchMxId = (id: string): RegExpMatchArray | null => id.match(/^([@$+#])([^\s:]+):(\S+)$/);
|
const matchMxId = (id: string): RegExpMatchArray | null => id.match(/^([@!$+#])([^\s:]+):(\S+)$/);
|
||||||
|
|
||||||
const validMxId = (id: string): boolean => !!matchMxId(id);
|
const validMxId = (id: string): boolean => !!matchMxId(id);
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,83 @@ function servePublicAssets() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CORS proxy for Matrix homeservers during development
|
||||||
|
function corsProxyMiddleware() {
|
||||||
|
return {
|
||||||
|
name: 'vite-plugin-cors-proxy',
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use(async (req, res, next) => {
|
||||||
|
// Proxy /.well-known requests: /_matrix-cors-proxy/homeserver.com/.well-known/matrix/client
|
||||||
|
const corsProxyMatch = req.url?.match(/^\/_matrix-cors-proxy\/([^/]+)(\/.*)/);
|
||||||
|
if (corsProxyMatch) {
|
||||||
|
const [, hostname, pathname] = corsProxyMatch;
|
||||||
|
const targetUrl = `https://${hostname}${pathname}`;
|
||||||
|
|
||||||
|
console.log(`[CORS Proxy] ${req.method} ${req.url}`);
|
||||||
|
console.log(`[CORS Proxy] Target: ${targetUrl}`);
|
||||||
|
|
||||||
|
// Handle preflight OPTIONS request
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||||
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||||
|
res.writeHead(204);
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Collect request body for POST/PUT
|
||||||
|
let body = null;
|
||||||
|
if (req.method === 'POST' || req.method === 'PUT') {
|
||||||
|
const chunks = [];
|
||||||
|
for await (const chunk of req) {
|
||||||
|
chunks.push(chunk);
|
||||||
|
}
|
||||||
|
body = Buffer.concat(chunks).toString();
|
||||||
|
console.log(`[CORS Proxy] Request body:`, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward request with method, headers, and body
|
||||||
|
const fetchOptions = {
|
||||||
|
method: req.method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': req.headers['content-type'] || 'application/json',
|
||||||
|
'Authorization': req.headers['authorization'] || '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
fetchOptions.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(targetUrl, fetchOptions);
|
||||||
|
const data = await response.text();
|
||||||
|
|
||||||
|
console.log(`[CORS Proxy] Response status: ${response.status}`);
|
||||||
|
console.log(`[CORS Proxy] Response data:`, data.substring(0, 500));
|
||||||
|
|
||||||
|
// Set CORS headers
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||||
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
||||||
|
res.setHeader('Content-Type', response.headers.get('content-type') || 'application/json');
|
||||||
|
|
||||||
|
res.writeHead(response.status);
|
||||||
|
res.end(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[CORS Proxy] Error for ${targetUrl}:`, error);
|
||||||
|
res.writeHead(500);
|
||||||
|
res.end(JSON.stringify({ error: error.message }));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
appType: 'spa',
|
appType: 'spa',
|
||||||
publicDir: false,
|
publicDir: false,
|
||||||
@@ -143,12 +220,14 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
port: 38347,
|
port: 38347,
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
|
cors: true, // Enable CORS for dev server
|
||||||
fs: {
|
fs: {
|
||||||
// Allow serving files from one level up to the project root
|
// Allow serving files from one level up to the project root
|
||||||
allow: ['..'],
|
allow: ['..'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
corsProxyMiddleware(), // Add CORS proxy for development
|
||||||
serverMatrixSdkCryptoWasm('/node_modules/.vite/deps/pkg/matrix_sdk_crypto_wasm_bg.wasm'),
|
serverMatrixSdkCryptoWasm('/node_modules/.vite/deps/pkg/matrix_sdk_crypto_wasm_bg.wasm'),
|
||||||
serveGifWorker(),
|
serveGifWorker(),
|
||||||
servePublicAssets(),
|
servePublicAssets(),
|
||||||
|
|||||||
Reference in New Issue
Block a user