|
| 1 | +#!/usr/bin/env node |
| 2 | + |
| 3 | +const path = require("path") |
| 4 | +const fs = require("fs") |
| 5 | +const exec = require("child_process").exec |
| 6 | +const https = require("https") |
| 7 | +const getAppDataPath = require("appdata-path") |
| 8 | + |
| 9 | +const MKCERT_VERSION = "v1.3.0" |
| 10 | +const CERT_PATH = getAppDataPath("https-localhost") |
| 11 | + |
| 12 | +// get the executable name |
| 13 | +function getExe() { |
| 14 | + /* istanbul ignore next: tested on all platform on travis */ |
| 15 | + switch (process.platform) { |
| 16 | + case "darwin": |
| 17 | + return "mkcert-" + MKCERT_VERSION + "-darwin-amd64" |
| 18 | + case "linux": |
| 19 | + return "mkcert-" + MKCERT_VERSION + "-linux-amd64" |
| 20 | + case "win32": |
| 21 | + return "mkcert-" + MKCERT_VERSION + "-windows-amd64.exe" |
| 22 | + default: |
| 23 | + console.warn("Cannot generate the localhost certificate on your " + |
| 24 | + "platform. Please, consider contacting the developer if you can help.") |
| 25 | + process.exit(0) |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +// download a binary file |
| 30 | +function download(url, path) { |
| 31 | + console.log("Downloading the mkcert executable...") |
| 32 | + const file = fs.createWriteStream(path) |
| 33 | + return new Promise(resolve => { |
| 34 | + function get(url, file) { |
| 35 | + https.get(url, (response) => { |
| 36 | + if (response.statusCode === 302) get(response.headers.location, file) |
| 37 | + else response.pipe(file).on("finish", resolve) |
| 38 | + }) |
| 39 | + } |
| 40 | + get(url, file) |
| 41 | + }) |
| 42 | +} |
| 43 | + |
| 44 | +// execute the binary executable to generate the certificates |
| 45 | +function mkcert(appDataPath, exe) { |
| 46 | + const logPath = path.join(appDataPath, "mkcert.log") |
| 47 | + const errPath = path.join(appDataPath, "mkcert.err") |
| 48 | + // escape spaces in appDataPath (Mac OS) |
| 49 | + appDataPath = appDataPath.replace(" ", "\\ ") |
| 50 | + const exePath = path.join(appDataPath, exe) |
| 51 | + const crtPath = path.join(appDataPath, "localhost.crt") |
| 52 | + const keyPath = path.join(appDataPath, "localhost.key") |
| 53 | + const cmd = exePath + " -install -cert-file " + crtPath + |
| 54 | + " -key-file " + keyPath + " localhost" |
| 55 | + return new Promise((resolve, reject) => { |
| 56 | + console.log("Running mkcert to generate certificates...") |
| 57 | + exec(cmd, (err, stdout, stderr) => { |
| 58 | + // log |
| 59 | + const errFun = err => { |
| 60 | + /* istanbul ignore if: cannot be tested */ |
| 61 | + if (err) console.error(err) |
| 62 | + } |
| 63 | + fs.writeFile(logPath, stdout, errFun) |
| 64 | + fs.writeFile(errPath, stderr, errFun) |
| 65 | + /* istanbul ignore if: cannot be tested */ |
| 66 | + if (err) reject(err) |
| 67 | + resolve() |
| 68 | + }) |
| 69 | + }) |
| 70 | +} |
| 71 | + |
| 72 | +async function generate(appDataPath = CERT_PATH) { |
| 73 | + console.info("Generating certificates...") |
| 74 | + // mkdir if not exists |
| 75 | + /* istanbul ignore else: not relevant */ |
| 76 | + if (!fs.existsSync(appDataPath)) |
| 77 | + fs.mkdirSync(appDataPath) |
| 78 | + // build the executable url and path |
| 79 | + const url = "https://github.com/FiloSottile/mkcert/releases/download/" + |
| 80 | + MKCERT_VERSION + "/" |
| 81 | + const exe = getExe() |
| 82 | + const exePath = path.join(appDataPath, exe) |
| 83 | + // download the executable |
| 84 | + await download(url + exe, exePath) |
| 85 | + // make binary executable |
| 86 | + fs.chmodSync(exePath, "0755") |
| 87 | + // execute the binary |
| 88 | + await mkcert(appDataPath, exe) |
| 89 | + console.log("Certificates generated, installed and trusted. Ready to go!") |
| 90 | +} |
| 91 | + |
| 92 | +async function getCerts() { |
| 93 | + const certPath = process.env.CERT_PATH || CERT_PATH |
| 94 | + try { |
| 95 | + return { |
| 96 | + key: fs.readFileSync(path.join(certPath, "localhost.key")), |
| 97 | + cert: fs.readFileSync(path.join(certPath, "localhost.crt")) |
| 98 | + } |
| 99 | + } catch (e) { |
| 100 | + if (certPath !== CERT_PATH) { |
| 101 | + console.error("Cannot find localhost.key and localhost.crt in the" + |
| 102 | + " specified path: " + certPath) |
| 103 | + process.exit(1) |
| 104 | + } else { |
| 105 | + // Missing certificates (first run) |
| 106 | + // generate the certificate |
| 107 | + await generate(CERT_PATH) |
| 108 | + // recursive call |
| 109 | + return getCerts() |
| 110 | + } |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +// delete a folder and the file inside it |
| 115 | +function remove(appDataPath = CERT_PATH) { |
| 116 | + if (fs.existsSync(appDataPath)) { |
| 117 | + fs.readdirSync(appDataPath) |
| 118 | + .forEach(file => fs.unlinkSync(path.join(appDataPath, file))) |
| 119 | + fs.rmdirSync(appDataPath) |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +// run as script |
| 124 | +/* istanbul ignore if: cannot be tested */ |
| 125 | +if (require.main === module) |
| 126 | + // if run with -u or --uninstall |
| 127 | + if (process.argv.length === 3 && |
| 128 | + (process.argv[2] === "-u" || process.argv[2] === "--uninstall")) { |
| 129 | + remove() |
| 130 | + console.info("Certificates removed.") |
| 131 | + } else try { // install |
| 132 | + generate() |
| 133 | + } catch (err) { console.error("\nExec error: " + err) } |
| 134 | + |
| 135 | +// export as module |
| 136 | +module.exports = { |
| 137 | + getCerts, |
| 138 | + generate, |
| 139 | + remove |
| 140 | +} |
0 commit comments