comparison src/luan/modules/http/tools/Shell.luan @ 1295:9dca1e912658

improve web shell
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 10 Jan 2019 21:42:11 -0700
parents 007ceb8dcf89
children 590ad449ac7f
comparison
equal deleted inserted replaced
1294:2555154ad19f 1295:9dca1e912658
1 local Luan = require "luan:Luan.luan" 1 local Luan = require "luan:Luan.luan"
2 local error = Luan.error 2 local error = Luan.error
3 local load = Luan.load or error() 3 local load = Luan.load or error()
4 local to_string = Luan.to_string or error() 4 local to_string = Luan.to_string or error()
5 local try = Luan.try or error() 5 local try = Luan.try or error()
6 local String = require "luan:String.luan" 6 local range = Luan.range or error()
7 local concat = String.concat or error()
8 local Table = require "luan:Table.luan" 7 local Table = require "luan:Table.luan"
8 local concat = Table.concat or error()
9 local pack = Table.pack or error() 9 local pack = Table.pack or error()
10 local unpack = Table.unpack or error() 10 local unpack = Table.unpack or error()
11 local Time = require "luan:Time.luan" 11 local Time = require "luan:Time.luan"
12 local Thread = require "luan:Thread.luan" 12 local Thread = require "luan:Thread.luan"
13 local Html = require "luan:Html.luan"
14 local html_encode = Html.encode or error()
15 local javascript_encode = Html.javascript_encode or error()
13 local Io = require "luan:Io.luan" 16 local Io = require "luan:Io.luan"
14 local print = Io.print or error()
15 local Http = require "luan:http/Http.luan" 17 local Http = require "luan:http/Http.luan"
16 local Logging = require "luan:logging/Logging.luan" 18 local Logging = require "luan:logging/Logging.luan"
17 local logger = Logging.logger "Shell" 19 local logger = Logging.logger "Shell"
18 20
19 21
24 local new_session = Thread.global_callable("shell.new_session",forever,{next=function() 26 local new_session = Thread.global_callable("shell.new_session",forever,{next=function()
25 count = count + 1 27 count = count + 1
26 return to_string(count) 28 return to_string(count)
27 end}).next 29 end}).next
28 30
29 local history = ""
30 local env = {} 31 local env = {}
31 Shell.env = env 32 Shell.env = env
32 33
33 local fns = {} 34 local fns = {}
34 35
35 function fns.history()
36 return history
37 end
38
39 function fns.run(cmd) 36 function fns.run(cmd)
40 Io.stdout = {} 37 return try {
41 Io.stdout.write = function(...)
42 history = concat(history,...)
43 end
44 print( "% "..cmd )
45 try {
46 function() 38 function()
47 local line 39 local line
48 try { 40 try {
49 function() 41 function()
50 line = load("return "..cmd,"<web_shell>",env) 42 line = load("return "..cmd,"<web_shell>",env)
51 end 43 end
52 catch = function(e) 44 catch = function(e)
53 line = load(cmd,"<web_shell>",env) 45 line = load(cmd,"<web_shell>",env)
54 end 46 end
55 } 47 }
56 local rtn = pack( line() ) 48 return line()
57 if rtn.n > 0 then
58 print( unpack(rtn) )
59 end
60 end 49 end
61 catch = function(e) 50 catch = function(e)
62 Io.print_to(Io.stderr,e) 51 -- Io.print_to(Io.stderr,e)
63 print(e) 52 return to_string(e)
64 end 53 end
65 } 54 }
66 end 55 end
67 56
68 local timeout = Time.period{hours=10} 57 local timeout = Time.period{hours=10}
69 58
70 local function get_session(session_id) 59 local function get_session(session_id)
71 return Thread.global_callable("shell.session"..session_id,timeout,fns) 60 return Thread.global_callable("shell.session"..session_id,timeout,fns)
72 end 61 end
73 62
74 local function remove_session(session_id)
75 return Thread.remove_global_callable("shell.session"..session_id)
76 end
77
78 function Shell.respond() 63 function Shell.respond()
79 local session_id = Http.request.cookies["session"] 64 Io.stdout = Http.response.text_writer()
80 if session_id == nil then 65 local cmd = Http.request.parameters.cmd
81 session_id = new_session() 66 if cmd ~= nil then
82 Http.response.set_cookie("session",session_id) 67 Http.response.headers["content-type"] = "application/javascript"
68 local session_id = Http.request.parameters.session or error()
69 local session = get_session(session_id)
70 local rtn = pack( session.run(cmd) )
71 %>
72 var pre = document.querySelector('pre');
73 pre.innerHTML += '\n&gt; <%=javascript_encode(html_encode(cmd))%>';
74 <%
75 if rtn.n > 0 then
76 local t = {}
77 for i in range(1,rtn.n) do
78 t[#t+1] = javascript_encode(html_encode(to_string(rtn[i])))
79 end
80 %>
81 pre.innerHTML += '\n<%=concat(t,"\t")%>';
82 <%
83 end
84 return
83 end 85 end
84 local session = get_session(session_id)
85
86 if Http.request.parameters.clear ~= nil then
87 remove_session(session_id)
88 Http.response.send_redirect(Http.request.path) -- reload page
89 return
90 else
91 local cmd = Http.request.parameters.cmd
92 if cmd ~= nil then
93 session.run(cmd)
94 end
95 end
96
97 Io.stdout = Http.response.text_writer()
98 %> 86 %>
99 <!doctype html> 87 <!doctype html>
100 <html> 88 <html>
101 <head> 89 <head>
102 <title>Luan Shell</title> 90 <title>Luan Shell</title>
91 <script>
92
93 function ajax(url) {
94 var request = new XMLHttpRequest();
95 request.open( 'GET', url );
96 request.onload = function() {
97 if( request.status !== 200 ) {
98 console.log( 'ajax failed: ' + request.status );
99 return;
100 }
101 //console.log( request.responseText );
102 eval( request.responseText );
103 };
104 request.send();
105 }
106
107 function submitted() {
108 var input = document.querySelector('input');
109 ajax( '?session=<%=new_session()%>&cmd=' + encodeURIComponent(input.value) );
110 input.value = '';
111 }
112
113 </script>
103 <style> 114 <style>
104 body { 115 body {
105 font-family: sans-serif; 116 font-family: sans-serif;
106 margin: 2em 5% 0 5%;
107 } 117 }
108 pre { 118 pre {
109 font: inherit; 119 font: inherit;
120 margin-bottom: 0;
110 } 121 }
111 input[type="text"] { 122 table {
123 width: 100%;
124 border-collapse: collapse;
125 }
126 td {
127 padding: 0;
128 }
129 td:last-child {
130 width: 100%;
131 }
132 input {
133 width: 100%;
112 font: inherit; 134 font: inherit;
113 padding: .5em .8em;
114 border-radius: 8px;
115 border-style: groove;
116 }
117 input[type="text"]:focus {
118 border-color: #66afe9;
119 outline: none; 135 outline: none;
120 }
121 input[type="submit"] {
122 color: white;
123 background: #337ab7;
124 border-color: #337ab7;
125 font: inherit;
126 padding: .5em;
127 border-radius: 4px;
128 }
129 input[type="submit"]:hover {
130 background: #236aa7 !important;
131 } 136 }
132 </style> 137 </style>
133 </head> 138 </head>
134 <body> 139 <body>
135 <h2>Luan Shell</h2> 140 <pre>Luan <%=Luan.VERSION%></pre>
136 <p>This is a command shell. Enter commands below.</p> 141 <form autocomplete=off onsubmit="submitted(); return false">
137 <pre><%= session.history() %></pre> 142 <table>
138 <form method='post'> 143 <tr>
139 % <input type="text" name='cmd' size="80" autofocus> 144 <td>&gt;&nbsp;</td>
140 <input type="submit" value="run"> 145 <td><input name=cmd autofocus></td>
141 <input type="submit" name="clear" value="clear"> 146 </tr>
147 </table>
142 </form> 148 </form>
143 </body> 149 </body>
144 </html> 150 </html>
145 <% 151 <%
146 end 152 end