From 2ea194ce24febbe6ed353ecc4e03b5bfa64f6a3d Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Wed, 26 Oct 2022 16:48:11 +0200 Subject: [PATCH 1/5] feat: introduce meta.authContext Introduce meta.authContext to define which kind of context is required on a route. This gets evaluated for target routes and (if present) context routes. The old meta.auth boolean has been deprecated. For now it gets transformed into the respective meta.authContext value. --- packages/web-app-files/src/router/public.ts | 4 +- .../web-pkg/src/composables/router/types.ts | 11 +++ packages/web-runtime/src/App.vue | 4 +- packages/web-runtime/src/router/helpers.ts | 99 ++++++++++++------- packages/web-runtime/src/router/index.ts | 16 +-- 5 files changed, 84 insertions(+), 50 deletions(-) diff --git a/packages/web-app-files/src/router/public.ts b/packages/web-app-files/src/router/public.ts index ab638976a90..706ee7a3d2c 100644 --- a/packages/web-app-files/src/router/public.ts +++ b/packages/web-app-files/src/router/public.ts @@ -28,7 +28,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: ':driveAliasAndItem*', component: components.Spaces.DriveResolver, meta: { - auth: false, + authContext: 'publicLink', patchCleanPath: true } } @@ -46,7 +46,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: ':token?', component: components.FilesDrop, meta: { - auth: false, + authContext: 'publicLink', title: $gettext('Public file upload') } } diff --git a/packages/web-pkg/src/composables/router/types.ts b/packages/web-pkg/src/composables/router/types.ts index ac707736603..bf0350e8ed4 100644 --- a/packages/web-pkg/src/composables/router/types.ts +++ b/packages/web-pkg/src/composables/router/types.ts @@ -1,3 +1,5 @@ +import { RouteMeta } from 'vue-router' + type Dictionary = { [key: string]: T } export type QueryValue = string | (string | null)[] @@ -5,3 +7,12 @@ export type LocationQuery = Dictionary export type ParamValue = string export type LocationParams = Dictionary + +export const authContextValues = ['anonymous', 'user', 'publicLink', 'hybrid'] as const +export type AuthContext = typeof authContextValues[number] + +export interface WebRouteMeta extends RouteMeta { + title?: string + authContext?: AuthContext + patchCleanPath?: boolean +} diff --git a/packages/web-runtime/src/App.vue b/packages/web-runtime/src/App.vue index adaf7941e5d..40c93f34588 100644 --- a/packages/web-runtime/src/App.vue +++ b/packages/web-runtime/src/App.vue @@ -49,7 +49,7 @@ import LayoutLoading from './layouts/Loading.vue' import LayoutPlain from './layouts/Plain.vue' import { getBackendVersion, getWebVersion } from './container/versions' import { defineComponent } from '@vue/composition-api' -import { isPublicLinkContext, isUserContext, isAuthenticationRequired } from './router' +import { isPublicLinkContext, isUserContext, isAnonymousContext } from './router' import { additionalTranslations } from './helpers/additionalTranslations' // eslint-disable-line import { eventBus } from 'web-pkg/src/services' @@ -70,7 +70,7 @@ export default defineComponent({ ...mapGetters(['configuration', 'capabilities', 'getSettingsValue']), ...mapGetters('runtime/auth', ['isUserContextReady', 'isPublicLinkContextReady']), layout() { - if (!this.$route.name || !isAuthenticationRequired(this.$router, this.$route)) { + if (!this.$route.name || isAnonymousContext(this.$router, this.$route)) { return LayoutPlain } diff --git a/packages/web-runtime/src/router/helpers.ts b/packages/web-runtime/src/router/helpers.ts index b6a0fdbce11..e010208bbca 100644 --- a/packages/web-runtime/src/router/helpers.ts +++ b/packages/web-runtime/src/router/helpers.ts @@ -1,6 +1,11 @@ import { base, router } from './index' import Router, { Route, RouteRecordPublic } from 'vue-router' -import { contextQueryToFileContextProps, LocationParams } from 'web-pkg/src/composables' +import { + authContextValues, + contextQueryToFileContextProps, + LocationParams, + WebRouteMeta +} from 'web-pkg/src/composables' export const buildUrl = (pathname) => { const isHistoryMode = !!base @@ -40,41 +45,42 @@ export const buildUrl = (pathname) => { * @returns {boolean} */ export const isUserContext = (router: Router, to: Route): boolean => { - if (!isAuthenticationRequired(router, to)) { - return false - } - - if (to.meta?.auth !== false) { + const meta = getRouteMeta(to) + if (meta.authContext === 'user') { return true } + if (meta.authContext !== 'hybrid') { + return false + } const contextRoute = getContextRoute(router, to) - return !contextRoute || contextRoute.meta?.auth !== false + return !contextRoute || getRouteMeta({ meta: contextRoute.meta } as Route).authContext === 'user' } /** - * Checks if the `to` route or the route it was reached from (i.e. the `contextRoute`) is a public link (with or without password). + * Checks if the `to` route or the route it was reached from (i.e. the `contextRoute`) needs a resolved public link context (with or without password). * * @param router {Router} * @param to {Route} * @returns {boolean} */ export const isPublicLinkContext = (router: Router, to: Route): boolean => { - if (!isAuthenticationRequired(router, to)) { - return false - } - - const publicLinkRouteNames = ['files-public-link', 'files-public-upload'] - if (publicLinkRouteNames.includes(to.name)) { + if (to.params.driveAliasAndItem?.startsWith('public/')) { return true } - if (to.params.driveAliasAndItem?.startsWith('public/')) { + const meta = getRouteMeta(to) + if (meta.authContext === 'publicLink') { return true } + if (meta.authContext !== 'hybrid') { + return false + } const contextRoute = getContextRoute(router, to) - return contextRoute && publicLinkRouteNames.includes(contextRoute?.name) + return ( + contextRoute && getRouteMeta({ meta: contextRoute.meta } as Route).authContext === 'publicLink' + ) } /** @@ -107,33 +113,14 @@ const extractPublicLinkTokenFromRouteParams = (params: LocationParams): string = } /** - * Asserts whether any form of authentication is required, i.e. - * - a user or - * - public link (with or without password), which represents an impersonation of a user via public share + * Asserts that no form of authentication is required. * * @param router {Router} * @param to {Route} * @returns {boolean} */ -export const isAuthenticationRequired = (router: Router, to: Route): boolean => { - const publicRouteNames = [ - 'login', - 'oidcCallback', - 'oidcSilentRedirect', - 'resolvePublicLink', - 'accessDenied' - ] - - if (publicRouteNames.includes(to.name)) { - return false - } - - const contextRoute = getContextRoute(router, to) - if (publicRouteNames.includes(contextRoute?.name)) { - return false - } - - return true +export const isAnonymousContext = (router: Router, to: Route): boolean => { + return getRouteMeta(to).authContext === 'anonymous' } /** @@ -154,3 +141,39 @@ const getContextRoute = (router: Router, to: Route): RouteRecordPublic | null => return router.getRoutes().find((r) => r.name === to.query[contextRouteNameKey]) } + +const getRouteMeta = (to: Route): WebRouteMeta => { + if (!to.meta) { + return { + authContext: 'user' + } + } + + // rewrite deprecated `auth` property to the respective `authContext` value + if (!to.meta.authContext && Object.prototype.hasOwnProperty.call(to.meta, 'auth')) { + to.meta.authContext = to.meta.auth ? 'user' : 'hybrid' + console.warn( + `route key meta.auth is deprecated. Please switch to meta.authContext="${to.meta.authContext}" in route "${to.name}".` + ) + } + + if (to?.meta?.authContext) { + if (authContextValues.includes(to.meta.authContext)) { + return to.meta + } + console.warn( + `invalid authContext "${to.meta.authContext}" in route "${ + to.name + }". must be one of [${authContextValues.join(', ')}].` + ) + } + if (to?.meta) { + return { + ...to.meta, + authContext: 'user' + } + } + return { + authContext: 'user' + } +} diff --git a/packages/web-runtime/src/router/index.ts b/packages/web-runtime/src/router/index.ts index c67209564f6..bdada899b84 100644 --- a/packages/web-runtime/src/router/index.ts +++ b/packages/web-runtime/src/router/index.ts @@ -40,49 +40,49 @@ export const router = patchRouter( path: '/login', name: 'login', component: LoginPage, - meta: { title: $gettext('Login') } + meta: { title: $gettext('Login'), authContext: 'anonymous' } }, { path: '/logout', name: 'logout', component: LogoutPage, - meta: { title: $gettext('Logout') } + meta: { title: $gettext('Logout'), authContext: 'anonymous' } }, { path: '/oidc-callback', name: 'oidcCallback', component: OidcCallbackPage, - meta: { title: $gettext('Oidc callback') } + meta: { title: $gettext('Oidc callback'), authContext: 'anonymous' } }, { path: '/oidc-silent-redirect', name: 'oidcSilentRedirect', component: OidcCallbackPage, - meta: { title: $gettext('Oidc redirect') } + meta: { title: $gettext('Oidc redirect'), authContext: 'anonymous' } }, { path: '/f/:fileId', name: 'resolvePrivateLink', component: ResolvePrivateLinkPage, - meta: { title: $gettext('Private link') } + meta: { title: $gettext('Private link'), authContext: 'user' } }, { path: '/s/:token', name: 'resolvePublicLink', component: ResolvePublicLinkPage, - meta: { title: $gettext('Public link') } + meta: { title: $gettext('Public link'), authContext: 'anonymous' } }, { path: '/access-denied', name: 'accessDenied', component: AccessDeniedPage, - meta: { title: $gettext('Access denied') } + meta: { title: $gettext('Access denied'), authContext: 'anonymous' } }, { path: '/account', name: 'account', component: Account, - meta: { title: $gettext('Account') } + meta: { title: $gettext('Account'), authContext: 'user' } } ] }) From 361fca53a9c1c0e24ff32f5329e9ea681d4d7fbc Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Wed, 26 Oct 2022 16:58:49 +0200 Subject: [PATCH 2/5] feat: set authContext requirement explicitly All routes in the web mono repo now have a meta.authContext defined. --- packages/web-app-draw-io/src/index.js | 2 +- packages/web-app-external/src/index.js | 2 +- packages/web-app-files/src/router/common.ts | 2 ++ packages/web-app-files/src/router/deprecated.ts | 8 +------- packages/web-app-files/src/router/shares.ts | 3 +++ packages/web-app-files/src/router/spaces.ts | 2 ++ packages/web-app-files/src/router/trash.ts | 1 + packages/web-app-pdf-viewer/src/index.js | 2 +- packages/web-app-preview/src/index.js | 2 +- packages/web-app-search/src/index.ts | 1 + packages/web-app-text-editor/src/index.js | 2 +- packages/web-app-user-management/src/index.js | 2 ++ packages/web-pkg/src/composables/router/types.ts | 1 + 13 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/web-app-draw-io/src/index.js b/packages/web-app-draw-io/src/index.js index eece4850391..0312bbe70e1 100644 --- a/packages/web-app-draw-io/src/index.js +++ b/packages/web-app-draw-io/src/index.js @@ -7,7 +7,7 @@ const routes = [ path: '/:driveAliasAndItem*', component: App, meta: { - auth: false, + authContext: 'hybrid', patchCleanPath: true } } diff --git a/packages/web-app-external/src/index.js b/packages/web-app-external/src/index.js index d7d87fef322..92c6449d552 100644 --- a/packages/web-app-external/src/index.js +++ b/packages/web-app-external/src/index.js @@ -13,7 +13,7 @@ const routes = [ path: '/:driveAliasAndItem*', component: App, meta: { - auth: false, + authContext: 'hybrid', patchCleanPath: true } } diff --git a/packages/web-app-files/src/router/common.ts b/packages/web-app-files/src/router/common.ts index cf91457fcfb..74e22b9ddb6 100644 --- a/packages/web-app-files/src/router/common.ts +++ b/packages/web-app-files/src/router/common.ts @@ -25,6 +25,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: 'list/:page?', component: components.SearchResults, meta: { + authContext: 'user', title: $gettext('Search results'), contextQueryItems: ['term', 'provider'] } @@ -40,6 +41,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: '', component: components.Favorites, meta: { + authContext: 'user', title: $gettext('Favorite files') } } diff --git a/packages/web-app-files/src/router/deprecated.ts b/packages/web-app-files/src/router/deprecated.ts index c7f189cff92..96a50c20b76 100644 --- a/packages/web-app-files/src/router/deprecated.ts +++ b/packages/web-app-files/src/router/deprecated.ts @@ -19,7 +19,7 @@ const deprecatedRedirect = (routeConfig: { redirect: (to: Route) => Location }): RouteConfig => { return { - meta: routeConfig.meta, + meta: { ...routeConfig.meta, authContext: 'anonymous' }, // authContext belongs to the redirect target, not to the redirect itself. path: routeConfig.path, redirect: (to) => { const location = routeConfig.redirect(to) @@ -82,9 +82,6 @@ export const buildRoutes = (): RouteConfig[] => }, { path: '/public/list/:item*', - meta: { - auth: false - }, redirect: (to) => createLocationPublic('files-public-link', to) }, { @@ -93,9 +90,6 @@ export const buildRoutes = (): RouteConfig[] => }, { path: '/public-link/:token', - meta: { - auth: false - }, redirect: (to) => ({ name: 'resolvePublicLink', params: { token: to.params.token } }) } ].map(deprecatedRedirect) diff --git a/packages/web-app-files/src/router/shares.ts b/packages/web-app-files/src/router/shares.ts index 7e4690457b6..a4f1c87cc4a 100644 --- a/packages/web-app-files/src/router/shares.ts +++ b/packages/web-app-files/src/router/shares.ts @@ -28,6 +28,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: 'with-me', component: components.Shares.SharedWithMe, meta: { + authContext: 'user', title: $gettext('Files shared with me') } }, @@ -36,6 +37,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: 'with-others', component: components.Shares.SharedWithOthers, meta: { + authContext: 'user', title: $gettext('Files shared with others') } }, @@ -44,6 +46,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: 'via-link', component: components.Shares.SharedViaLink, meta: { + authContext: 'user', title: $gettext('Files shared via link') } } diff --git a/packages/web-app-files/src/router/spaces.ts b/packages/web-app-files/src/router/spaces.ts index 3f84b9343b7..f3d5eb5bde0 100644 --- a/packages/web-app-files/src/router/spaces.ts +++ b/packages/web-app-files/src/router/spaces.ts @@ -27,6 +27,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ name: locationSpacesProjects.name, component: components.Spaces.Projects, meta: { + authContext: 'user', title: $gettext('Spaces') } }, @@ -35,6 +36,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ name: locationSpacesGeneric.name, component: components.Spaces.DriveResolver, meta: { + authContext: 'user', patchCleanPath: true } } diff --git a/packages/web-app-files/src/router/trash.ts b/packages/web-app-files/src/router/trash.ts index 8b9daaf2e5a..b770f33ce39 100644 --- a/packages/web-app-files/src/router/trash.ts +++ b/packages/web-app-files/src/router/trash.ts @@ -21,6 +21,7 @@ export const buildRoutes = (components: RouteComponents): RouteConfig[] => [ path: ':driveAliasAndItem*', component: components.Spaces.DriveResolver, meta: { + authContext: 'user', patchCleanPath: true } } diff --git a/packages/web-app-pdf-viewer/src/index.js b/packages/web-app-pdf-viewer/src/index.js index 2a7ad1ccb31..ec17da753c6 100644 --- a/packages/web-app-pdf-viewer/src/index.js +++ b/packages/web-app-pdf-viewer/src/index.js @@ -12,7 +12,7 @@ const routes = [ component: App, name: 'pdf-viewer', meta: { - auth: false, + authContext: 'hybrid', title: $gettext('PDF Viewer'), patchCleanPath: true } diff --git a/packages/web-app-preview/src/index.js b/packages/web-app-preview/src/index.js index 22c3d529531..18167c46cc9 100644 --- a/packages/web-app-preview/src/index.js +++ b/packages/web-app-preview/src/index.js @@ -14,7 +14,7 @@ const routes = [ component: App, name: 'media', meta: { - auth: false, + authContext: 'hybrid', title: $gettext('Preview'), patchCleanPath: true } diff --git a/packages/web-app-search/src/index.ts b/packages/web-app-search/src/index.ts index a3cc316ddfa..1d4527d6b44 100644 --- a/packages/web-app-search/src/index.ts +++ b/packages/web-app-search/src/index.ts @@ -35,6 +35,7 @@ export default { path: 'list/:page?', component: List, meta: { + authContext: 'user', contextQueryItems: ['term', 'provider'] } } diff --git a/packages/web-app-text-editor/src/index.js b/packages/web-app-text-editor/src/index.js index be9014c2d03..f495d6470ea 100644 --- a/packages/web-app-text-editor/src/index.js +++ b/packages/web-app-text-editor/src/index.js @@ -14,8 +14,8 @@ const routes = [ component: App, name: 'text-editor', meta: { + authContext: 'hybrid', title: $gettext('Text Editor'), - auth: false, patchCleanPath: true } } diff --git a/packages/web-app-user-management/src/index.js b/packages/web-app-user-management/src/index.js index 7662c6c8e52..353b07d3821 100644 --- a/packages/web-app-user-management/src/index.js +++ b/packages/web-app-user-management/src/index.js @@ -25,6 +25,7 @@ const routes = [ name: 'user-management-users', component: Users, meta: { + authContext: 'user', title: $gettext('Users') } }, @@ -33,6 +34,7 @@ const routes = [ name: 'user-management-groups', component: Groups, meta: { + authContext: 'user', title: $gettext('Groups') } } diff --git a/packages/web-pkg/src/composables/router/types.ts b/packages/web-pkg/src/composables/router/types.ts index bf0350e8ed4..f0880de49b6 100644 --- a/packages/web-pkg/src/composables/router/types.ts +++ b/packages/web-pkg/src/composables/router/types.ts @@ -15,4 +15,5 @@ export interface WebRouteMeta extends RouteMeta { title?: string authContext?: AuthContext patchCleanPath?: boolean + contextQueryItems?: string[] } From 12636590c66f77605bb28209857c47291b59debb Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Wed, 26 Oct 2022 17:03:35 +0200 Subject: [PATCH 3/5] docs: add changelog item for authContext feature --- changelog/unreleased/enhancement-route-meta-props | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changelog/unreleased/enhancement-route-meta-props diff --git a/changelog/unreleased/enhancement-route-meta-props b/changelog/unreleased/enhancement-route-meta-props new file mode 100644 index 00000000000..5d175e25463 --- /dev/null +++ b/changelog/unreleased/enhancement-route-meta-props @@ -0,0 +1,8 @@ +Enhancement: Auth context in route meta props + +The route meta prop has been extended by a new "meta.authContext" property (can be one out of "anonymous", "user", "publicLink" or "hybrid"). With this, app developers can now define anonymous routes, which was hardcoded to a few well known route names before. +Anonymous routes are rendered in the application layout, i.e. with the top bar, as the ownCloud Web Chrome should always be visible to the user (except for a few handpicked exceptions in the web runtime, which are still rendered in the plain layout). + +https://github.com/owncloud/web/issues/7234 +https://github.com/owncloud/web/issues/7863 +https://github.com/owncloud/web/pull/7874 From a501b04d37c3532cea65904224b3927ec8cd4830 Mon Sep 17 00:00:00 2001 From: Benedikt Kulmann Date: Fri, 28 Oct 2022 16:07:22 +0200 Subject: [PATCH 4/5] refactor: enrich Plain page layout and simplify associated pages --- packages/web-runtime/src/App.vue | 14 +- packages/web-runtime/src/layouts/Plain.vue | 28 +++- .../web-runtime/src/pages/accessDenied.vue | 98 ++++++------- packages/web-runtime/src/pages/login.vue | 129 ++++++++---------- packages/web-runtime/src/pages/logout.vue | 55 ++++++-- .../src/pages/missingOrInvalidConfig.vue | 73 +++++----- .../web-runtime/src/pages/oidcCallback.vue | 107 +++++++-------- .../src/pages/resolvePrivateLink.vue | 99 ++++++-------- .../src/pages/resolvePublicLink.vue | 26 ++-- 9 files changed, 330 insertions(+), 299 deletions(-) diff --git a/packages/web-runtime/src/App.vue b/packages/web-runtime/src/App.vue index 40c93f34588..9806c01086e 100644 --- a/packages/web-runtime/src/App.vue +++ b/packages/web-runtime/src/App.vue @@ -49,7 +49,7 @@ import LayoutLoading from './layouts/Loading.vue' import LayoutPlain from './layouts/Plain.vue' import { getBackendVersion, getWebVersion } from './container/versions' import { defineComponent } from '@vue/composition-api' -import { isPublicLinkContext, isUserContext, isAnonymousContext } from './router' +import { isPublicLinkContext, isUserContext } from './router' import { additionalTranslations } from './helpers/additionalTranslations' // eslint-disable-line import { eventBus } from 'web-pkg/src/services' @@ -70,17 +70,23 @@ export default defineComponent({ ...mapGetters(['configuration', 'capabilities', 'getSettingsValue']), ...mapGetters('runtime/auth', ['isUserContextReady', 'isPublicLinkContextReady']), layout() { - if (!this.$route.name || isAnonymousContext(this.$router, this.$route)) { + const plainLayoutRoutes = [ + 'login', + 'logout', + 'oidcCallback', + 'oidcSilentRedirect', + 'resolvePublicLink', + 'accessDenied' + ] + if (!this.$route.name || plainLayoutRoutes.includes(this.$route.name)) { return LayoutPlain } - if (isPublicLinkContext(this.$router, this.$route)) { return this.isPublicLinkContextReady ? LayoutApplication : LayoutLoading } if (isUserContext(this.$router, this.$route)) { return this.isUserContextReady ? LayoutApplication : LayoutLoading } - return LayoutApplication }, favicon() { diff --git a/packages/web-runtime/src/layouts/Plain.vue b/packages/web-runtime/src/layouts/Plain.vue index 5dcf63037ac..56dd366f20d 100644 --- a/packages/web-runtime/src/layouts/Plain.vue +++ b/packages/web-runtime/src/layouts/Plain.vue @@ -1,13 +1,35 @@ diff --git a/packages/web-runtime/src/pages/accessDenied.vue b/packages/web-runtime/src/pages/accessDenied.vue index 7497e585cd3..1cda5ce3851 100644 --- a/packages/web-runtime/src/pages/accessDenied.vue +++ b/packages/web-runtime/src/pages/accessDenied.vue @@ -1,63 +1,63 @@ - - diff --git a/packages/web-runtime/src/pages/login.vue b/packages/web-runtime/src/pages/login.vue index 0fe6ddb7910..f3d3384bfa7 100644 --- a/packages/web-runtime/src/pages/login.vue +++ b/packages/web-runtime/src/pages/login.vue @@ -1,91 +1,76 @@ diff --git a/packages/web-runtime/src/pages/missingOrInvalidConfig.vue b/packages/web-runtime/src/pages/missingOrInvalidConfig.vue index 85237c32bc4..3268772c848 100644 --- a/packages/web-runtime/src/pages/missingOrInvalidConfig.vue +++ b/packages/web-runtime/src/pages/missingOrInvalidConfig.vue @@ -1,51 +1,54 @@ - diff --git a/packages/web-runtime/src/pages/oidcCallback.vue b/packages/web-runtime/src/pages/oidcCallback.vue index da860832c0a..4c954e829ba 100644 --- a/packages/web-runtime/src/pages/oidcCallback.vue +++ b/packages/web-runtime/src/pages/oidcCallback.vue @@ -1,68 +1,61 @@ - diff --git a/packages/web-runtime/src/pages/resolvePrivateLink.vue b/packages/web-runtime/src/pages/resolvePrivateLink.vue index a1e0cc8ce62..c2590f42702 100644 --- a/packages/web-runtime/src/pages/resolvePrivateLink.vue +++ b/packages/web-runtime/src/pages/resolvePrivateLink.vue @@ -1,50 +1,52 @@