updated API to reflect moved domains

This commit is contained in:
tigerbot 2017-10-11 12:18:01 -06:00
parent 61af4707ee
commit 79ef9694b7
2 changed files with 123 additions and 115 deletions

View File

@ -380,21 +380,21 @@ module.exports.create = function (deps, conf) {
var config = { restful: {} }; var config = { restful: {} };
config.restful.readConfig = function (req, res, next) { config.restful.readConfig = function (req, res, next) {
var part = conf; var part = new (require('./config').ConfigChanger)(conf);
if (req.params.group) { if (req.params.group) {
part = part[req.params.group]; part = part[req.params.group];
} }
if (part && req.params.name) { if (part && req.params.domId) {
part = part[req.params.name]; part = part.domains.find(req.params.domId);
} }
if (part && req.params.id) { if (part && req.params.mod) {
part = part.find(function (mod) { return mod.id === req.params.id; }); part = part[req.params.mod];
} }
if (part && req.params.name2) { if (part && req.params.modGrp) {
part = part[req.params.name2]; part = part[req.params.modGrp];
} }
if (part && req.params.id2) { if (part && req.params.modId) {
part = part.find(function (mod) { return mod.id === req.params.id2; }); part = part.find(req.params.modId);
} }
if (part) { if (part) {
@ -439,27 +439,35 @@ module.exports.create = function (deps, conf) {
var err; var err;
deps.PromiseA.resolve().then(function () { deps.PromiseA.resolve().then(function () {
var changer = new (require('./config').ConfigChanger)(conf); var changer = new (require('./config').ConfigChanger)(conf);
if (!changer[group] || !changer[group].modules) {
err = new Error("'"+group+"' is not a valid settings group or has not modules");
err.statusCode = 404;
throw err;
}
var modList; var modList;
if (req.params.id) { if (req.params.domId) {
if (changer[group].domains) { var dom = changer.domains.find(req.params.domId);
modList = (changer[group].domains.find(req.params.id) || {}).modules; if (!dom) {
err = new Error("no domain with ID '"+req.params.domId+"'");
} else if (!dom.modules[group]) {
err = new Error("domains don't contain '"+group+"' modules");
} else {
modList = dom.modules[group];
} }
} else {
if (!changer[group] || !changer[group].modules) {
err = new Error("'"+group+"' is not a valid settings group or doesn't support modules");
} else { } else {
modList = changer[group].modules; modList = changer[group].modules;
} }
if (!modList) { }
err = new Error("'"+group+"' has no domains list or '"+req.params.id+"' does not exist");
if (err) {
err.statusCode = 404; err.statusCode = 404;
throw err; throw err;
} }
modList.add(req.body); var update = req.body;
if (!Array.isArray(update)) {
update = [ update ];
}
update.forEach(modList.add, modList);
var errors = changer.validate(); var errors = changer.validate();
if (errors.length) { if (errors.length) {
throw Object.assign(new Error(), errors[0], {statusCode: 400}); throw Object.assign(new Error(), errors[0], {statusCode: 400});
@ -467,13 +475,13 @@ module.exports.create = function (deps, conf) {
return deps.storage.config.save(changer); return deps.storage.config.save(changer);
}).then(function (config) { }).then(function (config) {
var base; var result;
if (!req.params.id) { if (!req.params.domId) {
base = config[group]; result = config[group].modules;
} else { } else {
base = config[group].domains.find(function (dom) { return dom.id === req.params.id; }); result = config.domains.find(function (dom) { return dom.id === req.params.domId; }).modules[group];
} }
res.send(deps.recase.snakeCopy(base.modules)); res.send(deps.recase.snakeCopy(result));
}, function (err) { }, function (err) {
res.statusCode = err.statusCode || 500; res.statusCode = err.statusCode || 500;
err.message = err.message || err.toString(); err.message = err.message || err.toString();
@ -481,17 +489,15 @@ module.exports.create = function (deps, conf) {
}); });
}; };
config.restful.createDomain = function (req, res) { config.restful.createDomain = function (req, res) {
var group = req.params.group;
var err;
deps.PromiseA.resolve().then(function () { deps.PromiseA.resolve().then(function () {
var changer = new (require('./config').ConfigChanger)(conf); var changer = new (require('./config').ConfigChanger)(conf);
if (!changer[group] || !changer[group].domains) {
err = new Error("'"+group+"' is not a valid settings group or has no domains list");
err.statusCode = 404;
throw err;
}
changer[group].domains.add(req.body); var update = req.body;
if (!Array.isArray(update)) {
update = [ update ];
}
update.forEach(changer.domains.add, changer.domains);
var errors = changer.validate(); var errors = changer.validate();
if (errors.length) { if (errors.length) {
throw Object.assign(new Error(), errors[0], {statusCode: 400}); throw Object.assign(new Error(), errors[0], {statusCode: 400});
@ -499,7 +505,7 @@ module.exports.create = function (deps, conf) {
return deps.storage.config.save(changer); return deps.storage.config.save(changer);
}).then(function (config) { }).then(function (config) {
res.send(deps.recase.snakeCopy(config[group].domains)); res.send(deps.recase.snakeCopy(config.domains));
}, function (err) { }, function (err) {
res.statusCode = err.statusCode || 500; res.statusCode = err.statusCode || 500;
err.message = err.message || err.toString(); err.message = err.message || err.toString();
@ -518,14 +524,15 @@ module.exports.create = function (deps, conf) {
app.use( '/config', makeCorsHandler()); app.use( '/config', makeCorsHandler());
app.get( '/config', config.restful.readConfig); app.get( '/config', config.restful.readConfig);
app.get( '/config/:group', config.restful.readConfig); app.get( '/config/:group', config.restful.readConfig);
app.get( '/config/:group/:name(modules|domains)/:id?', config.restful.readConfig); app.get( '/config/:group/:mod(modules)/:modId?', config.restful.readConfig);
app.get( '/config/:group/:name(domains)/:id/:name2(modules)/:id2?', config.restful.readConfig); app.get( '/config/domains/:domId/:mod(modules)?', config.restful.readConfig);
app.get( '/config/domains/:domId/:mod(modules)/:modGrp/:modId?', config.restful.readConfig);
app.post( '/config', config.restful.saveBaseConfig); app.post( '/config', config.restful.saveBaseConfig);
app.post( '/config/:group', config.restful.saveBaseConfig); app.post( '/config/:group(?!domains)', config.restful.saveBaseConfig);
app.post( '/config/:group/modules', config.restful.createModule); app.post( '/config/:group(?!domains)/modules', config.restful.createModule);
app.post( '/config/:group/domains', config.restful.createDomain); app.post( '/config/domains', config.restful.createDomain);
app.post( '/config/:group/domains/:id/modules', config.restful.createModule); app.post( '/config/domains/:domId/modules/:group',config.restful.createModule);
return app; return app;
}; };

View File

@ -63,54 +63,64 @@ Object.keys(moduleSchemas).forEach(function (name) {
validator.addSchema(schema, schema.id); validator.addSchema(schema, schema.id);
}); });
function addDomainsSchema(base, modList) { function toSchemaRef(name) {
var modSchemas = modList.map(function (name) {
return { '$ref': '/modules/'+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
} }
var moduleRefs = {
http: [ 'proxy', 'static', 'redirect' ].map(toSchemaRef)
, tls: [ 'proxy', 'acme' ].map(toSchemaRef)
, tcp: [ 'forward' ].map(toSchemaRef)
, udp: [ 'proxy' ].map(toSchemaRef)
}; };
base.properties.domains = {
function addDomainRequirement(itemSchema) {
itemSchema.required = (itemSchema.required || []).concat('domains');
itemSchema.properties = itemSchema.properties || {};
itemSchema.domains = { type: 'array', items: { type: 'string' }, minLength: 1};
return itemSchema;
}
var domainSchema = {
type: 'array' type: 'array'
, items: { , items: {
type: 'object' type: 'object'
, required: [ 'id', 'names', ]
, properties: { , properties: {
id: { type: 'string' } id: { type: 'string' }
, names: { type: 'array', items: { type: 'string' }, minLength: 1} , names: { type: 'array', items: { type: 'string' }, minLength: 1}
, modules: { type: 'array', items: { oneOf: modSchemas }} , modules: {
type: 'object'
, properties: {
tls: { type: 'array', items: { oneOf: moduleRefs.tls }}
, http: { type: 'array', items: { oneOf: moduleRefs.http }}
}
, additionalProperties: false
}
} }
} }
}; };
}
var httpSchema = { var httpSchema = {
type: 'object' type: 'object'
, properties: { , properties: {
modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.http }) }
// These properties should be snake_case to match the API and config format // These properties should be snake_case to match the API and config format
primary_domain: { type: 'string' } , primary_domain: { type: 'string' }
, allow_insecure: { type: 'boolean' } , allow_insecure: { type: 'boolean' }
, trust_proxy: { type: 'boolean' } , trust_proxy: { type: 'boolean' }
, bind: { not: {} } // this is a forbidden deprecated setting. // these are forbidden deprecated settings.
, bind: { not: {} }
, domains: { not: {} }
} }
}; };
addDomainsSchema(httpSchema, ['proxy', 'static', 'redirect']);
var tlsSchema = { var tlsSchema = {
type: 'object' type: 'object'
, properties: { , properties: {
acme: { modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.tls }) }
, acme: {
type: 'object' type: 'object'
// These properties should be snake_case to match the API and config format // These properties should be snake_case to match the API and config format
, required: [ 'email', 'approved_domains' ] , required: [ 'email', 'approved_domains' ]
@ -120,19 +130,20 @@ var tlsSchema = {
, challenge_type: { type: 'string' } , challenge_type: { type: 'string' }
, approved_domains: { type: 'array', items: { type: 'string' }, minLength: 1} , approved_domains: { type: 'array', items: { type: 'string' }, minLength: 1}
, bind: { not: {} } // this is a forbidden deprecated setting. // these are forbidden deprecated settings.
, bind: { not: {} }
, domains: { not: {} }
} }
} }
} }
}; };
addDomainsSchema(tlsSchema, ['proxy', 'acme']);
var tcpSchema = { var tcpSchema = {
type: 'object' type: 'object'
, required: [ 'bind' ] , required: [ 'bind' ]
, properties: { , properties: {
bind: { type: 'array', items: portSchema, minLength: 1 } bind: { type: 'array', items: portSchema, minLength: 1 }
, modules: { type: 'array', items: { '$ref': '/modules/forward' }} , modules: { type: 'array', items: { oneOf: moduleRefs.tcp }}
} }
}; };
@ -140,7 +151,7 @@ var udpSchema = {
type: 'object' type: 'object'
, properties: { , properties: {
bind: { type: 'array', items: portSchema } bind: { type: 'array', items: portSchema }
, modules: { type: 'array', items: { '$ref': '/modules/proxy' }} , modules: { type: 'array', items: { oneOf: moduleRefs.udp }}
} }
}; };
@ -176,9 +187,10 @@ var deviceSchema = {
var mainSchema = { var mainSchema = {
type: 'object' type: 'object'
, required: [ 'http', 'tls', 'tcp', 'udp', 'mdns', 'ddns' ] , required: [ 'domains', 'http', 'tls', 'tcp', 'udp', 'mdns', 'ddns' ]
, properties: { , properties: {
http: httpSchema domains:domainSchema
, http: httpSchema
, tls: tlsSchema , tls: tlsSchema
, tcp: tcpSchema , tcp: tcpSchema
, udp: udpSchema , udp: udpSchema
@ -228,7 +240,10 @@ class DomainList extends Array {
Object.assign(this, JSON.parse(JSON.stringify(rawList))); Object.assign(this, JSON.parse(JSON.stringify(rawList)));
} }
this.forEach(function (dom) { this.forEach(function (dom) {
dom.modules = new ModuleList(dom.modules); dom.modules = {
http: new ModuleList((dom.modules || {}).http),
tls: new ModuleList((dom.modules || {}).tls),
};
}); });
} }
@ -245,15 +260,19 @@ class DomainList extends Array {
throw new Error("all domain names must be strings"); throw new Error("all domain names must be strings");
} }
var modList = new ModuleList(); var modLists = {
if (Array.isArray(dom.modules)) { http: new ModuleList(),
dom.modules.forEach(function (mod) { tls: new ModuleList()
modList.add(mod); };
}); if (dom.modules && Array.isArray(dom.modules.http)) {
dom.modules.http.forEach(modLists.http.add, modLists.http);
}
if (dom.modules && Array.isArray(dom.modules.tls)) {
dom.modules.tls.forEach(modLists.tls.add, modLists.tls);
} }
dom.id = require('crypto').randomBytes(4).toString('hex'); dom.id = require('crypto').randomBytes(4).toString('hex');
dom.modules = modList; dom.modules = modLists;
this.push(dom); this.push(dom);
} }
} }
@ -263,10 +282,9 @@ class ConfigChanger {
Object.assign(this, JSON.parse(JSON.stringify(start))); Object.assign(this, JSON.parse(JSON.stringify(start)));
delete this.device; delete this.device;
this.domains = new DomainList(this.domains);
this.http.modules = new ModuleList(this.http.modules); this.http.modules = new ModuleList(this.http.modules);
this.http.domains = new DomainList(this.http.domains);
this.tls.modules = new ModuleList(this.tls.modules); this.tls.modules = new ModuleList(this.tls.modules);
this.tls.domains = new DomainList(this.tls.domains);
this.tcp.modules = new ModuleList(this.tcp.modules); this.tcp.modules = new ModuleList(this.tcp.modules);
this.udp.modules = new ModuleList(this.udp.modules); this.udp.modules = new ModuleList(this.udp.modules);
} }
@ -274,32 +292,15 @@ class ConfigChanger {
update(update) { update(update) {
var self = this; var self = this;
if (update.http && update.http.modules) { if (update.domains) {
update.http.modules.forEach(self.http.modules.add.bind(self.http.modules)); update.domains.forEach(self.domains.add, self.domains);
delete update.http.modules;
} }
if (update.http && update.http.domains) { [ 'http', 'tls', 'tcp', 'udp' ].forEach(function (name) {
update.http.domains.forEach(self.http.domains.add.bind(self.http.domains)); if (update[name] && update[name].modules) {
delete update.http.domains; update[name].modules.forEach(self[name].modules.add, self[name].modules);
} delete update[name].modules;
if (update.tls && update.tls.modules) {
update.tls.modules.forEach(self.tls.modules.add.bind(self.tls.modules));
delete update.tls.modules;
}
if (update.tls && update.tls.domains) {
update.tls.domains.forEach(self.tls.domains.add.bind(self.tls.domains));
delete update.tls.domains;
}
if (update.tcp && update.tcp.modules) {
update.tcp.modules.forEach(self.tcp.modules.add.bind(self.tcp.modules));
delete update.tcp.modules;
}
if (update.udp && update.udp.modules) {
update.udp.modules.forEach(self.udp.modules.add.bind(self.udp.modules));
delete update.udp.modules;
} }
});
function mergeSettings(orig, changes) { function mergeSettings(orig, changes) {
Object.keys(changes).forEach(function (key) { Object.keys(changes).forEach(function (key) {