-- ───────────────────────────────────────────────────────────────────────────── -- ic-web – ESX-Bridge, Rechte und Logging -- -- Alle Framework-Zugriffe zentral. ESX wird bei Bedarf geholt, nicht beim -- Laden: beim Start ist es je nach Reihenfolge noch nicht bereit, und ein -- einmal fehlgeschlagener Versuch haette die Bridge dauerhaft lahmgelegt. -- ───────────────────────────────────────────────────────────────────────────── ICWeb = ICWeb or {} ICWeb.FW = {} local FW = ICWeb.FW local ESX local function esx() if ESX then return ESX end local ok, obj = pcall(function() return exports['es_extended']:getSharedObject() end) if ok and obj then ESX = obj end return ESX end local function player(src) local api = esx() return api and api.GetPlayerFromId(src) or nil end function FW.GetIdentifier(src) local xPlayer = player(src) return xPlayer and xPlayer.identifier or nil end function FW.GetName(src) local xPlayer = player(src) if not xPlayer then return GetPlayerName(src) or 'Unbekannt' end if xPlayer.getName then return xPlayer.getName() end return xPlayer.name or 'Unbekannt' end -- Job und Dienstgrad. Beides wird fuer die Registrierung gebraucht. function FW.GetJob(src) local xPlayer = player(src) if not xPlayer or not xPlayer.job then return nil, 0 end return xPlayer.job.name, tonumber(xPlayer.job.grade) or 0 end -- Jobliste fuer die Adminansicht. Der Job entscheidet nicht mehr ueber Rechte, -- er steht nur noch als Besitzervermerk an einer Domaene. function FW.GetJobs() local api = esx() if not api or not api.GetJobs then return {} end local out = {} for name, job in pairs(api.GetJobs()) do out[#out + 1] = { name = name, label = job.label or name } end table.sort(out, function(a, b) return a.label < b.label end) return out end function FW.Notify(src, message) local xPlayer = player(src) if xPlayer and xPlayer.showNotification then xPlayer.showNotification(message) return end TriggerClientEvent('esx:showNotification', src, message) end -- ── Adminrechte ───────────────────────────────────────────────────────────── function FW.IsAdmin(src) if not src or src == 0 then return true end -- Serverkonsole local mode = Config.Admin.mode or 'either' local okEsx = false local xPlayer = player(src) if xPlayer and xPlayer.getGroup then okEsx = Config.Admin.esxGroups[xPlayer.getGroup()] == true end local okAce = IsPlayerAceAllowed(src, Config.Admin.acePermission) == true if mode == 'esx' then return okEsx end if mode == 'ace' then return okAce end if mode == 'both' then return okEsx and okAce end return okEsx or okAce end -- ── Logging ───────────────────────────────────────────────────────────────── -- Ausschliesslich ueber fmsdk. Fehlt es, wird verworfen statt zu crashen. ICWeb.Log = {} function ICWeb.Log.Write(src, action, details, level) if GetResourceState('fmsdk') ~= 'started' then if Config.Debug then print(('[ic-web] fmsdk fehlt, Log verworfen: %s'):format(tostring(action))) end return false end local meta = details or {} meta.action = (Config.Logging.actionPrefix or 'ic_web.') .. tostring(action) meta.resource = GetCurrentResourceName() meta.playerSource = src or nil meta.identifier = src and FW.GetIdentifier(src) or nil local ok = pcall(function() exports.fmsdk:Log(Config.Logging.dataset or 'ic_web', level or 'info', ('Webhosting: %s'):format(meta.action), meta) end) return ok end -- ── Drossel ───────────────────────────────────────────────────────────────── -- Jedes RegisterNetEvent ist frei aufrufbar. local buckets = {} function ICWeb.ThrottleOk(src, name, maxPerWindow, windowMs) local now = GetGameTimer() local key = ('%s:%s'):format(src, name) local entry = buckets[key] if not entry or (now - entry.start) > windowMs then buckets[key] = { start = now, count = 1 } return true end entry.count = entry.count + 1 return entry.count <= maxPerWindow end AddEventHandler('playerDropped', function() local prefix = tostring(source) .. ':' for key in pairs(buckets) do if key:sub(1, #prefix) == prefix then buckets[key] = nil end end end)