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: {} };
config.restful.readConfig = function (req, res, next) {
var part = conf;
var part = new (require('./config').ConfigChanger)(conf);
if (req.params.group) {
part = part[req.params.group];
}
if (part && req.params.name) {
part = part[req.params.name];
if (part && req.params.domId) {
part = part.domains.find(req.params.domId);
}
if (part && req.params.id) {
part = part.find(function (mod) { return mod.id === req.params.id; });
if (part && req.params.mod) {
part = part[req.params.mod];
}
if (part && req.params.name2) {
part = part[req.params.name2];
if (part && req.params.modGrp) {
part = part[req.params.modGrp];
}
if (part && req.params.id2) {
part = part.find(function (mod) { return mod.id === req.params.id2; });
if (part && req.params.modId) {
part = part.find(req.params.modId);
}
if (part) {
@ -439,27 +439,35 @@ module.exports.create = function (deps, conf) {
var err;
deps.PromiseA.resolve().then(function () {
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;
if (req.params.id) {
if (changer[group].domains) {
modList = (changer[group].domains.find(req.params.id) || {}).modules;
if (req.params.domId) {
var dom = changer.domains.find(req.params.domId);
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 {
modList = changer[group].modules;
if (!changer[group] || !changer[group].modules) {
err = new Error("'"+group+"' is not a valid settings group or doesn't support modules");
} else {
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;
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();
if (errors.length) {
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);
}).then(function (config) {
var base;
if (!req.params.id) {
base = config[group];
var result;
if (!req.params.domId) {
result = config[group].modules;
} 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) {
res.statusCode = err.statusCode || 500;
err.message = err.message || err.toString();
@ -481,17 +489,15 @@ module.exports.create = function (deps, conf) {
});
};
config.restful.createDomain = function (req, res) {
var group = req.params.group;
var err;
deps.PromiseA.resolve().then(function () {
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();
if (errors.length) {
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);
}).then(function (config) {
res.send(deps.recase.snakeCopy(config[group].domains));
res.send(deps.recase.snakeCopy(config.domains));
}, function (err) {
res.statusCode = err.statusCode || 500;
err.message = err.message || err.toString();
@ -518,14 +524,15 @@ module.exports.create = function (deps, conf) {
app.use( '/config', makeCorsHandler());
app.get( '/config', 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/:name(domains)/:id/:name2(modules)/:id2?', config.restful.readConfig);
app.get( '/config/:group/:mod(modules)/:modId?', 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/:group', config.restful.saveBaseConfig);
app.post( '/config/:group/modules', config.restful.createModule);
app.post( '/config/:group/domains', config.restful.createDomain);
app.post( '/config/:group/domains/:id/modules', config.restful.createModule);
app.post( '/config/:group(?!domains)', config.restful.saveBaseConfig);
app.post( '/config/:group(?!domains)/modules', config.restful.createModule);
app.post( '/config/domains', config.restful.createDomain);
app.post( '/config/domains/:domId/modules/:group',config.restful.createModule);
return app;
};

View File

@ -63,54 +63,64 @@ Object.keys(moduleSchemas).forEach(function (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 }}
}
}
};
function toSchemaRef(name) {
return { '$ref': '/modules/'+name };
}
var moduleRefs = {
http: [ 'proxy', 'static', 'redirect' ].map(toSchemaRef)
, tls: [ 'proxy', 'acme' ].map(toSchemaRef)
, tcp: [ 'forward' ].map(toSchemaRef)
, udp: [ 'proxy' ].map(toSchemaRef)
};
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'
, 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 }}
}
, additionalProperties: false
}
}
}
};
var httpSchema = {
type: 'object'
, properties: {
modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.http }) }
// 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' }
, 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 = {
type: 'object'
, properties: {
acme: {
modules: { type: 'array', items: addDomainRequirement({ oneOf: moduleRefs.tls }) }
, acme: {
type: 'object'
// These properties should be snake_case to match the API and config format
, required: [ 'email', 'approved_domains' ]
@ -120,19 +130,20 @@ var tlsSchema = {
, challenge_type: { type: 'string' }
, 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 = {
type: 'object'
, required: [ 'bind' ]
, properties: {
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'
, properties: {
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 = {
type: 'object'
, required: [ 'http', 'tls', 'tcp', 'udp', 'mdns', 'ddns' ]
, required: [ 'domains', 'http', 'tls', 'tcp', 'udp', 'mdns', 'ddns' ]
, properties: {
http: httpSchema
domains:domainSchema
, http: httpSchema
, tls: tlsSchema
, tcp: tcpSchema
, udp: udpSchema
@ -228,7 +240,10 @@ class DomainList extends Array {
Object.assign(this, JSON.parse(JSON.stringify(rawList)));
}
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");
}
var modList = new ModuleList();
if (Array.isArray(dom.modules)) {
dom.modules.forEach(function (mod) {
modList.add(mod);
});
var modLists = {
http: new ModuleList(),
tls: new ModuleList()
};
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.modules = modList;
dom.modules = modLists;
this.push(dom);
}
}
@ -263,10 +282,9 @@ class ConfigChanger {
Object.assign(this, JSON.parse(JSON.stringify(start)));
delete this.device;
this.domains = new DomainList(this.domains);
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.domains = new DomainList(this.tls.domains);
this.tcp.modules = new ModuleList(this.tcp.modules);
this.udp.modules = new ModuleList(this.udp.modules);
}
@ -274,32 +292,15 @@ class ConfigChanger {
update(update) {
var self = this;
if (update.http && update.http.modules) {
update.http.modules.forEach(self.http.modules.add.bind(self.http.modules));
delete update.http.modules;
}
if (update.http && update.http.domains) {
update.http.domains.forEach(self.http.domains.add.bind(self.http.domains));
delete update.http.domains;
}
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;
if (update.domains) {
update.domains.forEach(self.domains.add, self.domains);
}
[ 'http', 'tls', 'tcp', 'udp' ].forEach(function (name) {
if (update[name] && update[name].modules) {
update[name].modules.forEach(self[name].modules.add, self[name].modules);
delete update[name].modules;
}
});
function mergeSettings(orig, changes) {
Object.keys(changes).forEach(function (key) {