add runcode resource

This commit is contained in:
moscovium
2017-09-05 11:58:39 +02:00
parent 2415bad533
commit cbee7d1960
7 changed files with 280 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
client_script 'runcode_cl.lua'
server_script 'runcode_sv.lua'
server_script 'runcode_web.lua'
client_script 'runcode_shared.lua'
server_script 'runcode_shared.lua'
resource_manifest_version '44febabe-d386-4d18-afbe-5e627f4af937'

View File

@@ -0,0 +1,3 @@
RegisterCommand('runcode', function(source, args, rawCommand)
RunCode('return ' .. rawCommand:sub(8))
end, true)

View File

@@ -0,0 +1,15 @@
RegisterNetEvent('runcode:gotSnippet')
AddEventHandler('runcode:gotSnippet', function(id, code)
local res, err = RunCode(code)
if not err then
if type(res) == 'vector3' then
res = json.encode({ table.unpack(res) })
elseif type(res) == 'table' then
res = json.encode(res)
end
end
TriggerServerEvent('runcode:gotResult', id, res, err)
end)

View File

@@ -0,0 +1,17 @@
function RunCode(code)
local code, err = load(code, '@runcode')
if err then
print(err)
return nil, err
end
local status, result = pcall(code)
print(result)
if status then
return result
else
return nil, result
end
end

View File

@@ -0,0 +1,7 @@
RegisterCommand('run', function(source, args, rawCommand)
local res, err = RunCode('return ' .. rawCommand:sub(4))
end, true)
RegisterCommand('crun', function(source, args, rawCommand)
TriggerClientEvent('runcode:gotSnippet', source, -1, 'return ' .. rawCommand:sub(5))
end, true)

View File

@@ -0,0 +1,133 @@
local cachedFiles = {}
local function sendFile(res, fileName)
if cachedFiles[fileName] then
res.send(cachedFiles[fileName])
return
end
local fileData = LoadResourceFile(GetCurrentResourceName(), 'web/' .. fileName)
if not fileData then
res.writeHead(404)
res.send('Not found.')
return
end
cachedFiles[fileName] = fileData
res.send(fileData)
end
local codeId = 1
local codes = {}
local function handlePost(req, res)
req.setDataHandler(function(body)
local data = json.decode(body)
if not data or not data.password or not data.code then
res.send(json.encode({ error = 'Bad request.'}))
return
end
if GetConvar('rcon_password', '') == '' then
res.send(json.encode({ error = 'The server has an empty rcon_password.'}))
return
end
if data.password ~= GetConvar('rcon_password', '') then
res.send(json.encode({ error = 'Bad password.'}))
return
end
if not data.client or data.client == '' then
CreateThread(function()
local result, err = RunCode(data.code)
res.send(json.encode({
result = result,
error = err
}))
end)
else
codes[codeId] = {
timeout = GetGameTimer() + 1000,
res = res
}
TriggerClientEvent('runcode:gotSnippet', tonumber(data.client), codeId, data.code)
codeId = codeId + 1
end
end)
end
local function returnCode(id, res, err)
if not codes[id] then
return
end
local code = codes[id]
codes[id] = nil
local gotFrom
if source then
gotFrom = GetPlayerName(source) .. ' [' .. tostring(source) .. ']'
end
code.res.send(json.encode({
result = res,
error = err,
from = gotFrom
}))
end
CreateThread(function()
while true do
Wait(100)
for k, v in ipairs(codes) do
if GetGameTimer() > v.timeout then
source = nil
returnCode(k, '', 'Timed out waiting on the target client.')
end
end
end
end)
RegisterNetEvent('runcode:gotResult')
AddEventHandler('runcode:gotResult', returnCode)
SetHttpHandler(function(req, res)
local path = req.path
if req.method == 'POST' then
return handlePost(req, res)
end
-- client shortcuts
if req.path == '/clients' then
local clientList = {}
for _, id in ipairs(GetPlayers()) do
table.insert(clientList, { GetPlayerName(id), id })
end
res.send(json.encode({
clients = clientList
}))
return
end
-- should this be the index?
if req.path == '/' then
path = 'index.html'
end
-- remove any '..' from the path
path = path:gsub("%.%.", "")
return sendFile(res, path)
end)

View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html>
<head>
<title>fivem runcode</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<style type="text/css">
body {
font-family: "Segoe UI", sans-serif;
}
</style>
</head>
<body>
<div id="code-container" style="width:800px;height:600px;border:1px solid grey"></div><br>
Password: <input type="password" id="password"> (use your rcon password)<br>
Client ID: <input type="text" id="client"> (leave blank for server)<br>
<div id="clients"></div>
<button id="run">Run</button>
<div id="result">
</div>
<!--
to use a local deployment, uncomment; do note currently the server isn't optimized to serve >1MB files
<script src="monaco-editor/vs/loader.js"></script>
-->
<script src="https://unpkg.com/monaco-editor@0.10.0/min/vs/loader.js"></script>
<script>
function fetchClients() {
fetch('/runcode/clients').then(res => res.json()).then(res => {
const el = document.querySelector('#clients');
el.innerHTML = '';
const clients = res.clients;
clients.push(['All', -1]);
for (const client of clients) {
const l = document.createElement('a');
l.addEventListener('click', e => {
document.querySelector('#client').value = client[1];
e.preventDefault();
});
l.setAttribute('href', 'javascript:void(0)');
l.appendChild(document.createTextNode(client[0]));
el.appendChild(l);
el.appendChild(document.createTextNode(' '));
}
});
}
setInterval(() => fetchClients(), 1000);
require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@0.10.0/min/vs' }});
require(['vs/editor/editor.main'], function() {
const editor = monaco.editor.create(document.getElementById('code-container'), {
value: 'return 42',
language: 'lua'
});
document.querySelector('#run').addEventListener('click', e => {
const text = editor.getValue();
fetch('/runcode/', {
method: 'post',
body: JSON.stringify({
password: document.querySelector('#password').value,
client: document.querySelector('#client').value,
code: text
})
}).then(res => res.json()).then(res => {
const resultElement = document.querySelector('#result');
if (res.error) {
resultElement.style.color = '#aa0000';
} else {
resultElement.style.color = '#000000';
}
resultElement.innerHTML = res.error || res.result;
if (res.from) {
resultElement.innerHTML += ' (from ' + res.from + ')';
}
});
e.preventDefault();
});
});
</script>
</body>
</html>