/* eslint-disable no-unused-vars */
const modulename = 'WebServer:MasterActions:Action';
import { DatabaseActionBanType, DatabaseActionType, DatabaseActionWarnType, DatabasePlayerType } from '@modules/Database/databaseTypes';
import { now } from '@lib/misc';
import { GenericApiErrorResp } from '@shared/genericApiTypes';
import consoleFactory from '@lib/console';
import { AuthedCtx } from '@modules/WebServer/ctxTypes';
import { SYM_RESET_CONFIG } from '@lib/symbols';
const console = consoleFactory(modulename);
/**
* Handle all the master actions... actions
*/
export default async function MasterActionsAction(ctx: AuthedCtx) {
//Sanity check
if (typeof ctx.params.action !== 'string') {
return ctx.send({ error: 'Invalid Request' });
}
const action = ctx.params.action;
//Check permissions
if (!ctx.admin.testPermission('master', modulename)) {
return ctx.send({ error: 'Only the master account has permission to view/use this page.' });
}
if (!ctx.txVars.isWebInterface) {
return ctx.send({ error: 'This functionality cannot be used by the in-game menu, please use the web version of txAdmin.' });
}
//Delegate to the specific action functions
if (action == 'cleanDatabase') {
return handleCleanDatabase(ctx);
} else if (action == 'revokeWhitelists') {
return handleRevokeWhitelists(ctx);
} else {
return ctx.send({ error: 'Unknown settings action.' });
}
};
/**
* Handle clean database request
*/
async function handleCleanDatabase(ctx: AuthedCtx) {
//Typescript stuff
type successResp = {
msElapsed: number;
playersRemoved: number;
actionsRemoved: number;
hwidsRemoved: number;
}
const sendTypedResp = (data: successResp | GenericApiErrorResp) => ctx.send(data);
//Sanity check
if (
typeof ctx.request.body.players !== 'string'
|| typeof ctx.request.body.bans !== 'string'
|| typeof ctx.request.body.warns !== 'string'
|| typeof ctx.request.body.hwids !== 'string'
) {
return sendTypedResp({ error: 'Invalid Request' });
}
const { players, bans, warns, hwids } = ctx.request.body;
const daySecs = 86400;
const currTs = now();
//Prepare filters
let playersFilter: Function;
if (players === 'none') {
playersFilter = (x: DatabasePlayerType) => false;
} else if (players === '60d') {
playersFilter = (x: DatabasePlayerType) => x.tsLastConnection < (currTs - 60 * daySecs) && !x.notes;
} else if (players === '30d') {
playersFilter = (x: DatabasePlayerType) => x.tsLastConnection < (currTs - 30 * daySecs) && !x.notes;
} else if (players === '15d') {
playersFilter = (x: DatabasePlayerType) => x.tsLastConnection < (currTs - 15 * daySecs) && !x.notes;
} else {
return sendTypedResp({ error: 'Invalid players filter type.' });
}
let bansFilter: Function;
if (bans === 'none') {
bansFilter = (x: DatabaseActionBanType) => false;
} else if (bans === 'revoked') {
bansFilter = (x: DatabaseActionBanType) => x.type === 'ban' && x.revocation.timestamp;
} else if (bans === 'revokedExpired') {
bansFilter = (x: DatabaseActionBanType) => x.type === 'ban' && (x.revocation.timestamp || (x.expiration && x.expiration < currTs));
} else if (bans === 'all') {
bansFilter = (x: DatabaseActionBanType) => x.type === 'ban';
} else {
return sendTypedResp({ error: 'Invalid bans filter type.' });
}
let warnsFilter: Function;
if (warns === 'none') {
warnsFilter = (x: DatabaseActionWarnType) => false;
} else if (warns === 'revoked') {
warnsFilter = (x: DatabaseActionWarnType) => x.type === 'warn' && x.revocation.timestamp;
} else if (warns === '30d') {
warnsFilter = (x: DatabaseActionWarnType) => x.type === 'warn' && x.timestamp < (currTs - 30 * daySecs);
} else if (warns === '15d') {
warnsFilter = (x: DatabaseActionWarnType) => x.type === 'warn' && x.timestamp < (currTs - 15 * daySecs);
} else if (warns === '7d') {
warnsFilter = (x: DatabaseActionWarnType) => x.type === 'warn' && x.timestamp < (currTs - 7 * daySecs);
} else if (warns === 'all') {
warnsFilter = (x: DatabaseActionWarnType) => x.type === 'warn';
} else {
return sendTypedResp({ error: 'Invalid warns filter type.' });
}
const actionsFilter = (x: DatabaseActionType) => {
return bansFilter(x) || warnsFilter(x);
};
let hwidsWipePlayers: boolean;
let hwidsWipeBans: boolean;
if (hwids === 'none') {
hwidsWipePlayers = false;
hwidsWipeBans = false;
} else if (hwids === 'players') {
hwidsWipePlayers = true;
hwidsWipeBans = false;
} else if (hwids === 'bans') {
hwidsWipePlayers = false;
hwidsWipeBans = true;
} else if (hwids === 'all') {
hwidsWipePlayers = true;
hwidsWipeBans = true;
} else {
return sendTypedResp({ error: 'Invalid HWIDs filter type.' });
}
//Run db cleaner
const tsStart = Date.now();
let playersRemoved = 0;
try {
playersRemoved = txCore.database.cleanup.bulkRemove('players', playersFilter);
} catch (error) {
return sendTypedResp({ error: `Failed to clean players with error:
${(error as Error).message}` });
}
let actionsRemoved = 0;
try {
actionsRemoved = txCore.database.cleanup.bulkRemove('actions', actionsFilter);
} catch (error) {
return sendTypedResp({ error: `Failed to clean actions with error:
${(error as Error).message}` });
}
let hwidsRemoved = 0;
try {
hwidsRemoved = txCore.database.cleanup.wipeHwids(hwidsWipePlayers, hwidsWipeBans);
} catch (error) {
return sendTypedResp({ error: `Failed to clean HWIDs with error:
${(error as Error).message}` });
}
//Return results
const msElapsed = Date.now() - tsStart;
return sendTypedResp({ msElapsed, playersRemoved, actionsRemoved, hwidsRemoved });
}
/**
* Handle clean database request
*/
async function handleRevokeWhitelists(ctx: AuthedCtx) {
//Typescript stuff
type successResp = {
msElapsed: number;
cntRemoved: number;
}
const sendTypedResp = (data: successResp | GenericApiErrorResp) => ctx.send(data);
//Sanity check
if (typeof ctx.request.body.filter !== 'string') {
return sendTypedResp({ error: 'Invalid Request' });
}
const filterInput = ctx.request.body.filter;
const daySecs = 86400;
const currTs = now();
let filterFunc: Function;
if (filterInput === 'all') {
filterFunc = (p: DatabasePlayerType) => true;
} else if (filterInput === '30d') {
filterFunc = (p: DatabasePlayerType) => p.tsLastConnection < (currTs - 30 * daySecs);
} else if (filterInput === '15d') {
filterFunc = (p: DatabasePlayerType) => p.tsLastConnection < (currTs - 15 * daySecs);
} else if (filterInput === '7d') {
filterFunc = (p: DatabasePlayerType) => p.tsLastConnection < (currTs - 7 * daySecs);
} else {
return sendTypedResp({ error: 'Invalid whitelists filter type.' });
}
try {
const tsStart = Date.now();
const cntRemoved = txCore.database.players.bulkRevokeWhitelist(filterFunc);
const msElapsed = Date.now() - tsStart;
return sendTypedResp({ msElapsed, cntRemoved });
} catch (error) {
return sendTypedResp({ error: `Failed to clean players with error:
${(error as Error).message}` });
}
}