From ed311d0cd4aeb44f6c6015a15bff1d2bdb6e3618 Mon Sep 17 00:00:00 2001 From: Richard Herrera Date: Thu, 30 Mar 2017 16:39:04 -0700 Subject: [PATCH 1/3] feat: Allow users to serve Storybook over HTTPS --- dist/server/index.js | 36 +++++++++++++++++++++++++++++++++--- src/server/index.js | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/dist/server/index.js b/dist/server/index.js index 3e97f6f72280..a8770b1da862 100755 --- a/dist/server/index.js +++ b/dist/server/index.js @@ -1,10 +1,16 @@ #!/usr/bin/env node 'use strict'; +var _server; + var _express = require('express'); var _express2 = _interopRequireDefault(_express); +var _https = require('https'); + +var _https2 = _interopRequireDefault(_https); + var _serveFavicon = require('serve-favicon'); var _serveFavicon2 = _interopRequireDefault(_serveFavicon); @@ -47,7 +53,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var logger = console; -_commander2.default.version(_package2.default.version).option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt).option('-h, --host [string]', 'Host to run Storybook').option('-s, --static-dir ', 'Directory where to load static files from').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('--dont-track', 'Do not send anonymous usage stats.').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv); +_commander2.default.version(_package2.default.version).option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt).option('-h, --host [string]', 'Host to run Storybook').option('-s, --static-dir ', 'Directory where to load static files from').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('--dont-track', 'Do not send anonymous usage stats.').option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.').option('--ssl-ca ', 'Provide an SSL certificate authority. (Required with --https)').option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)').option('--ssl-key ', 'Provide an SSL key. (Required with --https)').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv); logger.info(_chalk2.default.bold(_package2.default.name + ' v' + _package2.default.version + '\n')); @@ -84,6 +90,30 @@ if (_commander2.default.host) { } var app = (0, _express2.default)(); +var server = app; + +if (_commander2.default.https) { + if (!_commander2.default.sslCa) { + logger.error('Error: --ssl-ca is required with --https'); + process.exit(-1); + } + if (!_commander2.default.sslCert) { + logger.error('Error: --ssl-cert is required with --https'); + process.exit(-1); + } + if (!_commander2.default.sslKey) { + logger.error('Error: --ssl-key is required with --https'); + process.exit(-1); + } + + var sslOptions = { + ca: _fs2.default.readFileSync(_commander2.default.sslCa, 'utf-8'), + cert: _fs2.default.readFileSync(_commander2.default.sslCert, 'utf-8'), + key: _fs2.default.readFileSync(_commander2.default.sslKey, 'utf-8') + }; + + server = _https2.default.createServer(sslOptions, app); +} var hasCustomFavicon = false; @@ -126,11 +156,11 @@ process.env.STORYBOOK_GIT_BRANCH = process.env.STORYBOOK_GIT_BRANCH || exec('git // `getBaseConfig` function which is called inside the middleware app.use((0, _middleware2.default)(configDir)); -app.listen.apply(app, listenAddr.concat([function (error) { +(_server = server).listen.apply(_server, listenAddr.concat([function (error) { if (error) { throw error; } else { - var address = 'http://' + (_commander2.default.host || 'localhost') + ':' + _commander2.default.port + '/'; + var address = 'http' + (_commander2.default.https ? 's' : '') + '://' + (_commander2.default.host || 'localhost') + ':' + _commander2.default.port + '/'; logger.info('\nReact Storybook started on => ' + _chalk2.default.cyan(address) + '\n'); (0, _track_usage.track)(); } diff --git a/src/server/index.js b/src/server/index.js index be08194add7a..a102b799bad1 100755 --- a/src/server/index.js +++ b/src/server/index.js @@ -1,6 +1,7 @@ #!/usr/bin/env node import express from 'express'; +import https from 'https'; import favicon from 'serve-favicon'; import program from 'commander'; import path from 'path'; @@ -23,6 +24,10 @@ program .option('-s, --static-dir ', 'Directory where to load static files from') .option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from') .option('--dont-track', 'Do not send anonymous usage stats.') + .option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.') + .option('--ssl-ca ', 'Provide an SSL certificate authority. (Required with --https)') + .option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)') + .option('--ssl-key ', 'Provide an SSL key. (Required with --https)') .option('-d, --db-path [db-file]', 'DEPRECATED!') .option('--enable-db', 'DEPRECATED!') .parse(process.argv); @@ -66,6 +71,30 @@ if (program.host) { } const app = express(); +let server = app; + +if (program.https) { + if (!program.sslCa) { + logger.error('Error: --ssl-ca is required with --https'); + process.exit(-1); + } + if (!program.sslCert) { + logger.error('Error: --ssl-cert is required with --https'); + process.exit(-1); + } + if (!program.sslKey) { + logger.error('Error: --ssl-key is required with --https'); + process.exit(-1); + } + + const sslOptions = { + ca: fs.readFileSync(program.sslCa, 'utf-8'), + cert: fs.readFileSync(program.sslCert, 'utf-8'), + key: fs.readFileSync(program.sslKey, 'utf-8'), + }; + + server = https.createServer(sslOptions, app); +} let hasCustomFavicon = false; @@ -106,11 +135,11 @@ process.env.STORYBOOK_GIT_BRANCH = process.env.STORYBOOK_GIT_BRANCH || exec('git // `getBaseConfig` function which is called inside the middleware app.use(storybook(configDir)); -app.listen(...listenAddr, function (error) { +server.listen(...listenAddr, function (error) { if (error) { throw error; } else { - const address = `http://${program.host || 'localhost'}:${program.port}/`; + const address = `http${program.https ? 's' : ''}://${program.host || 'localhost'}:${program.port}/`; logger.info(`\nReact Storybook started on => ${chalk.cyan(address)}\n`); track(); } From 7885565111c663b4a2e4104b7354a63cd85774f1 Mon Sep 17 00:00:00 2001 From: Richard Herrera Date: Sun, 2 Apr 2017 09:44:50 -0700 Subject: [PATCH 2/3] refactor: provide ca value as Array Per the Node docs, the `ca` value should be an Array: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener --- dist/server/index.js | 12 ++++++------ src/server/index.js | 8 ++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/dist/server/index.js b/dist/server/index.js index a8770b1da862..f6cde7cecf48 100755 --- a/dist/server/index.js +++ b/dist/server/index.js @@ -53,7 +53,9 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var logger = console; -_commander2.default.version(_package2.default.version).option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt).option('-h, --host [string]', 'Host to run Storybook').option('-s, --static-dir ', 'Directory where to load static files from').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('--dont-track', 'Do not send anonymous usage stats.').option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.').option('--ssl-ca ', 'Provide an SSL certificate authority. (Required with --https)').option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)').option('--ssl-key ', 'Provide an SSL key. (Required with --https)').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv); +_commander2.default.version(_package2.default.version).option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt).option('-h, --host [string]', 'Host to run Storybook').option('-s, --static-dir ', 'Directory where to load static files from').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('--dont-track', 'Do not send anonymous usage stats.').option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.').option('--ssl-ca ', 'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)', function (val) { + return val.split(); +}).option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)').option('--ssl-key ', 'Provide an SSL key. (Required with --https)').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv); logger.info(_chalk2.default.bold(_package2.default.name + ' v' + _package2.default.version + '\n')); @@ -93,10 +95,6 @@ var app = (0, _express2.default)(); var server = app; if (_commander2.default.https) { - if (!_commander2.default.sslCa) { - logger.error('Error: --ssl-ca is required with --https'); - process.exit(-1); - } if (!_commander2.default.sslCert) { logger.error('Error: --ssl-cert is required with --https'); process.exit(-1); @@ -107,7 +105,9 @@ if (_commander2.default.https) { } var sslOptions = { - ca: _fs2.default.readFileSync(_commander2.default.sslCa, 'utf-8'), + ca: _commander2.default.sslCa.map(function (ca) { + return _fs2.default.readFileSync(ca, 'utf-8'); + }), cert: _fs2.default.readFileSync(_commander2.default.sslCert, 'utf-8'), key: _fs2.default.readFileSync(_commander2.default.sslKey, 'utf-8') }; diff --git a/src/server/index.js b/src/server/index.js index a102b799bad1..2bff61a20847 100755 --- a/src/server/index.js +++ b/src/server/index.js @@ -25,7 +25,7 @@ program .option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from') .option('--dont-track', 'Do not send anonymous usage stats.') .option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.') - .option('--ssl-ca ', 'Provide an SSL certificate authority. (Required with --https)') + .option('--ssl-ca ', 'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)', val => val.split()) .option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)') .option('--ssl-key ', 'Provide an SSL key. (Required with --https)') .option('-d, --db-path [db-file]', 'DEPRECATED!') @@ -74,10 +74,6 @@ const app = express(); let server = app; if (program.https) { - if (!program.sslCa) { - logger.error('Error: --ssl-ca is required with --https'); - process.exit(-1); - } if (!program.sslCert) { logger.error('Error: --ssl-cert is required with --https'); process.exit(-1); @@ -88,7 +84,7 @@ if (program.https) { } const sslOptions = { - ca: fs.readFileSync(program.sslCa, 'utf-8'), + ca: program.sslCa.map(ca => fs.readFileSync(ca, 'utf-8')), cert: fs.readFileSync(program.sslCert, 'utf-8'), key: fs.readFileSync(program.sslKey, 'utf-8'), }; From b52b51a7e515cb9bae99e6fe14f50e496bcd2acc Mon Sep 17 00:00:00 2001 From: Richard Herrera Date: Sun, 2 Apr 2017 10:34:54 -0700 Subject: [PATCH 3/3] refactor: handle optional sslCa argument --- dist/server/index.js | 7 ++----- src/server/index.js | 4 ++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/dist/server/index.js b/dist/server/index.js index f6cde7cecf48..64e5d4a223b7 100755 --- a/dist/server/index.js +++ b/dist/server/index.js @@ -52,10 +52,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var logger = console; - -_commander2.default.version(_package2.default.version).option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt).option('-h, --host [string]', 'Host to run Storybook').option('-s, --static-dir ', 'Directory where to load static files from').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('--dont-track', 'Do not send anonymous usage stats.').option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.').option('--ssl-ca ', 'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)', function (val) { - return val.split(); -}).option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)').option('--ssl-key ', 'Provide an SSL key. (Required with --https)').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv); +_commander2.default.version(_package2.default.version).option('-p, --port [number]', 'Port to run Storybook (Required)', parseInt).option('-h, --host [string]', 'Host to run Storybook').option('-s, --static-dir ', 'Directory where to load static files from').option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from').option('--dont-track', 'Do not send anonymous usage stats.').option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.').option('--ssl-ca ', 'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)', _utils.parseList).option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)').option('--ssl-key ', 'Provide an SSL key. (Required with --https)').option('-d, --db-path [db-file]', 'DEPRECATED!').option('--enable-db', 'DEPRECATED!').parse(process.argv); logger.info(_chalk2.default.bold(_package2.default.name + ' v' + _package2.default.version + '\n')); @@ -105,7 +102,7 @@ if (_commander2.default.https) { } var sslOptions = { - ca: _commander2.default.sslCa.map(function (ca) { + ca: (_commander2.default.sslCa || []).map(function (ca) { return _fs2.default.readFileSync(ca, 'utf-8'); }), cert: _fs2.default.readFileSync(_commander2.default.sslCert, 'utf-8'), diff --git a/src/server/index.js b/src/server/index.js index 2bff61a20847..559ff99832f1 100755 --- a/src/server/index.js +++ b/src/server/index.js @@ -25,7 +25,7 @@ program .option('-c, --config-dir [dir-name]', 'Directory where to load Storybook configurations from') .option('--dont-track', 'Do not send anonymous usage stats.') .option('--https', 'Serve Storybook over HTTPS. Note: You must provide your own certificate information.') - .option('--ssl-ca ', 'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)', val => val.split()) + .option('--ssl-ca ', 'Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate)', parseList) .option('--ssl-cert ', 'Provide an SSL certificate. (Required with --https)') .option('--ssl-key ', 'Provide an SSL key. (Required with --https)') .option('-d, --db-path [db-file]', 'DEPRECATED!') @@ -84,7 +84,7 @@ if (program.https) { } const sslOptions = { - ca: program.sslCa.map(ca => fs.readFileSync(ca, 'utf-8')), + ca: (program.sslCa || []).map(ca => fs.readFileSync(ca, 'utf-8')), cert: fs.readFileSync(program.sslCert, 'utf-8'), key: fs.readFileSync(program.sslKey, 'utf-8'), };