Skip to content

Commit

Permalink
feat(api): validate config with jsonschema
Browse files Browse the repository at this point in the history
  • Loading branch information
jakowenko committed Oct 31, 2021
1 parent 3bf0271 commit ad23c7b
Show file tree
Hide file tree
Showing 5 changed files with 259 additions and 0 deletions.
14 changes: 14 additions & 0 deletions api/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 api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"form-data": "^4.0.0",
"get-orientation": "^1.1.2",
"js-yaml": "^4.1.0",
"jsonschema": "^1.4.0",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.21",
"luxon": "^2.0.2",
Expand Down
2 changes: 2 additions & 0 deletions api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ const storage = require('./src/util/storage.util');
const database = require('./src/util/db.util');
const config = require('./src/constants/config');
const shutdown = require('./src/util/shutdown.util');
const validate = require('./src/schemas/validate');

module.exports.start = async () => {
config.setup();
storage.setup();
console.log(`Double Take v${version}`);
validate(config());
console.verbose(config());
await database.init();
const server = http.Server(require('./src/app')).listen(SERVER.PORT);
Expand Down
225 changes: 225 additions & 0 deletions api/src/schemas/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
module.exports.config = {
type: 'object',
required: ['detectors'],
properties: {
auth: { type: 'boolean' },
token: {
type: 'object',
properties: {
image: { type: ['string', 'number'] },
},
},
mqtt: {
type: 'object',
required: ['host'],
properties: {
host: { type: 'string' },
topics: {
type: 'object',
properties: {
frigate: { type: 'string' },
homeassistant: { type: ['string', 'boolean'] },
matches: { type: 'string' },
cameras: { type: 'string' },
},
},
},
},
detect: { $ref: '/detect' },
frigate: {
type: 'object',
required: ['url'],
properties: {
url: { type: 'string' },
labels: { type: 'array' },
attempts: {
type: 'object',
properties: {
latest: { type: 'number' },
snapshot: { type: 'number' },
mqtt: { type: 'boolean' },
delay: { type: 'number' },
},
},
image: {
type: 'object',
properties: {
height: { type: 'number' },
},
},
cameras: {
type: 'array',
},
zones: { $ref: '/zones' },
events: {
type: 'object',
patternProperties: {
'.*': {
properties: {
attempts: {
type: 'object',
properties: {
latest: { type: 'number' },
snapshot: { type: 'number' },
mqtt: { type: 'boolean' },
delay: { type: 'number' },
},
},
image: {
type: 'object',
properties: {
height: { type: 'number' },
latest: { type: 'string' },
snapshot: { type: 'string' },
},
},
},
},
},
},
},
},
cameras: {
type: 'object',
patternProperties: {
'.*': {
properties: {
masks: {
type: 'object',
required: ['coordinates', 'size'],
properties: {
coordinates: { type: ['string', 'array'] },
visible: { type: 'boolean' },
size: { type: 'string' },
},
},
detect: { $ref: '/detect' },
snapshot: {
type: 'object',
properties: {
url: { type: 'string' },
topic: { type: 'string' },
},
},
},
},
},
},
detectors: {
type: 'object',
anyOf: [{ required: ['compreface'] }, { required: ['deepstack'] }, { required: ['facebox'] }],
properties: {
compreface: {
type: 'object',
required: ['url', 'key'],
properties: {
url: { type: 'string' },
},
},
deepstack: {
type: 'object',
required: ['url'],
properties: {
url: { type: 'string' },
},
},
facebox: {
type: 'object',
required: ['url'],
properties: {
url: { type: 'string' },
},
},
},
},
notify: {
type: 'object',
properties: {
gotify: {
type: 'object',
required: ['url'],
properties: {
url: { type: 'string' },
cameras: { type: 'array' },
zones: { $ref: '/zones' },
},
},
},
},
time: {
type: 'object',
properties: {
format: { type: ['string', 'null'] },
timezone: { type: 'string' },
},
},
logs: {
type: 'object',
properties: {
level: { type: 'string' },
},
},
ui: {
type: 'object',
properties: {
pagination: {
type: 'object',
properties: {
limit: { type: 'number' },
},
},
thumbnails: {
type: 'object',
properties: {
quality: { type: 'number' },
width: { type: 'number' },
},
},
logs: {
type: 'object',
properties: {
lines: { type: 'number' },
},
},
},
},
},
};

module.exports.detect = {
id: '/detect',
type: 'object',
properties: {
match: {
type: 'object',
properties: {
save: { type: 'boolean' },
base64: { type: ['string', 'boolean'] },
confidence: { type: 'number' },
purge: { type: 'number' },
min_area: { type: 'number' },
},
},
unknown: {
type: 'object',
properties: {
save: { type: 'boolean' },
base64: { type: ['string', 'boolean'] },
confidence: { type: 'number' },
purge: { type: 'number' },
min_area: { type: 'number' },
},
},
},
};

module.exports.zones = {
id: '/zones',
type: 'array',
items: {
properties: {
camera: { type: 'string' },
zone: { type: 'string' },
},
},
};
17 changes: 17 additions & 0 deletions api/src/schemas/validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const { Validator } = require('jsonschema');
const { config, detect, zones } = require('.');

module.exports = (object) => {
const v = new Validator();
v.addSchema(detect);
v.addSchema(zones);

const { errors } = v.validate(object, config);
if (errors.length)
console.error(`${errors.length} validation ${errors.length === 1 ? 'error' : 'errors'}`);
errors.forEach((error) => {
const path = !error.path.length ? 'config' : error.path.join('.');
const instance = typeof error.instance === 'string' ? ` (${error.instance})` : '';
console.error(`${path}${instance} ${error.message}`);
});
};

0 comments on commit ad23c7b

Please sign in to comment.