a special treat

This commit is contained in:
AJ ONeal 2015-12-20 02:40:10 -08:00
parent 7a208b0308
commit 8ccd89ddfd
7 changed files with 16514 additions and 3 deletions

222
bin/lex.js Executable file
View File

@ -0,0 +1,222 @@
#!/usr/bin/env node
'use strict';
var fs = require('fs');
var path = require('path');
var mkdirp = require('fs');
var cli = require('cli');
var mkdirp = require('mkdirp');
var homedir = require('os').homedir();
var configDir = path.join(homedir, 'letsencrypt');
var desktop = path.join(homedir, 'Desktop');
var vhostDir = path.join(fs.existsSync(desktop) ? desktop : configDir, 'www');
var welcomeHtml = fs.readFileSync(path.join(__dirname, '..', 'lib', 'public', 'welcome.html'), 'utf8');
var express = require('express');
cli.parse({
'agree-tos': [ false, " Agree to the Let's Encrypt Subscriber Agreement", 'boolean', false ]
, email: [ false, " Email used for registration and recovery contact. (default: null)", 'email' ]
, domains: [ false, " Domain names to apply. To include the www domain with your main domain your can enter both with a comma. Ex --domains example.com,www.example.com (default: [])", 'string' ]
, debug: [ false, " show traces and logs", 'boolean', false ]
, server: [ false, " ACME Directory Resource URI.", 'string', 'https://acme-v01.api.letsencrypt.org/directory)' ]
});
// ignore certonly and extraneous arguments
cli.main(function(_, options) {
console.log('');
var args = {};
Object.keys(options).forEach(function (key) {
var val = options[key];
if ('string' === typeof val) {
val = val.replace(/^~/, homedir);
}
key = key.replace(/\-([a-z0-9A-Z])/g, function (c) { return c[1].toUpperCase(); });
args[key] = val;
});
if (args.domains) {
args.domains = args.domains.split(',');
}
makeDirectories();
function makeDirectories() {
mkdirp(configDir, function (err) {
if (err) {
console.error("Could not create config directory '" + configDir + "':", err.code);
console.error(err.stack);
return;
}
mkdirp(vhostDir, function (err) {
if (err) {
console.error("Could not create vhost directory '" + vhostDir + "':", err.code);
console.error(err.stack);
return;
}
startServers();
});
});
}
function configure(le, args, cb) {
var vhost;
var pubDir;
var index;
if (!(args.email && args.agreeTos && args.server && args.domains)) {
cb({ error : { message: "missing one or more of agreeTos,domains,email,server" } });
return;
}
vhost = args.domains[0];
pubDir = path.join(vhostDir, vhost);
index = path.join(pubDir, 'index.html');
makeLandingPage();
function makeLandingPage() {
mkdirp(pubDir, function (err) {
if (err) {
cb(err);
return;
}
fs.exists(index, function (exists) {
if (exists) {
configureForHttps();
return;
}
fs.writeFile(path.join(pubDir, 'index.html'), welcomeHtml.replace(/:hostname/g, vhost), 'utf8', function (err) {
if (err) {
cb(err);
return;
}
configureForHttps();
});
});
});
}
function configureForHttps() {
le.setConfig(args, cb);
}
}
function createConfigurator(le) {
var app = express();
app.use('/', express.static(path.join(__dirname, '..', 'lib', 'configurator')));
app.use(require('body-parser').json());
app.get('/api/com.daplie.lex/sites', function (req, res, next) {
le.getConfigs({ configDir: configDir }, function (err, configs) {
if (err) {
next(err);
return;
}
res.send(configs);
});
});
app.post('/api/com.daplie.lex/sites', function (req, res, next) {
var data = req.body;
configure(le, data, function (err, configs) {
if (err) {
console.error(err.stack);
next(err);
return;
}
res.send(configs);
});
});
return app;
}
function startServers() {
// Note: using staging server url, remove .testing() for production
var LE = require('letsencrypt');
var challengeStore = require('../lib/challenge-handlers');
var le = LE.create({
configDir: configDir
, manual: true
, privkeyPath: LE.privkeyPath
, fullchainPath: LE.fullchainPath
, certPath: LE.certPath
, chainPath: LE.chainPath
}, {
setChallenge: challengeStore.set
, removeChallenge: challengeStore.remove
});
var lex = require('../');
var app = express();
var vhosts = {};
vhosts['localhost.daplie.com'] = createConfigurator(le, vhosts);
app.use('/', function (req, res, next) {
var hostname = req.hostname.replace(/^www\./, '');
var pubDir = path.join(vhostDir, hostname);
if (vhosts[hostname]) {
vhosts[hostname](req, res, next);
return;
}
fs.exists(pubDir, function (exists) {
if (exists) {
vhosts[hostname] = express().use('/', express.static(pubDir));
vhosts[hostname](req, res, next);
} else {
vhosts['localhost.daplie.com'](req, res, next);
}
});
});
app.use('/', express.static(path.join(__dirname, '..', 'lib', 'public')));
lex.create({
onRequest: app
, configDir: configDir
, letsencrypt: le
, approveRegistration: function (domain, cb) {
le.getConfig({ domains: [domain] }, function (err, config) {
if (!(config && config.checkpoints >= 0)) {
cb(null, null);
return;
}
cb(null, {
email: config.email
// can't remember which it is, but the pyconf is different that the regular variable
, agreeTos: config.tos || config.agree || config.agreeTos
, server: config.server || LE.productionServerUrl
, domains: config.domains || [domain]
});
});
}
}).listen([80], [443, 5001], function () {
console.log("ENCRYPT __ALL__ THE DOMAINS!");
});
}
/*
// should get back account, path to certs, pems, etc?
console.log('\nCertificates installed at:');
console.log(Object.keys(results).filter(function (key) {
return /Path/.test(key);
}).map(function (key) {
return results[key];
}).join('\n'));
*/
});

41
lib/configurator/app.js Normal file
View File

@ -0,0 +1,41 @@
$(function () {
'use strict';
var tpl = $('.js-hostnames').html();
$('.js-hostnames').html('');
$('body').on('submit', 'form.js-add-site', function (ev) {
ev.preventDefault();
// I don't think this actually has any meaning when listening on body
ev.stopPropagation();
var data = {
email: $('.js-email').val()
, agreeTos: !!$('.js-agree-tos').prop('checked')
, server: $('.js-server').val()
, domains: $('.js-domains').val().split(/\s*,\s*/)
};
$.ajax({
method: 'POST'
, url: '/api/com.daplie.lex/sites'
, data: JSON.stringify(data)
, headers: { 'Content-Type': 'application/json; charset=utf-8' }
}).then(function (data) {
console.log(data);
});
});
$.ajax({
method: 'GET'
, url: '/api/com.daplie.lex/sites'
}).then(function (data) {
var $hostnames = $('.js-hostnames');
$hostnames.html('');
data.forEach(function (config) {
var $conf = $(tpl);
$conf.text(config.domains);
$hostnames.append($conf);
});
});
});

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<title>Welcome to :hostname!</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
<link rel="stylesheet" type="text/css" href="./bootstrap.css">
</head>
<body>
<div class="container">
<div class="jumbotron">
<h1>Add a Site</h1>
<form class="js-add-site">
<label>Domain</label>: <input type="text" class="js-domains" placeholder="ex: example.com,www.example.com">
<br/>
<label>Email</label>: <input type="email" class="js-email" placeholder="ex: user@example.com">
<br/>
<label>LE Server</label>: <input type="text" class="js-server" placeholder="ex: https://acme-staging.api.letsencrypt.org/directory" value="https://acme-v01.api.letsencrypt.org/directory">
<br/>
<label><input type="checkbox" class="js-agree-tos"> Agree to Let's Encrypt Terms of Service?</label>
<br/>
<button class="btn btn-primary" type="submit">Add Site</button>
</form>
</div>
<div class="row">
<p>Sites:
<div class="js-hostnames-container">
<ul class="js-hostnames">
<li class="js-hostname"></li>
</ul>
</div>
</div>
</div>
<script src="./jquery.js"></script>
<script src="./app.js"></script>
</body>
</html>

6961
lib/public/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

9210
lib/public/jquery.js vendored Normal file

File diff suppressed because it is too large Load Diff

24
lib/public/welcome.html Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Welcome to :hostname!</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta http-equiv="Content-Security-Policy" content="default-src 'self';">
<link rel="stylesheet" type="text/css" href="./bootstrap.css">
</head>
<body>
<div class="container">
<div class="jumbotron">
<h1>Hello, world!</h1>
<p>Your website is located in one of two places:</p>
<ul>
<li>on your <code>Desktop</code> inside of the <code>www</code> directory and <code>:hostname</code></li>
<li><code>~/www/:hostname</code> (your home folder, under <code>www/:hostname</code></li>
<ul>
<p>This very file is called <code>index.html</code>.
You can open it up in <code>notepad</code> and change it up a little if you'd like.</p>
</div>
</div>
<script src="./jquery.js"></script>
</body>
</html>

View File

@ -3,6 +3,16 @@
"version": "1.0.2",
"description": "Free SSL and Automatic HTTPS for node.js with Express, Connect, and other middleware systems",
"main": "index.js",
"bin": {
"letsencrypt-express": "bin/lex.js",
"lex": "bin/lex.js"
},
"files": [
"lib/",
"bin/",
"examples/",
"index.js"
],
"directories": {
"example": "examples"
},
@ -33,9 +43,15 @@
"url": "https://github.com/Daplie/letsencrypt-express/issues"
},
"homepage": "https://github.com/Daplie/letsencrypt-express#readme",
"dependencies": {
"homedir": "^0.6.0",
"letsencrypt": "^1.3.0",
"devDependencies": {
"body-parser": "^1.14.2",
"cli": "^0.11.1",
"express": "^4.13.3",
"localhost.daplie.com-certificates": "^1.1.2",
"mkdirp": "^0.5.1"
}
"dependencies": {
"homedir": "^0.6.0",
"letsencrypt": "^1.3.0"
}
}