UNPKG

@nasriya/hypercloud

Version:

Nasriya HyperCloud is a lightweight Node.js HTTP2 framework.

233 lines (232 loc) 11.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const util_1 = require("util"); const child_process_1 = require("child_process"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const http_1 = __importDefault(require("http")); const helpers_1 = __importDefault(require("../../utils/helpers")); const execAsync = (0, util_1.promisify)(child_process_1.exec); const execFileAsync = (0, util_1.promisify)(child_process_1.execFile); const _dirname = __dirname; class SSLManager { #_defaults = Object.freeze({ certbotPath: 'C:\\Program Files\\Certbot', certName: 'nasriyasoftware', filesLocations: { certificatePath: path_1.default.resolve(path_1.default.join(_dirname, 'config')), reqBatFile: path_1.default.resolve(path_1.default.join(_dirname, 'config/req_cert.bat')), key: path_1.default.resolve(path_1.default.join(_dirname, 'config/privateKey.pem')), cert: path_1.default.resolve(path_1.default.join(_dirname, 'config/cert.crt')) } }); #_data = { type: 'selfSigned', letsEncrypt: { certName: undefined, domains: [], email: undefined, staging: false, challengePort: 80 }, storePath: '', }; #_cache = { bat_str: '', staging: false, certInfo: { issued_on: null, expire_at: null }, server: null, port: 0, filesLocations: { certificatePath: path_1.default.resolve(path_1.default.join(_dirname, 'config')), reqBatFile: path_1.default.resolve(path_1.default.join(_dirname, 'config/req_cert.bat')), key: path_1.default.resolve(path_1.default.join(_dirname, 'config/privateKey.pem')), cert: path_1.default.resolve(path_1.default.join(_dirname, 'config/cert.crt')) } }; #_utils = { /** * Function to execute win-acme command * @param {string} command */ executeCertbot: async (command) => { const { stdout, stderr } = await execAsync(`"${this.#_defaults.certbotPath}\\run.bat" ${command}`); if (stdout) { return stdout; } if (stderr) { console.error(stderr); throw stderr; } }, getFileOpenCommand(filePath) { let openCommand; if (process.platform === 'win32') { openCommand = `start ${filePath}`; } else if (process.platform === 'darwin') { openCommand = `open "${filePath}"`; } else { // Assuming Linux or other Unix-like systems openCommand = `xdg-open "${filePath}"`; } return openCommand; }, /**Uses the project's name from the `package.json` file */ getProjectName() { const dir = path_1.default.join(process.cwd(), 'package.json'); const pkg = JSON.parse(fs_1.default.readFileSync(dir, { encoding: 'utf-8' })); return pkg.name; }, removeFile(filePath) { if (fs_1.default.existsSync(filePath)) { fs_1.default.rmSync(filePath); } }, certInfo: { addBatMessage: (msg) => { if (!this.#_cache.bat_str.includes('@echo off')) { this.#_cache.bat_str = `@echo off\n${this.#_cache.bat_str}`; } this.#_cache.bat_str += `echo ${msg}\n`; }, authServer: { run: async () => { let num = 0; // Creating and running a server at your port or port 80 this.#_cache.server = http_1.default.createServer(async (req, res) => { helpers_1.default.printConsole(`Auth Request #${num++}`); // Parse the URL to extract the challenge token const urlParts = req.url.split('/'); const challengeToken = urlParts[urlParts.length - 1]; // Check if the request is for the challenge route if (req.url.startsWith('/.well-known/acme-challenge/')) { // Construct the file path for the challenge file const challengeFilePath = path_1.default.join(this.#_cache.filesLocations.certificatePath, 'challenge', '.well-known\\acme-challenge', challengeToken); // Read the challenge file and respond with its content fs_1.default.readFile(challengeFilePath, 'utf8', (err, data) => { if (err) { res.writeHead(404, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*' }); res.end('Challenge file not found'); } else { res.writeHead(200, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*' }); res.end(data); } }); } }); this.#_cache.server.listen(this.#_cache.port); }, stop: () => { this.#_cache.server?.close(); this.#_cache.server = null; } }, /**Use this to request/renew certificate */ request: async (force = false) => { const d = this.#_data.letsEncrypt; if (helpers_1.default.is.undefined(d)) { throw `Let's Encrypt data are undefined`; } const domainString = d.domains.map(i => `-d ${i}`).join(' '); const certificatePath = this.#_cache.filesLocations.certificatePath; const challengePath = `${certificatePath}\\challenge`; const command = `certbot certonly --webroot -w ${challengePath} --cert-name ${d.certName} --agree-tos --non-interactive${force === true ? ' --force-renewal' : ''} --email ${d.email} ${domainString} --work-dir ${certificatePath} --logs-dir ${certificatePath}\\logs -v${d.staging === true ? ' --test-cert' : ''}\nexit`; this.#_cache.bat_str = command; const batFilePath = this.#_cache.filesLocations.reqBatFile; fs_1.default.writeFileSync(batFilePath, this.#_cache.bat_str.trim(), { encoding: 'utf8', flag: 'w' }); const batFileDir = path_1.default.dirname(batFilePath); const batFileName = path_1.default.basename(batFilePath); try { await execFileAsync(batFileName, { cwd: batFileDir }); } catch (error) { if (error instanceof Error) { error.message = `Error executing bat file: ${error.message}`; } throw error; } return { cert: fs_1.default.readFileSync(`C:\\Certbot\\live\\${d.certName}\\cert.pem`, { encoding: 'utf-8' }), key: fs_1.default.readFileSync(`C:\\Certbot\\live\\${d.certName}\\privkey.pem`, { encoding: 'utf-8' }) }; }, generateSelfSigned: async () => { const key = fs_1.default.readFileSync(path_1.default.join(__dirname, './self-signed/key.pem'), { encoding: 'utf-8' }); const cert = fs_1.default.readFileSync(path_1.default.join(__dirname, './self-signed/cert.pem'), { encoding: 'utf-8' }); return { key, cert }; }, cleanUp: () => { this.#_cache.bat_str = ''; ; if (this.#_cache.server) { this.#_cache.server.close(); this.#_cache.server = null; } this.#_utils.removeFile(this.#_cache.filesLocations.reqBatFile); this.#_utils.removeFile(this.#_cache.filesLocations.cert); this.#_utils.removeFile(this.#_cache.filesLocations.key); } } }; /** * @param {object} options * @returns {Promise<{key: string;cert: string;}>} */ async generate(options) { helpers_1.default.printConsole('Running HyperCloud SSL Manager'); try { const response = { key: '', cert: '' }; if (options.type === 'selfSigned') { helpers_1.default.printConsole('HyperCloud SSL: Generating self-signed certificate...'); const res = await this.#_utils.certInfo.generateSelfSigned(); ; response.cert = res.cert; response.key = res.key; } else { if ('letsEncrypt' in options) { if (helpers_1.default.is.undefined(options.letsEncrypt)) { throw ''; } helpers_1.default.printConsole('HyperCloud SSL: Running a temp auth server...'); this.#_data.letsEncrypt = options.letsEncrypt; await this.#_utils.certInfo.authServer.run(); this.#_utils.certInfo.authServer.stop(); helpers_1.default.printConsole('HyperCloud SSL: Requesting and authenticating...'); const { key, cert } = await this.#_utils.certInfo.request(); response.key = fs_1.default.readFileSync(key, { encoding: 'utf-8' }); response.cert = fs_1.default.readFileSync(cert, { encoding: 'utf-8' }); if (!response.cert || !response.key) { throw 'Unable to obtain SSL certificate from Let\'s Encrypt.'; } } } this.#_data.storePath = options.storePath ? options.storePath : this.#_defaults.filesLocations.certificatePath; if (!fs_1.default.existsSync(this.#_data.storePath)) { fs_1.default.mkdirSync(this.#_data.storePath, { recursive: true }); } helpers_1.default.printConsole('HyperCloud SSL: Storing the obtained SSL certificate & key...'); fs_1.default.writeFileSync(path_1.default.join(this.#_data.storePath, 'privateKey.key'), response.key, { encoding: 'utf-8', flag: 'w' }); fs_1.default.writeFileSync(path_1.default.join(this.#_data.storePath, 'cert.crt'), response.cert, { encoding: 'utf-8', flag: 'w' }); return response; } catch (error) { console.error(error); throw error; } finally { helpers_1.default.printConsole('HyperCloud SSL: Cleaning up after SSL manager...'); this.#_utils.certInfo.cleanUp(); } } } exports.default = SSLManager;