From 6536125bee6a5abfefac17c67593f1da74ac87b6 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Tue, 16 Jul 2024 09:18:29 +0200 Subject: [PATCH] refactor: initialize app provider apps earlier Since the ocis backend guarantees that the /app/list endpoint exists we can hardcode the url and initialize the app provider apps before the vue-router is being initialized. This saves us a redirect on page reload in an app provider app. See https://github.com/owncloud/ocis/issues/9489 --- packages/web-app-external/src/Redirect.vue | 5 --- packages/web-app-external/src/index.ts | 13 ------- .../src/services/appProvider/service.ts | 20 ++++------ .../web-runtime/src/container/bootstrap.ts | 14 +++---- packages/web-runtime/src/index.ts | 38 +++++++++++-------- 5 files changed, 36 insertions(+), 54 deletions(-) diff --git a/packages/web-app-external/src/Redirect.vue b/packages/web-app-external/src/Redirect.vue index 161d7928797..caa9400da67 100644 --- a/packages/web-app-external/src/Redirect.vue +++ b/packages/web-app-external/src/Redirect.vue @@ -18,7 +18,6 @@ import { queryItemAsString, useAppProviderService, useRouteMeta, - useRouteParam, useRouteQuery } from '@ownclouders/web-pkg' import { useRouter } from 'vue-router' @@ -36,11 +35,7 @@ export default defineComponent({ const appQuery = useRouteQuery('app') const appNameQuery = useRouteQuery('appName') - const appNameParam = useRouteParam('appCatchAll') const appName = computed(() => { - if (unref(appNameParam)) { - return unref(appNameParam) - } if (unref(appQuery)) { return queryItemAsString(unref(appQuery)) } diff --git a/packages/web-app-external/src/index.ts b/packages/web-app-external/src/index.ts index 30744523e1f..7a44f8d487f 100644 --- a/packages/web-app-external/src/index.ts +++ b/packages/web-app-external/src/index.ts @@ -30,19 +30,6 @@ export default defineWebApplication({ id: 'external' } const routes = [ - { - // catch-all route for page reloads, because dynamic external app routes are not available immediately on page reload. - // can be deleted as soon as app provider apps are not loaded after login anymore (app provider listing endpoint must be hardcoded and public) - // prerequisite: https://github.com/owncloud/ocis/issues/9489 - name: 'catch-all', - path: '-:appCatchAll/:driveAliasAndItem(.*)?', - component: Redirect, - meta: { - authContext: 'hybrid', - title: $gettext('Redirecting to external app'), - patchCleanPath: true - } - }, { // fallback route for old external-app URLs, in case someone made a bookmark. Can be removed with the next major release. name: 'apps', diff --git a/packages/web-pkg/src/services/appProvider/service.ts b/packages/web-pkg/src/services/appProvider/service.ts index 5e232ac9f1a..65f5a44c643 100644 --- a/packages/web-pkg/src/services/appProvider/service.ts +++ b/packages/web-pkg/src/services/appProvider/service.ts @@ -1,29 +1,23 @@ import { MimeType, MimeTypesToAppsSchema } from './schemas' import { ref, unref } from 'vue' -import { CapabilityStore } from '../../composables' import { ClientService } from '../client' +import { urlJoin } from '@ownclouders/web-client' export class AppProviderService { private _mimeTypes = ref([]) - private capabilityStore: CapabilityStore - private clientService: ClientService + private readonly serverUrl: string + private readonly clientService: ClientService - constructor(capabilityStore: CapabilityStore, clientService: ClientService) { - this.capabilityStore = capabilityStore + constructor(serverUrl: string, clientService: ClientService) { + this.serverUrl = serverUrl this.clientService = clientService } public async loadData(): Promise { - const appProviderCapability = this.capabilityStore.filesAppProviders.find( - (appProvider) => appProvider.enabled - ) - if (!appProviderCapability) { - return - } - + const appListUrl = urlJoin(this.serverUrl, 'app', 'list') const { data: { 'mime-types': mimeTypes } - } = await this.clientService.httpUnAuthenticated.get(appProviderCapability.apps_url, { + } = await this.clientService.httpUnAuthenticated.get(appListUrl, { schema: MimeTypesToAppsSchema }) this._mimeTypes.value = mimeTypes diff --git a/packages/web-runtime/src/container/bootstrap.ts b/packages/web-runtime/src/container/bootstrap.ts index 81522a50ffd..4cb26dc3787 100644 --- a/packages/web-runtime/src/container/bootstrap.ts +++ b/packages/web-runtime/src/container/bootstrap.ts @@ -163,7 +163,7 @@ export const announceConfiguration = async ({ * * @param configStore */ -export const announceClient = async (configStore: ConfigStore): Promise => { +export const announceAuthClient = async (configStore: ConfigStore): Promise => { const openIdConnect = configStore.openIdConnect || {} if (!openIdConnect.dynamic) { @@ -185,13 +185,13 @@ export const initializeApplications = async ({ configStore, router, appProviderService, - dynamicApps + appProviderApps }: { app: App configStore: ConfigStore router: Router appProviderService: AppProviderService - dynamicApps?: boolean + appProviderApps?: boolean }): Promise => { type RawApplication = { path?: string @@ -199,7 +199,7 @@ export const initializeApplications = async ({ } let applicationResults: PromiseSettledResult[] = [] - if (dynamicApps) { + if (appProviderApps) { applicationResults = await Promise.allSettled( appProviderService.appNames.map((appName) => buildApplication({ @@ -574,14 +574,14 @@ export const announceAuthService = ({ */ export const announceAppProviderService = ({ app, - capabilityStore, + serverUrl, clientService }: { app: App - capabilityStore: CapabilityStore + serverUrl: string clientService: ClientService }): AppProviderService => { - const appProviderService = new AppProviderService(capabilityStore, clientService) + const appProviderService = new AppProviderService(serverUrl, clientService) app.config.globalProperties.$appProviderService = appProviderService app.provide('$appProviderService', appProviderService) return appProviderService diff --git a/packages/web-runtime/src/index.ts b/packages/web-runtime/src/index.ts index bf74be4b38c..9422831f68f 100644 --- a/packages/web-runtime/src/index.ts +++ b/packages/web-runtime/src/index.ts @@ -1,6 +1,6 @@ import { loadDesignSystem, pages, loadTranslations, supportedLanguages } from './defaults' import { router } from './router' -import { PortalTarget, AppProviderService } from '@ownclouders/web-pkg' +import { PortalTarget } from '@ownclouders/web-pkg' import { createHead } from '@vueuse/head' import { abilitiesPlugin } from '@casl/vue' import { createMongoAbility } from '@casl/ability' @@ -9,7 +9,7 @@ import { announceConfiguration, initializeApplications, announceApplicationsReady, - announceClient, + announceAuthClient, announceDefaults, announceClientService, announceTheme, @@ -89,13 +89,16 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback: webWorkersStore }) - let appProviderService: AppProviderService if (!isSilentRedirect) { const designSystem = await loadDesignSystem() announceUppyService({ app }) startSentry(configStore, app) - appProviderService = announceAppProviderService({ app, capabilityStore, clientService }) + const appProviderService = announceAppProviderService({ + app, + serverUrl: configStore.serverUrl, + clientService + }) announceArchiverService({ app, configStore, userStore, capabilityStore }) announceLoadingService({ app }) announcePreviewService({ @@ -106,7 +109,7 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback: capabilityStore }) announcePasswordPolicyService({ app }) - await announceClient(configStore) + await announceAuthClient(configStore) app.config.globalProperties.$wormhole = createWormhole() app.use(PortalVue, { @@ -132,6 +135,19 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback: themePromise ]) + // Important: has to happen AFTER native applications are loaded. + // Reason: the `external` app serves as a blueprint for creating the app provider apps. + if (applicationStore.has('web-app-external')) { + await appProviderService.loadData() + await initializeApplications({ + app, + configStore, + router, + appProviderService, + appProviderApps: true + }) + } + announceTranslations({ appsStore, gettext, coreTranslations, customTranslations }) announceCustomStyles({ configStore }) @@ -165,20 +181,10 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback: } announceVersions({ capabilityStore }) - await appProviderService.loadData() - const appProviderApps = await initializeApplications({ - app, - configStore, - router, - appProviderService, - dynamicApps: true - }) - appProviderApps.forEach((application) => application.mounted(app)) - await announceApplicationsReady({ app, appsStore, - applications: [...applications, ...appProviderApps] + applications }) appsReadyCallback() },