-- Prevent running in monitor mode if not TX_SERVER_MODE then return end if TX_LUACOMHOST == "invalid" or TX_LUACOMTOKEN == "invalid" then txPrint('^1API Host or Pipe Token ConVars not found. Do not start this resource if not using txAdmin.') return end if TX_LUACOMTOKEN == "removed" then txPrint('^1Please do not restart the monitor resource.') return end -- ============================================= -- Lua Admin Manager -- ============================================= -- Variables & Consts local failedAuths = {} local attemptCooldown = 15000 -- Handle auth failures local function handleAuthFail(src, reason) local srcString = tostring(src) TX_ADMINS[srcString] = nil failedAuths[srcString] = GetGameTimer() reason = reason or "unknown" debugPrint("Auth rejected #"..srcString.." ("..reason..")") TriggerClientEvent('txcl:setAdmin', src, false, false, reason) TriggerEvent('txAdmin:events:adminAuth', { netid = src, isAdmin = false, }) end -- Handle menu auth requests RegisterNetEvent('txsv:checkIfAdmin', function() local src = source local srcString = tostring(source) debugPrint('Handling authentication request from player #'..srcString) -- Rate Limiter if type(failedAuths[srcString]) == 'number' and failedAuths[srcString] + attemptCooldown > GetGameTimer() then return handleAuthFail(source, "too many auth attempts") end -- Prepping http request local url = "http://"..TX_LUACOMHOST.."/auth/self" local headers = { ['Content-Type'] = 'application/json', ['X-TxAdmin-Token'] = TX_LUACOMTOKEN, ['X-TxAdmin-Identifiers'] = table.concat(GetPlayerIdentifiers(src), ',') } -- Making http request PerformHttpRequest(url, function(httpCode, data, resultHeaders) -- Validating response local resp = json.decode(data) if not resp then return handleAuthFail(src, "invalid response") end if resp.logout ~= nil and resp.logout then return handleAuthFail(src, resp.reason or 'unknown reject reason') end if type(resp.name) ~= "string" then return handleAuthFail(src, "invalid response") end if type(resp.permissions) ~= 'table' then resp.permissions = {} end -- Setting up admin local adminTag = "[#"..src.."] "..resp.name debugPrint(("^2Authenticated admin ^5%s^2 with permissions: %s"):format( src, adminTag, json.encode(resp.permissions) )) TX_ADMINS[srcString] = { tag = adminTag, username = resp.name, perms = resp.permissions, bucket = 0 } sendInitialPlayerlist(src) TriggerClientEvent('txcl:setAdmin', src, resp.name, resp.permissions) TriggerEvent('txAdmin:events:adminAuth', { netid = src, isAdmin = true, username = resp.name, }) end, 'GET', '', headers) end) -- Remove admin from table when disconnected AddEventHandler('playerDropped', function() TX_ADMINS[tostring(source)] = nil end) -- Handle updated admin list AddEventHandler('txAdmin:events:adminsUpdated', function(onlineAdminIDs) debugPrint('^3Admins list updated. Online admins: ' .. json.encode(onlineAdminIDs)) -- Collect old and new admin IDs as key to prevent duplicate local refreshAdminIds = {} for id, _ in pairs(TX_ADMINS) do refreshAdminIds[id] = true end for _, id in pairs(onlineAdminIDs) do refreshAdminIds[tostring(id)] = true end debugPrint('^3Forcing clients to re-auth') -- Resetting all admin permissions and rate limiter TX_ADMINS = {} failedAuths = {} -- Informing clients that they need to reauth for id, _ in pairs(refreshAdminIds) do TriggerClientEvent('txcl:reAuth', tonumber(id)) end -- Broadcasting the invalidation of all admins TriggerEvent('txAdmin:events:adminAuth', { netid = -1, isAdmin = false, }) end)