UNPKG

laravel-echo-node-server

Version:

Laravel Echo Node JS Server for Socket.io

418 lines (417 loc) 15.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Cli = void 0; const fs = require("fs"); const path = require("path"); const colors = require("colors"); const echo = require("./../../dist"); const inquirer = require("inquirer"); const crypto = require("crypto"); class Cli { constructor() { this.envVariables = { LARAVEL_ECHO_NODE_SERVER_AUTH_HOST: "authHost", LARAVEL_ECHO_NODE_SERVER_DEBUG: "devMode", LARAVEL_ECHO_NODE_SERVER_HOST: "host", LARAVEL_ECHO_NODE_SERVER_PORT: "port", LARAVEL_ECHO_NODE_SERVER_REDIS_HOST: "databaseConfig.redis.host", LARAVEL_ECHO_NODE_SERVER_REDIS_PORT: "databaseConfig.redis.port", LARAVEL_ECHO_NODE_SERVER_REDIS_PASSWORD: "databaseConfig.redis.password", LARAVEL_ECHO_NODE_SERVER_PROTO: "protocol", LARAVEL_ECHO_NODE_SERVER_SSL_CERT: "sslCertPath", LARAVEL_ECHO_NODE_SERVER_SSL_KEY: "sslKeyPath", LARAVEL_ECHO_NODE_SERVER_SSL_CHAIN: "sslCertChainPath", LARAVEL_ECHO_NODE_SERVER_SSL_PASS: "sslPassphrase", LARAVEL_ECHO_NODE_SERVER_SSL_SELF_SIGNED: "allowSelfSigned", LARAVEL_ECHO_NODE_SERVER_MAX_CONCURRENT_AUTH_REQUESTS: "maxConcurrentAuthRequests", }; this.defaultOptions = echo.defaultOptions; } configure(yargs) { yargs.option({ config: { type: "string", default: "laravel-echo-node-server.json", describe: "The name of the config file to create." } }); this.setupConfig(yargs.argv.config).then((options) => { options = Object.assign({}, this.defaultOptions, options); if (options.addClient) { const client = { appId: this.createAppId(), key: this.createApiKey() }; options.clients.push(client); console.log("appId: " + colors.magenta(client.appId)); console.log("key: " + colors.magenta(client.key)); } if (options.corsAllow) { options.apiOriginAllow.allowCors = true; options.apiOriginAllow.allowOrigin = options.allowOrigin; options.apiOriginAllow.allowMethods = options.allowMethods; options.apiOriginAllow.allowHeaders = options.allowHeaders; } this.saveConfig(options).then(file => { console.log("Configuration file saved. Run " + colors.magenta.bold("laravel-echo-node-server start" + (file != "laravel-echo-node-server.json" ? ' --config="' + file + '"' : "")) + " to run server."); process.exit(); }, error => { console.error(colors.red(error)); }); }, (error) => console.error(error)); } resolveEnvFileOptions(options) { require("dotenv").config(); for (let key in this.envVariables) { let value = (process.env[key] || "").toString(); let replacementVar; if (value) { const path = this.envVariables[key].split("."); let modifier = options; while (path.length > 1) { modifier = modifier[path.shift()]; } if ((replacementVar = value.match(/\${(.*?)}/))) { value = (process.env[replacementVar[1]] || "").toString(); } modifier[path.shift()] = value; } } return options; } setupConfig(defaultFile) { return inquirer.prompt([ { name: "devMode", default: false, message: "Do you want to run this server in development mode?", type: "confirm" }, { name: "port", default: "6001", message: "Which port would you like to serve from?" }, { name: "allowSubDomains", default: false, message: "Do you want to allow subdomains for authentication?", type: "confirm", }, { name: "multiTenant", default: false, message: "Do you want to allow multitenant for authentication?", type: "confirm", }, { name: "database", message: "Which database would you like to use to store presence channel members?", type: "list", choices: ["redis", "sqlite"] }, { name: "authHost", default: "http://localhost", message: "Enter the host of your Laravel authentication server." }, { name: "protocol", message: "Will you be serving on http or https?", type: "list", choices: ["http", "https"] }, { name: "sslCertPath", message: "Enter the path to your SSL cert file.", when: function (options) { return options.protocol == "https"; } }, { name: "sslKeyPath", message: "Enter the path to your SSL key file.", when: function (options) { return options.protocol == "https"; } }, { name: "addClient", default: false, message: "Do you want to generate a client ID/Key for HTTP API?", type: "confirm" }, { name: "corsAllow", default: false, message: "Do you want to setup cross domain access to the API?", type: "confirm" }, { name: "allowOrigin", default: "http://localhost:80", message: "Specify the URI that may access the API:", when: function (options) { return options.corsAllow; } }, { name: "allowMethods", default: "GET, POST", message: "Enter the HTTP methods that are allowed for CORS:", when: function (options) { return options.corsAllow; } }, { name: "allowHeaders", default: "Origin, Content-Type, X-Auth-Token, X-Requested-With, Accept, Authorization, X-CSRF-TOKEN, X-Socket-Id", message: "Enter the HTTP headers that are allowed for CORS:", when: function (options) { return options.corsAllow; } }, { name: "file", default: defaultFile, message: "What do you want this config to be saved as?" } ]); } saveConfig(options) { const opts = Object.keys(options) .filter(key => key in this.defaultOptions) .reduce((filtered, key) => { filtered[key] = options[key]; return filtered; }, {}); return new Promise((resolve, reject) => { if (Object.keys(opts).length > 0) { fs.writeFile(this.getConfigFile(options.file), JSON.stringify(opts, null, "\t"), (error) => { if (error) { return reject(error); } resolve(options.file); }); } else { reject("No valid options provided."); } }); } start(yargs) { yargs.option({ config: { type: "string", describe: "The config file to use." }, dir: { type: "string", describe: "The working directory to use." }, force: { type: "boolean", describe: "If a server is already running, stop it." }, dev: { type: "boolean", describe: "Run in dev mode." } }); const configFile = this.getConfigFile(yargs.argv.config, yargs.argv.dir); fs.access(configFile, fs.constants.F_OK, (error) => { if (error) { console.error(colors.red("Error: The config file could not be found.")); return false; } const options = this.readConfigFile(configFile); options.devMode = `${yargs.argv.dev || options.devMode || false}` === "true"; const lockFile = path.join(path.dirname(configFile), path.basename(configFile, ".json") + ".lock"); if (fs.existsSync(lockFile)) { let lockProcess; try { lockProcess = parseInt(JSON.parse(fs.readFileSync(lockFile, "utf8")).process); } catch (_a) { console.error(colors.red("Error: There was a problem reading the existing lock file.")); } if (lockProcess) { try { process.kill(lockProcess, 0); if (yargs.argv.force) { process.kill(lockProcess); console.log(colors.yellow("Warning: Closing process " + lockProcess + " because you used the '--force' option.")); } else { console.error(colors.red("Error: There is already a server running! Use the option '--force' to stop it and start another one.")); return false; } } catch (_b) { } } } fs.writeFile(lockFile, JSON.stringify({ process: process.pid }, null, "\t"), (error) => { if (error) { console.error(colors.red("Error: Cannot write lock file.")); return false; } process.on("exit", () => { try { fs.unlinkSync(lockFile); } catch (_a) { } }); process.on("SIGINT", () => process.exit()); process.on("SIGHUP", () => process.exit()); process.on("SIGTERM", () => process.exit()); echo.run(options); }); }); } stop(yargs) { yargs.option({ config: { type: "string", describe: "The config file to use." }, dir: { type: "string", describe: "The working directory to use." } }); const configFile = this.getConfigFile(yargs.argv.config, yargs.argv.dir); const lockFile = path.join(path.dirname(configFile), path.basename(configFile, ".json") + ".lock"); if (fs.existsSync(lockFile)) { let lockProcess; try { lockProcess = parseInt(JSON.parse(fs.readFileSync(lockFile, "utf8")).process); } catch (_a) { console.error(colors.red("Error: There was a problem reading the lock file.")); } if (lockProcess) { try { fs.unlinkSync(lockFile); process.kill(lockProcess); console.log(colors.green("Closed the running server.")); } catch (e) { console.error(e); console.log(colors.red("No running servers to close.")); } } } else { console.log(colors.red("Error: Could not find any lock file.")); } } getRandomString(bytes) { return crypto.randomBytes(bytes).toString("hex"); } createApiKey() { return this.getRandomString(16); } createAppId() { return this.getRandomString(8); } clientAdd(yargs) { yargs.option({ config: { type: "string", describe: "The config file to use." }, dir: { type: "string", describe: "The working directory to use." } }); const options = this.readConfigFile(this.getConfigFile(yargs.argv.config, yargs.argv.dir)); const appId = yargs.argv._[1] || this.createAppId(); options.clients = options.clients || []; if (appId) { let index = null; const client = options.clients.find((client, i) => { if (client.appId === appId) { index = i; return true; } return false; }); if (client && index !== null) { client.key = this.createApiKey(); options.clients[index] = client; console.log(colors.green("API Client updated!")); } else { const newClient = { appId: appId, key: this.createApiKey(), }; options.clients.push(newClient); console.log(colors.green("API Client added!")); } console.log(colors.magenta("appId: " + appId)); console.log(colors.magenta("key: " + ((client === null || client === void 0 ? void 0 : client.key) || "undefined"))); this.saveConfig(options); } } clientRemove(yargs) { yargs.option({ config: { type: "string", describe: "The config file to use." }, dir: { type: "string", describe: "The working directory to use." } }); const options = this.readConfigFile(this.getConfigFile(yargs.argv.config, yargs.argv.dir)); const appId = yargs.argv._[1] || null; options.clients = options.clients || []; let index = null; const client = options.clients.find((client, i) => { if (client.appId === appId) { index = i; return true; } return false; }); if (index !== null && index >= 0) { options.clients.splice(index, 1); console.log(colors.green("Client removed: " + appId)); } else { console.log(colors.red("Error: Client not found for appId: " + appId)); } console.log(colors.green("Client removed: " + appId)); this.saveConfig(options); } getConfigFile(file, dir) { const filePath = path.join(dir || "", file || "laravel-echo-node-server.json"); return path.isAbsolute(filePath) ? filePath : path.join(process.cwd(), filePath); } readConfigFile(file) { let data = {}; try { data = JSON.parse(fs.readFileSync(file, "utf8")); } catch (_a) { console.error(colors.red("Error: There was a problem reading the config file.")); process.exit(); } return this.resolveEnvFileOptions(data); } } exports.Cli = Cli;