feat: read config from env if config.json is not found
This commit is contained in:
parent
05706007f1
commit
a462b6af1b
6 changed files with 141 additions and 5 deletions
21
.env.example
Normal file
21
.env.example
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
ACORE_ARMORY_AOWOW_URL="https://wotlkdb.com"
|
||||||
|
ACORE_ARMORY_WEBSITE_URL="https://mywebsite.com"
|
||||||
|
ACORE_ARMORY_WEBSITE_NAME="My Website"
|
||||||
|
ACORE_ARMORY_IFRAME_MODE__ENABLED=0
|
||||||
|
ACORE_ARMORY_IFRAME_MODE__URL="https://mywebsite.com/armory"
|
||||||
|
ACORE_ARMORY_LOAD_DBCS=1
|
||||||
|
ACORE_ARMORY_HIDE_GAME_MASTERS=1
|
||||||
|
ACORE_ARMORY_REALMS__0__NAME="AzerothCore"
|
||||||
|
ACORE_ARMORY_REALMS__0__REALM_ID=1
|
||||||
|
ACORE_ARMORY_REALMS__0__AUTH_DATABASE="acore_auth"
|
||||||
|
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__HOST="localhost"
|
||||||
|
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__PORT=3306
|
||||||
|
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__USER="root"
|
||||||
|
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__PASSWORD="root"
|
||||||
|
ACORE_ARMORY_REALMS__0__CHARACTERS_DATABASE__DATABASE="acore_characters"
|
||||||
|
ACORE_ARMORY_WORLD_DATABASE__HOST="localhost"
|
||||||
|
ACORE_ARMORY_WORLD_DATABASE__PORT=3306
|
||||||
|
ACORE_ARMORY_WORLD_DATABASE__USER="root"
|
||||||
|
ACORE_ARMORY_WORLD_DATABASE__PASSWORD="root"
|
||||||
|
ACORE_ARMORY_WORLD_DATABASE__DATABASE="acore_world"
|
||||||
|
ACORE_ARMORY_DB_QUERY_TIMEOUT=10000
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,6 +11,7 @@ data/*
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
config.json
|
config.json
|
||||||
|
.env
|
||||||
|
|
||||||
# Log files
|
# Log files
|
||||||
*.log
|
*.log
|
||||||
|
|
|
||||||
18
package-lock.json
generated
18
package-lock.json
generated
|
|
@ -1,17 +1,18 @@
|
||||||
{
|
{
|
||||||
"name": "azerothcore-armory",
|
"name": "azerothcore-armory",
|
||||||
"version": "0.10.0",
|
"version": "0.11.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "azerothcore-armory",
|
"name": "azerothcore-armory",
|
||||||
"version": "0.10.0",
|
"version": "0.11.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@supercharge/promise-pool": "^2.1.0",
|
"@supercharge/promise-pool": "^2.1.0",
|
||||||
"camelcase": "^6.3.0",
|
"camelcase": "^6.3.0",
|
||||||
"cli-progress": "^3.10.0",
|
"cli-progress": "^3.10.0",
|
||||||
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-handlebars": "^6.0.2",
|
"express-handlebars": "^6.0.2",
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
|
|
@ -1589,6 +1590,14 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
|
||||||
|
"integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/duplexer3": {
|
"node_modules/duplexer3": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
|
||||||
|
|
@ -6394,6 +6403,11 @@
|
||||||
"is-obj": "^2.0.0"
|
"is-obj": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "16.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
|
||||||
|
"integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q=="
|
||||||
|
},
|
||||||
"duplexer3": {
|
"duplexer3": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "azerothcore-armory",
|
"name": "azerothcore-armory",
|
||||||
"version": "0.10.0",
|
"version": "0.11.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
"@supercharge/promise-pool": "^2.1.0",
|
"@supercharge/promise-pool": "^2.1.0",
|
||||||
"camelcase": "^6.3.0",
|
"camelcase": "^6.3.0",
|
||||||
"cli-progress": "^3.10.0",
|
"cli-progress": "^3.10.0",
|
||||||
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.17.3",
|
"express": "^4.17.3",
|
||||||
"express-handlebars": "^6.0.2",
|
"express-handlebars": "^6.0.2",
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,19 @@ export class Config {
|
||||||
public worldDatabase: IDatabaseConfig;
|
public worldDatabase: IDatabaseConfig;
|
||||||
public dbQueryTimeout: number;
|
public dbQueryTimeout: number;
|
||||||
|
|
||||||
|
private static envPrefix: string = "ACORE_ARMORY";
|
||||||
private static checkedMissingField: boolean = false;
|
private static checkedMissingField: boolean = false;
|
||||||
|
|
||||||
public static async load(logger: winston.Logger): Promise<Config> {
|
public static async load(logger: winston.Logger): Promise<Config> {
|
||||||
|
try {
|
||||||
|
await fsp.access("config.json");
|
||||||
|
return await Config.loadFromFile(logger);
|
||||||
|
} catch (err) {
|
||||||
|
return await Config.loadFromEnv(logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async loadFromFile(logger: winston.Logger): Promise<Config> {
|
||||||
const json: Buffer = await fsp.readFile("config.json");
|
const json: Buffer = await fsp.readFile("config.json");
|
||||||
const config = JSON.parse(json.toString()) as Config;
|
const config = JSON.parse(json.toString()) as Config;
|
||||||
|
|
||||||
|
|
@ -50,13 +60,101 @@ export class Config {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async loadFromEnv(logger: winston.Logger): Promise<Config> {
|
||||||
|
const config = {};
|
||||||
|
const json = await fsp.readFile("config.default.json");
|
||||||
|
const defaultConfig = JSON.parse(json.toString());
|
||||||
|
Config.loadObjFromEnv(logger, config, defaultConfig);
|
||||||
|
Config.checkedMissingField = true;
|
||||||
|
return config as Config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static loadObjFromEnv(logger: winston.Logger, obj: object, model: object, parentName: string = "") {
|
||||||
|
if (parentName !== "") {
|
||||||
|
parentName += ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const field in model) {
|
||||||
|
if (!model.hasOwnProperty(field)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(model[field])) {
|
||||||
|
obj[field] = Config.loadArrayFromEnv(logger, model[field][0], parentName + field);
|
||||||
|
} else if (typeof model[field] === "object") {
|
||||||
|
obj[field] = {};
|
||||||
|
Config.loadObjFromEnv(logger, obj[field], model[field], parentName + field);
|
||||||
|
} else if (!obj.hasOwnProperty(field)) {
|
||||||
|
const key = Config.getEnvKey(parentName + field);
|
||||||
|
if (process.env.hasOwnProperty(key)) {
|
||||||
|
obj[field] = Config.parseEnvValue(process.env[key], model[field]);
|
||||||
|
} else if (!Config.checkedMissingField) {
|
||||||
|
logger.warn(`Config field ${key} is missing from .env!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static loadArrayFromEnv(logger: winston.Logger, model: any, parentName: string = ""): any[] {
|
||||||
|
if (parentName !== "") {
|
||||||
|
parentName += ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
const arr = [];
|
||||||
|
let i = 0;
|
||||||
|
while (true) {
|
||||||
|
const key = Config.getEnvKey(parentName + i);
|
||||||
|
const found = Object.keys(process.env).some(k => k.startsWith(key));
|
||||||
|
if (!found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(model)) {
|
||||||
|
arr.push(Config.loadArrayFromEnv(logger, model[0], parentName + i));
|
||||||
|
} else if (typeof model === "object") {
|
||||||
|
const obj = {};
|
||||||
|
Config.loadObjFromEnv(logger, obj, model, parentName + i);
|
||||||
|
if (Object.keys(obj).length > 0) {
|
||||||
|
arr.push(obj);
|
||||||
|
}
|
||||||
|
} else if (process.env.hasOwnProperty(key)) {
|
||||||
|
arr.push(Config.parseEnvValue(process.env[key], model));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getEnvKey(key: string): string {
|
||||||
|
return Config.envPrefix + "_" + key
|
||||||
|
.replace(/\./g, "__")
|
||||||
|
.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`)
|
||||||
|
.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static parseEnvValue(value: string, model: any): any {
|
||||||
|
const type = typeof model;
|
||||||
|
const lower = value.toLowerCase();
|
||||||
|
if (type === "boolean") {
|
||||||
|
return lower === "true" || value === "1";
|
||||||
|
}
|
||||||
|
if (type === "number") {
|
||||||
|
return parseFloat(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
private static checkAllMissingFields(logger: winston.Logger, obj: object, model: object, parentName: string = "") {
|
private static checkAllMissingFields(logger: winston.Logger, obj: object, model: object, parentName: string = "") {
|
||||||
const missing = Config.hasMissingFields(obj, model);
|
const missing = Config.hasMissingFields(obj, model);
|
||||||
if (parentName !== "") {
|
if (parentName !== "") {
|
||||||
parentName += ".";
|
parentName += ".";
|
||||||
}
|
}
|
||||||
for (const field of missing) {
|
for (const field of missing) {
|
||||||
logger.warn(`Field ${parentName}${field} is missing in config.json!`);
|
logger.warn(`Field ${parentName}${field} is missing from config.json!`);
|
||||||
}
|
}
|
||||||
for (const key in model) {
|
for (const key in model) {
|
||||||
if (typeof model[key] === "object" && obj.hasOwnProperty(key)) {
|
if (typeof model[key] === "object" && obj.hasOwnProperty(key)) {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
|
import "dotenv/config";
|
||||||
import { Armory } from "./Armory";
|
import { Armory } from "./Armory";
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
async function main(): Promise<void> {
|
||||||
require("source-map-support").install();
|
require("source-map-support").install();
|
||||||
|
|
||||||
const armory = new Armory();
|
const armory = new Armory();
|
||||||
armory.start();
|
await armory.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue