| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var validator = new (require('jsonschema').Validator)(); | 
					
						
							|  |  |  | var recase = require('recase').create({}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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: { | 
					
						
							| 
									
										
										
										
											2017-10-12 11:57:43 -06:00
										 |  |  |       email:          { type: 'string' } | 
					
						
							|  |  |  |     , server:         { type: 'string' } | 
					
						
							|  |  |  |     , challenge_type: { type: 'string' } | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // the dns control modules for DDNS
 | 
					
						
							| 
									
										
										
										
											2017-10-25 11:00:06 -06:00
										 |  |  | , 'dns@oauth3.org': { | 
					
						
							|  |  |  |     type: 'object' | 
					
						
							| 
									
										
										
										
											2017-10-18 15:37:35 -06:00
										 |  |  |   , required: [ 'token_id' ] | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |   , properties: { | 
					
						
							| 
									
										
										
										
											2017-10-18 15:37:35 -06:00
										 |  |  |       token_id: { type: 'string' } | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-10-12 14:35:19 -06:00
										 |  |  | // forward is basically the same as proxy, but specifies the relevant incoming port(s).
 | 
					
						
							|  |  |  | // only allows for the raw transport layers (TCP/UDP)
 | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  | moduleSchemas.forward = JSON.parse(JSON.stringify(moduleSchemas.proxy)); | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | 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); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-26 14:39:51 -06:00
										 |  |  | function addDomainRequirement(itemSchema) { | 
					
						
							|  |  |  |   var result = Object.assign({}, itemSchema); | 
					
						
							|  |  |  |   result.required = (result.required || []).concat('domains'); | 
					
						
							|  |  |  |   result.properties = Object.assign({}, result.properties); | 
					
						
							|  |  |  |   result.properties.domains = { type: 'array', items: { type: 'string' }, minLength: 1}; | 
					
						
							|  |  |  |   return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  | function toSchemaRef(name) { | 
					
						
							|  |  |  |   return { '$ref': '/modules/'+name }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | var moduleRefs = { | 
					
						
							|  |  |  |   http: [ 'proxy', 'static', 'redirect' ].map(toSchemaRef) | 
					
						
							|  |  |  | , tls:  [ 'proxy', 'acme' ].map(toSchemaRef) | 
					
						
							|  |  |  | , tcp:  [ 'forward' ].map(toSchemaRef) | 
					
						
							| 
									
										
										
										
											2017-10-12 14:35:19 -06:00
										 |  |  | , udp:  [ 'forward' ].map(toSchemaRef) | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  | , ddns: [ 'dns@oauth3.org' ].map(toSchemaRef) | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-26 14:39:51 -06:00
										 |  |  | // TCP is a bit special in that it has a module that doesn't operate based on domain name
 | 
					
						
							|  |  |  | // (ie forward), and a modules that does (ie proxy). It therefore has different module
 | 
					
						
							|  |  |  | // when part of the `domains` config, and when not part of the `domains` config the proxy
 | 
					
						
							|  |  |  | // modules must have the `domains` property while forward should not have it.
 | 
					
						
							|  |  |  | moduleRefs.tcp.push(addDomainRequirement(toSchemaRef('proxy'))); | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | var domainSchema = { | 
					
						
							|  |  |  |   type: 'array' | 
					
						
							|  |  |  | , items: { | 
					
						
							|  |  |  |     type: 'object' | 
					
						
							|  |  |  |   , properties: { | 
					
						
							|  |  |  |       id:      { type: 'string' } | 
					
						
							|  |  |  |     , names:   { type: 'array', items: { type: 'string' }, minLength: 1} | 
					
						
							|  |  |  |     , modules: { | 
					
						
							|  |  |  |         type: 'object' | 
					
						
							|  |  |  |       , properties: { | 
					
						
							|  |  |  |           tls:  { type: 'array', items: { oneOf: moduleRefs.tls }} | 
					
						
							|  |  |  |         , http: { type: 'array', items: { oneOf: moduleRefs.http }} | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |         , ddns: { type: 'array', items: { oneOf: moduleRefs.ddns }} | 
					
						
							| 
									
										
										
										
											2017-10-26 14:39:51 -06:00
										 |  |  |         , tcp:  { type: 'array', items: { oneOf: ['proxy'].map(toSchemaRef)}} | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |         } | 
					
						
							|  |  |  |       , additionalProperties: false | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | var httpSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , properties: { | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.http }) } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |     // These properties should be snake_case to match the API and config format
 | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |   , primary_domain: { type: 'string' } | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   , allow_insecure: { type: 'boolean' } | 
					
						
							|  |  |  |   , trust_proxy:    { type: 'boolean' } | 
					
						
							| 
									
										
										
										
											2017-10-10 12:34:32 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     // these are forbidden deprecated settings.
 | 
					
						
							|  |  |  |   , bind:    { not: {} } | 
					
						
							|  |  |  |   , domains: { not: {} } | 
					
						
							| 
									
										
										
										
											2017-10-10 12:34:32 -06:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var tlsSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , properties: { | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.tls }) } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-12 11:57:43 -06:00
										 |  |  |     // these are forbidden deprecated settings.
 | 
					
						
							|  |  |  |   , acme:    { not: {} } | 
					
						
							|  |  |  |   , bind:    { not: {} } | 
					
						
							|  |  |  |   , domains: { not: {} } | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var tcpSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , required: [ 'bind' ] | 
					
						
							|  |  |  | , properties: { | 
					
						
							|  |  |  |     bind:    { type: 'array', items: portSchema, minLength: 1 } | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |   , modules: { type: 'array', items: { oneOf: moduleRefs.tcp }} | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-10 11:32:18 -06:00
										 |  |  | var udpSchema = { | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   type: 'object' | 
					
						
							|  |  |  | , properties: { | 
					
						
							|  |  |  |     bind:    { type: 'array', items: portSchema } | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |   , modules: { type: 'array', items: { oneOf: moduleRefs.udp }} | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var mdnsSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , required: [ 'port', 'broadcast', 'ttl' ] | 
					
						
							|  |  |  | , properties: { | 
					
						
							|  |  |  |     port:      portSchema | 
					
						
							|  |  |  |   , broadcast: { type: 'string' } | 
					
						
							|  |  |  |   , ttl:       { type: 'integer', minimum: 0, maximum: 2147483647 } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-31 12:14:37 -06:00
										 |  |  | var tunnelSvrSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , properties: { | 
					
						
							|  |  |  |     servernames: { type: 'array', items: { type: 'string' }} | 
					
						
							|  |  |  |   , secret:      { type: 'string' } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | var ddnsSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , properties: { | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |     loopback: { | 
					
						
							|  |  |  |       type: 'object' | 
					
						
							|  |  |  |     , required: [ 'type', 'domain' ] | 
					
						
							|  |  |  |     , properties: { | 
					
						
							|  |  |  |         type:   { type: 'string', const: 'tunnel@oauth3.org' } | 
					
						
							|  |  |  |       , domain: { type: 'string'} | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   , tunnel: { | 
					
						
							|  |  |  |       type: 'object' | 
					
						
							| 
									
										
										
										
											2017-10-18 15:37:35 -06:00
										 |  |  |     , required: [ 'type', 'token_id' ] | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |     , properties: { | 
					
						
							|  |  |  |         type:  { type: 'string', const: 'tunnel@oauth3.org' } | 
					
						
							| 
									
										
										
										
											2017-10-18 15:37:35 -06:00
										 |  |  |       , token_id: { type: 'string'} | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-26 14:39:51 -06:00
										 |  |  |   , modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.ddns })} | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | var socks5Schema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , properties: { | 
					
						
							|  |  |  |     enabled: { type: 'boolean' } | 
					
						
							|  |  |  |   , port:    portSchema | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  | var deviceSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							|  |  |  | , properties: { | 
					
						
							|  |  |  |     hostname: { type: 'string' } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | var mainSchema = { | 
					
						
							|  |  |  |   type: 'object' | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  | , required: [ 'domains', 'http', 'tls', 'tcp', 'udp', 'mdns', 'ddns' ] | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  | , properties: { | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     domains:domainSchema | 
					
						
							|  |  |  |   , http:   httpSchema | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   , tls:    tlsSchema | 
					
						
							|  |  |  |   , tcp:    tcpSchema | 
					
						
							| 
									
										
										
										
											2017-10-10 11:32:18 -06:00
										 |  |  |   , udp:    udpSchema | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   , mdns:   mdnsSchema | 
					
						
							|  |  |  |   , ddns:   ddnsSchema | 
					
						
							|  |  |  |   , socks5: socks5Schema | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |   , device: deviceSchema | 
					
						
							| 
									
										
										
										
											2017-10-31 12:14:37 -06:00
										 |  |  |   , tunnel_server: tunnelSvrSchema | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | , additionalProperties: false | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  | function validate(config) { | 
					
						
							| 
									
										
										
										
											2017-10-06 17:50:16 -06:00
										 |  |  |   return validator.validate(recase.snakeCopy(config), mainSchema).errors; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  | } | 
					
						
							|  |  |  | module.exports.validate = validate; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-11 17:13:33 -06:00
										 |  |  | class IdList extends Array { | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |   constructor(rawList) { | 
					
						
							|  |  |  |     super(); | 
					
						
							|  |  |  |     if (Array.isArray(rawList)) { | 
					
						
							|  |  |  |       Object.assign(this, JSON.parse(JSON.stringify(rawList))); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-11 17:13:33 -06:00
										 |  |  |     this._itemName = 'item'; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-11 17:13:33 -06:00
										 |  |  |   findId(id) { | 
					
						
							|  |  |  |     return Array.prototype.find.call(this, function (dom) { | 
					
						
							|  |  |  |       return dom.id === id; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-10-11 17:13:33 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   add(item) { | 
					
						
							|  |  |  |     item.id = require('crypto').randomBytes(4).toString('hex'); | 
					
						
							|  |  |  |     this.push(item); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   update(id, update) { | 
					
						
							|  |  |  |     var item = this.findId(id); | 
					
						
							|  |  |  |     if (!item) { | 
					
						
							|  |  |  |       var error = new Error("no "+this._itemName+" with ID '"+id+"'"); | 
					
						
							|  |  |  |       error.statusCode = 404; | 
					
						
							|  |  |  |       throw error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     Object.assign(this.findId(id), update); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   remove(id) { | 
					
						
							|  |  |  |     var index = this.findIndex(function (dom) { | 
					
						
							|  |  |  |       return dom.id === id; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     if (index < 0) { | 
					
						
							|  |  |  |       var error = new Error("no "+this._itemName+" with ID '"+id+"'"); | 
					
						
							|  |  |  |       error.statusCode = 404; | 
					
						
							|  |  |  |       throw error; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this.splice(index, 1); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | class ModuleList extends IdList { | 
					
						
							|  |  |  |   constructor(rawList) { | 
					
						
							|  |  |  |     super(rawList); | 
					
						
							|  |  |  |     this._itemName = 'module'; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |   add(mod) { | 
					
						
							|  |  |  |     if (!mod.type) { | 
					
						
							|  |  |  |       throw new Error("module must have a 'type' defined"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (!moduleSchemas[mod.type]) { | 
					
						
							|  |  |  |       throw new Error("invalid module type '"+mod.type+"'"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mod.id = require('crypto').randomBytes(4).toString('hex'); | 
					
						
							|  |  |  |     this.push(mod); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-10-11 17:13:33 -06:00
										 |  |  | class DomainList extends IdList { | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |   constructor(rawList) { | 
					
						
							| 
									
										
										
										
											2017-10-11 17:13:33 -06:00
										 |  |  |     super(rawList); | 
					
						
							|  |  |  |     this._itemName = 'domain'; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |     this.forEach(function (dom) { | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |       dom.modules = { | 
					
						
							| 
									
										
										
										
											2017-10-12 11:57:43 -06:00
										 |  |  |         http: new ModuleList((dom.modules || {}).http) | 
					
						
							|  |  |  |       , tls:  new ModuleList((dom.modules || {}).tls) | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |       , ddns: new ModuleList((dom.modules || {}).ddns) | 
					
						
							| 
									
										
										
										
											2017-10-26 14:39:51 -06:00
										 |  |  |       , tcp:  new ModuleList((dom.modules || {}).tcp) | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |       }; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   add(dom) { | 
					
						
							|  |  |  |     if (!Array.isArray(dom.names) || !dom.names.length) { | 
					
						
							|  |  |  |       throw new Error("domains must have a non-empty array for 'names'"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (dom.names.some(function (name) { return typeof name !== 'string'; })) { | 
					
						
							|  |  |  |       throw new Error("all domain names must be strings"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     var modLists = { | 
					
						
							| 
									
										
										
										
											2017-10-12 11:57:43 -06:00
										 |  |  |       http: new ModuleList() | 
					
						
							|  |  |  |     , tls:  new ModuleList() | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |     , ddns: new ModuleList() | 
					
						
							| 
									
										
										
										
											2017-10-26 14:39:51 -06:00
										 |  |  |     , tcp:  new ModuleList() | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2017-10-16 12:59:45 -06:00
										 |  |  |     // We add these after instead of in the constructor to run the validation and manipulation
 | 
					
						
							|  |  |  |     // in the ModList add function since these are all new modules.
 | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |     if (dom.modules) { | 
					
						
							|  |  |  |       Object.keys(modLists).forEach(function (key) { | 
					
						
							|  |  |  |         if (Array.isArray(dom.modules[key])) { | 
					
						
							|  |  |  |           dom.modules[key].forEach(modLists[key].add, modLists[key]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dom.id = require('crypto').randomBytes(4).toString('hex'); | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     dom.modules = modLists; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |     this.push(dom); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ConfigChanger { | 
					
						
							|  |  |  |   constructor(start) { | 
					
						
							|  |  |  |     Object.assign(this, JSON.parse(JSON.stringify(start))); | 
					
						
							|  |  |  |     delete this.device; | 
					
						
							| 
									
										
										
										
											2017-10-19 17:45:05 -06:00
										 |  |  |     delete this.debug; | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     this.domains = new DomainList(this.domains); | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |     this.http.modules = new ModuleList(this.http.modules); | 
					
						
							|  |  |  |     this.tls.modules  = new ModuleList(this.tls.modules); | 
					
						
							|  |  |  |     this.tcp.modules  = new ModuleList(this.tcp.modules); | 
					
						
							| 
									
										
										
										
											2017-10-10 11:32:18 -06:00
										 |  |  |     this.udp.modules  = new ModuleList(this.udp.modules); | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |     this.ddns.modules = new ModuleList(this.ddns.modules); | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   update(update) { | 
					
						
							|  |  |  |     var self = this; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |     if (update.domains) { | 
					
						
							|  |  |  |       update.domains.forEach(self.domains.add, self.domains); | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-10-18 12:06:01 -06:00
										 |  |  |     [ 'http', 'tls', 'tcp', 'udp', 'ddns' ].forEach(function (name) { | 
					
						
							| 
									
										
										
										
											2017-10-11 12:18:01 -06:00
										 |  |  |       if (update[name] && update[name].modules) { | 
					
						
							|  |  |  |         update[name].modules.forEach(self[name].modules.add, self[name].modules); | 
					
						
							|  |  |  |         delete update[name].modules; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-10-10 11:08:19 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     function mergeSettings(orig, changes) { | 
					
						
							|  |  |  |       Object.keys(changes).forEach(function (key) { | 
					
						
							|  |  |  |         // TODO: use an API that can properly handle updating arrays.
 | 
					
						
							|  |  |  |         if (!changes[key] || (typeof changes[key] !== 'object') || Array.isArray(changes[key])) { | 
					
						
							|  |  |  |           orig[key] = changes[key]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else if (!orig[key] || typeof orig[key] !== 'object') { | 
					
						
							|  |  |  |           orig[key] = changes[key]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |           mergeSettings(orig[key], changes[key]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     mergeSettings(this, update); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return validate(this); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   validate() { | 
					
						
							|  |  |  |     return validate(this); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | module.exports.ConfigChanger = ConfigChanger; |