mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
* SDA-2287 - Only create context bridge when CI is enabled and create new config filed for defining origin * SDA-2287 - Revert loading context bridge as it is not necessary
402 lines
15 KiB
TypeScript
402 lines
15 KiB
TypeScript
import { app, dialog } from 'electron';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
import * as util from 'util';
|
|
import { buildNumber } from '../../package.json';
|
|
import { isDevEnv, isElectronQA, isLinux, isMac } from '../common/env';
|
|
import { logger } from '../common/logger';
|
|
import { filterOutSelectedValues, pick } from '../common/utils';
|
|
|
|
const writeFile = util.promisify(fs.writeFile);
|
|
|
|
export enum CloudConfigDataTypes {
|
|
NOT_SET = 'NOT_SET',
|
|
ENABLED = 'ENABLED',
|
|
DISABLED = 'DISABLED',
|
|
}
|
|
export interface IConfig {
|
|
url: string;
|
|
minimizeOnClose: CloudConfigDataTypes;
|
|
launchOnStartup: CloudConfigDataTypes;
|
|
alwaysOnTop: CloudConfigDataTypes;
|
|
bringToFront: CloudConfigDataTypes;
|
|
whitelistUrl: string;
|
|
isCustomTitleBar: CloudConfigDataTypes;
|
|
memoryRefresh: CloudConfigDataTypes;
|
|
memoryThreshold: string;
|
|
disableGpu: boolean;
|
|
enableRendererLogs: boolean;
|
|
devToolsEnabled: boolean;
|
|
ctWhitelist: string[];
|
|
podWhitelist: string[];
|
|
autoLaunchPath: string;
|
|
permissions: IPermission;
|
|
customFlags: ICustomFlag;
|
|
buildNumber?: string;
|
|
configVersion?: string;
|
|
notificationSettings: INotificationSetting;
|
|
mainWinPos?: ICustomRectangle;
|
|
locale?: string;
|
|
installVariant?: string;
|
|
}
|
|
|
|
export interface IGlobalConfig {
|
|
contextOriginUrl: string;
|
|
url: string;
|
|
contextIsolation: boolean;
|
|
}
|
|
|
|
export interface ICloudConfig {
|
|
configVersion?: string;
|
|
podLevelEntitlements: IPodLevelEntitlements;
|
|
acpFeatureLevelEntitlements: IACPFeatureLevelEntitlements;
|
|
pmpEntitlements: IPMPEntitlements;
|
|
}
|
|
|
|
export interface IPodLevelEntitlements {
|
|
minimizeOnClose: CloudConfigDataTypes;
|
|
isCustomTitleBar: CloudConfigDataTypes;
|
|
alwaysOnTop: CloudConfigDataTypes;
|
|
memoryRefresh: CloudConfigDataTypes;
|
|
bringToFront: CloudConfigDataTypes;
|
|
disableThrottling: CloudConfigDataTypes;
|
|
launchOnStartup: CloudConfigDataTypes;
|
|
memoryThreshold: string;
|
|
ctWhitelist: string;
|
|
podWhitelist: string;
|
|
authNegotiateDelegateWhitelist: string;
|
|
whitelistUrl: string;
|
|
authServerWhitelist: string;
|
|
autoLaunchPath: string;
|
|
}
|
|
|
|
export interface IACPFeatureLevelEntitlements {
|
|
devToolsEnabled: boolean;
|
|
permissions: IPermission;
|
|
}
|
|
|
|
export interface IPMPEntitlements {
|
|
minimizeOnClose: CloudConfigDataTypes;
|
|
bringToFront: CloudConfigDataTypes;
|
|
memoryRefresh: CloudConfigDataTypes;
|
|
refreshAppThreshold: CloudConfigDataTypes;
|
|
disableThrottling: CloudConfigDataTypes;
|
|
}
|
|
|
|
export interface IPermission {
|
|
media: boolean;
|
|
geolocation: boolean;
|
|
notifications: boolean;
|
|
midiSysex: boolean;
|
|
pointerLock: boolean;
|
|
fullscreen: boolean;
|
|
openExternal: boolean;
|
|
}
|
|
|
|
export interface ICustomFlag {
|
|
authServerWhitelist: string;
|
|
authNegotiateDelegateWhitelist: string;
|
|
disableThrottling: CloudConfigDataTypes;
|
|
}
|
|
|
|
export interface INotificationSetting {
|
|
position: string;
|
|
display: string;
|
|
}
|
|
|
|
export interface ICustomRectangle extends Partial<Electron.Rectangle> {
|
|
isMaximized?: boolean;
|
|
isFullScreen?: boolean;
|
|
}
|
|
|
|
class Config {
|
|
public userConfig: IConfig | {};
|
|
public globalConfig: IConfig | {};
|
|
public cloudConfig: ICloudConfig | {};
|
|
public filteredCloudConfig: ICloudConfig | {};
|
|
private isFirstTime: boolean = true;
|
|
private installVariant: string | undefined;
|
|
private readonly configFileName: string;
|
|
private readonly installVariantFilename: string;
|
|
private readonly installVariantPath: string;
|
|
private readonly userConfigPath: string;
|
|
private readonly appPath: string;
|
|
private readonly globalConfigPath: string;
|
|
private readonly cloudConfigPath: string;
|
|
|
|
constructor() {
|
|
this.configFileName = 'Symphony.config';
|
|
this.installVariantFilename = 'InstallVariant.info';
|
|
this.userConfigPath = path.join(app.getPath('userData'), this.configFileName);
|
|
this.cloudConfigPath = path.join(app.getPath('userData'), 'cloudConfig.config');
|
|
this.appPath = isDevEnv ? app.getAppPath() : path.dirname(app.getPath('exe'));
|
|
this.globalConfigPath = isDevEnv
|
|
? path.join(this.appPath, path.join('config', this.configFileName))
|
|
: path.join(this.appPath, (isMac) ? '..' : '', 'config', this.configFileName);
|
|
|
|
this.installVariantPath = isDevEnv
|
|
? path.join(this.appPath, path.join('config', this.installVariantFilename))
|
|
: path.join(this.appPath, (isMac) ? '..' : '', 'config', this.installVariantFilename);
|
|
|
|
if (isLinux) {
|
|
this.globalConfigPath = path.join(this.appPath, (isElectronQA) ? '..' : '', 'config', this.configFileName);
|
|
this.installVariantPath = path.join(this.appPath, (isElectronQA) ? '..' : '', 'config', this.installVariantFilename);
|
|
}
|
|
|
|
this.globalConfig = {};
|
|
this.userConfig = {};
|
|
this.cloudConfig = {};
|
|
this.filteredCloudConfig = {};
|
|
this.readUserConfig();
|
|
this.readGlobalConfig();
|
|
this.readInstallVariant();
|
|
this.readCloudConfig();
|
|
|
|
this.checkFirstTimeLaunch();
|
|
}
|
|
|
|
/**
|
|
* Returns the specified fields from both user and global config file
|
|
* and keep values from user config as priority
|
|
*
|
|
* @param fields
|
|
*/
|
|
public getConfigFields(fields: string[]): IConfig {
|
|
const configFields = { ...this.getGlobalConfigFields(fields), ...this.getUserConfigFields(fields), ...this.getFilteredCloudConfigFields(fields) } as IConfig;
|
|
logger.info(`config-handler: getting combined config values for the fields ${fields}`, configFields);
|
|
return configFields;
|
|
}
|
|
|
|
/**
|
|
* Returns the specified fields from user config file
|
|
*
|
|
* @param fields {Array}
|
|
*/
|
|
public getUserConfigFields(fields: string[]): IConfig {
|
|
const userConfigData = pick(this.userConfig, fields) as IConfig;
|
|
logger.info(`config-handler: getting user config values for the fields ${fields}`, userConfigData);
|
|
return userConfigData;
|
|
}
|
|
|
|
/**
|
|
* Returns the specified fields from global config file
|
|
*
|
|
* @param fields {Array}
|
|
*/
|
|
public getGlobalConfigFields(fields: string[]): IGlobalConfig {
|
|
const globalConfigData = pick(this.globalConfig, fields) as IGlobalConfig;
|
|
logger.info(`config-handler: getting global config values for the fields ${fields}`, globalConfigData);
|
|
return globalConfigData;
|
|
}
|
|
|
|
/**
|
|
* Returns filtered & prioritised fields from cloud config file
|
|
*
|
|
* @param fields {Array}
|
|
*/
|
|
public getFilteredCloudConfigFields(fields: string[]): IConfig | {} {
|
|
const filteredCloudConfigData = pick(this.filteredCloudConfig, fields) as IConfig;
|
|
logger.info(`config-handler: getting filtered cloud config values for the ${fields}`, filteredCloudConfigData);
|
|
return filteredCloudConfigData;
|
|
}
|
|
|
|
/**
|
|
* Returns the actual cloud config with priority
|
|
* @param fields
|
|
*/
|
|
public getCloudConfigFields(fields: string[]): IConfig {
|
|
const { acpFeatureLevelEntitlements, podLevelEntitlements, pmpEntitlements } = this.cloudConfig as ICloudConfig;
|
|
const cloudConfig = { ...acpFeatureLevelEntitlements, ...podLevelEntitlements, ...pmpEntitlements };
|
|
logger.info(`config-handler: prioritized cloud config data`, cloudConfig);
|
|
const cloudConfigData = pick(cloudConfig, fields) as IConfig;
|
|
logger.info(`config-handler: getting prioritized cloud config values for the fields ${fields}`, cloudConfigData);
|
|
return cloudConfigData;
|
|
}
|
|
|
|
/**
|
|
* updates new data to the user config
|
|
*
|
|
* @param data {IConfig}
|
|
*/
|
|
public async updateUserConfig(data: Partial<IConfig>): Promise<void> {
|
|
logger.info(`config-handler: updating user config values with the data`, JSON.stringify(data));
|
|
this.userConfig = { ...this.userConfig, ...data };
|
|
try {
|
|
await writeFile(this.userConfigPath, JSON.stringify(this.userConfig), { encoding: 'utf8' });
|
|
logger.info(`config-handler: updated user config values with the data ${JSON.stringify(data)}`);
|
|
} catch (error) {
|
|
logger.error(`config-handler: failed to update user config file with ${JSON.stringify(data)}`, error);
|
|
dialog.showErrorBox(`Update failed`, `Failed to update user config due to error: ${error}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* updates new data to the cloud config
|
|
*
|
|
* @param data {IConfig}
|
|
*/
|
|
public async updateCloudConfig(data: Partial<ICloudConfig>): Promise<void> {
|
|
logger.info(`config-handler: Updating the cloud config data from SFE: `, data);
|
|
this.cloudConfig = { ...this.cloudConfig, ...data };
|
|
// recalculate cloud config when we have data from SFE
|
|
this.filterCloudConfig();
|
|
logger.info(`config-handler: prioritized and filtered cloud config: `, this.filteredCloudConfig);
|
|
try {
|
|
await writeFile(this.cloudConfigPath, JSON.stringify(this.cloudConfig), { encoding: 'utf8' });
|
|
logger.info(`config-handler: writing cloud config values to file`);
|
|
} catch (error) {
|
|
logger.error(`config-handler: failed to update cloud config file with ${data}`, error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return true if the app is launched for the first time
|
|
* otherwise false
|
|
*/
|
|
public isFirstTimeLaunch(): boolean {
|
|
return this.isFirstTime;
|
|
}
|
|
|
|
/**
|
|
* Method that updates user config file
|
|
* by modifying the old config file
|
|
*/
|
|
public async setUpFirstTimeLaunch(): Promise<void> {
|
|
const execPath = path.dirname(this.appPath);
|
|
const shouldUpdateUserConfig = execPath.indexOf('AppData\\Local\\Programs') !== -1 || isMac;
|
|
if (shouldUpdateUserConfig) {
|
|
const {
|
|
minimizeOnClose,
|
|
launchOnStartup,
|
|
alwaysOnTop,
|
|
memoryRefresh,
|
|
bringToFront,
|
|
isCustomTitleBar,
|
|
...filteredFields }: IConfig = this.userConfig as IConfig;
|
|
// update to the new build number
|
|
filteredFields.buildNumber = buildNumber;
|
|
filteredFields.installVariant = this.installVariant;
|
|
logger.info(`config-handler: setting first time launch for build`, buildNumber);
|
|
return await this.updateUserConfig(filteredFields);
|
|
}
|
|
await this.updateUserConfig({ buildNumber, installVariant: this.installVariant });
|
|
}
|
|
|
|
/**
|
|
* filters out the cloud config
|
|
*/
|
|
private filterCloudConfig(): void {
|
|
const { acpFeatureLevelEntitlements, podLevelEntitlements, pmpEntitlements } = this.cloudConfig as ICloudConfig;
|
|
|
|
// Filter out some values
|
|
const filteredACP = filterOutSelectedValues(acpFeatureLevelEntitlements, [ true, 'NOT_SET', '', [] ]);
|
|
const filteredPod = filterOutSelectedValues(podLevelEntitlements, [ true, 'NOT_SET', '', [] ]);
|
|
const filteredPMP = filterOutSelectedValues(pmpEntitlements, [ true, 'NOT_SET', '', [] ]);
|
|
|
|
// priority is PMP > ACP > SDA
|
|
this.filteredCloudConfig = { ...filteredACP, ...filteredPod, ...filteredPMP };
|
|
}
|
|
|
|
/**
|
|
* Parses the config data string
|
|
*
|
|
* @param data
|
|
*/
|
|
private parseConfigData(data: string): object {
|
|
let parsedData;
|
|
if (!data) {
|
|
logger.error(`config-handler: unable to read config file`);
|
|
throw new Error('unable to read user config file');
|
|
}
|
|
try {
|
|
parsedData = JSON.parse(data);
|
|
logger.info(`config-handler: parsed JSON file with data`, parsedData);
|
|
} catch (e) {
|
|
logger.error(`config-handler: parsing JSON file failed due to error ${e}`);
|
|
throw new Error(e);
|
|
}
|
|
return parsedData;
|
|
}
|
|
|
|
/**
|
|
* Reads a stores the user config file
|
|
*
|
|
* If user config doesn't exits?
|
|
* this creates a new one with { configVersion: current_app_version, buildNumber: current_app_build_number }
|
|
*/
|
|
private async readUserConfig() {
|
|
if (!fs.existsSync(this.userConfigPath)) {
|
|
// Need to wait until app ready event to access user data
|
|
await app.whenReady();
|
|
await this.readGlobalConfig();
|
|
logger.info(`config-handler: user config doesn't exist! will create new one and update config`);
|
|
const { url, ...rest } = this.globalConfig as IConfig;
|
|
await this.updateUserConfig({ configVersion: app.getVersion().toString(), buildNumber, ...rest } as IConfig);
|
|
}
|
|
this.userConfig = this.parseConfigData(fs.readFileSync(this.userConfigPath, 'utf8'));
|
|
logger.info(`config-handler: User configuration: `, this.userConfig);
|
|
}
|
|
|
|
/**
|
|
* Reads a stores the global config file
|
|
*/
|
|
private readGlobalConfig() {
|
|
this.globalConfig = this.parseConfigData(fs.readFileSync(this.globalConfigPath, 'utf8'));
|
|
logger.info(`config-handler: Global configuration: `, this.globalConfig);
|
|
}
|
|
|
|
/**
|
|
* Reads the install variant from a file
|
|
*/
|
|
private readInstallVariant() {
|
|
this.installVariant = fs.readFileSync(this.installVariantPath, 'utf8');
|
|
logger.info(`config-handler: Install variant: `, this.installVariant);
|
|
}
|
|
|
|
/**
|
|
* Reads and stores the cloud config file
|
|
*
|
|
* If cloud config doesn't exits?
|
|
* this creates a new one with { }
|
|
*/
|
|
private async readCloudConfig() {
|
|
if (!fs.existsSync(this.cloudConfigPath)) {
|
|
await app.whenReady();
|
|
await this.updateCloudConfig({ configVersion: app.getVersion().toString() });
|
|
}
|
|
this.cloudConfig = this.parseConfigData(fs.readFileSync(this.cloudConfigPath, 'utf8'));
|
|
// recalculate cloud config when we the application starts
|
|
this.filterCloudConfig();
|
|
logger.info(`config-handler: Cloud configuration: `, this.userConfig);
|
|
}
|
|
|
|
/**
|
|
* Verifies if the application is launched for the first time
|
|
*/
|
|
private async checkFirstTimeLaunch() {
|
|
logger.info('config-handler: checking first time launch');
|
|
const installVariant = this.userConfig && (this.userConfig as IConfig).installVariant || null;
|
|
|
|
if (!installVariant) {
|
|
logger.info(`config-handler: there's no install variant found, this is a first time launch`);
|
|
this.isFirstTime = true;
|
|
return;
|
|
}
|
|
|
|
if (installVariant && typeof installVariant === 'string' && installVariant !== this.installVariant) {
|
|
logger.info(`config-handler: install variant found is of a different instance, this is a first time launch`);
|
|
this.isFirstTime = true;
|
|
return;
|
|
}
|
|
logger.info(`config-handler: install variant is the same as the existing one, not a first time launch`);
|
|
this.isFirstTime = false;
|
|
}
|
|
}
|
|
|
|
const config = new Config();
|
|
|
|
export {
|
|
config,
|
|
};
|