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 }