UNPKG

@etsoo/materialui

Version:

TypeScript Material-UI Implementation

246 lines (245 loc) 8.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServiceApp = void 0; const appscript_1 = require("@etsoo/appscript"); const ReactApp_1 = require("./ReactApp"); const coreTokenKey = "core-refresh-token"; const tryLoginKey = "tryLogin"; /** * Core Service App * Service login to core system, get the refesh token and access token * Use the acess token to the service api, get a service access token * Use the new acess token and refresh token to login */ class ServiceApp extends ReactApp_1.ReactApp { /** * Core endpoint */ coreEndpoint; /** * Core system API */ coreApi; /** * Core system origin */ coreOrigin; coreAccessToken; /** * Constructor * @param settings Settings * @param name Application name * @param debug Debug mode */ constructor(settings, name, debug = false) { super(settings, name, debug); // Custom core API name can be done with override this.coreName const coreEndpoint = this.settings.endpoints?.[this.coreName]; if (coreEndpoint == null) { throw new Error("Core API endpont is required."); } this.coreEndpoint = coreEndpoint; this.coreOrigin = new URL(coreEndpoint.webUrl).origin; this.coreApi = this.createApi(this.coreName, coreEndpoint); this.keepLogin = true; } /** * Get token authorization request data * @param api API, if not provided, use the core API * @returns Result */ getTokenAuthRQ(api) { api ??= this.coreApi; const auth = api.getAuthorization(); if (auth == null) { throw new Error("Authorization is required."); } return { accessToken: auth.token, tokenScheme: auth.scheme }; } /** * Load core system UI * @param tryLogin Try login or not */ loadCore(tryLogin = false) { if (appscript_1.BridgeUtils.host == null) { let url = this.coreEndpoint.webUrl; if (!tryLogin) url = url.addUrlParam(tryLoginKey, tryLogin); globalThis.location.href = url; } else { const startUrl = tryLogin ? undefined : "".addUrlParam(tryLoginKey, tryLogin); appscript_1.BridgeUtils.host.loadApp(this.coreName, startUrl); } } /** * Go to the login page * @param data Login parameters */ toLoginPage(data) { // Destruct const { removeUrl, showLoading, params } = data ?? {}; // Cache current URL this.cachedUrl = removeUrl ? undefined : globalThis.location.href; // Get the redirect URL new appscript_1.AuthApi(this).getLogInUrl().then((url) => { if (!url) return; // Add try login flag if (params != null) { url = url.addUrlParams(params); } this.loadUrlEx(url); }); // Make sure apply new device id for new login this.clearDeviceId(); } /** * Load URL with core origin * @param url URL */ loadUrlEx(url) { super.loadUrl(url, this.coreOrigin); } /** * Signout, with userLogout and toLoginPage * @param action Callback */ signout(action) { // Clear core token this.storage.setData(coreTokenKey, undefined); // Super call return super.signout(action); } /** * * @param user Current user * @param core Core system API token data * @param keep Keep in local storage or not * @param dispatch User state dispatch */ userLoginEx(user, core, dispatch) { if (user.clientDeviceId && user.passphrase) { // Save the passphrase // Interpolated string expressions are different between TypeScript and C# for the null value const passphrase = this.decrypt(user.passphrase, `${user.uid ?? ""}-${this.settings.appId}`); if (passphrase) { this.deviceId = user.clientDeviceId; this.updatePassphrase(passphrase); } } // User login const { refreshToken } = user; // Core system login // It's the extreme case, the core system token should not be the same with the current app user token core ??= { refreshToken, accessToken: user.token, tokenType: user.tokenScheme ?? "Bearer", expiresIn: user.seconds }; // Cache the core system data this.saveCoreToken(core); // User login and trigger the dispatch at last this.userLogin(user, refreshToken, dispatch); } /** * Save core system data * @param data Data */ saveCoreToken(data) { // Hold the core system access token this.coreAccessToken = data.accessToken; // Cache the core system refresh token this.storage.setData(coreTokenKey, this.encrypt(data.refreshToken)); // Exchange tokens this.exchangeTokenAll(data); } /** * On switch organization handler * This method is called when the organization is switched successfully */ onSwitchOrg() { } /** * Switch organization * @param organizationId Organization ID * @param fromOrganizationId From organization ID * @param payload Payload */ async switchOrg(organizationId, fromOrganizationId, payload) { if (!this.coreAccessToken) { throw new Error("Core access token is required to switch organization."); } const [result, refreshToken] = await new appscript_1.AuthApi(this).switchOrg({ organizationId, fromOrganizationId, token: this.coreAccessToken }, payload); if (result == null) return; if (!result.ok) { return result; } if (result.data == null) { throw new Error("Invalid switch organization result."); } let core; if ("core" in result.data && typeof result.data.core === "string") { core = JSON.parse(result.data.core); delete result.data.core; } // Override the user data's refresh token const user = refreshToken ? { ...result.data, refreshToken } : result.data; // User login without dispatch this.userLoginEx(user, core, false); // Handle the switch organization await this.onSwitchOrg(); // Trigger the dispatch at last this.doLoginDispatch(user); return result; } async refreshTokenSucceed(user, token, callback) { // Check core system token const coreToken = this.storage.getData(coreTokenKey); if (!coreToken) { callback?.({ ok: false, type: "noData", title: "Core token is blank" }); return; } const coreTokenDecrypted = this.decrypt(coreToken); if (!coreTokenDecrypted) { callback?.({ ok: false, type: "noData", title: "Core token decrypted is blank" }); return; } // Call the core system API refresh token const data = await this.apiRefreshTokenData(this.coreApi, { token: coreTokenDecrypted, appId: this.settings.appId }); if (data == null) return; // Cache the core system refresh token // Follow similar logic in userLoginEx this.saveCoreToken(data); // Call the super await super.refreshTokenSucceed(user, token, callback); } /** * Try login * @param params Login parameters */ async tryLogin(params) { // Destruct params ??= {}; let { onFailure, ...rest } = params; if (onFailure == null) { onFailure = params.onFailure = (type) => { console.log(`Try login failed: ${type}.`); this.toLoginPage(rest); }; } return await super.tryLogin(params); } } exports.ServiceApp = ServiceApp;