const modulename = 'WebServer:PlayerModal'; import dateFormat from 'dateformat'; import playerResolver from '@lib/player/playerResolver'; import { PlayerHistoryItem, PlayerModalResp, PlayerModalPlayerData } from '@shared/playerApiTypes'; import { DatabaseActionType } from '@modules/Database/databaseTypes'; import { ServerPlayer } from '@lib/player/playerClasses'; import consoleFactory from '@lib/console'; import { AuthedCtx } from '@modules/WebServer/ctxTypes'; import { now } from '@lib/misc'; import { SYM_CURRENT_MUTEX } from '@lib/symbols'; const console = consoleFactory(modulename); //Helpers const processHistoryLog = (hist: DatabaseActionType[]) => { try { return hist.map((log): PlayerHistoryItem => { return { id: log.id, type: log.type, reason: log.reason, author: log.author, ts: log.timestamp, exp: log.expiration ? log.expiration : undefined, revokedBy: log.revocation.author ? log.revocation.author : undefined, revokedAt: log.revocation.timestamp ? log.revocation.timestamp : undefined, }; }); } catch (error) { console.error(`Error processing player history: ${(error as Error).message}`); return []; } }; /** * Returns the data for the player's modal * NOTE: sending license instead of id to be able to show data even for offline players */ export default async function PlayerModal(ctx: AuthedCtx) { //Sanity check if (typeof ctx.query === 'undefined') { return ctx.utils.error(400, 'Invalid Request'); } const { mutex, netid, license } = ctx.query; const sendTypedResp = (data: PlayerModalResp) => ctx.send(data); //Finding the player let player; try { const refMutex = mutex === 'current' ? SYM_CURRENT_MUTEX : mutex; player = playerResolver(refMutex, parseInt((netid as string)), license); } catch (error) { return sendTypedResp({ error: (error as Error).message }); } //Prepping player data const playerData: PlayerModalPlayerData = { displayName: player.displayName, pureName: player.pureName, isRegistered: player.isRegistered, isConnected: player.isConnected, license: player.license, ids: player.ids, hwids: player.hwids, actionHistory: processHistoryLog(player.getHistory()), } if (player instanceof ServerPlayer) { playerData.netid = player.netid; playerData.sessionTime = Math.ceil((now() - player.tsConnected) / 60); } const playerDbData = player.getDbData(); if (playerDbData) { playerData.tsJoined = playerDbData.tsJoined; playerData.playTime = playerDbData.playTime; playerData.tsWhitelisted = playerDbData.tsWhitelisted ? playerDbData.tsWhitelisted : undefined; playerData.oldIds = playerDbData.ids; playerData.oldHwids = playerDbData.hwids; playerData.tsLastConnection = playerDbData.tsLastConnection; if (playerDbData.notes?.lastAdmin && playerDbData.notes?.tsLastEdit) { playerData.notes = playerDbData.notes.text; const lastEditObj = new Date(playerDbData.notes.tsLastEdit * 1000); const lastEditString = dateFormat(lastEditObj, 'longDate'); playerData.notesLog = `Last modified by ${playerDbData.notes.lastAdmin} at ${lastEditString}`; } } // console.dir(metaFields); // console.dir(playerData); return sendTypedResp({ serverTime: now(), banTemplates: txConfig.banlist.templates, //TODO: move this to websocket push player: playerData }); };