forked from coolaj86/goldilocks.js
		
	added JSON Schema to validate the config
This commit is contained in:
		
							parent
							
								
									8f4a733391
								
							
						
					
					
						commit
						5761ab9d62
					
				@ -202,9 +202,9 @@ var tcpProm;
 | 
			
		||||
function fillConfig(config, args) {
 | 
			
		||||
  config.debug = config.debug || args.debug;
 | 
			
		||||
 | 
			
		||||
  if (!config.ddns) {
 | 
			
		||||
    config.ddns = { enabled: false };
 | 
			
		||||
  }
 | 
			
		||||
  config.socks5 = config.socks5 || { enabled: false };
 | 
			
		||||
  config.ddns   = config.ddns   || { enabled: false };
 | 
			
		||||
 | 
			
		||||
  // Use Object.assign to copy any real config values over the default values so we can
 | 
			
		||||
  // easily make sure all the fields we need exist .
 | 
			
		||||
  var mdnsDefaults = { disabled: false, port: 5353, broadcast: '224.0.0.251', ttl: 300 };
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										201
									
								
								lib/admin/config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								lib/admin/config.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,201 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var validator = new (require('jsonschema').Validator)();
 | 
			
		||||
var recase = require('recase').create({});
 | 
			
		||||
 | 
			
		||||
function deepCopy(obj) {
 | 
			
		||||
  if (!obj || typeof obj !== 'object') {
 | 
			
		||||
    return obj;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var result;
 | 
			
		||||
  if (Array.isArray(obj)) {
 | 
			
		||||
    result = [];
 | 
			
		||||
  } else {
 | 
			
		||||
    result = {};
 | 
			
		||||
  }
 | 
			
		||||
  Object.keys(obj).forEach(function (key) {
 | 
			
		||||
    result[key] = deepCopy(obj[key]);
 | 
			
		||||
  });
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var portSchema = { type: 'number', minimum: 1, maximum: 65535 };
 | 
			
		||||
 | 
			
		||||
var moduleSchemas = {
 | 
			
		||||
  // the proxy module is common to basically all categories.
 | 
			
		||||
  proxy: {
 | 
			
		||||
    type: 'object'
 | 
			
		||||
  , oneOf: [
 | 
			
		||||
      { required: [ 'address' ] }
 | 
			
		||||
    , { required: [ 'port' ] }
 | 
			
		||||
    ]
 | 
			
		||||
  , properties: {
 | 
			
		||||
      address: { type: 'string' }
 | 
			
		||||
    , host:    { type: 'string' }
 | 
			
		||||
    , port:    portSchema
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // redirect and static modules are for HTTP
 | 
			
		||||
, redirect: {
 | 
			
		||||
    type: 'object'
 | 
			
		||||
  , required: [ 'to', 'from' ]
 | 
			
		||||
  , properties: {
 | 
			
		||||
      to:     { type: 'string'}
 | 
			
		||||
    , from:   { type: 'string'}
 | 
			
		||||
    , status: { type: 'integer', minimum: 1, maximum: 999 }
 | 
			
		||||
  , }
 | 
			
		||||
  }
 | 
			
		||||
, static: {
 | 
			
		||||
    type: 'object'
 | 
			
		||||
  , required: [ 'root' ]
 | 
			
		||||
  , properties: {
 | 
			
		||||
      root: { type: 'string' }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // the acme module is for TLS
 | 
			
		||||
, acme: {
 | 
			
		||||
    type: 'object'
 | 
			
		||||
  , required: [ 'email' ]
 | 
			
		||||
  , properties: {
 | 
			
		||||
      email:         { type: 'string' }
 | 
			
		||||
    , server:        { type: 'string' }
 | 
			
		||||
    , challengeType: { type: 'string' }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
// forward is basically the name for the TCP proxy
 | 
			
		||||
moduleSchemas.forward = deepCopy(moduleSchemas.proxy);
 | 
			
		||||
moduleSchemas.forward.required = [ 'ports' ];
 | 
			
		||||
moduleSchemas.forward.properties.ports = { type: 'array', items: portSchema };
 | 
			
		||||
 | 
			
		||||
Object.keys(moduleSchemas).forEach(function (name) {
 | 
			
		||||
  var schema = moduleSchemas[name];
 | 
			
		||||
  schema.id = '/modules/'+name;
 | 
			
		||||
  schema.required = ['id', 'type'].concat(schema.required || []);
 | 
			
		||||
  schema.properties.id   = { type: 'string' };
 | 
			
		||||
  schema.properties.type = { type: 'string', const: name };
 | 
			
		||||
  validator.addSchema(schema, schema.id);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function addDomainsSchema(base, modList) {
 | 
			
		||||
  var modSchemas = modList.map(function (name) {
 | 
			
		||||
    return { '$ref': '/modules/'+name };
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  base.required = [ 'modules', 'domains' ].concat(base.required || []);
 | 
			
		||||
  base.properties.modules = {
 | 
			
		||||
    type: 'array'
 | 
			
		||||
  , items: {
 | 
			
		||||
      type: 'object'
 | 
			
		||||
    , required: [ 'domains' ]
 | 
			
		||||
    , properties: {
 | 
			
		||||
        domains: { type: 'array', items: { type: 'string' }, minLength: 1}
 | 
			
		||||
      }
 | 
			
		||||
    , oneOf: modSchemas
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  base.properties.domains = {
 | 
			
		||||
    type: 'array'
 | 
			
		||||
  , items: {
 | 
			
		||||
      type: 'object'
 | 
			
		||||
    , required: [ 'id', 'names', ]
 | 
			
		||||
    , properties: {
 | 
			
		||||
        id:      { type: 'string' }
 | 
			
		||||
      , names:   { type: 'array', items: { type: 'string' }, minLength: 1}
 | 
			
		||||
      , modules: { type: 'array', items: { oneOf: modSchemas }}
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var httpSchema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, properties: {
 | 
			
		||||
    // These properties should be snake_case to match the API and config format
 | 
			
		||||
    primary_domain: { type: 'string' }
 | 
			
		||||
  , allow_insecure: { type: 'boolean' }
 | 
			
		||||
  , trust_proxy:    { type: 'boolean' }
 | 
			
		||||
, }
 | 
			
		||||
};
 | 
			
		||||
addDomainsSchema(httpSchema, ['proxy', 'static', 'redirect']);
 | 
			
		||||
 | 
			
		||||
var tlsSchema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, properties: {
 | 
			
		||||
    acme: {
 | 
			
		||||
      type: 'object'
 | 
			
		||||
      // These properties should be snake_case to match the API and config format
 | 
			
		||||
    , required: [ 'email', 'approved_domains' ]
 | 
			
		||||
    , properties: {
 | 
			
		||||
        email:            { type: 'string' }
 | 
			
		||||
      , server:           { type: 'string' }
 | 
			
		||||
      , challenge_type:   { type: 'string' }
 | 
			
		||||
      , approved_domains: { type: 'array', items: { type: 'string' }, minLength: 1}
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
addDomainsSchema(tlsSchema, ['proxy', 'acme']);
 | 
			
		||||
 | 
			
		||||
var tcpSchema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, required: [ 'bind' ]
 | 
			
		||||
, properties: {
 | 
			
		||||
    bind:    { type: 'array', items: portSchema, minLength: 1 }
 | 
			
		||||
  , modules: { type: 'array', items: { '$ref': '/modules/forward' }}
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var dnsSchema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, properties: {
 | 
			
		||||
    bind:    { type: 'array', items: portSchema }
 | 
			
		||||
  , modules: { type: 'array', items: { '$ref': '/modules/proxy' }}
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var mdnsSchema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, required: [ 'port', 'broadcast', 'ttl' ]
 | 
			
		||||
, properties: {
 | 
			
		||||
    port:      portSchema
 | 
			
		||||
  , broadcast: { type: 'string' }
 | 
			
		||||
  , ttl:       { type: 'integer', minimum: 0, maximum: 2147483647 }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var ddnsSchema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, properties: {
 | 
			
		||||
    enabled: { type: 'boolean' }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
var socks5Schema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, properties: {
 | 
			
		||||
    enabled: { type: 'boolean' }
 | 
			
		||||
  , port:    portSchema
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
var mainSchema = {
 | 
			
		||||
  type: 'object'
 | 
			
		||||
, required: [ 'http', 'tls', 'tcp', 'dns', 'mdns', 'ddns' ]
 | 
			
		||||
, properties: {
 | 
			
		||||
    http:   httpSchema
 | 
			
		||||
  , tls:    tlsSchema
 | 
			
		||||
  , tcp:    tcpSchema
 | 
			
		||||
  , dns:    dnsSchema
 | 
			
		||||
  , mdns:   mdnsSchema
 | 
			
		||||
  , ddns:   ddnsSchema
 | 
			
		||||
  , socks5: socks5Schema
 | 
			
		||||
  }
 | 
			
		||||
, additionalProperties: false
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports.validate = function (config) {
 | 
			
		||||
  return validator.validate(recase.snakeCopy(config), mainSchema).errors;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -1029,6 +1029,11 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
 | 
			
		||||
      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
 | 
			
		||||
    },
 | 
			
		||||
    "jsonschema": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-XDJApzBauMg0TinJNP4iVcJl99PQ4JbWKK7nwzpOIkAOVveDKgh/2xm41T3x7Spu4PWMhnnQpNJmUSIUgl6sKg=="
 | 
			
		||||
    },
 | 
			
		||||
    "jsonwebtoken": {
 | 
			
		||||
      "version": "7.4.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.1.tgz",
 | 
			
		||||
@ -1967,14 +1972,6 @@
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
 | 
			
		||||
      "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
 | 
			
		||||
    },
 | 
			
		||||
    "stream-pair": {
 | 
			
		||||
      "version": "1.0.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/stream-pair/-/stream-pair-1.0.3.tgz",
 | 
			
		||||
      "integrity": "sha1-vIdY/jnTgQuva3VMj5BI8PuRNn0=",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "readable-stream": "2.2.11"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "string_decoder": {
 | 
			
		||||
      "version": "1.0.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz",
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,7 @@
 | 
			
		||||
    "human-readable-ids": "git+https://git.daplie.com/Daplie/human-readable-ids-js#master",
 | 
			
		||||
    "ipaddr.js": "git+https://github.com/whitequark/ipaddr.js.git#v1.3.0",
 | 
			
		||||
    "js-yaml": "^3.8.3",
 | 
			
		||||
    "jsonschema": "^1.2.0",
 | 
			
		||||
    "jsonwebtoken": "^7.4.0",
 | 
			
		||||
    "le-challenge-ddns": "git+https://git.daplie.com/Daplie/le-challenge-ddns.git#master",
 | 
			
		||||
    "le-challenge-fs": "git+https://git.daplie.com/Daplie/le-challenge-webroot.git#master",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user