Merge branch 'main' into feature-vanilla-context-provider-element

This commit is contained in:
Mads Rasmussen
2023-05-12 16:29:01 +02:00
8 changed files with 86 additions and 73 deletions

View File

@@ -9,7 +9,5 @@
<base href="/" />
</head>
<body class="uui-font uui-text" style="margin: 0; padding: 0; overflow: hidden">
<umb-app></umb-app>
</body>
<body class="uui-font uui-text" style="margin: 0; padding: 0; overflow: hidden"></body>
</html>

View File

@@ -1,4 +1,3 @@
export * from './resource.controller';
export * from './serverUrl.token';
export * from './tryExecute.function';
export * from './tryExecuteAndNotify.function';

View File

@@ -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<string>(
'UmbServerUrl',
'The base URL of the configured Umbraco server.'
);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<UmbAppContext>('UMB_APP');

View File

@@ -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 <umb-debug> component
this.addEventListener(umbDebugContextEventType, (event: any) => {
// Once we got to the outter most component <umb-app>
@@ -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;

View File

@@ -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);