Last active
December 20, 2015 14:59
-
-
Save Gaubee/6150731 to your computer and use it in GitHub Desktop.
nodejs远程命令行系统
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>hehe</title> | |
<meta content="The Shell command runner Based on Nodejs"> | |
<style> | |
/** | |
* basic styles for the javascript sandbox console - joss crowcroft | |
* | |
* http://josscrowcroft.github.com/javascript-sandbox-console/ | |
*/ | |
#sandbox, | |
#sandbox pre.output, | |
#sandbox pre.output span, | |
#sandbox textarea, | |
#sandbox textarea:focus { | |
font-size:14px; | |
line-height:1.3; | |
font-weight: normal; | |
font-family:"Consolas", "Andale Mono", "Courier New", "Courier", monospace; | |
border:0 none; | |
outline:0 none; | |
-webkit-box-shadow:none; | |
-moz-box-shadow:none; | |
box-shadow:none; | |
} | |
#sandbox { | |
background:#333; | |
color: #ccc; | |
background: #333; | |
padding:20px 20px 15px; | |
-webkit-border-radius: 10px; | |
-moz-border-radius: 10px; | |
border-radius: 10px; | |
max-width:640px; | |
margin:30px auto; | |
} | |
#sandbox pre.output { | |
display:block; | |
white-space:pre; | |
width:100%; | |
height:285px; | |
overflow-y:auto; | |
position:relative; | |
padding:0; | |
margin:0 0 10px; | |
border:0 none; | |
} | |
#sandbox pre.output span { color:#f7f7f7; } | |
#sandbox pre.output span.command { color:#ccc; display: block;} | |
#sandbox pre.output span.prefix { color:#777; } | |
#sandbox pre.output span.undefined { color:#777; } | |
#sandbox pre.output span.string { color:#99f; display: block;} | |
#sandbox pre.output span.number { color:#7f7; } | |
#sandbox pre.output span.error { color:#f77; } | |
#sandbox .input { | |
padding:0 0 0 15px; | |
position:relative; | |
} | |
#sandbox .input:before { | |
content:">"; | |
position:absolute; | |
top: 1px; | |
left: 0; | |
color:#ddd | |
} | |
#sandbox textarea { | |
color:#f7f7f7; | |
background:#333; | |
border:0 none; | |
outline:0 none; | |
padding:0; | |
margin:0; | |
resize: none; | |
width:100%; | |
overflow:hidden; | |
} | |
#sandbox textarea:focus { | |
outline:0 none; | |
} | |
#sandbox pre.output::-webkit-scrollbar, | |
#sandbox pre.output::-webkit-scrollbar-button, | |
#sandbox pre.output::-webkit-scrollbar-track, | |
#sandbox pre.output::-webkit-scrollbar-track-piece, | |
#sandbox pre.output::-webkit-scrollbar-thumb, | |
#sandbox pre.output::-webkit-scrollbar-corner, | |
#sandbox pre.output::-webkit-resizer { | |
background: transparent; | |
} | |
#sandbox pre.output::-webkit-scrollbar { | |
width: 7px; | |
height: 7px; | |
-webkit-border-radius: 4px; | |
border-radius: 4px; | |
} | |
#sandbox pre.output::-webkit-scrollbar-track-piece { | |
-webkit-border-radius: 5px; | |
border-radius: 5px; | |
} | |
#sandbox pre.output::-webkit-scrollbar-thumb { | |
background: #4f4f4f; | |
border-radius: 5px; | |
} | |
#sandbox pre.output::-webkit-scrollbar-button { | |
width:0; | |
height:0; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="sandbox"> | |
<pre id="output" class="output"></pre> | |
<div class="input"> | |
<textarea id="command" rows="1" placeholder="$ Enter Shell Commands and hit enter" onkeydown="switchingMode()" onkeyup="spawnShell()"></textarea> | |
</div> | |
</div> | |
</body> | |
<script type="text/javascript"> | |
var Session = { | |
commandDOM: document.getElementById('command'), | |
outputDOM:document.getElementById('output'), | |
printf:{ | |
_newLine:function(str,claaName){ | |
var line = document.createElement("span"); | |
line.className = claaName||""; | |
line.innerText = str; | |
return line; | |
}, | |
_pushLine:function(newLine){ | |
Session.outputDOM.appendChild(newLine); | |
}, | |
_pushPrefix:function(){ | |
this._pushLine(this._newLine(" => ","prefix")); | |
}, | |
inp:function inp(str){ | |
this._pushLine(this._newLine(str,"command")); | |
}, | |
mes:function mes(str){ | |
this._pushPrefix(); | |
this._pushLine(this._newLine(str)); | |
}, | |
err:function err(str){ | |
this._pushPrefix(); | |
this._pushLine(this._newLine(str.replace("<h1>",""),"error")); | |
}, | |
unf:function unf(str){ | |
this._pushPrefix(); | |
this._pushLine(this._newLine(str.replace("<h1>",""),"error")); | |
}, | |
end:function end(str){ | |
this._pushLine(this._newLine(str,"string")); | |
} | |
}, | |
commandHistory: [], | |
historyPoint:0, | |
KEY: { | |
ENTER: 13, | |
UP:38, | |
DOWN:40, | |
CTRL:17, | |
C:67 | |
}, | |
MODE:{ | |
NORMAL:0, | |
CTRL:0 | |
}, | |
currentProcessId:null | |
}; | |
var ajax = { | |
XMLOBJ:new(self.XMLHttpRequest||ActiveXObject)("Microsoft.XMLHTTP"), | |
get:function ajaxGet(url,callback){ | |
var xmlhttp = ajax.XMLOBJ | |
xmlhttp.onreadystatechange = function() { | |
if (xmlhttp.readyState == 4) { | |
callback(xmlhttp.status,xmlhttp.responseText) | |
} | |
}; | |
xmlhttp.open("GET",url,true); | |
xmlhttp.send(); | |
}, | |
post:function ajaxPost(url,data,callback){ | |
var xmlhttp = ajax.XMLOBJ | |
xmlhttp.onreadystatechange = function() { | |
if (xmlhttp.readyState == 4) { | |
callback(xmlhttp.status,xmlhttp.responseText) | |
} | |
}; | |
xmlhttp.open("POST",url,true); | |
xmlhttp.send(data); | |
} | |
}; | |
function getData(processMessage){ | |
ajax.get("/shellData/"+processMessage.pid,function getProcessData(status,data){ | |
if (status === 200) { | |
try{ | |
var processData = eval("("+data+")"); | |
console.log(data) | |
console.log(processData,processData.data); | |
processData.data&&Session.printf.mes(processData.data); | |
processData.err&&Session.printf.err(processData.err); | |
if (processData.exit) { | |
Session.printf.end(processData.exit); | |
}else{ | |
console.log("no exit") | |
setTimeout(function(){ | |
getData(processMessage); | |
},200); | |
} | |
}catch(e){ | |
console.log(e.message); | |
Session.printf.err("'-- "+data+" --'"); | |
} | |
}else{ | |
Session.printf.unf(data); | |
} | |
}) | |
}; | |
function switchingMode(){ | |
switch(event.which){ | |
case Session.KEY.CTRL: | |
Session.MODE.CTRL = 1; | |
break; | |
} | |
}; | |
function spawnShell() { | |
switch (event.which) { | |
case Session.KEY.ENTER: | |
var command = Session.commandDOM.value.split("\n")[0]; | |
if(command !== Session.commandHistory[Session.commandHistory.length-1]){ | |
function callback(status,data){ | |
try{ | |
console.log(status,data,eval("("+data+")")); | |
var processMessage = eval("("+data+")"); | |
Session.printf.inp(command); | |
getData(processMessage); | |
}catch(e){ | |
Session.printf.err(e.message); | |
} | |
} | |
if (command.indexOf(":js")===0) { | |
ajax.post("/eval",command.replace(":js",""),callback); | |
}else if(command.indexOf(":upload")===0){ | |
ajax.get("/upload/"+command.replace(":upload",""),callback); | |
}else{ | |
ajax.get("/shell/"+command,callback); | |
} | |
Session.commandHistory.push(command); | |
} | |
Session.commandDOM.value = ""; | |
Session.historyPoint = 0; | |
break; | |
case Session.KEY.UP: | |
Session.historyPoint+=1; | |
Session.commandDOM.value = Session.commandHistory[Session.commandHistory.length-Session.historyPoint]||""; | |
break; | |
case Session.KEY.DOWN: | |
Session.historyPoint-=1; | |
Session.commandDOM.value = Session.commandHistory[Session.commandHistory.length-Session.historyPoint]||""; | |
break; | |
case Session.KEY.CTRL: | |
Session.MODE.CTRL = 0; | |
break; | |
case Session.KEY.C: | |
if (Session.MODE.CTRL) { | |
//kill child | |
} | |
break; | |
} | |
console.log(event.which) | |
} | |
</script> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var http = require("http"); | |
var url = require("url"); | |
var fs = require("fs"); | |
var Net = { | |
"https:": require('https'), | |
"http:": require('http') | |
} | |
function mkdirSync(url,mode,cb){ | |
var arr = url.split("/"); | |
mode = mode || 0755; | |
cb = cb || function(){}; | |
if(arr[0]==="."){//处理 ./aaa | |
arr.shift(); | |
} | |
if(arr[0] == ".."){//处理 ../ddd/d | |
arr.splice(0,2,arr[0]+"/"+arr[1]) | |
} | |
function inner(cur){ | |
if(!fs.existsSync(cur)){//不存在就创建一个 | |
fs.mkdirSync(cur, mode) | |
} | |
if(arr.length){ | |
inner(cur + "/"+arr.shift()); | |
}else{ | |
cb(); | |
} | |
} | |
arr.length && inner(arr.shift()); | |
} | |
function writeFile(filePath,fileName,data,callback){ | |
mkdirSync(filePath) | |
fs.open(filePath+"/"+fileName,"w",0644,function(err,fd){//open or create | |
if(err) throw err; | |
if ((typeof data).toString()==="object") { | |
data = JSON.stringify(data); | |
}else{ | |
data = data.toString(); | |
} | |
fs.write(fd,data,0,"binary",function(err){ | |
callback&&callback(err,filePath,fileName,data); | |
fs.closeSync(fd); | |
}) | |
}) | |
} | |
var getFileData = function(opctions){//{onFun,endFun,errFun} | |
var bufferHelper = ""; | |
var baseURL = opctions.url; | |
if((typeof opctions.data).toString() === "object"){ | |
var urlData = opctions.data||{};//null | |
var urlDataFormated = ""; | |
for(var i in urlData){ | |
urlDataFormated+="&"+i+"="+urlData[i]; | |
} | |
if (urlData) { | |
baseURL+="?"+urlDataFormated.replace("&",""); | |
}; | |
}else{ | |
baseURL += opctions.data||""; | |
} | |
console.log("uploading... "+baseURL); | |
var getURL = url.parse(baseURL); | |
getURL.headers={ | |
'Host': getURL.host, | |
'Connection': "keep-alive", | |
// 'Accept': "image/webp,*/*;q=0.8", | |
// 'Cache-Control': "max-age=0", | |
// 'If-None-Match': '"51da1003-5bcec"', | |
// 'If-Modified-Since': "Mon, 08 Jul 2013 01:04:03 GMT", | |
'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.45 Safari/537.36", | |
'Referer': "http://"+getURL.hostname, | |
'Accept-Encoding': "gzip,deflate,sdch", | |
'Accept-Language': "zh-CN,zh;q=0.8", | |
'Cookie': opctions.cookie||Math.random().toString(36).substring(2) | |
}; | |
opctions = opctions||{}; | |
var onFun = function (data) {//加载数据,一般会执行多次 | |
bufferHelper+=data; | |
opctions.onFun&&opctions.onFun(data); | |
}; | |
var endFunBase = opctions.endFun||function (data) { | |
console.log(data); | |
}; | |
var endFun = function(){ | |
endFunBase.call(this,bufferHelper) | |
} | |
var errFun = opctions.errFun||function(err) { | |
console.log("http get error:",err); | |
}; | |
// console.log(getURL); | |
var req = Net[getURL.protocol].get(getURL, function (res) { | |
// res.setEncoding('utf8'); | |
res.setEncoding("binary"); | |
res.on('data',onFun).on('end',endFun) | |
}).on('error',errFun); | |
return req; | |
}; | |
var exec = require('child_process').exec; | |
var allProcesses = {}; | |
function ProcessHandle(newProcess, newPid) { | |
var self = this; | |
self.process = newProcess; | |
self.pid = newPid; | |
self.status = "start"; | |
self.data = []; | |
self.err = []; | |
self.exit = ""; | |
if (typeof newPid === "string"&&(newPid.indexOf("js") === 0||newPid.indexOf("upload") === 0)) { | |
return this; | |
} | |
newProcess.stdout.on("data", function(data) { | |
self._pushData(data) | |
}); | |
newProcess.stderr.on("data", function(data) { | |
self._pushErr(data) | |
}); | |
newProcess.on("exit", function(code) { | |
self._exitProcess(code) | |
}); | |
// self.stdout = allProcesses._pushData; | |
// self.stderr = application._popErr; | |
// self.exit = application._exitProcess; | |
}; | |
ProcessHandle.prototype = { | |
_pushData: function stdout(data) { | |
this.status = "ondata"; | |
this.data.push(data); | |
}, | |
_pushErr: function stderr(data) { | |
this.status = "error"; | |
this.err.push(data); | |
}, | |
_exitProcess: function exit(code) { | |
this.status = "exit"; | |
this.exit = ["Process", this.pid, "exited with code", code].join(" "); | |
}, | |
_popData: function clearStack() { | |
var dataStr = this.data.join("\n").split("\n").join("\n"); //.join("<br>"); | |
this.data = []; | |
return dataStr; | |
}, | |
_popErr: function clearStack() { | |
var errStr = this.err.join("\n").split("\n").join("\n"); //.join("<br>"); | |
this.err = []; | |
return errStr; | |
} | |
} | |
function start(route, handle) { | |
function onRequest(request, response) { | |
function responseWrite(content, statusCode, contentType) { | |
if (typeof statusCode === "string") { | |
contentType = statusCode; | |
statusCode = 200; | |
} | |
response.writeHead(contentType || 200, { | |
"Content-Type": statusCode || "text/html" | |
}); | |
response.write(content); | |
response.end(); | |
}; | |
// console.log(request); | |
request.setEncoding("utf8"); | |
var URL = url.parse(request.url), | |
pathname = URL.pathname; | |
if (request.method === 'GET') { | |
console.log(pathname); | |
for (var i = 0, routerItem; routerItem = routerKeys[i]; i += 1) { | |
if (pathname.indexOf(routerItem) === 0) { | |
router[routerItem](pathname.replace(routerItem, ""), responseWrite, request, response); | |
break; | |
} | |
} | |
} else if (request.method === 'POST') { | |
var postData = ""; | |
request.addListener("data", function(chunk) { | |
postData += chunk; | |
}); | |
request.addListener("end", function() { | |
for (var i = 0, routerItem; routerItem = routerKeys[i]; i += 1) { | |
if (pathname.indexOf(routerItem) === 0) { | |
router[routerItem](postData, responseWrite, request, response); | |
break; | |
} | |
} | |
}); | |
} | |
if (i === routerKeys.length) { | |
responseWrite("<h1>404", 404); | |
} | |
} | |
http.createServer(onRequest).listen(8888); | |
console.log("Server has started."); | |
} | |
var router = { | |
"/index": function index(indexSuffix, responseWrite) { | |
fs.readFile("index.html", { | |
encoding: "utf8", | |
flag: "r" | |
}, function(err, fileData) { | |
if (err) { | |
throw err; | |
} | |
responseWrite(fileData); | |
}); | |
}, | |
"/eval": function evaljs(code, responseWrite) {//post!! | |
console.log("eval:"+code); | |
var result, | |
newJsProcess = Function(code), | |
newJsPid = setTimeout(function() { | |
try { | |
allProcesses[newJsPid].data.push(JSON.stringify(newJsProcess())); | |
} catch (e) { | |
allProcesses[newJsPid].err.push(e.message); | |
} | |
allProcesses[newJsPid].status = "exit"; | |
allProcesses[newJsPid].exit = "end js code at " + new Date; | |
}, 18); | |
newJsPid = "js" + newJsPid._idleTimeout; | |
allProcesses[newJsPid] = new ProcessHandle(newJsProcess, newJsPid); | |
responseWrite(JSON.stringify({ | |
shell: encodeURI(code), | |
decodeURIShell: code, | |
pid: newJsPid, | |
status: "start" //on data,on error,on exit | |
})) | |
}, | |
"/shell/": function shell(shellStr, responseWrite) { | |
var decodeShellStr = decodeURI(shellStr).trim(); | |
// decodeShellArr = decodeShellStr.split(/[\s]+/); | |
// newProcess = spawn(decodeShellArr[0],decodeShellArr.slice(1)), | |
if (decodeShellStr.indexOf(":js") === 0) { | |
router["/eval/"](decodeShellStr.replace(":js", ""), responseWrite); | |
return; | |
} | |
console.log("shell:"+shellStr); | |
newProcess = exec(decodeShellStr), | |
newPid = newProcess.pid; | |
allProcesses[newPid] = new ProcessHandle(newProcess, newPid); | |
responseWrite(JSON.stringify({ | |
shell: shellStr, | |
decodeURIShell: decodeShellStr, | |
pid: newPid, | |
status: "start" //on data,on error,on exit | |
})) | |
}, | |
"/shellData/": function getShellData(pid, responseWrite) { | |
var process = allProcesses[pid]; | |
if (!process) { | |
responseWrite("<h1>no this(" + pid + ") process!") | |
} else { | |
responseWrite(JSON.stringify({ | |
status: process.status, | |
data: process._popData(), | |
err: process._popErr(), | |
exit: process.exit | |
})); | |
if (process.exit === "exit") { | |
delete allProcesses[pid]; | |
} | |
} | |
}, | |
"/upload/": function upload(fileUrl, responseWrite) { | |
var decodeFileUrl = decodeURI(fileUrl).trim(), | |
fileSuffix = decodeFileUrl.split("."), | |
newuploadPid = "upload" + (Math.random().toString(36)+"").substring(2); | |
allProcesses[newuploadPid] = new ProcessHandle(null, newuploadPid); | |
fileSuffix = fileSuffix[fileSuffix.length-1]; | |
if (fileSuffix.length>10) {fileSuffix=""} | |
getFileData({ | |
url:decodeFileUrl, | |
onFun:function(data){ | |
// console.log(allProcesses[newuploadPid].data,data.length); | |
allProcesses[newuploadPid].data = [~~(allProcesses[newuploadPid].data.join("").replace("B",""))+data.length+"B",""]; | |
allProcesses[newuploadPid].status = "ondata"; | |
}, | |
endFun:function(data){ | |
allProcesses[newuploadPid].data = [String(data).length+"B"]; | |
writeFile((new Date).valueOf()+fileSuffix,newuploadPid+"."+fileSuffix,data,function(err,filePath,fileName,data){ | |
if (err) { | |
allProcesses[newuploadPid].err.push(err); | |
} | |
allProcesses[newuploadPid].status = "exit"; | |
allProcesses[newuploadPid].exit = filePath+"/"+fileName; | |
}) | |
} | |
}); | |
responseWrite(JSON.stringify({ | |
shell: fileUrl, | |
decodeURIShell: decodeFileUrl, | |
pid: newuploadPid, | |
status: "start" //on data,on error,on exit | |
})) | |
}, | |
"/kill": function kill() { | |
throw ""; | |
}, | |
"/killProcess": function killProcess(pid, responseWrite) { | |
if (pid.indexOf("js")===0) { | |
clearTimeout(pid.replace("js","")); | |
}else{ | |
allProcesses[pid].kill(); | |
} | |
responseWrite(["Process", this.pid, "killed"].join(" ")); | |
delete allProcesses[pid]; | |
} | |
} | |
var routerKeys = Object.keys(router); | |
start(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
修复了MIME的问题,"application/x-javascript"在Appfog平台上有问题。
添加了:js + code 前缀运行服务端的javascript,通过return 返回运行结构
添加了:upload + fileUrl 上传文件,默认返回上传后的文件地址