2026-04-14 17:41:39 +02:00

638 lines
21 KiB
JavaScript

const path = require("path")
const express = require("express")
const app = express()
const mysql = require('mysql')
const sha1 = require("sha1")
const crypto = require("crypto")
const ose = require("node-os-utils")
const os = require("os")
const rateLimit = require('express-rate-limit')
const fetch = require('node-fetch')
const { request } = require('undici')
const moment = require('moment')
const multer = require('multer')
const rootDir = GetResourcePath(GetCurrentResourceName());
const { parse: LuaTableParse } = require('lua-json')
const fs = require('fs/promises')
const SQLKeys = {}
let VersionData = {}
app.listen(GetConvarInt("mAdminPort", 40130), '0.0.0.0')
let config = {}
let CharactersCache = []
let DiscordProfilesCache = []
let onlinePlayers = []
let FrameworkObject = null
const ConnectionString = GetConvar("mysql_connection_string", "")
// function CreateConnection() {
// let sqlServer = undefined
// if (ConnectionString.includes(";")) {
// const credentialsp = ConnectionString.split(";")
// const credentials = {}
// credentialsp.map(c => {
// const keyvalue = c.split("=")
// if (keyvalue[1] === undefined) return
// credentials[keyvalue[0]] = keyvalue[1]
// })
// sqlServer = mysql.createConnection(credentials)
// } else {
// sqlServer = mysql.createConnection(ConnectionString)
// }
// return sqlServer
// }
function CreateConnection() {
let sqlServer = undefined
if (ConnectionString.includes(";")) {
const credentialsp = ConnectionString.split(";")
const credentials = {}
credentialsp.forEach(c => {
const keyvalue = c.split("=")
if (keyvalue[1] === undefined) return
credentials[keyvalue[0].trim()] = keyvalue[1].trim()
})
sqlServer = mysql.createConnection({
host: credentials["server"],
user: credentials["userid"],
password: credentials["password"],
database: credentials["database"]
})
} else {
sqlServer = mysql.createConnection(ConnectionString)
}
return sqlServer
}
var sqlServer = null
let PeakPlayerCount = 0
const VehicleImageStorage = multer.diskStorage({
destination: path.join(rootDir, "Web/Panel/Vehicles/img/VehicleImages"),
filename: function(req, file, cb) {
const originalname = file.originalname;
const extname = originalname.slice(
originalname.lastIndexOf("."),
originalname.length
);
const fileName = `${Date.now()}${extname}`;
cb(null, fileName);
}
})
const JobLogoStorage = multer.diskStorage({
destination: path.join(rootDir, "Web/Panel/Jobs/img/JobLogos"),
filename: function(req, file, cb) {
const originalname = file.originalname;
const extname = originalname.slice(
originalname.lastIndexOf("."),
originalname.length
);
const fileName = `${Date.now()}${extname}`;
cb(null, fileName);
}
})
const FactionLogoStorage = multer.diskStorage({
destination: path.join(rootDir, "Web/Panel/Factions/img/FactionLogos"),
filename: function(req, file, cb) {
const originalname = file.originalname;
const extname = originalname.slice(
originalname.lastIndexOf("."),
originalname.length
);
const fileName = `${Date.now()}${extname}`;
cb(null, fileName);
}
})
const UploadVehicleImage = multer({ storage: VehicleImageStorage })
const UploadJobLogo = multer({ storage: JobLogoStorage })
const UploadFactionLogo = multer({ storage: FactionLogoStorage })
const usersKeys = [
// {
// userId: 1,
// key: "LVOREX",
// ip: "123",
// userId: 0,
// userName: "",
// darkMode: false,
// rank: ""
// }
]
function randomString(length) {
const strings = []
for (let i = 97; i <= 122; i++) {
strings.push(String.fromCharCode(i).toUpperCase());
}
let result = []
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * strings.length)
result.push(strings[randomIndex])
}
return result.join("")
}
function randomInteger(length) {
const numbers = [0,1,2,3,4,5,6,7,8,9]
let result = []
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * numbers.length)
result.push(numbers[randomIndex])
}
return result.join("")
}
async function getPlayerDiscordProfile(discordId) {
if (!discordId) return undefined
if (discordId.includes(":")) {
discordId = discordId.split(":")[1]
}
let avatarRequest = await fetch(`https://discord.com/api/v9/users/${discordId}`, {
headers: {
"authorization": `Bot ${config.token}`
}
})
avatarRequest = await avatarRequest.json()
return `https://cdn.discordapp.com/avatars/${discordId}/${avatarRequest.avatar}.png`
}
async function GetPatches() {
let PatchRequest = await fetch(`https://raw.githubusercontent.com/lvorex/madmin-patches/main/patches.json`)
PatchRequest = await PatchRequest.json()
return PatchRequest.Patches
}
async function handleSQLDisconnect() {
const newConnection = CreateConnection()
sqlServer = newConnection
newConnection.connect((err) => {
if (err) {
if (config.Debug) {
console.log(err)
}
setTimeout(handleSQLDisconnect, 2000)
return
} else {
console.log("^2Database connected.^7")
}
})
newConnection.on('error', (err) => {
if (config.Debug) {
console.log(err)
console.log("^1Database error appeared. Trying to connected database again.^7")
}
handleSQLDisconnect()
})
}
handleSQLDisconnect()
async function checkPlayerIsOnline(uid, res, canBeOffline) {
const onlinePlayers = await getOnlinePlayers()
const uidType = config.Framework.includes("qb") ? "citizenid" : "identifier"
const NeededPlayer = onlinePlayers.find(player => player[uidType] === uid)
if (!NeededPlayer) {
if (!canBeOffline) {
res.json({ code: 404, message: "Player is offline." })
return false
} else {
return undefined
}
}
return NeededPlayer
}
async function getPlayerByLicense(license) {
const onlinePlayers = await getOnlinePlayers()
const NeededPlayer = onlinePlayers.find(player => player.license.split(":")[1] === license.split(":")[1])
if (!NeededPlayer) return false
return NeededPlayer
}
async function query(sql) {
return new Promise((resolve) => {
if (!sqlServer) {
if (config.Debug) {
console.log("^1sqlServer not initialized yet.^7")
}
return resolve(false)
}
sqlServer.query(sql, function(err, res, fields) {
if (err) {
if (config.Debug) {
console.log(err)
}
return resolve(false)
} else {
return resolve(res)
}
})
})
}
async function getOnlinePlayers() {
return new Promise((resolve, reject) => {
let allPlayers
if (config.Framework.includes("qb")) {
allPlayers = FrameworkObject.Functions.GetPlayers()
} else {
allPlayers = exports["mAdmin"].GetAllPlayers()
}
let toReturnPlayers = []
for (let i = 0; i < allPlayers.length; i++) {
if (allPlayers[i]) {
if (config.Framework.includes("qb")) {
let qPlayer = FrameworkObject.Functions.GetPlayer(Number(allPlayers[i]))
qPlayer.PlayerData.playerId = Number(allPlayers[i])
toReturnPlayers[i] = qPlayer.PlayerData
} else {
let xPlayer = FrameworkObject.GetPlayerFromId(Number(allPlayers[i]))
if (xPlayer && xPlayer.group && xPlayer.identifier) {
xPlayer.playerId = Number(allPlayers[i])
toReturnPlayers[i] = xPlayer
} else continue
}
}
}
return resolve(toReturnPlayers)
})
}
async function controlKey(req, key) {
let keyFound = false
let userKeyIndex = 0
let userKey = usersKeys.find(w => sha1(w.key) === key)
if (!userKey) {
keyFound = false
return {
keyFound,
userKeyIndex,
userKey
}
}
userKeyIndex = usersKeys.findIndex(w => sha1(w.key) === key)
if (userKey.ip === req.socket.remoteAddress) {
keyFound = true
} else if (!req.socket.remoteAddress.includes("127.0.0.1")) {
keyFound = false
usersKeys.splice(userKeyIndex, 1)
} else {
keyFound = true
}
return {
keyFound,
userKeyIndex,
userKey
}
}
async function checkPermission(rank, page, index) {
let result = await query(`
select * from \`madmin_permissions\`
where \`name\` = '${rank}'
`)
if (result === false) return false
if (result.length === 0) {
console.log(`^1${rank} Not found.^7`)
return false
}
const Pattern = JSON.parse(result[0].pattern)
if (Pattern.FullPermission === true) return true;
return Pattern[page][index]
}
async function getAllPermissions(rank) {
let result = await query(`
select * from \`madmin_permissions\`
where \`name\` = '${rank}'
`)
if (result === false) return false
if (result.length === 0) {
console.log(`^1${rank} Not found.^7`)
return false
}
const Pattern = JSON.parse(result[0].pattern)
return Pattern
}
function cooldownWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => { return resolve(true) }, time)
})
}
function getPlayerIdentifierByType(src, type) {
let neededIdentifier = undefined
for (let i = 0; i < GetNumPlayerIdentifiers(src); i++) {
if (neededIdentifier) continue
const identifier = GetPlayerIdentifier(src, i);
if (identifier.toLowerCase().includes(type.toLowerCase())) {
neededIdentifier = identifier
continue
}
}
return neededIdentifier
}
RegisterNetEvent("madmin:setversion")
on("madmin:setversion", async (version, latestVersion) => {
VersionData = {
CurrentVersion: version,
Outdated: Number(version) < latestVersion ? latestVersion.toFixed(1) : false
}
})
app.use("/setConfig.lvorex", express.json())
app.post("/setConfig.lvorex", async (req, res) => {
if (req.socket.remoteAddress !== res.socket.remoteAddress) {
res.json({ code: 401, message: "Not authorized." })
return
}
const postBody = req.body
config = postBody
FrameworkObject = await GetFrameworkObject(config)
console.log("^3Loading script...^7")
await SetCharacters()
setInterval(CheckCharacterUpdate, 1000*60*30)
emit("madmin:server:startliveconsole")
let result = await query(`
select * from \`madmin_permissions\`
where \`name\` = 'Full Permission'
`)
if (result === false) return
if (result.length === 0) {
result = await query(`
insert into \`madmin_permissions\` (
\`name\`,
\`pattern\`,
\`createdBy\`
) values (
'Full Permission',
'{"FullPermission": true,"Dashboard":[true,true,true,true,true,true],"Players":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],"Accounts":[true,true,true],"LiveMap":[true,true],"Vehicles":[true,true,true,true,true,true],"Items":[true,true],"Jobs":[true,true,true,true,true],"Factions":[true,true,true,true,true],"Logs":[true],"LiveConsole":[true],"Resources":[true,true],"Admins":[true,true,true],"Management":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true]}',
'System'
)
`)
if (result === false) return
}
result = await query(`
select * from \`madmin_permissions\`
where \`name\` = 'Not Authorized'
`)
if (result === false) return
if (result.length === 0) {
result = await query(`
insert into \`madmin_permissions\` (
\`name\`,
\`createdBy\`
) values (
'Not Authorized',
'System'
)
`)
if (result === false) return
}
result = await query(`
select * from \`madmin_accounts\`
where \`username\` = '${config.PanelAdminUsername}'
`)
if (result === false) return console.log("SQL Error Appeared.")
if (result.length === 0) {
result = await query(`
insert into \`madmin_accounts\` (
\`username\`,
\`password\`,
\`accountType\`,
\`rank\`,
\`ip\`
) values (
'${config.PanelAdminUsername.replaceAll("'", "\\'")}',
'${sha1(config.PanelAdminPassword)}',
'Roleplay',
'Full Permission',
'::1'
)
`)
if (result === false) return console.log("SQL Error Appeared.")
}
console.log("^2Script loaded and ready to use.^7")
})
const SetSpecificCharacter = async (uid) => {
let result = await query(`
select * from \`${config.Framework.includes("qb") ? "players" : "users"}\`
where \`${config.Framework.includes("qb") ? "citizenid" : "identifier"}\` = '${uid}'
`)
if (result === false) return false
if (result.length === 0) return false
result = result [0]
result.playerId = "-"
result.playerStatus = "Offline"
result.admin = 0
result.cfgAdmin = 0
if (config.Framework.includes("esx")) {
result.name = result.firstname+" "+result.lastname
if (config.Permissions.some(p => p === result.group)) {
result.admin = result.group
result.cfgAdmin = 1
}
}
if (config.Framework.includes("qb")) {
result.assignjob = AssignJob(JSON.parse(result.job).name)
} else {
result.assignjob = AssignJob(result.job)
}
let dcResult = await query(`select * from \`madmin_characters\` where \`license\` like '%${result[config.Framework.includes("qb") ? "license" : "identifier"].split(":")[1]}'`)
if (dcResult === false) return false
if (dcResult.length > 0) {
let cins = "ms"
let sure = Number(dcResult[0].online_time)
if (sure > 1000 && sure < 1000 * 60) {
cins = "s"
sure = sure / 1000
} else if (sure > 1000 * 60 && sure < 1000 * 60 * 60) {
cins = "mn"
sure = sure / (1000 * 60)
} else if (sure > 1000 * 60 * 60) {
cins = "hr"
sure = sure / (1000 * 60 * 60)
}
sure = parseInt(String(sure))
if (
IsPrincipalAceAllowed(`identifier.${config.AceIdentifier}:${dcResult[0][config.AceIdentifier].split(":")[1]}`, `command`)
) {
if (result.admin === 0) {
result.admin = 1
}
result.cfgAdmin = 1
}
result.onlineTime = `${sure}${cins}`
result.discordId = dcResult[0].discord
if (result.discordId !== "undefined") {
result.playerAvatar = dcResult[0].discord_avatar
} else {
result.playerAvatar = "img/DefaultIcon.png"
}
}
CharactersCache.push(result)
return result
}
const SetCharacters = async () => {
let result = await query(`SELECT * FROM \`${config.Framework.includes("qb") ? "players" : "users"}\``)
if (result === false) return false
const uidType = config.Framework.includes("qb") ? "citizenid" : "identifier"
const onlinePlayers = await getOnlinePlayers()
for await (const player of result) {
let oPlayer = onlinePlayers.find(oPlayer => oPlayer[uidType] === player[uidType])
player.playerId = oPlayer ? oPlayer.playerId : "-"
player.playerStatus = oPlayer ? "Online" : "Offline"
player.admin = 0
player.cfgAdmin = 0
if (config.Framework.includes("esx")) {
player.name = player.firstname+" "+player.lastname
if (config.Permissions.some(p => p === player.group)) {
player.admin = player.group
player.cfgAdmin = 1
}
}
if (player.playerId !== "-") {
if (config.Framework.includes("qb")) {
const Permissions = FrameworkObject.Functions.GetPermission(player.playerId)
// const qPlayer = FrameworkObject.Functions.GetPlayer(player.playerId)
player.job = `{"name": "${oPlayer.job.name}", "label": "${oPlayer.job.label}", "grade": {"name": "${oPlayer.job.grade.name}"}}`
if (Permissions.length !== 0) {
const LastPerm = Object.entries(Permissions).map(w => { return w[0] }).join(", ")
player.admin = LastPerm
}
} else if (config.Framework.includes("esx")) {
const xPlayer = FrameworkObject.GetPlayerFromId(player.playerId)
player.job = xPlayer.job.name
player.job_grade = xPlayer.job.grade
const group = xPlayer.getGroup()
if (group !== null || !group) player.admin = group
}
}
if (config.Framework.includes("qb")) {
player.assignjob = AssignJob(JSON.parse(player.job).name)
} else {
player.assignjob = AssignJob(player.job)
}
if (oPlayer) {
player.discordId = getPlayerIdentifierByType(oPlayer.playerId, "discord")
const DiscordProfileCache = DiscordProfilesCache.find(p => p.uid === player[uidType])
if (DiscordProfileCache) {
player.playerAvatar = DiscordProfileCache.avatar
} else {
player.playerAvatar = "img/DefaultIcon.png"
}
}
// let dcResult = await query(`SELECT * FROM \`madmin_characters\` WHERE \`identifier\` = '${player[uidType]}'`)
let dcResult = await query(`select * from \`madmin_characters\` where \`license\` like '%${player[config.Framework.includes("qb") ? "license" : "identifier"].split(":")[1]}'`)
if (dcResult === false) return false
if (dcResult.length > 0) {
let cins = "ms"
let sure = Number(dcResult[0].online_time)
if (sure > 1000 && sure < 1000 * 60) {
cins = "s"
sure = sure / 1000
} else if (sure > 1000 * 60 && sure < 1000 * 60 * 60) {
cins = "mn"
sure = sure / (1000 * 60)
} else if (sure > 1000 * 60 * 60) {
cins = "hr"
sure = sure / (1000 * 60 * 60)
}
player.playerName = dcResult[0].name
sure = parseInt(String(sure))
if (
IsPrincipalAceAllowed(`identifier.${config.AceIdentifier}:${dcResult[0][config.AceIdentifier].split(":")[1]}`, `command`)
) {
if (player.admin === 0) {
player.admin = 1
}
player.cfgAdmin = 1
}
player.onlineTime = `${sure}${cins}`
player.discordId = dcResult[0].discord
if (player.discordId !== "undefined") {
player.playerAvatar = dcResult[0].discord_avatar
} else {
player.playerAvatar = "img/DefaultIcon.png"
}
}
dcResult = await query(`SELECT * FROM \`madmin_characters\` WHERE \`identifier\` = '${player[uidType]}'`)
if (dcResult === false) return false
if (dcResult.length > 0) {
let cins = "ms"
let sure = Number(dcResult[0].online_time)
if (sure > 1000 && sure < 1000 * 60) {
cins = "s"
sure = sure / 1000
} else if (sure > 1000 * 60 && sure < 1000 * 60 * 60) {
cins = "mn"
sure = sure / (1000 * 60)
} else if (sure > 1000 * 60 * 60) {
cins = "hr"
sure = sure / (1000 * 60 * 60)
}
sure = parseInt(String(sure))
player.onlineTime = `${sure}${cins}`
}
}
CharactersCache = result
return
}
const CheckCharacterUpdate = async () => {
let result = await query(`select * from \`${config.Framework.includes("qb") ? "players" : "users"}\``)
if (result.length === CharactersCache.length || result === false) return
await SetCharacters()
return
}