diff --git a/resources/[tools]/nordi_license/server/main.lua b/resources/[tools]/nordi_license/server/main.lua deleted file mode 100644 index 7a01a8a88..000000000 --- a/resources/[tools]/nordi_license/server/main.lua +++ /dev/null @@ -1,722 +0,0 @@ -local QBCore = exports['qb-core']:GetCoreObject() - --- Lokale Variablen -local licenseCache = {} -local cacheTimeout = 300000 -- 5 Minuten - --- Hilfsfunktionen -local function debugPrint(message) - if Config.Debug then - print("^3[License-System Server] " .. message .. "^7") - end -end - --- Spieler-Name abrufen -local function getPlayerName(src) - local Player = QBCore.Functions.GetPlayer(src) - if not Player then return "Unbekannt" end - - local charinfo = Player.PlayerData.charinfo - if charinfo and charinfo.firstname and charinfo.lastname then - return charinfo.firstname .. " " .. charinfo.lastname - end - - return "Unbekannt" -end - --- Spieler-Daten abrufen -local function getPlayerData(src) - local Player = QBCore.Functions.GetPlayer(src) - if not Player then return nil end - - return { - citizenid = Player.PlayerData.citizenid, - name = getPlayerName(src), - charinfo = Player.PlayerData.charinfo, - job = Player.PlayerData.job - } -end - --- Berechtigung prüfen -local function hasPermission(src) - local Player = QBCore.Functions.GetPlayer(src) - if not Player then return false end - - local job = Player.PlayerData.job - if not job then return false end - - local hasAuth = Config.AuthorizedJobs[job.name] or false - debugPrint("Berechtigung für " .. job.name .. ": " .. tostring(hasAuth)) - return hasAuth -end - --- Cache-Funktionen -local function getCachedLicense(citizenid, licenseType) - local cacheKey = citizenid .. "_" .. licenseType - local cached = licenseCache[cacheKey] - - if cached and (os.time() * 1000 - cached.timestamp) < cacheTimeout then - debugPrint("Cache-Hit für: " .. cacheKey) - return cached.data - end - - return nil -end - -local function setCachedLicense(citizenid, licenseType, data) - local cacheKey = citizenid .. "_" .. licenseType - licenseCache[cacheKey] = { - data = data, - timestamp = os.time() * 1000 - } - debugPrint("Lizenz gecacht: " .. cacheKey) -end - --- Cache bereinigen -local function cleanupCache() - local now = os.time() * 1000 - local cleaned = 0 - - for key, cached in pairs(licenseCache) do - if (now - cached.timestamp) > cacheTimeout then - licenseCache[key] = nil - cleaned = cleaned + 1 - end - end - - if cleaned > 0 then - debugPrint("Cache bereinigt: " .. cleaned .. " Einträge") - end -end - --- Cache-Cleanup Thread -CreateThread(function() - while true do - Wait(300000) -- 5 Minuten - cleanupCache() - end -end) - --- Spieler-Name aus JSON extrahieren (MariaDB-kompatibel) -local function extractPlayerName(charinfo_json) - if not charinfo_json then return "Unbekannt" end - - local success, charinfo = pcall(json.decode, charinfo_json) - if success and charinfo and charinfo.firstname and charinfo.lastname then - return charinfo.firstname .. " " .. charinfo.lastname - end - - return "Unbekannt" -end - --- Lizenz aus Datenbank abrufen (KORRIGIERT für MariaDB) -local function getLicenseFromDB(citizenid, licenseType) - debugPrint("=== getLicenseFromDB START ===") - debugPrint("CitizenID: " .. tostring(citizenid)) - debugPrint("LicenseType: " .. tostring(licenseType)) - - -- Cache prüfen - local cached = getCachedLicense(citizenid, licenseType) - if cached then - return cached - end - - -- MariaDB-kompatible Query ohne JSON-Operatoren - local query = [[ - SELECT pl.*, - p.charinfo as holder_charinfo, - pi.charinfo as issued_by_charinfo - FROM player_licenses pl - LEFT JOIN players p ON p.citizenid = pl.citizenid - LEFT JOIN players pi ON pi.citizenid = pl.issued_by - WHERE pl.citizenid = ? AND pl.license_type = ? - ORDER BY pl.created_at DESC - LIMIT 1 - ]] - - local result = MySQL.query.await(query, {citizenid, licenseType}) - - if result and #result > 0 then - local license = result[1] - - -- Namen aus JSON extrahieren - license.holder_name = extractPlayerName(license.holder_charinfo) - license.issued_by_name = extractPlayerName(license.issued_by_charinfo) - - -- Fallback für issued_by_name - if license.issued_by_name == "Unbekannt" then - license.issued_by_name = "System" - end - - -- Classes parsen - if license.classes then - local success, classes = pcall(json.decode, license.classes) - if success and type(classes) == "table" then - license.classes = classes - else - license.classes = {} - end - else - license.classes = {} - end - - -- Cleanup - charinfo nicht mehr benötigt - license.holder_charinfo = nil - license.issued_by_charinfo = nil - - debugPrint("Lizenz aus DB geladen: " .. license.license_type) - - -- In Cache speichern - setCachedLicense(citizenid, licenseType, license) - - return license - end - - debugPrint("Keine Lizenz in DB gefunden") - return nil -end - --- Alle Lizenzen eines Spielers abrufen (KORRIGIERT für MariaDB) -local function getAllPlayerLicenses(citizenid) - debugPrint("=== getAllPlayerLicenses START ===") - debugPrint("CitizenID: " .. tostring(citizenid)) - - -- MariaDB-kompatible Query - local query = [[ - SELECT pl.*, - p.charinfo as holder_charinfo, - pi.charinfo as issued_by_charinfo - FROM player_licenses pl - LEFT JOIN players p ON p.citizenid = pl.citizenid - LEFT JOIN players pi ON pi.citizenid = pl.issued_by - WHERE pl.citizenid = ? - ORDER BY pl.license_type, pl.created_at DESC - ]] - - local result = MySQL.query.await(query, {citizenid}) - - if result and #result > 0 then - local licenses = {} - local seenTypes = {} - - for _, license in ipairs(result) do - -- Nur die neueste Lizenz pro Typ nehmen - if not seenTypes[license.license_type] then - seenTypes[license.license_type] = true - - -- Namen aus JSON extrahieren - license.holder_name = extractPlayerName(license.holder_charinfo) - license.issued_by_name = extractPlayerName(license.issued_by_charinfo) - - -- Fallback für issued_by_name - if license.issued_by_name == "Unbekannt" then - license.issued_by_name = "System" - end - - -- Classes parsen - if license.classes then - local success, classes = pcall(json.decode, license.classes) - if success and type(classes) == "table" then - license.classes = classes - else - license.classes = {} - end - else - license.classes = {} - end - - -- Cleanup - license.holder_charinfo = nil - license.issued_by_charinfo = nil - - table.insert(licenses, license) - end - end - - debugPrint("Gefundene Lizenzen: " .. #licenses) - return licenses - end - - debugPrint("Keine Lizenzen gefunden") - return {} -end - --- Lizenz in Datenbank speichern (KORRIGIERT) -local function saveLicenseToDB(citizenid, licenseType, issuedBy, classes) - debugPrint("=== saveLicenseToDB START ===") - debugPrint("CitizenID: " .. tostring(citizenid)) - debugPrint("LicenseType: " .. tostring(licenseType)) - debugPrint("IssuedBy: " .. tostring(issuedBy)) - - local config = Config.LicenseTypes[licenseType] - if not config then - debugPrint("^1Fehler: Unbekannter Lizenztyp: " .. licenseType .. "^7") - return false - end - - -- Spieler-Name für das name-Feld abrufen (MariaDB-kompatibel) - local holderQuery = "SELECT charinfo FROM players WHERE citizenid = ?" - local holderResult = MySQL.query.await(holderQuery, {citizenid}) - local holderName = "Unbekannt" - - if holderResult and #holderResult > 0 and holderResult[1].charinfo then - holderName = extractPlayerName(holderResult[1].charinfo) - end - - -- Datum berechnen - local issueDate = os.date("%d.%m.%Y") - local expireDate = nil - - if config.validity_days then - local expireTimestamp = os.time() + (config.validity_days * 24 * 60 * 60) - expireDate = os.date("%d.%m.%Y", expireTimestamp) - end - - -- Classes zu JSON konvertieren - local classesJson = json.encode(classes or {}) - - -- Alte Lizenz deaktivieren - local deactivateQuery = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ?" - MySQL.query.await(deactivateQuery, {citizenid, licenseType}) - - -- Neue Lizenz einfügen - local insertQuery = [[ - INSERT INTO player_licenses - (citizenid, license_type, name, issue_date, expire_date, issued_by, is_active, classes, created_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - ]] - - local insertData = { - citizenid, - licenseType, - holderName, - issueDate, - expireDate, - issuedBy, - 1, - classesJson, - os.time() - } - - debugPrint("Führe INSERT-Query aus...") - debugPrint("Daten: " .. json.encode(insertData)) - - local success, result = pcall(MySQL.insert.await, insertQuery, insertData) - - if success and result then - debugPrint("Lizenz erfolgreich gespeichert. ID: " .. result) - - -- Cache invalidieren - local cacheKey = citizenid .. "_" .. licenseType - licenseCache[cacheKey] = nil - - return true - else - debugPrint("^1Fehler beim Speichern der Lizenz: " .. tostring(result) .. "^7") - return false - end -end - --- Lizenz entziehen -local function revokeLicenseInDB(citizenid, licenseType) - debugPrint("=== revokeLicenseInDB START ===") - debugPrint("CitizenID: " .. tostring(citizenid)) - debugPrint("LicenseType: " .. tostring(licenseType)) - - local query = "UPDATE player_licenses SET is_active = 0 WHERE citizenid = ? AND license_type = ? AND is_active = 1" - local success, result = pcall(MySQL.query.await, query, {citizenid, licenseType}) - - if success and result then - debugPrint("Lizenz erfolgreich entzogen") - - -- Cache invalidieren - local cacheKey = citizenid .. "_" .. licenseType - licenseCache[cacheKey] = nil - - return true - else - debugPrint("^1Fehler beim Entziehen der Lizenz: " .. tostring(result) .. "^7") - return false - end -end - --- Erweiterte Fehlerbehandlung für Datenbankoperationen -local function safeDBOperation(operation, errorMessage) - local success, result = pcall(operation) - if not success then - debugPrint("^1DB-Fehler: " .. (errorMessage or "Unbekannt") .. "^7") - debugPrint("^1Details: " .. tostring(result) .. "^7") - return nil - end - return result -end - --- EVENT HANDLER: Lizenz anfordern -RegisterNetEvent('license-system:server:requestLicense', function(targetId) - local src = source - debugPrint("=== Event: requestLicense ===") - debugPrint("Source: " .. src .. ", Target: " .. targetId) - - if not hasPermission(src) then - debugPrint("Keine Berechtigung für Spieler: " .. src) - TriggerClientEvent('license-system:client:receiveLicense', src, nil) - return - end - - local targetPlayer = QBCore.Functions.GetPlayer(targetId) - if not targetPlayer then - debugPrint("Ziel-Spieler nicht gefunden: " .. targetId) - TriggerClientEvent('license-system:client:receiveLicense', src, nil) - return - end - - local citizenid = targetPlayer.PlayerData.citizenid - - -- Erste verfügbare Lizenz finden - local foundLicense = nil - for licenseType, _ in pairs(Config.LicenseTypes) do - local license = safeDBOperation(function() - return getLicenseFromDB(citizenid, licenseType) - end, "Fehler beim Abrufen der Lizenz") - - if license and license.is_active == 1 then - foundLicense = { - license = license, - config = Config.LicenseTypes[licenseType] - } - break - end - end - - if foundLicense then - debugPrint("Sende Lizenz an Client: " .. foundLicense.license.license_type) - TriggerClientEvent('license-system:client:receiveLicense', src, foundLicense) - else - debugPrint("Keine aktive Lizenz gefunden") - TriggerClientEvent('license-system:client:receiveLicense', src, nil) - end -end) - --- EVENT HANDLER: Eigene Lizenz anfordern -RegisterNetEvent('license-system:server:requestMyLicense', function(licenseType) - local src = source - debugPrint("=== Event: requestMyLicense ===") - debugPrint("Source: " .. src .. ", LicenseType: " .. licenseType) - - local Player = QBCore.Functions.GetPlayer(src) - if not Player then - debugPrint("Spieler nicht gefunden: " .. src) - TriggerClientEvent('license-system:client:receiveMyLicense', src, nil, licenseType) - return - end - - local citizenid = Player.PlayerData.citizenid - local license = safeDBOperation(function() - return getLicenseFromDB(citizenid, licenseType) - end, "Fehler beim Abrufen der eigenen Lizenz") - - if license and license.is_active == 1 then - local licenseData = { - license = license, - config = Config.LicenseTypes[licenseType] - } - debugPrint("Sende eigene Lizenz an Client: " .. licenseType) - TriggerClientEvent('license-system:client:receiveMyLicense', src, licenseData, licenseType) - else - debugPrint("Keine aktive eigene Lizenz gefunden: " .. licenseType) - TriggerClientEvent('license-system:client:receiveMyLicense', src, nil, licenseType) - end -end) - --- EVENT HANDLER: Alle Spieler-Lizenzen anfordern -RegisterNetEvent('license-system:server:requestPlayerLicenses', function(targetId) - local src = source - debugPrint("=== Event: requestPlayerLicenses ===") - debugPrint("Source: " .. src .. ", Target: " .. targetId) - - if not hasPermission(src) then - debugPrint("Keine Berechtigung für Spieler: " .. src) - TriggerClientEvent('license-system:client:receivePlayerLicenses', src, {}, targetId, "Unbekannt") - return - end - - local targetPlayer = QBCore.Functions.GetPlayer(targetId) - if not targetPlayer then - debugPrint("Ziel-Spieler nicht gefunden: " .. targetId) - TriggerClientEvent('license-system:client:receivePlayerLicenses', src, {}, targetId, "Unbekannt") - return - end - - local citizenid = targetPlayer.PlayerData.citizenid - local targetName = getPlayerName(targetId) - - local licenses = safeDBOperation(function() - return getAllPlayerLicenses(citizenid) - end, "Fehler beim Abrufen aller Spieler-Lizenzen") or {} - - debugPrint("Sende " .. #licenses .. " Lizenzen für " .. targetName) - TriggerClientEvent('license-system:client:receivePlayerLicenses', src, licenses, targetId, targetName) -end) - --- EVENT HANDLER: Lizenz ausstellen -RegisterNetEvent('license-system:server:issueLicense', function(targetId, licenseType, classes) - local src = source - debugPrint("=== Event: issueLicense ===") - debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType) - - if not hasPermission(src) then - debugPrint("Keine Berechtigung für Spieler: " .. src) - TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type) - return - end - - local targetPlayer = QBCore.Functions.GetPlayer(targetId) - if not targetPlayer then - debugPrint("Ziel-Spieler nicht gefunden: " .. targetId) - TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error') - return - end - - local issuerPlayer = QBCore.Functions.GetPlayer(src) - if not issuerPlayer then - debugPrint("Aussteller nicht gefunden: " .. src) - return - end - - local targetCitizenId = targetPlayer.PlayerData.citizenid - local issuerCitizenId = issuerPlayer.PlayerData.citizenid - - -- Prüfen ob Lizenz bereits existiert - local existingLicense = safeDBOperation(function() - return getLicenseFromDB(targetCitizenId, licenseType) - end, "Fehler beim Prüfen bestehender Lizenz") - - if existingLicense and existingLicense.is_active == 1 then - debugPrint("Lizenz bereits vorhanden und aktiv") - TriggerClientEvent('QBCore:Notify', src, 'Spieler hat bereits eine aktive ' .. (Config.LicenseTypes[licenseType].label or licenseType) .. '!', 'error') - return - end - - -- Kosten prüfen - local config = Config.LicenseTypes[licenseType] - if config.price and config.price > 0 then - if issuerPlayer.PlayerData.money.cash < config.price then - debugPrint("Nicht genug Geld für Lizenz-Ausstellung") - TriggerClientEvent('QBCore:Notify', src, 'Nicht genug Bargeld! Benötigt: $' .. config.price, 'error') - return - end - - -- Geld abziehen - issuerPlayer.Functions.RemoveMoney('cash', config.price, 'license-issued') - TriggerClientEvent('QBCore:Notify', src, 'Lizenz-Gebühr bezahlt: $' .. config.price, 'success') - end - - -- Lizenz in Datenbank speichern - local success = safeDBOperation(function() - return saveLicenseToDB(targetCitizenId, licenseType, issuerCitizenId, classes) - end, "Fehler beim Speichern der Lizenz") - - if success then - local targetName = getPlayerName(targetId) - local issuerName = getPlayerName(src) - - debugPrint("Lizenz erfolgreich ausgestellt") - - -- Benachrichtigungen - TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich ausgestellt für ' .. targetName, 'success') - TriggerClientEvent('QBCore:Notify', targetId, 'Du hast eine neue Lizenz erhalten: ' .. config.label, 'success') - - -- Events senden - TriggerClientEvent('license-system:client:licenseIssued', src, targetId, licenseType) - TriggerClientEvent('license-system:client:refreshMenu', src) - - -- Log - debugPrint("Lizenz " .. licenseType .. " ausgestellt von " .. issuerName .. " für " .. targetName) - else - debugPrint("^1Fehler beim Ausstellen der Lizenz^7") - TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Ausstellen der Lizenz!', 'error') - end -end) - --- EVENT HANDLER: Lizenz entziehen -RegisterNetEvent('license-system:server:revokeLicense', function(targetId, licenseType) - local src = source - debugPrint("=== Event: revokeLicense ===") - debugPrint("Source: " .. src .. ", Target: " .. targetId .. ", Type: " .. licenseType) - - if not hasPermission(src) then - debugPrint("Keine Berechtigung für Spieler: " .. src) - TriggerClientEvent('QBCore:Notify', src, Config.Notifications.no_permission.message, Config.Notifications.no_permission.type) - return - end - - local targetPlayer = QBCore.Functions.GetPlayer(targetId) - if not targetPlayer then - debugPrint("Ziel-Spieler nicht gefunden: " .. targetId) - TriggerClientEvent('QBCore:Notify', src, 'Spieler nicht gefunden!', 'error') - return - end - - local targetCitizenId = targetPlayer.PlayerData.citizenid - - -- Lizenz entziehen - local success = safeDBOperation(function() - return revokeLicenseInDB(targetCitizenId, licenseType) - end, "Fehler beim Entziehen der Lizenz") - - if success then - local targetName = getPlayerName(targetId) - local issuerName = getPlayerName(src) - local config = Config.LicenseTypes[licenseType] - - debugPrint("Lizenz erfolgreich entzogen") - - -- Benachrichtigungen - TriggerClientEvent('QBCore:Notify', src, 'Lizenz erfolgreich entzogen von ' .. targetName, 'success') - TriggerClientEvent('QBCore:Notify', targetId, 'Deine Lizenz wurde entzogen: ' .. (config.label or licenseType), 'error') - - -- Events senden - TriggerClientEvent('license-system:client:licenseRevoked', src, targetId, licenseType) - TriggerClientEvent('license-system:client:refreshMenu', src) - - -- Log - debugPrint("Lizenz " .. licenseType .. " entzogen von " .. issuerName .. " für " .. targetName) - else - debugPrint("^1Fehler beim Entziehen der Lizenz^7") - TriggerClientEvent('QBCore:Notify', src, 'Fehler beim Entziehen der Lizenz!', 'error') - end -end) - --- EVENT HANDLER: Foto speichern -RegisterNetEvent('license-system:server:savePhoto', function(citizenid, photoData) - local src = source - debugPrint("=== Event: savePhoto ===") - debugPrint("Source: " .. src .. ", CitizenID: " .. citizenid) - - -- Hier könnte das Foto in der Datenbank oder im Dateisystem gespeichert werden - debugPrint("Foto-Daten erhalten für: " .. citizenid) - - TriggerClientEvent('QBCore:Notify', src, 'Foto gespeichert!', 'success') -end) - --- EXPORT FUNKTIONEN -exports('hasLicense', function(citizenid, licenseType) - if not citizenid or not licenseType then return false end - - local license = safeDBOperation(function() - return getLicenseFromDB(citizenid, licenseType) - end, "Fehler beim Prüfen der Lizenz") - - return license and license.is_active == 1 -end) - -exports('issueLicense', function(citizenid, licenseType, issuedBy, classes) - if not citizenid or not licenseType then return false end - - issuedBy = issuedBy or 'system' - return safeDBOperation(function() - return saveLicenseToDB(citizenid, licenseType, issuedBy, classes) - end, "Fehler beim Ausstellen der Lizenz") or false -end) - -exports('revokeLicense', function(citizenid, licenseType) - if not citizenid or not licenseType then return false end - - return safeDBOperation(function() - return revokeLicenseInDB(citizenid, licenseType) - end, "Fehler beim Entziehen der Lizenz") or false -end) - -exports('getPlayerLicenses', function(citizenid) - if not citizenid then return {} end - - return safeDBOperation(function() - return getAllPlayerLicenses(citizenid) - end, "Fehler beim Abrufen der Spieler-Lizenzen") or {} -end) - -exports('getPlayerLicense', function(citizenid, licenseType) - if not citizenid or not licenseType then return nil end - - return safeDBOperation(function() - return getLicenseFromDB(citizenid, licenseType) - end, "Fehler beim Abrufen der Lizenz") -end) - --- INITIALISIERUNG -CreateThread(function() - debugPrint("License-System Server gestartet (MariaDB-kompatibel)") - - -- Warten bis QBCore geladen ist - while not QBCore do - Wait(100) - end - - debugPrint("QBCore erfolgreich geladen") - - -- Datenbank-Verbindung testen - local testResult = safeDBOperation(function() - return MySQL.query.await("SELECT 1 as test") - end, "Datenbank-Verbindungstest") - - if testResult then - debugPrint("Datenbank-Verbindung erfolgreich") - else - debugPrint("^1Datenbank-Verbindung fehlgeschlagen^7") - end - - debugPrint("License-System Server vollständig initialisiert") -end) - --- CLEANUP -AddEventHandler('onResourceStop', function(resourceName) - if GetCurrentResourceName() == resourceName then - debugPrint("License-System Server gestoppt") - licenseCache = {} - end -end) - --- DEBUG COMMANDS -RegisterCommand('licensestats', function(source, args, rawCommand) - if source == 0 then -- Console only - local cacheCount = 0 - for _ in pairs(licenseCache) do - cacheCount = cacheCount + 1 - end - - print("=== LICENSE SYSTEM STATS ===") - print("Cache Entries: " .. cacheCount) - print("Config License Types: " .. (Config.LicenseTypes and #Config.LicenseTypes or 0)) - print("============================") - end -end, true) - -RegisterCommand('licenseclearcache', function(source, args, rawCommand) - if source == 0 then -- Console only - local oldCount = 0 - for _ in pairs(licenseCache) do - oldCount = oldCount + 1 - end - - licenseCache = {} - print("License-Cache geleert. Entfernte Einträge: " .. oldCount) - end -end, true) - --- Erweiterte Logging-Funktion -local function logLicenseAction(action, src, targetId, licenseType, details) - local timestamp = os.date("%Y-%m-%d %H:%M:%S") - local srcName = getPlayerName(src) - local targetName = targetId and getPlayerName(targetId) or "N/A" - - local logMessage = string.format( - "[%s] %s | Quelle: %s (%s) | Ziel: %s (%s) | Typ: %s | Details: %s", - timestamp, action, srcName, src, targetName, targetId or "N/A", licenseType or "N/A", details or "N/A" - ) - - debugPrint("LOG: " .. logMessage) - - -- Hier könnte das Log in eine Datei oder Datenbank geschrieben werden -end - -debugPrint("License-System Server vollständig geladen (MariaDB-kompatibel)")