updated API to reflect moved domains
This commit is contained in:
parent
61af4707ee
commit
79ef9694b7
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 };
|
||||||
});
|
}
|
||||||
|
var moduleRefs = {
|
||||||
|
http: [ 'proxy', 'static', 'redirect' ].map(toSchemaRef)
|
||||||
|
, tls: [ 'proxy', 'acme' ].map(toSchemaRef)
|
||||||
|
, tcp: [ 'forward' ].map(toSchemaRef)
|
||||||
|
, udp: [ 'proxy' ].map(toSchemaRef)
|
||||||
|
};
|
||||||
|
|
||||||
base.required = [ 'modules', 'domains' ].concat(base.required || []);
|
function addDomainRequirement(itemSchema) {
|
||||||
base.properties.modules = {
|
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: [ 'domains' ]
|
|
||||||
, properties: {
|
|
||||||
domains: { type: 'array', items: { type: 'string' }, minLength: 1}
|
|
||||||
}
|
|
||||||
, oneOf: modSchemas
|
|
||||||
}
|
|
||||||
};
|
|
||||||
base.properties.domains = {
|
|
||||||
type: 'array'
|
|
||||||
, items: {
|
|
||||||
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) {
|
||||||
|
|
Loading…
Reference in New Issue