diff --git a/src/Umbraco.Web.UI.Client/index.html b/src/Umbraco.Web.UI.Client/index.html index eaa7101209..aa036cc98d 100644 --- a/src/Umbraco.Web.UI.Client/index.html +++ b/src/Umbraco.Web.UI.Client/index.html @@ -9,7 +9,5 @@ - - - + diff --git a/src/Umbraco.Web.UI.Client/libs/resources/index.ts b/src/Umbraco.Web.UI.Client/libs/resources/index.ts index a7773b17b8..e0769f21ef 100644 --- a/src/Umbraco.Web.UI.Client/libs/resources/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/resources/index.ts @@ -1,4 +1,3 @@ export * from './resource.controller'; -export * from './serverUrl.token'; export * from './tryExecute.function'; export * from './tryExecuteAndNotify.function'; diff --git a/src/Umbraco.Web.UI.Client/libs/resources/serverUrl.token.ts b/src/Umbraco.Web.UI.Client/libs/resources/serverUrl.token.ts deleted file mode 100644 index 92ade0759a..0000000000 --- a/src/Umbraco.Web.UI.Client/libs/resources/serverUrl.token.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; - -/** - * The base URL of the configured Umbraco server. - * If the server is local, this will be an empty string. - * - * @remarks This is the base URL of the Umbraco server, not the base URL of the backoffice. - * - * @example https://localhost:44300 - * @example https://my-umbraco-site.com - * @example '' - */ -export const UMB_SERVER_URL = new UmbContextToken( - 'UmbServerUrl', - 'The base URL of the configured Umbraco server.' -); diff --git a/src/Umbraco.Web.UI.Client/src/app-config.interface.ts b/src/Umbraco.Web.UI.Client/src/app-config.interface.ts deleted file mode 100644 index 1ffdf91845..0000000000 --- a/src/Umbraco.Web.UI.Client/src/app-config.interface.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Configuration interface for the Umbraco App Element. - * @export - * @interface UmbAppConfig - */ -export interface UmbAppConfig { - /** - * The base path of the backoffice. - * @type {string} - * @memberof UmbAppConfig - */ - backofficePath: string; -} diff --git a/src/Umbraco.Web.UI.Client/src/app/app-context-config.interface.ts b/src/Umbraco.Web.UI.Client/src/app/app-context-config.interface.ts new file mode 100644 index 0000000000..9b9ab31ded --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/app/app-context-config.interface.ts @@ -0,0 +1,20 @@ +/** + * Configuration interface for the Umbraco App Context + * @export + * @interface UmbAppContextConfig + */ +export interface UmbAppContextConfig { + /** + * The base URL of the configured Umbraco server. + * @type {string} + * @memberof UmbAppContextConfig + */ + serverUrl: string; + + /** + * The base path of the backoffice. + * @type {string} + * @memberof UmbAppContextConfig + */ + backofficePath: string; +} diff --git a/src/Umbraco.Web.UI.Client/src/app.context.ts b/src/Umbraco.Web.UI.Client/src/app/app.context.ts similarity index 58% rename from src/Umbraco.Web.UI.Client/src/app.context.ts rename to src/Umbraco.Web.UI.Client/src/app/app.context.ts index 4429e3fed3..4e37c346e0 100644 --- a/src/Umbraco.Web.UI.Client/src/app.context.ts +++ b/src/Umbraco.Web.UI.Client/src/app/app.context.ts @@ -1,16 +1,22 @@ -import { UmbAppConfig } from './app-config.interface'; +import { UmbAppContextConfig } from './app-context-config.interface'; import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; export class UmbAppContext { + #serverUrl: string; #backofficePath: string; - constructor(config: UmbAppConfig) { + constructor(config: UmbAppContextConfig) { + this.#serverUrl = config.serverUrl; this.#backofficePath = config.backofficePath; } getBackofficePath() { return this.#backofficePath; } + + getServerUrl() { + return this.#serverUrl; + } } export const UMB_APP = new UmbContextToken('UMB_APP'); diff --git a/src/Umbraco.Web.UI.Client/src/app.ts b/src/Umbraco.Web.UI.Client/src/app/app.element.ts similarity index 78% rename from src/Umbraco.Web.UI.Client/src/app.ts rename to src/Umbraco.Web.UI.Client/src/app/app.element.ts index b170aee56a..6b0333013b 100644 --- a/src/Umbraco.Web.UI.Client/src/app.ts +++ b/src/Umbraco.Web.UI.Client/src/app/app.element.ts @@ -1,23 +1,21 @@ import '@umbraco-ui/uui-css/dist/uui-css.css'; -import './core/css/custom-properties.css'; - import 'element-internals-polyfill'; -import './core/router/router-slot.element'; -import './core/router/variant-router-slot.element'; +import '../core/router/router-slot.element'; +import '../core/router/variant-router-slot.element'; import { UUIIconRegistryEssential } from '@umbraco-ui/uui'; import { css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { UmbAuthFlow } from './core/auth/auth-flow'; -import { UmbIconStore } from './core/stores/icon/icon.store'; -import type { UmbErrorElement } from './error/error.element'; +import { UmbAuthFlow } from '../core/auth/auth-flow'; +import { UmbIconStore } from '../core/stores/icon/icon.store'; +import type { UmbErrorElement } from '../error/error.element'; import { UMB_APP, UmbAppContext } from './app.context'; import type { Guard, UmbRoute } from '@umbraco-cms/backoffice/router'; import { pathWithoutBasePath } from '@umbraco-cms/backoffice/router'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { UMB_SERVER_URL, tryExecute } from '@umbraco-cms/backoffice/resources'; +import { tryExecute } from '@umbraco-cms/backoffice/resources'; import { OpenAPI, RuntimeLevelModel, ServerResource } from '@umbraco-cms/backoffice/backend-api'; import { contextData, umbDebugContextEventType } from '@umbraco-cms/backoffice/context-api'; @@ -30,7 +28,7 @@ export class UmbAppElement extends UmbLitElement { * @remarks This is the base URL of the Umbraco server, not the base URL of the backoffice. */ @property({ type: String }) - private serverUrl = import.meta.env.VITE_UMBRACO_API_URL ?? ''; + serverUrl = ''; /** * The base path of the backoffice. @@ -39,65 +37,71 @@ export class UmbAppElement extends UmbLitElement { */ @property({ type: String }) // TODO: get from server config - private backofficePath = import.meta.env.DEV ? '/' : '/umbraco'; + backofficePath = '/umbraco'; + + /** + * Bypass authentication. + * @type {boolean} + */ + // TODO: this might not be the right solution + @property({ type: Boolean }) + bypassAuth = false; private _routes: UmbRoute[] = [ { path: 'install', - component: () => import('./installer/installer.element'), + component: () => import('../installer/installer.element'), }, { path: 'upgrade', - component: () => import('./upgrader/upgrader.element'), + component: () => import('../upgrader/upgrader.element'), guards: [this.#isAuthorizedGuard()], }, { path: '**', - component: () => import('./backoffice/backoffice.element'), + component: () => import('../backoffice/backoffice.element'), guards: [this.#isAuthorizedGuard()], }, ]; - #authFlow: UmbAuthFlow; + #authFlow?: UmbAuthFlow; #umbIconRegistry = new UmbIconStore(); #uuiIconRegistry = new UUIIconRegistryEssential(); #runtimeLevel = RuntimeLevelModel.UNKNOWN; - #isMocking = import.meta.env.VITE_UMBRACO_USE_MSW === 'on'; constructor() { super(); - // TODO: get all mocking logic out of this element. The app element doesn't need to know who is serving the data. - OpenAPI.BASE = this.#isMocking ? '' : this.serverUrl; - - this.#authFlow = new UmbAuthFlow( - OpenAPI.BASE !== '' ? OpenAPI.BASE : window.location.origin, - `${window.location.origin}${this.backofficePath}` - ); - - // TODO: Make a combined App Context - this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath })); - this.provideContext(UMB_SERVER_URL, OpenAPI.BASE); - - this._setup(); - this.#umbIconRegistry.attach(this); this.#uuiIconRegistry.attach(this); } - private async _setup() { + connectedCallback(): void { + super.connectedCallback(); + this.#setup(); + } + + async #setup() { + if (this.serverUrl === undefined) throw new Error('No serverUrl provided'); + + OpenAPI.BASE = this.serverUrl; + const redirectUrl = `${window.location.origin}${this.backofficePath}`; + + this.#authFlow = new UmbAuthFlow(this.serverUrl, redirectUrl); + + this.provideContext(UMB_APP, new UmbAppContext({ backofficePath: this.backofficePath, serverUrl: this.serverUrl })); + // Try to initialise the auth flow and get the runtime status try { // Get the current runtime level await this.#setInitStatus(); - // If we are not mocking, we need to initialise the connection to the Umbraco authentication server - if (!this.#isMocking) { + if (this.bypassAuth === false) { // Get service configuration from authentication server await this.#authFlow.setInitialState(); // Instruct all requests to use the auth flow to get and use the access_token for all subsequent requests - OpenAPI.TOKEN = () => this.#authFlow.performWithFreshTokens(); + OpenAPI.TOKEN = () => this.#authFlow!.performWithFreshTokens(); OpenAPI.WITH_CREDENTIALS = true; } @@ -124,7 +128,7 @@ export class UmbAppElement extends UmbLitElement { this.#errorPage(errorMsg, error); } - // TODO: wrap all debugging logic in a separate class + // TODO: wrap all debugging logic in a separate class. Maybe this could be part of the context-api? When we create a new root, we could attach the debugger to it? // Listen for the debug event from the component this.addEventListener(umbDebugContextEventType, (event: any) => { // Once we got to the outter most component @@ -194,7 +198,8 @@ export class UmbAppElement extends UmbLitElement { } #isAuthorized(): boolean { - return this.#isMocking ? true : this.#authFlow.loggedIn(); + if (!this.#authFlow) return false; + return this.bypassAuth ? true : this.#authFlow.loggedIn(); } #isAuthorizedGuard(): Guard { @@ -207,7 +212,7 @@ export class UmbAppElement extends UmbLitElement { window.sessionStorage.setItem('umb:auth:redirect', location.href); // Make a request to the auth server to start the auth flow - this.#authFlow.makeAuthorizationRequest(); + this.#authFlow!.makeAuthorizationRequest(); // Return false to prevent the route from being rendered return false; @@ -219,7 +224,7 @@ export class UmbAppElement extends UmbLitElement { this._routes = [ { path: '**', - component: () => import('./error/error.element'), + component: () => import('../error/error.element'), setup: (component) => { (component as UmbErrorElement).errorMessage = errorMsg; (component as UmbErrorElement).error = error; diff --git a/src/Umbraco.Web.UI.Client/src/index.ts b/src/Umbraco.Web.UI.Client/src/index.ts index 351d0bb2cb..a71ee72ad6 100644 --- a/src/Umbraco.Web.UI.Client/src/index.ts +++ b/src/Umbraco.Web.UI.Client/src/index.ts @@ -1,7 +1,21 @@ +import { UmbAppElement } from './app/app.element'; import { startMockServiceWorker } from './core/mocks/browser'; if (import.meta.env.VITE_UMBRACO_USE_MSW === 'on') { startMockServiceWorker(); } -import('./app'); +const appElement = new UmbAppElement(); +const isMocking = import.meta.env.VITE_UMBRACO_USE_MSW === 'on'; + +if (!isMocking) { + appElement.serverUrl = import.meta.env.VITE_UMBRACO_API_URL; +} + +if (import.meta.env.DEV) { + appElement.backofficePath = '/'; +} + +appElement.bypassAuth = isMocking; + +document.body.appendChild(appElement);