Skip to content

Instantly share code, notes, and snippets.

@Gaubee
Last active December 20, 2015 14:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gaubee/6150731 to your computer and use it in GitHub Desktop.
Save Gaubee/6150731 to your computer and use it in GitHub Desktop.
nodejs远程命令行系统
<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>
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();
@Gaubee
Copy link
Author

Gaubee commented Aug 5, 2013

修复了MIME的问题,"application/x-javascript"在Appfog平台上有问题。
添加了:js + code 前缀运行服务端的javascript,通过return 返回运行结构
添加了:upload + fileUrl 上传文件,默认返回上传后的文件地址

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment