230 строки
11 KiB
JavaScript
230 строки
11 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const program = require('commander');
|
|
const fs = require('fs');
|
|
const os = require("os");
|
|
const inquirer = require('inquirer')
|
|
const { v4 } = require('uuid')
|
|
const pkg = require('./package.json')
|
|
const crypto = require('crypto');
|
|
|
|
const SALT_SIZE = 128;
|
|
|
|
const hashPassword = (password) => {
|
|
const passwordBuffer = Buffer.from(password, "utf-8");
|
|
const passwordHash = crypto.createHash('sha256').update(passwordBuffer).digest();
|
|
|
|
const saltBuffer = crypto.randomBytes(SALT_SIZE);
|
|
const fullBuffer = Buffer.concat([passwordHash, saltBuffer]);
|
|
|
|
|
|
const hash = crypto.createHash('sha256').update(fullBuffer).digest('hex');
|
|
|
|
return { salt: "0x" + saltBuffer.toString('hex'), password: "0x" + hash };
|
|
}
|
|
|
|
function main() {
|
|
program
|
|
.version(pkg.version);
|
|
|
|
program
|
|
.command('init')
|
|
.action(function () {
|
|
console.log("Game Control Initialization");
|
|
console.log("---------------------------");
|
|
var questions = [
|
|
|
|
{
|
|
type: 'input',
|
|
name: 'serverFqdn',
|
|
message: "What is the fully-qualified DNS name of the server endpoint that will host Game Control?",
|
|
default: "localhost",
|
|
validate: (input, answers) => { return (input.startsWith("http") ? "Please enter a domain name without 'http' or 'https'." : true) }
|
|
},
|
|
{
|
|
type: 'list',
|
|
name: 'dbtype',
|
|
message: 'What kind of database would you like to use?',
|
|
choices: [
|
|
{
|
|
name: "Microsoft SQL Server Developer Edition, hosted in a Docker container",
|
|
value: 'sqlcontainer'
|
|
}
|
|
,
|
|
{
|
|
name: "External Microsoft SQL Server instance (for example, SQL Azure)",
|
|
value: 'external'
|
|
}
|
|
]
|
|
},
|
|
{
|
|
type: "password",
|
|
name: "sapassword",
|
|
message: "What password would you like to use for the 'sa' database administrator account? (leave blank to automatically generate a password)",
|
|
when: (answers) => (answers.dbtype === "sqlcontainer")
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'username',
|
|
message: "What is the username you would like to use for the default admin login to the Game Control site?",
|
|
default: "admin",
|
|
when: (answers) => (answers.dbtype === "sqlcontainer"),
|
|
validate: (input, answers) => {
|
|
if (input.length < 2 || input.length > 50) {
|
|
return "Please enter a username between 2 and 50 characters long.";
|
|
}
|
|
return true;
|
|
}
|
|
},
|
|
{
|
|
type: 'password',
|
|
name: 'password',
|
|
message: "What is the password you would like to use for the default admin login to the Game Control site?",
|
|
when: (answers) => (answers.dbtype === "sqlcontainer"),
|
|
validate: (input, answers) => {
|
|
if (input.length < 4) {
|
|
return "Please enter a longer password.";
|
|
}
|
|
return true;
|
|
}
|
|
},
|
|
{
|
|
type: 'confirm',
|
|
name: 'acceptEula',
|
|
message: "Do you accept the Microsoft SQL Server Developer Edition EULA found at https://go.microsoft.com/fwlink/?LinkId=746388 ?",
|
|
default: false,
|
|
when: (answers) => (answers.dbtype === "sqlcontainer")
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'sqlConnectionString',
|
|
message: "What is the connection string for your Microsoft SQL Server?",
|
|
when: (answers) => (answers.dbtype === "external")
|
|
},
|
|
{
|
|
type: 'confirm',
|
|
name: 'useMSA',
|
|
message: "Would you like to enable Microsoft Account authentication?",
|
|
default: true
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'msaClientId',
|
|
message: "To enable Microsoft Account authentication, follow the instructions at https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app to register an application.\nWhat is your application (client) ID for the Microsoft identity platform?",
|
|
default: "",
|
|
when: (answers) => (answers.useMSA === true)
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'azureBlobConnectionString',
|
|
message: "What is the connection string for your Azure Blob Storage account?"
|
|
},
|
|
{
|
|
type: 'input',
|
|
name: 'azureBlobBaseUrl',
|
|
message: "What is the base URL for your Azure Blob Storage account? (For example, https://gamecontrolblob.example.com/events/)"
|
|
},
|
|
{
|
|
type: 'confirm',
|
|
name: 'confirmation',
|
|
message: "Ready to create configuration files? (This will overwrite any existing settings, including credentials, and may cause data loss if you have an existing installation of Game Control!)",
|
|
default: false
|
|
}];
|
|
|
|
inquirer.prompt(questions).then((answers) => {
|
|
let env = [];
|
|
let clientEnv = [];
|
|
let serverEnv = [];
|
|
let databaseEnv = [];
|
|
|
|
let appSettings = {
|
|
"Logging": {
|
|
"IncludeScopes": false,
|
|
"LogLevel": {
|
|
"Default": "Debug",
|
|
"System": "Information",
|
|
"Microsoft": "Information"
|
|
}
|
|
},
|
|
"ConnectionStrings": {},
|
|
"JwtIssuerOptions": { "Issuer": "GameControl" },
|
|
"GameControl": {}
|
|
};
|
|
|
|
if (answers.confirmation) {
|
|
console.log("Writing .env files...");
|
|
if (answers.acceptEula) {
|
|
databaseEnv.push("ACCEPT_EULA=true");
|
|
}
|
|
|
|
if (answers.dbtype === "sqlcontainer") {
|
|
env.push("COMPOSE_PROFILES=everything");
|
|
const saPassword = answers.saPassword ? answers.saPassword : v4();
|
|
const gcPassword = v4();
|
|
|
|
databaseEnv.push("SA_PASSWORD=" + saPassword);
|
|
databaseEnv.push("GC_PASSWORD=" + gcPassword);
|
|
|
|
const {password, salt} = hashPassword(answers.password);
|
|
databaseEnv.push("LOGIN_USERNAME="+ answers.username);
|
|
databaseEnv.push("LOGIN_PASSWORD_HASH="+ password);
|
|
databaseEnv.push("LOGIN_PASSWORD_SALT="+ salt);
|
|
|
|
const connectionString = "Data Source=database;Initial Catalog=gamecontrol;Integrated Security=false;User ID=gc;Password=" + gcPassword + ";Connect Timeout=15;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;MultipleActiveResultSets=true"
|
|
serverEnv.push("ConnectionStrings__DefaultConnection=" + connectionString);
|
|
appSettings.ConnectionStrings.DefaultConnection = connectionString;
|
|
}
|
|
else if (answers.dbtype === "external") {
|
|
env.push("COMPOSE_PROFILES=clientserver");
|
|
serverEnv.push("ConnectionStrings__DefaultConnection=" + answers.sqlConnectionString);
|
|
appSettings.ConnectionStrings.DefaultConnection = answers.sqlConnectionString;
|
|
}
|
|
|
|
if (answers.useMSA && answers.msaClientId) {
|
|
clientEnv.push("REACT_APP_MICROSOFT_ACCOUNT_CLIENT_ID=" + answers.msaClientId);
|
|
serverEnv.push("GameControl__MsaClientId=" + answers.msaClientId);
|
|
appSettings.GameControl.MsaClientId = answers.msaClientId;
|
|
}
|
|
|
|
const origin = "http://" + answers.serverFqdn;
|
|
serverEnv.push("GameControl__CorsOrigin=" + origin);
|
|
appSettings.GameControl.CorsOrigin = origin;
|
|
serverEnv.push("JwtIssuerOptions__Audience=" + origin);
|
|
serverEnv.push("JwtIssuerOptions__Issuer=GameControl");
|
|
appSettings.JwtIssuerOptions.Audience = origin;
|
|
|
|
const gcSecretKey = v4();
|
|
serverEnv.push("GameControl__SecretKey=" + gcSecretKey);
|
|
appSettings.GameControl.SecretKey = gcSecretKey;
|
|
|
|
serverEnv.push("GameControl__BlobStorageConnectionString=" + answers.azureBlobConnectionString);
|
|
appSettings.GameControl.BlobStorageConnectionString = answers.azureBlobConnectionString;
|
|
|
|
let blobBaseUrl = answers.azureBlobBaseUrl;
|
|
if (!blobBaseUrl.endsWith("/")) {
|
|
blobBaseUrl += "/";
|
|
}
|
|
serverEnv.push("GameControl__BlobStorageBaseUrl=" + answers.blobBaseUrl);
|
|
appSettings.GameControl.BlobStorageBaseUrl = answers.blobBaseUrl;
|
|
|
|
fs.writeFileSync(".env", env.join(os.EOL));
|
|
if (answers.dbtype === "sqlcontainer") {
|
|
fs.writeFileSync("database.env", databaseEnv.join(os.EOL));
|
|
}
|
|
fs.writeFileSync("client.env", clientEnv.join(os.EOL));
|
|
fs.writeFileSync("client/.env", clientEnv.join(os.EOL));
|
|
fs.writeFileSync("server.env", serverEnv.join(os.EOL));
|
|
|
|
console.log("Writing server/appsettings.Development.json file...");
|
|
fs.writeFileSync("./server/appsettings.Development.json", JSON.stringify(appSettings));
|
|
|
|
console.log("Done!");
|
|
console.log("To launch Game Control, run: \n\tdocker-compose up");
|
|
}
|
|
});
|
|
});
|
|
|
|
program.parse(process.argv)
|
|
}
|
|
|
|
main() |