Skip to content

Commit

Permalink
Config written to, loaded from appdata for persistence, includes erro…
Browse files Browse the repository at this point in the history
…r checking & schema validation
  • Loading branch information
aredden committed Jan 8, 2021
1 parent a12000b commit 808a325
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 22 deletions.
7 changes: 6 additions & 1 deletion electron/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"electron-updater": "^4.3.5",
"forcefocus": "^1.1.0",
"is-admin": "^3.0.0",
"jsonschema": "^1.4.0",
"lodash": "^4.17.20",
"mathjs": "^8.0.1",
"node-hid": "^2.0.0-0",
Expand Down
165 changes: 147 additions & 18 deletions electron/src/IPCEvents/ConfigLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ import {
setG14Config,
setupElectronReload,
} from '../electron';
import validator, { configSchema } from '../Utilities/ConfigValidation';

dotenv.config();

//@ts-ignore
// eslint-ignore-next-line
const location = is_dev
type G14PathType = {
pathtype: 'Local' | 'Persistent';
directory: boolean;
};

const LOCAL_CFG = is_dev
? (process.env.CONFIG_LOC as string)
: path.join(
app.getPath('exe'),
Expand All @@ -29,38 +33,163 @@ const location = is_dev
);
let LOGGER = getLogger('ConfigLoader');

let APPDATA_CONFIG = path.join(
const APPDATA_CONFIG = path.join(
app.getPath('appData'),
'G14ControlV2',
'G14ControlV2.json'
);

export const tryWriteNewConfig = (config: Buffer) => {
let conf = JSON.parse(config.toString()) as G14Config;
fs.writeFile(APPDATA_CONFIG, JSON.stringify(conf), 'utf-8', (err) => {
if (err) {
LOGGER.info("Couldn't write to new config file: " + err);
let FINAL_CFG_LOC = LOCAL_CFG;

const validateConfig = (config: any) => {
let validateResult = validator.validate(config, configSchema);
LOGGER.info(
`Number of validation Errors: ${JSON.stringify(
validateResult.errors.length
)}`
);
if (validateResult.valid) {
return true;
} else {
return false;
}
};

export const setUpConfigPath: () => G14PathType = () => {
let p = path.join(app.getPath('appData'), 'G14ControlV2');
if (fs.existsSync(p)) {
let persistent = fs.existsSync(APPDATA_CONFIG);
if (persistent) {
return { pathtype: 'Persistent', directory: true };
} else {
LOGGER.info('Wrote successfully.');
return { pathtype: 'Local', directory: true };
}
});
}
return { pathtype: 'Local', directory: false };
};

export const buildPath = async () => {
let { pathtype, directory } = setUpConfigPath();
let local = true;
let persistConfig = true;
FINAL_CFG_LOC = LOCAL_CFG;
if (!directory) {
let result = await new Promise<boolean>((resolve) => {
fs.mkdir(path.join(app.getPath('appData'), 'G14ControlV2'), (err) => {
if (err) {
LOGGER.error(
`Error creating directory for persistent config:\n${JSON.stringify(
err,
null,
2
)}`
);
resolve(false);
} else {
LOGGER.info(`Successfully created G14Control directory in appdata.`);
resolve(true);
}
});
});
local = result;
}
if (pathtype === 'Local' && local) {
let config = JSON.parse((await loadConfig()).toString()) as G14Config;
let file = Buffer.from(JSON.stringify(config), 'utf-8');
let result = await new Promise<boolean>((resolve) => {
fs.writeFile(
APPDATA_CONFIG,
file,
{ encoding: 'utf8', flag: 'w+' },
(err) => {
if (err) {
LOGGER.error(
`Error writing persistent configuration:\n${JSON.stringify(
err,
null,
2
)}`
);
resolve(false);
} else {
LOGGER.info(
'Successfully wrote configuration data to persistent configuration file.'
);
resolve(true);
}
}
);
});
persistConfig = result;
}
if (persistConfig) {
LOGGER.info('Persistent configuration exists.');
FINAL_CFG_LOC = APPDATA_CONFIG;
let config = JSON.parse((await loadConfig()).toString()) as G14Config;
let validated = validateConfig(config);
if (!validated) {
LOGGER.error('Initial config could not be validated.');
FINAL_CFG_LOC = LOCAL_CFG;
let configValid = JSON.parse(
(await loadConfig()).toString()
) as G14Config;
FINAL_CFG_LOC = APPDATA_CONFIG;
await writeConfig(configValid);
configValid = JSON.parse((await loadConfig()).toString()) as G14Config;
let ok = validateConfig(configValid);
if (ok) {
LOGGER.info('Successfully fixed broken config file.');
} else {
LOGGER.error('Failed to fix configuration.');
dialog.showErrorBox(
'Configuration Error',
'Both local and appdata configuration could not be validated, please reinstall G14ControlV2 to fix.'
);
}
}
}

let ok = persistConfig && local;
if (ok) {
LOGGER.info('Using appdata persistent config.');
FINAL_CFG_LOC = APPDATA_CONFIG;
let config = JSON.parse(
((await loadConfig()) as Buffer).toString()
) as G14Config;
validateConfig(config);
} else {
LOGGER.info('Using local config.');
FINAL_CFG_LOC = LOCAL_CFG;
}
};

export const tryWriteNewConfig = (config: Buffer) => {
// let conf = JSON.parse(config.toString()) as G14Config;
// fs.writeFile(FINAL_CFG_LOC, JSON.stringify(conf), 'utf-8', (err) => {
// if (err) {
// LOGGER.info("Couldn't write to new config file: " + err);
// } else {
// LOGGER.info('Wrote successfully.');
// }
// });
};

export const loadConfig = async () => {
if (!location) {
LOGGER.error('config.json location undefined.' + location);
if (!FINAL_CFG_LOC) {
LOGGER.error('config.json location undefined.' + FINAL_CFG_LOC);

return false;
} else {
LOGGER.info(`Loading config from:\n[${FINAL_CFG_LOC}]`);
}
return new Promise((resolve, reject) => {
fs.readFile(location, (err, data) => {
fs.readFile(FINAL_CFG_LOC, (err, data) => {
if (err) {
LOGGER.info(
`Error reading file: ${location}.\n ${JSON.stringify(err)}`
`Error reading file: ${FINAL_CFG_LOC}.\n ${JSON.stringify(err)}`
);
reject(err);
} else {
tryWriteNewConfig(data);
resolve(data);
}
});
Expand All @@ -69,7 +198,7 @@ export const loadConfig = async () => {

export const writeConfig = async (config: G14Config) => {
return new Promise((resolve) => {
fs.writeFile(location, JSON.stringify(config), (err) => {
fs.writeFile(FINAL_CFG_LOC, JSON.stringify(config), (err) => {
if (err) {
LOGGER.error(
`Problem writing to config.json file. \nError: ${JSON.stringify(err)}`
Expand All @@ -83,7 +212,7 @@ export const writeConfig = async (config: G14Config) => {
});
};

export const buildConfigLoaderListeners = (ipc: IpcMain) => {
export const buildConfigLoaderListeners = async (ipc: IpcMain) => {
ipc.handle('loadConfig', async (_event, _args) => {
let config = await loadConfig().catch((err) => {
LOGGER.info(`Error loading config:\n${err}`);
Expand Down
71 changes: 71 additions & 0 deletions electron/src/Utilities/ConfigValidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/** @format */

import { Schema, Validator } from 'jsonschema';

export let configSchema: Schema = {
id: '/g14config',
type: 'object',
required: ['startup', 'current', 'plans', 'ryzenadj', 'fanCurves'],
properties: {
autoSwitch: {
type: 'object',
properties: {
enabled: { type: 'boolean' },
acPlan: { type: 'object' },
dcPlan: { type: 'object' },
},
},
startup: {
properties: {
checkBoostVisibility: { type: 'boolean' },
autoLaunchEnabled: { type: 'boolean' },
startMinimized: { type: 'boolean', required: false },
},
type: 'object',
},
current: {
properties: {
ryzenadj: { type: 'string' },
minToTray: { type: 'boolean', required: false },
fanCurve: {
properties: {
type: { type: 'any' },
name: { type: 'string' },
},
},
batteryLimit: { type: 'number | undefined' },
batteryLimitStatus: { type: 'boolean', required: false },
shortcuts: {
required: false,
properties: {
minmax: {
properties: {
enabled: { type: 'boolean' },
accelerator: { type: 'string' },
},
},
},
},
rogKey: { type: 'any' },
},
},
plans: { type: 'array' },
ryzenadj: {
required: true,
properties: {
defaults: { type: 'object' },
limits: { type: 'object' },
options: { type: 'array' },
},
},
loopTimes: {
type: 'any',
},
armouryPlan: { type: 'string' },
fanCurves: { type: 'array' },
},
};

let validator = new Validator();
validator.addSchema(configSchema, '/g14config');
export default validator;
1 change: 0 additions & 1 deletion electron/src/config/config.oonip
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"firstStart": true,
"current": {
"ryzenadj": "Default",
"fanCurve": {
Expand Down
4 changes: 3 additions & 1 deletion electron/src/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
import { HID } from 'node-hid';
import { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';
import url from 'url';
import { loadConfig, writeConfig } from './IPCEvents/ConfigLoader';
import { buildPath, loadConfig, writeConfig } from './IPCEvents/ConfigLoader';
import { setUpNewG14ControlKey } from './IPCEvents/HID/HIDDevice';
import { buildIpcConnection } from './IPCEvents/IPCListeners';
import { mapperBuilder } from './IPCEvents/RogKeyRemapperListener';
Expand Down Expand Up @@ -169,6 +169,8 @@ export async function createWindow(
app.quit();
}

await buildPath();

// load the config
try {
g14Config = JSON.parse((await loadConfig()).toString()) as G14Config;
Expand Down
7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"fkill": "^7.0.1",
"forcefocus": "^1.1.0",
"is-admin": "^3.0.0",
"jsonschema": "^1.4.0",
"lodash": "^4.17.20",
"node-hid": "^2.0.0-0",
"node-powershell": "^4.0.0",
Expand Down

0 comments on commit 808a325

Please sign in to comment.