diff --git a/changelog/unreleased/enhancement-reduce-network-load-on-token-renewal b/changelog/unreleased/enhancement-reduce-network-load-on-token-renewal new file mode 100644 index 00000000000..b354b22ff41 --- /dev/null +++ b/changelog/unreleased/enhancement-reduce-network-load-on-token-renewal @@ -0,0 +1,6 @@ +Enhancement: Reduce network load on token renewal + +We've reduced the network load on token renewal, resulting in better overall performance of the Web client. + +https://github.com/owncloud/web/pull/11077 +https://github.com/owncloud/web/issues/11069 diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts index dd7d3075f18..b261a9769ea 100644 --- a/packages/design-system/src/index.ts +++ b/packages/design-system/src/index.ts @@ -1,5 +1,4 @@ import { getSizeClass } from './utils/sizeClasses' -import './utils/webFontLoader' import { App } from 'vue' import * as components from './components' @@ -19,7 +18,9 @@ export const applyCustomProp = (key: string, value: string | undefined) => { } export default { - install(app: App, options: any = {}) { + async install(app: App, options: any = {}) { + await import('./utils/webFontLoader') + const themeOptions = options.tokens initializeCustomProps(themeOptions?.breakpoints, 'breakpoint-') initializeCustomProps(themeOptions?.colorPalette, 'color-') diff --git a/packages/web-runtime/src/container/bootstrap.ts b/packages/web-runtime/src/container/bootstrap.ts index 3e90203bdd7..907d0ecaba3 100644 --- a/packages/web-runtime/src/container/bootstrap.ts +++ b/packages/web-runtime/src/container/bootstrap.ts @@ -1,7 +1,7 @@ import { registerClient } from '../services/clientRegistration' import { buildApplication, NextApplication } from './application' import { RouteLocationRaw, Router, RouteRecordNormalized } from 'vue-router' -import { App, watch } from 'vue' +import { App, computed, watch } from 'vue' import { loadTheme } from '../helpers/theme' import { createGettext, GetTextOptions, Language, Translations } from 'vue3-gettext' import { getBackendVersion, getWebVersion } from './versions' @@ -28,7 +28,8 @@ import { ResourcesStore, SpacesStore, MessageStore, - SharesStore + SharesStore, + ArchiverService } from '@ownclouders/web-pkg' import { authService } from '../services/auth' import { @@ -410,6 +411,37 @@ export const announceClientService = ({ app.provide('$clientService', clientService) } +export const announceArchiverService = ({ + app, + configStore, + userStore, + capabilityStore +}: { + app: App + configStore: ConfigStore + userStore: UserStore + capabilityStore: CapabilityStore +}): void => { + app.config.globalProperties.$archiverService = new ArchiverService( + app.config.globalProperties.$clientService, + userStore, + configStore.serverUrl, + computed( + () => + capabilityStore.filesArchivers || [ + { + enabled: true, + version: '1.0.0', + formats: ['tar', 'zip'], + archiver_url: `${configStore.serverUrl}index.php/apps/files/ajax/download.php` + } + ] + ) + ) + + app.provide('$archiverService', app.config.globalProperties.$archiverService) +} + /** * @param vue */ diff --git a/packages/web-runtime/src/defaults/index.ts b/packages/web-runtime/src/defaults/index.ts index eff8b331b03..5d10b0be099 100644 --- a/packages/web-runtime/src/defaults/index.ts +++ b/packages/web-runtime/src/defaults/index.ts @@ -1,14 +1,15 @@ import merge from 'lodash-es/merge' import App from '../App.vue' +import TokenRenewal from '../pages/tokenRenewal.vue' import missingOrInvalidConfigPage from '../pages/missingOrInvalidConfig.vue' -// fontawesome-free attributions console message -import '@fortawesome/fontawesome-free/attribution' - -export { default as DesignSystem } from 'design-system' export * from './languages' -export const pages = { success: App, failure: missingOrInvalidConfigPage } +export const pages = { + success: App, + failure: missingOrInvalidConfigPage, + tokenRenewal: TokenRenewal +} export const loadTranslations = async () => { const { coreTranslations, clientTranslations, pkgTranslations, odsTranslations } = await import( @@ -17,3 +18,10 @@ export const loadTranslations = async () => { return merge({}, coreTranslations, clientTranslations, pkgTranslations, odsTranslations) } + +export const loadDesignSystem = async () => { + // fontawesome-free attributions console message + import('@fortawesome/fontawesome-free/attribution') + + return (await import('design-system')).default +} diff --git a/packages/web-runtime/src/helpers/silentRedirect.ts b/packages/web-runtime/src/helpers/silentRedirect.ts new file mode 100644 index 00000000000..a923cf97814 --- /dev/null +++ b/packages/web-runtime/src/helpers/silentRedirect.ts @@ -0,0 +1 @@ +export const isSilentRedirectRoute = () => window.location.pathname === '/web-oidc-silent-redirect' diff --git a/packages/web-runtime/src/index.ts b/packages/web-runtime/src/index.ts index 563b097d1a1..c3278526de9 100644 --- a/packages/web-runtime/src/index.ts +++ b/packages/web-runtime/src/index.ts @@ -1,9 +1,4 @@ -import { - DesignSystem as designSystem, - pages, - loadTranslations, - supportedLanguages -} from './defaults' +import { loadDesignSystem, pages, loadTranslations, supportedLanguages } from './defaults' import { router } from './router' import { PortalTarget } from '@ownclouders/web-pkg' import { createHead } from '@vueuse/head' @@ -31,7 +26,8 @@ import { announcePasswordPolicyService, registerSSEEventListeners, setViewOptions, - announceGettext + announceGettext, + announceArchiverService } from './container/bootstrap' import { applicationStore } from './container/store' import { @@ -41,18 +37,20 @@ import { PublicSpaceResource } from '@ownclouders/web-client' import { loadCustomTranslations } from 'web-runtime/src/helpers/customTranslations' -import { computed, createApp, watch } from 'vue' +import { createApp, watch } from 'vue' import PortalVue, { createWormhole } from 'portal-vue' import { createPinia } from 'pinia' import Avatar from './components/Avatar.vue' import focusMixin from './mixins/focusMixin' -import { ArchiverService } from '@ownclouders/web-pkg' import { UnifiedRoleDefinition } from '@ownclouders/web-client/graph/generated' import { extensionPoints } from './extensionPoints' +import { isSilentRedirectRoute } from './helpers/silentRedirect' export const bootstrapApp = async (configurationPath: string, appsReadyCallback: () => void) => { + const isSilentRedirect = isSilentRedirectRoute() + const pinia = createPinia() - const app = createApp(pages.success) + const app = createApp(isSilentRedirect ? pages.tokenRenewal : pages.success) app.use(pinia) const { @@ -74,65 +72,12 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback: app.provide('$router', router) await announceConfiguration({ path: configurationPath, configStore }) - startSentry(configStore, app) app.use(abilitiesPlugin, createMongoAbility([]), { useGlobalProperties: true }) const gettext = announceGettext({ app, availableLanguages: supportedLanguages }) - announceUppyService({ app }) announceClientService({ app, configStore, authStore }) - - // TODO: move to announceArchiverService function - app.config.globalProperties.$archiverService = new ArchiverService( - app.config.globalProperties.$clientService, - userStore, - configStore.serverUrl, - computed( - () => - capabilityStore.filesArchivers || [ - { - enabled: true, - version: '1.0.0', - formats: ['tar', 'zip'], - archiver_url: `${configStore.serverUrl}index.php/apps/files/ajax/download.php` - } - ] - ) - ) - app.provide('$archiverService', app.config.globalProperties.$archiverService) - announceLoadingService({ app }) - announcePreviewService({ - app, - configStore, - userStore, - authStore, - capabilityStore - }) - announcePasswordPolicyService({ app }) - await announceClient(configStore) - - app.config.globalProperties.$wormhole = createWormhole() - app.use(PortalVue, { - wormhole: app.config.globalProperties.$wormhole, - // do not register portal-target component so we can register our own wrapper - portalTargetName: false - }) - app.component('PortalTarget', PortalTarget) - - const applicationsPromise = initializeApplications({ app, configStore, router }) - const translationsPromise = loadTranslations() - const customTranslationsPromise = loadCustomTranslations({ configStore }) - const themePromise = announceTheme({ app, designSystem, configStore }) - const [coreTranslations, customTranslations] = await Promise.all([ - translationsPromise, - customTranslationsPromise, - applicationsPromise, - themePromise - ]) - - announceTranslations({ appsStore, gettext, coreTranslations, customTranslations }) - announceAuthService({ app, configStore, @@ -142,9 +87,49 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback: capabilityStore, webWorkersStore }) - announceCustomStyles({ configStore }) - announceCustomScripts({ configStore }) - announceDefaults({ appsStore, router, extensionRegistry, configStore }) + + if (!isSilentRedirect) { + const designSystem = await loadDesignSystem() + + announceUppyService({ app }) + startSentry(configStore, app) + announceArchiverService({ app, configStore, userStore, capabilityStore }) + announceLoadingService({ app }) + announcePreviewService({ + app, + configStore, + userStore, + authStore, + capabilityStore + }) + announcePasswordPolicyService({ app }) + await announceClient(configStore) + + app.config.globalProperties.$wormhole = createWormhole() + app.use(PortalVue, { + wormhole: app.config.globalProperties.$wormhole, + // do not register portal-target component so we can register our own wrapper + portalTargetName: false + }) + app.component('PortalTarget', PortalTarget) + + const applicationsPromise = initializeApplications({ app, configStore, router }) + const translationsPromise = loadTranslations() + const customTranslationsPromise = loadCustomTranslations({ configStore }) + const themePromise = announceTheme({ app, designSystem, configStore }) + const [coreTranslations, customTranslations] = await Promise.all([ + translationsPromise, + customTranslationsPromise, + applicationsPromise, + themePromise + ]) + + announceTranslations({ appsStore, gettext, coreTranslations, customTranslations }) + + announceCustomStyles({ configStore }) + announceCustomScripts({ configStore }) + announceDefaults({ appsStore, router, extensionRegistry, configStore }) + } app.use(router) app.use(createHead()) @@ -154,6 +139,10 @@ export const bootstrapApp = async (configurationPath: string, appsReadyCallback: app.mount('#owncloud') + if (isSilentRedirect) { + return + } + setViewOptions({ resourcesStore }) const applications = Array.from(applicationStore.values()) @@ -273,6 +262,7 @@ export const bootstrapErrorApp = async (err: Error): Promise => { const { capabilityStore, configStore } = announcePiniaStores() announceVersions({ capabilityStore }) const app = createApp(pages.failure) + const designSystem = await loadDesignSystem() await announceTheme({ app, designSystem, configStore }) console.error(err) const translations = await loadTranslations() diff --git a/packages/web-runtime/src/pages/oidcCallback.vue b/packages/web-runtime/src/pages/oidcCallback.vue index f69247fb7a4..992e39c7c3f 100644 --- a/packages/web-runtime/src/pages/oidcCallback.vue +++ b/packages/web-runtime/src/pages/oidcCallback.vue @@ -32,8 +32,8 @@ export default defineComponent({ const error = ref(false) - const footerSlogan = computed(() => currentTheme.value.common.slogan) - const logoImg = computed(() => currentTheme.value.logo.login) + const footerSlogan = computed(() => unref(currentTheme)?.common.slogan) + const logoImg = computed(() => unref(currentTheme)?.logo.login) const route = useRoute() diff --git a/packages/web-runtime/src/pages/tokenRenewal.vue b/packages/web-runtime/src/pages/tokenRenewal.vue new file mode 100644 index 00000000000..c28d60f54e3 --- /dev/null +++ b/packages/web-runtime/src/pages/tokenRenewal.vue @@ -0,0 +1,13 @@ + + diff --git a/packages/web-runtime/src/services/auth/authService.ts b/packages/web-runtime/src/services/auth/authService.ts index cb5fc88b1ac..279f130a790 100644 --- a/packages/web-runtime/src/services/auth/authService.ts +++ b/packages/web-runtime/src/services/auth/authService.ts @@ -22,6 +22,7 @@ import { Ability } from '@ownclouders/web-client' import { Language } from 'vue3-gettext' import { PublicLinkType } from '@ownclouders/web-client' import { WebWorkersStore } from '@ownclouders/web-pkg' +import { isSilentRedirectRoute } from '../../helpers/silentRedirect' export class AuthService implements AuthServiceInterface { private clientService: ClientService @@ -109,7 +110,9 @@ export class AuthService implements AuthServiceInterface { accessTokenExpiryThreshold: this.accessTokenExpiryThreshold }) - if (!this.tokenTimerWorker) { + // don't load worker in the silent redirect iframe + const isSilentRedirect = isSilentRedirectRoute() + if (!this.tokenTimerWorker && !isSilentRedirect) { const { options } = this.configStore if (!options.embed?.enabled || !options.embed?.delegateAuthentication) {