Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ yarn.lock
logo.psd
src/**/.tmp*
.idea
.vscode/
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ yarn express:run

**Updating `vue-cli-plugin-express` will update the Express server service :+1:**

## https mode

https is allowed via (lower items override)
* `https = true` in `pluginOptions`
* `--https true` in command line options

https certificate and key information is loaded via (top first, key and cert are loaded separately if possible)
* `httpsConfig.key`, `httpsConfig.cert` file buffer (same as `devServer.https` option)
* `vue.config.js`'s `devServer.https` option
* `httpsConfig.keyPath`, `httpsConfig.certPath` path string option
* `SERVER_PATH/cert/key.pem`, `cert.pem` loaded directly

## Injected Commands

- **`vue-cli-service express:watch`**
Expand Down
23 changes: 19 additions & 4 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,49 @@ import express from 'express';
import listEndpoints from 'express-list-endpoints';
import history from 'connect-history-api-fallback';
import httpServer from 'http';
import httpsServer from 'https';
import loadPem from './utils/loadPem';

export default ({
port,
host,
srvPath,
httpsConfig,
distPath,
hasTypescript,
shouldServeApp,
isInProduction,
https,
}) => {
return new Promise((resolve, reject) => {
const app = express();
const http = httpServer.Server(app);

let certInfo;
if (https) {
certInfo = {
key: loadPem({httpsConfig, srvPath, option: 'key'}),
cert: loadPem({httpsConfig, srvPath, option: 'cert'}),
};
}
const webServer = !https
? httpServer.Server(app)
: httpsServer.Server(
app,
certInfo
);
if (hasTypescript) {
require('ts-node/register/transpile-only');
}

const server = loadServer(srvPath);

server(app, http);
server(app, webServer);

if (isInProduction && shouldServeApp) {
app.use(history());
app.use(express.static(distPath));
}

http.listen(port, host, err => {
webServer.listen(port, host, err => {
if (err) {
reject(err);
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/servicePlugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = (api, options) => {
const srvPath = api.resolve(expressOptions.serverDir || config.serverDir);
const distPath = api.resolve(options.outputDir);
const hasTypescript = api.hasPlugin('typescript') || expressOptions.hasTypescript;
const httpsConfig = expressOptions.httpsConfig;

if (shouldServeApp && !isInProduction) {
addServerUrlToWebpackProxy(api);
Expand All @@ -25,6 +26,7 @@ module.exports = (api, options) => {
isInProduction,
distPath,
hasTypescript,
httpsConfig,
});
};

Expand Down
14 changes: 12 additions & 2 deletions src/servicePlugin/runCommand.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import serverUrl from '../utils/serverUrl';
import logSuccessLunch from '../utils/logSuccessLaunch';
import logSuccessLaunch from '../utils/logSuccessLaunch';
import server from '../server';

export default ({
Expand All @@ -9,8 +9,11 @@ export default ({
isInProduction,
distPath,
hasTypescript,
https,
httpsConfig,
}) => args => {
const run = async (resolve) => {
console.log(args, defaultOptions);
const {
port,
host,
Expand All @@ -19,6 +22,10 @@ export default ({
localUrlForTerminal,
} = await serverUrl.findServerUrl(args, defaultOptions);

// overwrite config with cmd arguments
https = args.https;

console.log(httpsConfig);
const routes = await server({
port,
host,
Expand All @@ -27,17 +34,20 @@ export default ({
hasTypescript,
shouldServeApp,
isInProduction,
https,
httpsConfig,
});

if (shouldServeApp && !isInProduction) {
serverUrl.writeToFile(localUrl);
}

logSuccessLunch({
logSuccessLaunch({
urls: { local: localUrlForTerminal, network: networkUrl },
routes,
isInProduction,
shouldServeApp,
https,
});

resolve();
Expand Down
78 changes: 78 additions & 0 deletions src/utils/loadPem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// loads pem files
import fs from 'fs';
import path from 'path';

export default ({httpsConfig, srvPath, option}) => {
// load from httpsConfig
let loadOption;
const messages = [];
function getFile () {
try {
// https config
try {
loadOption = 'httpsConfig';
let r;
if (!httpsConfig) {
messages.push('pluginOptions.httpsConfig is a falsy value');
} else {
r = httpsConfig[option];
if (r) {
return r;
}
messages.push(`httpsConfig did not have the option '${option}'`);
};
} catch (e) {
messages.push(e.message);
}

// vue config
try {
let r;
loadOption = 'vue config';
const vueConf = require(process.env.VUE_CLI_SERVICE_CONFIG_PATH || path.join(process.cwd() + '/vue.config.js'));
if (!vueConf || !vueConf.devServer || !vueConf.devServer.https) {
throw new Error(
'vue.config.js has no exports or is empty to load using the config.\n' +
'to use this load option, devServer.https option is required, and should not be false');
}
r = vueConf.devServer.https[option];
return r;
} catch (e) {
messages.push(e.message);
}

// srv/cert/*.pem
try {
loadOption = `${option}Path from httpsConfig`;
let r = httpsConfig && httpsConfig[`${option}Path`];
if (!r) {
messages.push(`httpsConfig did not have ${option}Path option`);
r = srvPath && path.join(srvPath, 'cert', `${option}.pem`);
loadOption = `read file from ${r}`;
}
if (r) {
const f = fs.existsSync(r) && fs.readFileSync(r);
if (f) {
return f;
}
messages.push(`${r} does not exist`);
}
} catch (e) {
messages.push(e.message);
}

throw new Error(
`\n======================================================` +
`\n ⛔ Unable to get ${option} for https. Resolve one of these issues to load a ${option} file` +
`\n${messages.map(m => ` ⚠️ ${m.replace('\n', '\n ')}`).join('\n')}` +
`\n======================================================`
);
} catch (e) {
console.error(`Last load option was ${loadOption}`);
throw e;
}
}
const file = getFile();
if (!Buffer.isBuffer(getFile())) throw new Error(`ssl ${option} is not a buffer. loaded from ${loadOption}. expected buffer, recieved: `, file);
return file;
};
2 changes: 1 addition & 1 deletion src/utils/logSuccessLaunch.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default ({ urls, routes, isInProduction, shouldServeApp }) => {
console.log(` ⚠️ Fallback to the app disabled: ${chalk.bold('your application is not served!')}`);
}
} else {
console.log(` ⚙ You're in ${chalk.bold('development')} mode. to start the application, run ${cmd('serve')}.`);
console.log(` ⚙ You're in ${chalk.bold('development')} mode. to start the application, run ${cmd('serve')}.`);
console.log();
if (shouldServeApp) {
console.log(` 🎉 Fallback to this server enabled: ${chalk.bold('you can use relative routes in your code!')}`);
Expand Down