- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark, Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI) - aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource, an aether-core gekoppelt (Rechte über aether_users.admin_group), mit WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU) - Prototypen admin/ vervollständigt - SQL-Schema aether_admin.sql - Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
115 lines
4.4 KiB
Lua
115 lines
4.4 KiB
Lua
-- =====================================================================
|
|
-- aether-admin / Modul: Reports / Support
|
|
--
|
|
-- Spieler melden über `/report` — das Team übernimmt, antwortet und
|
|
-- schließt hier. Antworten gehen als Hinweis zurück an den Melder,
|
|
-- falls er online ist.
|
|
-- =====================================================================
|
|
|
|
Admin = Admin or {}
|
|
|
|
--- Legt einen Report an (aus dem Spiel heraus aufgerufen)
|
|
--- @param quelle number Server-ID des Melders
|
|
--- @param betreff string
|
|
--- @param nachricht string|nil
|
|
--- @return number Report-ID
|
|
function Admin.ErstelleReport(quelle, betreff, nachricht)
|
|
local id = MySQL.insert.await([[
|
|
INSERT INTO aether_admin_reports (melder_license, melder_name, betreff, nachricht)
|
|
VALUES (?, ?, ?, ?)
|
|
]], { Admin.Auth.Lizenz(quelle), GetPlayerName(quelle),
|
|
tostring(betreff):sub(1, 160), nachricht and tostring(nachricht):sub(1, 2000) or nil })
|
|
|
|
-- Team im Dienst benachrichtigen
|
|
for _, sid in ipairs(GetPlayers() or {}) do
|
|
local s = tonumber(sid)
|
|
if Admin.Auth.Hat(s, 'support') then
|
|
TriggerClientEvent('aether-admin:client:hinweis', s,
|
|
'🎫 Neuer Report von ' .. GetPlayerName(quelle))
|
|
end
|
|
end
|
|
return id
|
|
end
|
|
|
|
Admin.Kern.Registriere('reports', 'laden', function(handelnder, daten)
|
|
local liste = MySQL.query.await([[
|
|
SELECT id, melder_name, betreff, nachricht, prioritaet, status, bearbeiter,
|
|
erstellt_am, aktualisiert_am
|
|
FROM aether_admin_reports
|
|
ORDER BY (status = 'geschlossen'), prioritaet, id DESC
|
|
LIMIT 100
|
|
]]) or {}
|
|
|
|
-- Für ein offenes Detail: Nachrichtenverlauf mitliefern
|
|
local detailId = tonumber(daten.detail)
|
|
local verlauf = {}
|
|
if detailId then
|
|
verlauf = MySQL.query.await([[
|
|
SELECT autor, ist_admin, nachricht, erstellt_am
|
|
FROM aether_admin_report_msgs WHERE report = ? ORDER BY id
|
|
]], { detailId }) or {}
|
|
end
|
|
|
|
return { reports = liste, verlauf = verlauf, detail = detailId,
|
|
rechte = Admin.Kern.RechteAbbild(handelnder.quelle) }
|
|
end)
|
|
|
|
Admin.Kern.Registriere('reports', 'claim', function(handelnder, daten)
|
|
local id = tonumber(daten.id)
|
|
if not id then return nil, 'Ungültige Kennung.' end
|
|
MySQL.update.await([[
|
|
UPDATE aether_admin_reports SET status = 'bearbeitung', bearbeiter = ?
|
|
WHERE id = ? AND status <> 'geschlossen'
|
|
]], { handelnder.name, id })
|
|
Admin.Log.Schreibe(handelnder, 'Report übernommen', 'Report #' .. id)
|
|
return { ok = true }
|
|
end)
|
|
|
|
Admin.Kern.Registriere('reports', 'status', function(handelnder, daten)
|
|
local id = tonumber(daten.id)
|
|
local status = tostring(daten.status or '')
|
|
local erlaubt = { offen = true, bearbeitung = true, geschlossen = true }
|
|
if not id or not erlaubt[status] then return nil, 'Ungültige Angabe.' end
|
|
|
|
MySQL.update.await('UPDATE aether_admin_reports SET status = ? WHERE id = ?', { status, id })
|
|
Admin.Log.Schreibe(handelnder, 'Report ' .. status, 'Report #' .. id)
|
|
return { ok = true }
|
|
end)
|
|
|
|
Admin.Kern.Registriere('reports', 'antwort', function(handelnder, daten)
|
|
local id = tonumber(daten.id)
|
|
local text = tostring(daten.text or ''):sub(1, 2000)
|
|
if not id then return nil, 'Ungültige Kennung.' end
|
|
if #text < 2 then return nil, 'Bitte eine Antwort schreiben.' end
|
|
|
|
local report = MySQL.query.await(
|
|
'SELECT melder_license, status FROM aether_admin_reports WHERE id = ?', { id })
|
|
if not report or not report[1] then return nil, 'Report nicht gefunden.' end
|
|
|
|
MySQL.insert.await([[
|
|
INSERT INTO aether_admin_report_msgs (report, autor, ist_admin, nachricht)
|
|
VALUES (?, ?, 1, ?)
|
|
]], { id, handelnder.name, text })
|
|
|
|
if report[1].status == 'offen' then
|
|
MySQL.update.await(
|
|
"UPDATE aether_admin_reports SET status = 'bearbeitung', bearbeiter = ? WHERE id = ?",
|
|
{ handelnder.name, id })
|
|
end
|
|
|
|
-- Melder benachrichtigen, falls online
|
|
local melderQuelle
|
|
for _, sid in ipairs(GetPlayers() or {}) do
|
|
if Admin.Auth.Lizenz(tonumber(sid)) == report[1].melder_license then
|
|
melderQuelle = tonumber(sid); break
|
|
end
|
|
end
|
|
if melderQuelle then
|
|
TriggerClientEvent('aether-admin:client:hinweis', melderQuelle,
|
|
'🎫 Antwort vom Team: ' .. text:sub(1, 120))
|
|
end
|
|
|
|
Admin.Log.Schreibe(handelnder, 'Report beantwortet', 'Report #' .. id)
|
|
return { ok = true }
|
|
end)
|