laravel-echo-node-server
Version:
Laravel Echo Node JS Server for Socket.io
418 lines (417 loc) • 15.9 kB
JavaScript
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;
;