feat: hide game masters

This commit is contained in:
Axel Cocat 2022-02-26 12:56:17 +01:00
parent 04ea344bb2
commit 1fd6b326dc
8 changed files with 201 additions and 4213 deletions

View file

@ -1,10 +1,13 @@
{ {
"aowowUrl": "https://wotlkdb.com", "aowowUrl": "https://wotlkdb.com",
"loadDbcs": true, "loadDbcs": true,
"hideGameMasters": true,
"realms": [ "realms": [
{ {
"name": "AzerothCore", "name": "AzerothCore",
"database": { "realmId": 1,
"authDatabase": "acore_auth",
"charactersDatabase": {
"host": "localhost", "host": "localhost",
"port": 3306, "port": 3306,
"user": "root", "user": "root",

4308
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "azerothcore-armory", "name": "azerothcore-armory",
"version": "0.5.0", "version": "0.6.0",
"description": "", "description": "",
"scripts": { "scripts": {
"build": "tsc -p tsconfig.json", "build": "tsc -p tsconfig.json",
@ -18,26 +18,25 @@
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
"@types/glob": "^7.2.0", "@types/glob": "^7.2.0",
"@types/mkdirp": "^1.0.2", "@types/mkdirp": "^1.0.2",
"@types/node": "^17.0.5", "@types/node": "^17.0.21",
"@types/node-fetch": "^2.5.12", "@types/node-fetch": "^2.6.1",
"@types/pako": "^1.0.3", "@types/pako": "^1.0.3",
"concurrently": "^6.5.1", "concurrently": "^7.0.0",
"nodemon": "^2.0.15", "nodemon": "^2.0.15",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^4.5.4" "typescript": "^4.5.5"
}, },
"dependencies": { "dependencies": {
"@supercharge/promise-pool": "^2.1.0", "@supercharge/promise-pool": "^2.1.0",
"camelcase": "^6.2.1", "camelcase": "^6.3.0",
"cli-progress": "^3.9.1", "cli-progress": "^3.10.0",
"express": "^4.17.2", "express": "^4.17.3",
"express-handlebars": "^6.0.2", "express-handlebars": "^6.0.2",
"glob": "^7.2.0", "glob": "^7.2.0",
"glob-promise": "^4.2.2", "glob-promise": "^4.2.2",
"handlebars-helpers": "^0.10.0",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"mysql2": "^2.3.3", "mysql2": "^2.3.3",
"node-fetch": "^2.6.6", "node-fetch": "^2.6.7",
"pako": "^2.0.4", "pako": "^2.0.4",
"pretty-ms": "^7.0.1", "pretty-ms": "^7.0.1",
"source-map-support": "^0.5.21" "source-map-support": "^0.5.21"

View file

@ -39,7 +39,7 @@ export class Armory {
console.log("Connecting to databases..."); console.log("Connecting to databases...");
this.worldDb = await createConnection(this.config.worldDatabase); this.worldDb = await createConnection(this.config.worldDatabase);
for (const realm of this.config.realms) { for (const realm of this.config.realms) {
this.charsDbs[realm.name.toLowerCase()] = await createConnection(realm.database); this.charsDbs[realm.name.toLowerCase()] = await createConnection(realm.charactersDatabase);
} }
console.log("Starting server..."); console.log("Starting server...");
@ -49,7 +49,6 @@ export class Armory {
partialsDir: path.join(process.cwd(), "static", "partials"), partialsDir: path.join(process.cwd(), "static", "partials"),
layoutsDir: path.join(process.cwd(), "static"), layoutsDir: path.join(process.cwd(), "static"),
defaultLayout: "layout.html", defaultLayout: "layout.html",
helpers: require("handlebars-helpers")(),
})); }));
app.set("view engine", "handlebars"); app.set("view engine", "handlebars");
app.set("views", path.join(process.cwd(), "static")); app.set("views", path.join(process.cwd(), "static"));

View file

@ -11,12 +11,15 @@ export interface IDatabaseConfig {
export interface IRealmConfig { export interface IRealmConfig {
name: string; name: string;
database: IDatabaseConfig; realmId: number;
authDatabase: string;
charactersDatabase: IDatabaseConfig;
} }
export class Config { export class Config {
public aowowUrl: string; public aowowUrl: string;
public loadDbcs: boolean; public loadDbcs: boolean;
public hideGameMasters: boolean;
public realms: IRealmConfig[]; public realms: IRealmConfig[];
public worldDatabase: IDatabaseConfig; public worldDatabase: IDatabaseConfig;

View file

@ -13,6 +13,7 @@ export interface IColumnSettings {
collation?: string; collation?: string;
formatter?: (data: string | number | null, row: any) => string; formatter?: (data: string | number | null, row: any) => string;
table?: string; table?: string;
database?: string;
} }
export interface IColumnJoin { export interface IColumnJoin {
@ -20,6 +21,7 @@ export interface IColumnJoin {
column1: string; column1: string;
table2: string; table2: string;
column2: string; column2: string;
database2?: string;
kind: "INNER" | "FULL OUTER" | "LEFT" | "RIGHT"; kind: "INNER" | "FULL OUTER" | "LEFT" | "RIGHT";
} }
@ -90,6 +92,11 @@ export class DataTablesSsp {
this.columnSettings = columnSettings; this.columnSettings = columnSettings;
} }
private colSettingsToStr(colSettings: IColumnSettings) {
const db = colSettings.database ? ("`" + colSettings.database + "`.") : "";
return `${db}\`${colSettings.table || this.table}\`.\`${colSettings.name}\``;
}
private limit() { private limit() {
if (this.start !== undefined && this.length !== -1) { if (this.start !== undefined && this.length !== -1) {
this.limitSql = `LIMIT ${this.length} OFFSET ${this.start}`; this.limitSql = `LIMIT ${this.length} OFFSET ${this.start}`;
@ -110,7 +117,7 @@ export class DataTablesSsp {
} }
const colSettings = this.columnSettings[requestColumn.data]; const colSettings = this.columnSettings[requestColumn.data];
orderBy.push(`\`${colSettings.table || this.table}\`.\`${colSettings.name}\` ${order.dir}`); orderBy.push(`${this.colSettingsToStr(colSettings)} ${order.dir}`);
} }
orderBy.push(`\`${this.table}\`.\`${this.primaryKey}\``); orderBy.push(`\`${this.table}\`.\`${this.primaryKey}\``);
@ -123,7 +130,8 @@ export class DataTablesSsp {
private join() { private join() {
for (const join of this.joins) { for (const join of this.joins) {
this.joinSql += `${join.kind} JOIN \`${join.table2}\` ON \`${join.table2}\`.\`${join.column2}\` = \`${join.table1}\`.\`${join.column1}\`\n`; const db2 = join.database2 ? "`" + join.database2 + "`." : "";
this.joinSql += `${join.kind} JOIN ${db2}\`${join.table2}\` ON ${db2}\`${join.table2}\`.\`${join.column2}\` = \`${join.table1}\`.\`${join.column1}\`\n`;
} }
return this; return this;
@ -141,7 +149,7 @@ export class DataTablesSsp {
const colSettings = this.columnSettings[col.data]; const colSettings = this.columnSettings[col.data];
const collate = colSettings.collation !== undefined ? `COLLATE ${colSettings.collation} ` : ""; const collate = colSettings.collation !== undefined ? `COLLATE ${colSettings.collation} ` : "";
filterWheres.push(`\`${colSettings.table || this.table}\`.\`${colSettings.name}\` ${collate}LIKE ?`); filterWheres.push(`${this.colSettingsToStr(colSettings)} ${collate}LIKE ?`);
this.filterBindings.push(`%${this.search.value}%`); this.filterBindings.push(`%${this.search.value}%`);
} }
if (filterWheres.length > 0) { if (filterWheres.length > 0) {
@ -153,9 +161,9 @@ export class DataTablesSsp {
return this; return this;
} }
private buildSql(): string { public sql(): string {
const columns = [ const columns = [
...this.columnSettings.map(c => `\`${c.table || this.table}\`.\`${c.name}\``), ...this.columnSettings.map(c => this.colSettingsToStr(c)),
...this.extraDataColumns, ...this.extraDataColumns,
]; ];
return ` return `
@ -205,7 +213,7 @@ export class DataTablesSsp {
const recordsFiltered = rows[0].count; const recordsFiltered = rows[0].count;
[rows, fields] = await this.db.query({ [rows, fields] = await this.db.query({
sql: this.buildSql(), sql: this.sql(),
rowsAsArray: true, rowsAsArray: true,
values: bindings, values: bindings,
}); });

View file

@ -157,7 +157,7 @@ export class CharacterController {
return; return;
} }
const charData = await this.getCharacterData(realmName, charName); const charData = await this.getCharacterData(realm, charName);
if (charData === null) { if (charData === null) {
// Could not find character // Could not find character
res.sendStatus(404); res.sendStatus(404);
@ -203,7 +203,7 @@ export class CharacterController {
return; return;
} }
const charData = await this.getCharacterData(realm.name, charName); const charData = await this.getCharacterData(realm, charName);
if (charData === null) { if (charData === null) {
// Could not find character // Could not find character
res.sendStatus(404); res.sendStatus(404);
@ -232,7 +232,7 @@ export class CharacterController {
return; return;
} }
const charData = await this.getCharacterData(realm.name, charName); const charData = await this.getCharacterData(realm, charName);
if (charData === null) { if (charData === null) {
// Could not find character // Could not find character
res.sendStatus(404); res.sendStatus(404);
@ -256,7 +256,7 @@ export class CharacterController {
return; return;
} }
const charData = await this.getCharacterData(realm.name, character); const charData = await this.getCharacterData(realm, character);
if (charData === null) { if (charData === null) {
// Could not find character // Could not find character
res.sendStatus(404); res.sendStatus(404);
@ -285,13 +285,16 @@ export class CharacterController {
return this.armory.config.realms.find(r => r.name.toLowerCase() === realm.toLowerCase()); return this.armory.config.realms.find(r => r.name.toLowerCase() === realm.toLowerCase());
} }
private async getCharacterData(realm: string, character: string | number): Promise<ICharacterData> { private async getCharacterData(realm: IRealmConfig, character: string | number): Promise<ICharacterData> {
const where = typeof character === "string" ? "LOWER(name) = LOWER(?)" : "guid = ?"; const where = typeof character === "string" ? "LOWER(name) = LOWER(?)" : "guid = ?";
const [rows, fields] = await this.armory.getCharactersDb(realm).query(` const [rows, fields] = await this.armory.getCharactersDb(realm.name).query(`
SELECT guid, name, race, class, gender, level, skin, face, hairStyle, hairColor, facialStyle, playerFlags, online SELECT \`guid\`, \`name\`, \`race\`, \`class\`, \`gender\`, \`level\`, \`skin\`, \`face\`, \`hairStyle\`, \`hairColor\`, \`facialStyle\`, \`playerFlags\`, \`online\`
FROM characters FROM \`characters\`
WHERE ${where} LEFT JOIN \`${realm.authDatabase}\`.\`account_access\` ON \`account_access\`.\`id\` = \`characters\`.\`account\`
`, [character]); WHERE
${where}
AND (\`account_access\`.\`id\` IS NULL OR \`account_access\`.\`RealmID\` NOT IN (-1, ${realm.realmId}) OR \`account_access\`.\`gmlevel\` = 0 OR ? = 0)
`, [character, this.armory.config.hideGameMasters ? 1 : 0]);
if ((rows as RowDataPacket[]).length === 0) { if ((rows as RowDataPacket[]).length === 0) {
return null; return null;

View file

@ -30,9 +30,11 @@ const classFiles = {
export class IndexController { export class IndexController {
private armory: Armory; private armory: Armory;
private charsetCache: { [key: string]: string };
public constructor(armory: Armory) { public constructor(armory: Armory) {
this.armory = armory; this.armory = armory;
this.charsetCache = {};
} }
public async index(req: express.Request, res: express.Response): Promise<void> { public async index(req: express.Request, res: express.Response): Promise<void> {
@ -53,18 +55,22 @@ export class IndexController {
} }
const db = this.armory.getCharactersDb(realm.name); const db = this.armory.getCharactersDb(realm.name);
let [rows, fields] = await db.query(`
SELECT CCSA.character_set_name FROM information_schema.\`TABLES\` T,
information_schema.\`COLLATION_CHARACTER_SET_APPLICABILITY\` CCSA
WHERE CCSA.collation_name = T.table_collation
AND T.table_schema = "${db.config.database}"
AND T.table_name = "characters"
`);
const charSet = rows[0].character_set_name;
const ssp = new DataTablesSsp(req.query, db, "characters", "guid", [ if (!(realm.name in this.charsetCache)) {
let [rows, fields] = await db.query(`
SELECT CCSA.character_set_name FROM information_schema.\`TABLES\` T,
information_schema.\`COLLATION_CHARACTER_SET_APPLICABILITY\` CCSA
WHERE CCSA.collation_name = T.table_collation
AND T.table_schema = "${db.config.database}"
AND T.table_name = "characters"
`);
this.charsetCache[realm.name] = rows[0].character_set_name;
}
const charSet = this.charsetCache[realm.name];
let ssp = new DataTablesSsp(req.query, db, "characters", "guid", [
{ name: "name", collation: `${charSet}_general_ci` }, { name: "name", collation: `${charSet}_general_ci` },
{ name: "name", table: "guild" }, { table: "guild", name: "name" },
{ name: "level" }, { name: "level" },
{ name: "race", formatter: (race, row) => `${raceFiles[race]}_${row[6] === 0 ? "male" : "female"}` }, { name: "race", formatter: (race, row) => `${raceFiles[race]}_${row[6] === 0 ? "male" : "female"}` },
{ name: "class", formatter: cls => classFiles[cls] }, { name: "class", formatter: cls => classFiles[cls] },
@ -76,6 +82,11 @@ export class IndexController {
]; ];
ssp.extraDataColumns = ["`characters`.`gender`"]; ssp.extraDataColumns = ["`characters`.`gender`"];
if (this.armory.config.hideGameMasters) {
ssp.joins.push({ table1: "characters", column1: "account", table2: "account_access", column2: "id", database2: realm.authDatabase, kind: "LEFT" });
ssp = ssp.where(`\`account_access\`.\`id\` IS NULL OR \`account_access\`.\`RealmID\` NOT IN (-1, ${realm.realmId}) OR \`account_access\`.\`gmlevel\` = 0`);
}
const result = await ssp const result = await ssp
.where("`deleteInfos_Account` IS NULL") .where("`deleteInfos_Account` IS NULL")
.run(); .run();