638 lines
21 KiB
JavaScript
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
|
|
} |