Skip to content

Commit

Permalink
feat: Validate configuration file and create a new one on error
Browse files Browse the repository at this point in the history
  • Loading branch information
Hypfer committed Jun 18, 2021
1 parent c9ccb4a commit 39e4613
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 33 deletions.
86 changes: 56 additions & 30 deletions backend/lib/Configuration.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const Ajv = require("ajv");
const fs = require("fs");
const os = require("os");
const path = require("path");
const {EventEmitter} = require("events");

const DEFAULT_SETTINGS = require("./res/default_config.json");
const Logger = require("./Logger");
const SCHEMAS = require("./doc/Configuration.openapi.json");
const Tools = require("./Tools");

class Configuration {
Expand All @@ -16,36 +18,7 @@ class Configuration {

this.location = process.env.VALETUDO_CONFIG_PATH ?? path.join(os.tmpdir(), "valetudo_config.json");

/* load an existing configuration file. if it is not present, create it using the default configuration */
if (fs.existsSync(this.location)) {
Logger.info("Loading configuration file:", this.location);

try {
this.settings = Object.assign(
this.settings,
JSON.parse(fs.readFileSync(this.location, {"encoding": "utf-8"}).toString())
);

this.persist();
} catch (e) {
Logger.error("Invalid configuration file: ", e.message);
Logger.info("Writing new file using defaults");

try {
fs.renameSync(this.location, this.location + ".backup");
Logger.info("Backup moved to " + this.location + ".backup");
} catch (e) {
Logger.info("Failed to move backup", e);
}

this.persist();
}
} else {
Logger.info("No configuration file present. Creating one at:", this.location);
Tools.MK_DIR_PATH(path.dirname(this.location));

this.persist();
}
this.loadConfig();
}

/**
Expand Down Expand Up @@ -82,6 +55,59 @@ class Configuration {
onUpdate(listener) {
this.eventEmitter.on(CONFIG_UPDATE_EVENT, listener);
}

/**
* @private
*/
loadConfig() {
/* load an existing configuration file. if it is not present or invalid, create it using the default configuration */
if (fs.existsSync(this.location)) {
Logger.info("Loading configuration file:", this.location);

try {
//@ts-ignore
const ajv = new Ajv();
Object.keys(SCHEMAS.components.schemas).forEach(schemaName => {
ajv.addSchema(SCHEMAS.components.schemas[schemaName], "#/components/schemas/" + schemaName);
});

const config = fs.readFileSync(this.location, {"encoding": "utf-8"}).toString();
const parsedConfig = JSON.parse(config);


if (!ajv.validate(SCHEMAS.components.schemas.Configuration, parsedConfig)) {
Logger.error("Error while validating configuration file", ajv.errors);

// noinspection ExceptionCaughtLocallyJS
throw new Error("Schema Validation Error");
}

this.settings = Object.assign(
this.settings,
parsedConfig
);

this.persist();
} catch (e) {
Logger.error("Invalid configuration file: ", e.message);
Logger.info("Writing new file using defaults");

try {
fs.renameSync(this.location, this.location + ".backup");
Logger.info("Backup moved to " + this.location + ".backup");
} catch (e) {
Logger.info("Failed to move backup", e);
}

this.persist();
}
} else {
Logger.info("No configuration file present. Creating one at:", this.location);
Tools.MK_DIR_PATH(path.dirname(this.location));

this.persist();
}
}
}

const CONFIG_UPDATE_EVENT = "ConfigUpdated";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,75 @@
{
"components": {
"schemas": {
"Configuration": {
"type": "object",
"additionalProperties": false,
"properties": {
"embedded": {
"type": "boolean",
"description": "Whether or not Valetudo is running on the robot itself.\nDetermines if we can do FS access and more."
},
"robot": {
"type": "object",
"additionalProperties": false,
"required": [
"implementation"
],
"properties": {
"implementation": {
"type": "string",
"description": "The name of the implementation that should be used.\nUse \"auto\" to autodetect when embedded."
},
"implementationSpecificConfig": {
"type": "object"
}
}
},
"webserver": {
"type": "object",
"additionalProperties": false,
"required": [
"port",
"basicAuth"
],
"properties": {
"port": {
"type": "number"
},
"basicAuth": {
"$ref": "#/components/schemas/BasicAuthConfigDTO"
}
}
},
"zonePresets": {
"type": "object"
},
"goToLocationPresets": {
"type": "object"
},
"mqtt": {
"$ref": "#/components/schemas/MqttConfigDTO"
},
"ntpClient": {
"$ref": "#/components/schemas/NTPClientConfigDTO"
},
"timers": {
"type": "object"
},
"allowSSHKeyUpload": {
"type": "boolean"
},
"logLevel": {
"type": "string",
"enum": [
"trace","debug","info","warn","error"
]
},
"debug": {
"type": "object"
}
}
},
"MqttConfigDTO": {
"type": "object",
"additionalProperties": false,
Expand Down
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"author": "",
"dependencies": {
"@destinationstransfers/ntp": "^2.0.0",
"ajv": "8.2.0",
"async-mqtt": "^2.6.1",
"body-parser": "^1.18.3",
"compression": "^1.7.2",
Expand Down
44 changes: 41 additions & 3 deletions 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 util/build_openapi_schema.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const options = {
},
apis: [
path.join(__dirname, "./backend/util/openapi_defs/*.openapi.json"),
path.join(__dirname, "./backend/lib/doc/*.openapi.json"),
path.join(__dirname, "./backend/lib/webserver/doc/*.openapi.json"),
path.join(__dirname, "./backend/lib/webserver/capabilityRouters/doc/*.openapi.json"),
path.join(__dirname, "./backend/lib/webserver/middlewares/doc/*.openapi.json"),
Expand Down

0 comments on commit 39e4613

Please sign in to comment.