diff --git a/index.js b/index.js index 1656cd7..4c33c49 100644 --- a/index.js +++ b/index.js @@ -16,6 +16,11 @@ LE.workDir = leCore.workDir; LE.acmeChallengPrefix = leCore.acmeChallengPrefix; LE.knownEndpoints = leCore.knownEndpoints; +LE.privkeyPath = ':config/live/:hostname/privkey.pem'; +LE.fullchainPath = ':config/live/:hostname/fullchain.pem'; +LE.certPath = ':config/live/:hostname/cert.pem'; +LE.chainPath = ':config/live/:hostname/chain.pem'; + // backwards compat LE.stagingServer = leCore.stagingServerUrl; LE.liveServer = leCore.productionServerUrl; @@ -118,6 +123,20 @@ LE.create = function (defaults, handlers, backend) { le = { backend: backend + , pyToJson: function (pyobj) { + if (!pyobj) { + return null; + } + + var jsobj = {}; + Object.keys(pyobj).forEach(function (key) { + jsobj[key] = pyobj[key]; + }); + jsobj.__lines = undefined; + jsobj.__keys = undefined; + + return jsobj; + } , validate: function (hostnames, cb) { // TODO check dns, etc if ((!hostnames.length && hostnames.every(le.isValidDomain))) { @@ -200,6 +219,31 @@ LE.create = function (defaults, handlers, backend) { args.duplicate = false; le.register(args, cb); } + , getConfig: function (args, cb) { + backend.getConfigAsync(args).then(function (pyobj) { + cb(null, le.pyToJson(pyobj)); + }, function (err) { + console.error(err.stack); + return cb(null, []); + }); + } + , getConfigs: function (args, cb) { + backend.getConfigsAsync(args).then(function (configs) { + cb(null, configs.map(le.pyToJson)); + }, function (err) { + if ('ENOENT' === err.code) { + cb(null, []); + } else { + console.error(err.stack); + cb(err); + } + }); + } + , setConfig: function (args, cb) { + backend.configureAsync(args).then(function (pyobj) { + cb(null, le.pyToJson(pyobj)); + }); + } , register: function (args, cb) { if (!Array.isArray(args.domains)) { cb(new Error('args.domains should be an array of domains')); diff --git a/lib/common.js b/lib/common.js index e1f4a09..446f9e1 100644 --- a/lib/common.js +++ b/lib/common.js @@ -59,6 +59,10 @@ module.exports.tplCopy = function merge(copy) { Object.keys(tpls).sort(function (a, b) { return b.length - a.length; }).forEach(function (tplname) { + if (!tpls[tplname]) { + // what can't be templated now may be templatable later + return; + } copy[key] = copy[key].replace(':' + tplname, tpls[tplname]); copy[key] = copy[key].replace(homeRe, homedir + path.sep); }); diff --git a/lib/core.js b/lib/core.js index 43780cf..097e408 100644 --- a/lib/core.js +++ b/lib/core.js @@ -3,6 +3,7 @@ var PromiseA = require('bluebird'); var mkdirpAsync = PromiseA.promisify(require('mkdirp')); var path = require('path'); +var fs = PromiseA.promisifyAll(require('fs')); var sfs = require('safe-replace'); var LE = require('../'); var LeCore = PromiseA.promisifyAll(require('letiny-core')); @@ -229,13 +230,21 @@ function writeCertificateAsync(args, defaults, handlers) { function getCertificateAsync(args, defaults, handlers) { var account = args.account; + var promise; - return leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537).then(function (domainKey) { + if (args.domainKeyPath) { + // TODO use existing pre-generated key if avaibale + promise = leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537); + } else { + promise = leCrypto.generateRsaKeypairAsync(args.rsaKeySize, 65537); + } + + promise.then(function (domainKey) { if (args.debug) { console.log("get certificate"); } - args.domainPrivateKeyPem = domainKey.privateKeyPem; + args.domainPrivateKeyPem = args.domainPrivateKeyPem || domainKey.privateKeyPem; //args.registration = domainKey; return LeCore.getCertificateAsync({ @@ -422,6 +431,58 @@ module.exports.create = function (defaults, handlers) { } return fetchFromConfigLiveDir(copy, defaults); } + , configureAsync: function (hargs) { + hargs.renewalPath = hargs.renewalPath || ':config/renewal/:hostname.conf'; + hargs.domains = []; + + var copy = merge(hargs, defaults); + tplCopy(copy); + + return getOrCreateRenewal(copy); + } + , getConfigAsync: function (hargs) { + hargs.renewalPath = hargs.renewalPath || ':config/renewal/:hostname.conf'; + hargs.domains = []; + + var copy = merge(hargs, defaults); + tplCopy(copy); + + if (copy.debug) { + console.log('[LE DEBUG] get configs', copy); + } + + return readRenewalConfig(copy).then(function (pyobj) { + var exists = pyobj.checkpoints >= 0; + if (!exists) { + return null; + } + + return pyobj; + }); + } + , getConfigsAsync: function (hargs) { + hargs.renewalDir = hargs.renewalDir || ':config/renewal/'; + hargs.renewalPath = hargs.renewalPath || ':config/renewal/:hostname.conf'; + hargs.domains = []; + + var copy = merge(hargs, defaults); + tplCopy(copy); + + if (copy.debug) { + console.log('[LE DEBUG] get configs', copy); + } + + return fs.readdirAsync(copy.renewalDir).then(function (nodes) { + nodes = nodes.filter(function (node) { + return /^[a-z0-9]+.*\.conf$/.test(node); + }); + + return PromiseA.all(nodes.map(function (node) { + copy.domains = [node.replace(/\.conf$/, '')]; + return wrapped.getConfigAsync(copy); + })); + }); + } }; return wrapped;