|
| 1 | +import { ref, computed } from 'vue' |
| 2 | +import { useClientService, useMessages } from '@ownclouders/web-pkg' |
| 3 | +import { useGettext } from 'vue3-gettext' |
| 4 | +import type { |
| 5 | + WayfProvider, |
| 6 | + WayfFederation, |
| 7 | + FederationsApiResponse, |
| 8 | + DiscoverRequest, |
| 9 | + DiscoverResponse |
| 10 | +} from '../types/wayf' |
| 11 | + |
| 12 | +export function useWayf() { |
| 13 | + const clientService = useClientService() |
| 14 | + const { showErrorMessage } = useMessages() |
| 15 | + const { $gettext } = useGettext() |
| 16 | + |
| 17 | + const loading = ref(false) |
| 18 | + const error = ref(false) |
| 19 | + const federations = ref<WayfFederation>({}) |
| 20 | + |
| 21 | + const isSelfDomain = (domain: string): boolean => { |
| 22 | + const currentHost = window.location.hostname.toLowerCase() |
| 23 | + const checkDomain = domain |
| 24 | + .toLowerCase() |
| 25 | + .replace(/^https?:\/\//, '') |
| 26 | + .replace(/:\d+$/, '') |
| 27 | + return currentHost === checkDomain |
| 28 | + } |
| 29 | + |
| 30 | + const discoverProvider = async (domain: string): Promise<string> => { |
| 31 | + try { |
| 32 | + loading.value = true |
| 33 | + const response = await clientService.httpUnAuthenticated.post<DiscoverResponse>( |
| 34 | + '/sciencemesh/discover', |
| 35 | + { domain } as DiscoverRequest |
| 36 | + ) |
| 37 | + |
| 38 | + if (!response.data || !response.data.inviteAcceptDialog) { |
| 39 | + throw new Error('No invite accept dialog found in discovery response') |
| 40 | + } |
| 41 | + |
| 42 | + return response.data.inviteAcceptDialog |
| 43 | + } catch (err) { |
| 44 | + console.error('Provider discovery failed:', err) |
| 45 | + showErrorMessage({ |
| 46 | + title: $gettext('Discovery Failed'), |
| 47 | + desc: $gettext('Could not discover provider at %{domain}', { domain }), |
| 48 | + errors: [err] |
| 49 | + }) |
| 50 | + throw err |
| 51 | + } finally { |
| 52 | + loading.value = false |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + const buildProviderUrl = (baseUrl: string, token: string, providerDomain?: string): string => { |
| 57 | + const url = new URL(baseUrl) |
| 58 | + if (providerDomain) url.searchParams.set('providerDomain', providerDomain) |
| 59 | + if (token) url.searchParams.set('token', token) |
| 60 | + return url.toString() |
| 61 | + } |
| 62 | + |
| 63 | + const navigateToProvider = async ( |
| 64 | + provider: WayfProvider, |
| 65 | + token: string, |
| 66 | + providerDomain?: string |
| 67 | + ) => { |
| 68 | + if (isSelfDomain(provider.fqdn)) { |
| 69 | + showErrorMessage({ |
| 70 | + title: $gettext('Invalid Provider Selection'), |
| 71 | + desc: $gettext( |
| 72 | + 'You cannot select your own instance as a provider. Please select a different provider to establish a federated connection.' |
| 73 | + ) |
| 74 | + }) |
| 75 | + return |
| 76 | + } |
| 77 | + |
| 78 | + try { |
| 79 | + loading.value = true |
| 80 | + let inviteDialogUrl = provider.inviteAcceptDialog |
| 81 | + |
| 82 | + // If inviteAcceptDialog is empty, call backend discovery |
| 83 | + if (!inviteDialogUrl || inviteDialogUrl.trim() === '') { |
| 84 | + inviteDialogUrl = await discoverProvider(provider.fqdn) |
| 85 | + } else { |
| 86 | + // If it's a relative path, make it absolute |
| 87 | + if (inviteDialogUrl.startsWith('/')) { |
| 88 | + const baseUrl = `https://${provider.fqdn}` |
| 89 | + inviteDialogUrl = `${baseUrl}${inviteDialogUrl}` |
| 90 | + } |
| 91 | + // If it's already absolute, use as-is |
| 92 | + } |
| 93 | + |
| 94 | + // Build final URL with query parameters and redirect |
| 95 | + const finalUrl = buildProviderUrl(inviteDialogUrl, token, providerDomain) |
| 96 | + window.location.href = finalUrl |
| 97 | + } catch (err) { |
| 98 | + console.error('Failed to navigate to provider:', err) |
| 99 | + // Error is already shown by discoverProvider, do not use showErrorMessage here |
| 100 | + } finally { |
| 101 | + loading.value = false |
| 102 | + } |
| 103 | + } |
| 104 | + |
| 105 | + const navigateToManualProvider = async ( |
| 106 | + input: string, |
| 107 | + token: string, |
| 108 | + providerDomain?: string |
| 109 | + ) => { |
| 110 | + const trimmedInput = input.trim() |
| 111 | + if (!trimmedInput) return |
| 112 | + |
| 113 | + if (isSelfDomain(trimmedInput)) { |
| 114 | + showErrorMessage({ |
| 115 | + title: $gettext('Invalid Provider Selection'), |
| 116 | + desc: $gettext( |
| 117 | + 'You cannot use your own instance as a provider. Please select a different provider to establish a federated connection.' |
| 118 | + ) |
| 119 | + }) |
| 120 | + return |
| 121 | + } |
| 122 | + |
| 123 | + try { |
| 124 | + loading.value = true |
| 125 | + const inviteDialogUrl = await discoverProvider(trimmedInput) |
| 126 | + const finalUrl = buildProviderUrl(inviteDialogUrl, token, providerDomain) |
| 127 | + window.location.href = finalUrl |
| 128 | + } catch (err) { |
| 129 | + console.error('Failed to navigate to manual provider:', err) |
| 130 | + // Error is already shown by discoverProvider, do not use showErrorMessage here |
| 131 | + } finally { |
| 132 | + loading.value = false |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + const loadFederations = async () => { |
| 137 | + try { |
| 138 | + loading.value = true |
| 139 | + error.value = false |
| 140 | + |
| 141 | + const response = await clientService.httpUnAuthenticated.get<FederationsApiResponse>( |
| 142 | + '/sciencemesh/federations' |
| 143 | + ) |
| 144 | + |
| 145 | + const transformedFederations: WayfFederation = {} |
| 146 | + response.data.forEach((fed) => { |
| 147 | + const providers = fed.servers |
| 148 | + .map((server) => ({ |
| 149 | + name: server.displayName, |
| 150 | + fqdn: new URL(server.url).hostname, |
| 151 | + // Keep empty if not provided by the server |
| 152 | + inviteAcceptDialog: server.inviteAcceptDialog || '' |
| 153 | + })) |
| 154 | + .filter((provider) => !isSelfDomain(provider.fqdn)) |
| 155 | + |
| 156 | + if (providers.length > 0) { |
| 157 | + transformedFederations[fed.federation] = providers |
| 158 | + } |
| 159 | + }) |
| 160 | + |
| 161 | + federations.value = transformedFederations |
| 162 | + } catch (err) { |
| 163 | + console.error('Failed to load federations:', err) |
| 164 | + error.value = true |
| 165 | + showErrorMessage({ |
| 166 | + title: $gettext('Failed to Load Providers'), |
| 167 | + desc: $gettext('Could not load the list of available providers'), |
| 168 | + errors: [err] |
| 169 | + }) |
| 170 | + } finally { |
| 171 | + loading.value = false |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + const filterProviders = (providers: WayfProvider[], query: string): WayfProvider[] => { |
| 176 | + const searchTerm = (query || '').toLowerCase() |
| 177 | + return providers.filter( |
| 178 | + (provider) => |
| 179 | + provider.name.toLowerCase().includes(searchTerm) || |
| 180 | + provider.fqdn.toLowerCase().includes(searchTerm) |
| 181 | + ) |
| 182 | + } |
| 183 | + |
| 184 | + return { |
| 185 | + loading: computed(() => loading.value), |
| 186 | + error: computed(() => error.value), |
| 187 | + federations: computed(() => federations.value), |
| 188 | + discoverProvider, |
| 189 | + buildProviderUrl, |
| 190 | + navigateToProvider, |
| 191 | + navigateToManualProvider, |
| 192 | + loadFederations, |
| 193 | + filterProviders |
| 194 | + } |
| 195 | +} |
0 commit comments