web_sandbox_console 0.1.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +143 -6
  3. data/app/assets/images/web_sandbox_console/logo-icon.png +0 -0
  4. data/app/assets/javascripts/web_sandbox_console/application.js +0 -1
  5. data/app/assets/javascripts/web_sandbox_console/code_editor.js +0 -0
  6. data/app/assets/javascripts/web_sandbox_console/codemirror.js +9778 -0
  7. data/app/assets/javascripts/web_sandbox_console/matchbrackets.js +158 -0
  8. data/app/assets/javascripts/web_sandbox_console/ruby.js +298 -0
  9. data/app/assets/stylesheets/web_sandbox_console/application.css +85 -14
  10. data/app/assets/stylesheets/web_sandbox_console/codemirror.css +349 -0
  11. data/app/assets/stylesheets/web_sandbox_console/common.css +231 -0
  12. data/app/assets/stylesheets/web_sandbox_console/docs.css +274 -0
  13. data/app/assets/stylesheets/web_sandbox_console/home.css +47 -0
  14. data/app/assets/stylesheets/web_sandbox_console/lucario.css +45 -0
  15. data/app/controllers/web_sandbox_console/application_controller.rb +4 -0
  16. data/app/controllers/web_sandbox_console/authorization_controller.rb +82 -0
  17. data/app/controllers/web_sandbox_console/home_controller.rb +44 -4
  18. data/app/views/layouts/web_sandbox_console/application.html.erb +82 -11
  19. data/app/views/web_sandbox_console/authorization/auth_page.html.erb +33 -0
  20. data/app/views/web_sandbox_console/home/do_view_file.js.erb +14 -0
  21. data/app/views/web_sandbox_console/home/download_page.html.erb +22 -0
  22. data/app/views/web_sandbox_console/home/eval_code.js.erb +1 -1
  23. data/app/views/web_sandbox_console/home/index.html.erb +34 -7
  24. data/app/views/web_sandbox_console/home/view_file.html.erb +39 -0
  25. data/config/routes.rb +12 -0
  26. data/lib/generators/web_sandbox_console/templates/web_sandbox_console.rb +20 -3
  27. data/lib/generators/web_sandbox_console/templates/web_sandbox_console.yml +5 -0
  28. data/lib/generators/web_sandbox_console/web_sandbox_console_generator.rb +5 -0
  29. data/lib/web_sandbox_console.rb +4 -0
  30. data/lib/web_sandbox_console/common.rb +29 -8
  31. data/lib/web_sandbox_console/configuration.rb +9 -1
  32. data/lib/web_sandbox_console/safe_ruby.rb +116 -16
  33. data/lib/web_sandbox_console/sandbox.rb +119 -20
  34. data/lib/web_sandbox_console/sandbox_error.rb +4 -0
  35. data/lib/web_sandbox_console/version.rb +1 -1
  36. data/lib/web_sandbox_console/view_file.rb +257 -0
  37. data/lib/web_sandbox_console/view_file_error.rb +4 -0
  38. metadata +28 -6
  39. data/app/jobs/web_sandbox_console/application_job.rb +0 -4
@@ -2,3 +2,50 @@
2
2
  Place all the styles related to the matching controller here.
3
3
  They will automatically be included in application.css.
4
4
  */
5
+
6
+
7
+ .file_or_dir{
8
+ width: 450px;
9
+ height: 30px;
10
+ }
11
+
12
+ .h30{
13
+ height: 30px;
14
+ }
15
+
16
+ .view-file-button{
17
+ background: #272822;
18
+ height: 36px;
19
+ width: 132px;
20
+ font-size: 15px;
21
+ color: white;
22
+ cursor: pointer;
23
+ }
24
+
25
+ /* 执行代码页面样式 */
26
+ .wrapper-console {
27
+ position: relative;
28
+ top: 8px;
29
+ width: 100%;
30
+ height: 300px;
31
+ }
32
+
33
+ .left {
34
+ height: 100%;
35
+ /* 为了不占用绝对定位的宽度 这里计算下宽度*/
36
+ width: calc(100% - 200px);
37
+ }
38
+
39
+ .right {
40
+ position: absolute;
41
+ width: 180px;
42
+ top: 0;
43
+ bottom: 0;
44
+ right: 0;
45
+ padding-left: 15px;
46
+ }
47
+
48
+ /* 选中后code背景色*/
49
+ .CodeMirror-selected {
50
+ background: #20aee3 !important;
51
+ }
@@ -0,0 +1,45 @@
1
+ /*
2
+ Name: lucario
3
+ Author: Raphael Amorim
4
+
5
+ Original Lucario color scheme (https://github.com/raphamorim/lucario)
6
+ */
7
+
8
+ .cm-s-lucario.CodeMirror, .cm-s-lucario .CodeMirror-gutters {
9
+ background-color: #2b3e50 !important;
10
+ color: #f8f8f2 !important;
11
+ border: none;
12
+ }
13
+ .cm-s-lucario .CodeMirror-gutters { color: #2b3e50; }
14
+ .cm-s-lucario .CodeMirror-cursor { border-left: solid thin #E6C845; }
15
+ .cm-s-lucario .CodeMirror-linenumber { color: #f8f8f2; }
16
+ .cm-s-lucario .CodeMirror-selected { background: #243443; }
17
+ .cm-s-lucario .CodeMirror-line::selection, .cm-s-lucario .CodeMirror-line > span::selection, .cm-s-lucario .CodeMirror-line > span > span::selection { background: #243443; }
18
+ .cm-s-lucario .CodeMirror-line::-moz-selection, .cm-s-lucario .CodeMirror-line > span::-moz-selection, .cm-s-lucario .CodeMirror-line > span > span::-moz-selection { background: #243443; }
19
+ .cm-s-lucario span.cm-comment { color: #5c98cd; }
20
+ .cm-s-lucario span.cm-string, .cm-s-lucario span.cm-string-2 { color: #E6DB74; }
21
+ .cm-s-lucario span.cm-number { color: #ca94ff; }
22
+ .cm-s-lucario span.cm-variable { color: #f8f8f2; }
23
+ .cm-s-lucario span.cm-variable-2 { color: #f8f8f2; }
24
+ .cm-s-lucario span.cm-def { color: #72C05D; }
25
+ .cm-s-lucario span.cm-operator { color: #66D9EF; }
26
+ .cm-s-lucario span.cm-keyword { color: #ff6541; }
27
+ .cm-s-lucario span.cm-atom { color: #bd93f9; }
28
+ .cm-s-lucario span.cm-meta { color: #f8f8f2; }
29
+ .cm-s-lucario span.cm-tag { color: #ff6541; }
30
+ .cm-s-lucario span.cm-attribute { color: #66D9EF; }
31
+ .cm-s-lucario span.cm-qualifier { color: #72C05D; }
32
+ .cm-s-lucario span.cm-property { color: #f8f8f2; }
33
+ .cm-s-lucario span.cm-builtin { color: #72C05D; }
34
+ .cm-s-lucario span.cm-variable-3, .cm-s-lucario span.cm-type { color: #ffb86c; }
35
+
36
+ .cm-s-lucario .CodeMirror-activeline-background { background: #243443; }
37
+ .cm-s-lucario .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; }
38
+
39
+
40
+ /* 特殊部分 补充 */
41
+ .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
42
+ .cm-s-default span.cm-arrow { color: red; }
43
+
44
+
45
+
@@ -1,6 +1,10 @@
1
1
  module WebSandboxConsole
2
2
  class ApplicationController < ActionController::Base
3
3
  protect_from_forgery with: :exception
4
+
5
+ before_action :restrict_ip
6
+ http_basic_authenticate_with name: WebSandboxConsole.http_basic_auth[:name].to_s, password: WebSandboxConsole.http_basic_auth[:password].to_s if WebSandboxConsole.http_basic_auth.present?
7
+
4
8
 
5
9
  # 限制ip
6
10
  def restrict_ip
@@ -0,0 +1,82 @@
1
+ require_dependency "web_sandbox_console/application_controller"
2
+
3
+ module WebSandboxConsole
4
+ class AuthorizationController < ApplicationController
5
+ before_action :restrict_fetch_token_times, only: :fetch_token
6
+
7
+ # 获取令牌
8
+ def fetch_token
9
+ @token = SecureRandom.uuid
10
+ save_cache_token(@token)
11
+ flash[:notice] = "令牌: #{@token},此令牌先保存,后续使用。"
12
+ redirect_to auth_page_path
13
+ end
14
+
15
+ # 授权
16
+ def auth
17
+ if params[:secret_text].blank?
18
+ flash[:notice] = '密文为空'
19
+ return redirect_to root_path
20
+ end
21
+
22
+ if public_key.blank?
23
+ flash[:notice] = '公钥未配置'
24
+ return redirect_to root_path
25
+ end
26
+
27
+ result_hash = decrypt_secret_text(params[:secret_text])
28
+ token = result_hash[:content]
29
+ if result_hash[:success] && fetch_cache_token(token)
30
+ flash[:notice] = "授权成功"
31
+ session[:pass_auth] = true
32
+ else
33
+ flash[:notice] = "授权失败:#{result_hash[:content]}"
34
+ end
35
+
36
+ redirect_to root_path
37
+ end
38
+
39
+ private
40
+ # 限制获取token次数 一天内不允许超过20次
41
+ def restrict_fetch_token_times
42
+ cache = Rails.cache
43
+ times = cache.fetch('fetch_token_times', expires_in: 1.day) {0}
44
+
45
+ if times > 20
46
+ flash[:notice] = '一天内获取令牌不允许超过20次'
47
+ redirect_to root_path
48
+ end
49
+ cache.write('fetch_token_times', times + 1)
50
+ end
51
+
52
+ # 保存token 到缓存
53
+ def save_cache_token(key, value = nil)
54
+ Rails.cache.write(key.to_s, value.presence || key.to_s, expires_in: 5.minutes)
55
+ end
56
+
57
+ # 获取 缓存中 token
58
+ def fetch_cache_token(key)
59
+ Rails.cache.read(key.to_s)
60
+ end
61
+
62
+ # 公钥
63
+ def public_key
64
+ WebSandboxConsole.public_key
65
+ end
66
+
67
+ # 解密
68
+ def decrypt_secret_text(secret_text)
69
+ begin
70
+ base_text = Base64.decode64(secret_text)
71
+ p_key = OpenSSL::PKey::RSA.new public_key
72
+ text = p_key.public_decrypt(base_text)
73
+ {success: true, content: text}
74
+ rescue OpenSSL::PKey::RSAError
75
+ {success: false, content: "密钥匹配失败"}
76
+ rescue Exception => e
77
+ {success: false, content: "发生未知错误: #{e.inspect};#{e.backtrace[0..2].join('\r\n')}"}
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -2,14 +2,54 @@ require_dependency "web_sandbox_console/application_controller"
2
2
 
3
3
  module WebSandboxConsole
4
4
  class HomeController < ApplicationController
5
- before_action :restrict_ip
6
- http_basic_authenticate_with name: WebSandboxConsole.http_basic_auth[:name], password: WebSandboxConsole.http_basic_auth[:password] if WebSandboxConsole.http_basic_auth.present?
7
-
5
+
8
6
  def index
9
7
  end
10
8
 
9
+ # 执行代码
11
10
  def eval_code
12
- @results = Sandbox.new(params[:code]).evalotor
11
+ sandbox = Sandbox.new(params[:code], session[:pass_auth])
12
+
13
+ @results = if params[:commit] == '异步执行'
14
+ sandbox.asyn_evalotor
15
+ else
16
+ sandbox.evalotor
17
+ end
18
+ end
19
+
20
+ def view_file
21
+ end
22
+
23
+ # 查看文件
24
+ def do_view_file
25
+ @result_hash = ViewFile.new(params).view
26
+ @tip_hash = WebSandboxConsole::ViewFile::PageTips
27
+ end
28
+
29
+ # 下载文件页面
30
+ def download_page
13
31
  end
32
+
33
+ # 下载文件
34
+ def download
35
+ if params[:file_name].blank?
36
+ flash[:notice] = "文件名不能为空"
37
+ return redirect_to download_page_path
38
+ end
39
+
40
+ file_full_path = "#{Rails.root}/log/#{params[:file_name]}"
41
+ unless File.exists?(file_full_path)
42
+ flash[:notice] = '文件不存在,请检查文件名;或在其它服务器请多次尝试'
43
+ return redirect_to download_page_path
44
+ end
45
+
46
+ # 打包
47
+ `tar czf #{file_full_path}.tar.gz #{file_full_path}`
48
+ # 如果是csv文件,需删除
49
+ File.delete(file_full_path) if file_full_path.split(".").last == 'csv'
50
+
51
+ send_file "#{file_full_path}.tar.gz"
52
+ end
53
+
14
54
  end
15
55
  end
@@ -1,14 +1,85 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
- <head>
4
- <title>Web sandbox console</title>
5
- <%= stylesheet_link_tag "web_sandbox_console/application", media: "all" %>
6
- <%= javascript_include_tag "web_sandbox_console/application" %>
7
- <%= csrf_meta_tags %>
8
- </head>
9
- <body>
10
- <div class="container">
11
- <%= yield %>
12
- </div>
13
- </body>
3
+ <head>
4
+ <title>Web sandbox console</title>
5
+ <%= stylesheet_link_tag "web_sandbox_console/application", media: "all" %>
6
+ <%= javascript_include_tag "web_sandbox_console/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+ <div id="main-wrapper">
11
+ <header class="topbar">
12
+ <nav class="navbar top-navbar">
13
+ <div class="navbar-header">
14
+ <a class="navbar-brand" href="/web_sandbox_console">
15
+ <b>
16
+ <img src="/assets/web_sandbox_console/logo-icon.png" />
17
+ </b>
18
+ <span style="left-padding: 10px;" class="font-color">运维控制台</span>
19
+ </a>
20
+ </div>
21
+
22
+ <div class="navbar-collapse">
23
+ </div>
24
+ </nav>
25
+ </header>
26
+
27
+ <aside class="left-sidebar">
28
+ <div class="scroll-sidebar ps">
29
+ <nav class="sidebar-nav">
30
+ <ul id="sidebar-nav">
31
+ <li>
32
+ <%= link_to root_path, class: 'waves-effect waves-dark' do %>
33
+ <i class="fa fa-bookmark-o"></i>
34
+ <span class="hide-menu <%= action_name == 'index' ? 'menu-active' : '' %>">执行代码</span>
35
+ <% end %>
36
+ </li>
37
+
38
+ <li>
39
+ <%= link_to web_sandbox_console.view_file_path, class: 'waves-effect waves-dark' do %>
40
+ <i class="fa fa-bookmark-o"></i>
41
+ <span class="hide-menu <%= action_name == 'view_file' ? 'menu-active' : '' %>">查看文件</span>
42
+ <% end %>
43
+ </li>
44
+
45
+ <li>
46
+ <%= link_to web_sandbox_console.download_page_path, class: 'waves-effect waves-dark' do %>
47
+ <i class="fa fa-bookmark-o"></i>
48
+ <span class="hide-menu <%= action_name == 'download_page' ? 'menu-active' : '' %>">下载文件</span>
49
+ <% end %>
50
+ </li>
51
+
52
+ <li>
53
+ <%= link_to web_sandbox_console.auth_page_path, class: 'waves-effect waves-dark' do %>
54
+ <i class="fa fa-bookmark-o"></i>
55
+ <span class="hide-menu <%= action_name == 'auth_page' ? 'menu-active' : '' %>">升级授权</span>
56
+ <% end %>
57
+ </li>
58
+
59
+ <li>
60
+ <%= link_to "https://github.com/dongmy54/web_sandbox_console", target: "_blank", class: 'waves-effect waves-dark' do %>
61
+ <i class="fa fa-bookmark-o"></i>
62
+ <span class="hide-menu">说明文档</span>
63
+ <% end %>
64
+ </li>
65
+ </ul>
66
+ </nav>
67
+
68
+ </div>
69
+ </aside>
70
+
71
+ <div class="page-wrapper">
72
+ <div class="container-fluid">
73
+ <%= yield %>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </body>
14
78
  </html>
79
+
80
+ <script type="text/javascript">
81
+ <% flash.each do |type, msg| %>
82
+ <% tip = type == 'notice' ? '提示' : '其它' %>
83
+ alert("<%= tip %>: <%= msg %>");
84
+ <% end %>
85
+ </script>
@@ -0,0 +1,33 @@
1
+ <h3>升级授权</h3>
2
+
3
+ <p>
4
+ <%= link_to '获取令牌', fetch_token_path %>
5
+ <span> PS: 注意获取令牌一天最多20次,有效期仅5分钟</span>
6
+ </p>
7
+
8
+ <div class="">
9
+ <%= form_tag web_sandbox_console.auth_path, method: :post do %>
10
+ <table>
11
+ <tr>
12
+ <td>加密密文</td>
13
+ <td>
14
+ <%= text_area_tag :secret_text, '', style: "width: 300px;height: 100px;" %>
15
+ </td>
16
+ <td><%= submit_tag '提交', data: { disable_with: "已发送,处理中..." }, class: 'view-file-button' %></td>
17
+ </table>
18
+ <% end %>
19
+ </div>
20
+
21
+ <h4>说明令牌使用说明:</h4>
22
+ <div>
23
+ <pre class="no-border">
24
+ require 'openssl'
25
+ require 'base64'
26
+
27
+ private_key = "你的私钥"
28
+ p_key = OpenSSL::PKey::RSA.new private_key
29
+ secret_text = p_key.private_encrypt("你的令牌")
30
+ encode_text = Base64.encode64(secret_text)
31
+ puts encode_text
32
+ </pre>
33
+ </div>
@@ -0,0 +1,14 @@
1
+ $(".output-content").empty();
2
+
3
+ <% line_num_show = @result_hash[:total_line_num] ? "原始文件总行数:#{@result_hash[:total_line_num]}" : "" %>
4
+ $(".output-content").append("<p>============ <%= Time.current %> 查找结果如下 <%= line_num_show %> ===============</p>");
5
+
6
+ <% @tip_hash.each do |key,value| %>
7
+ <% if @result_hash[key.to_sym] %>
8
+ $(".output-content").append("<p>PS: <%= value %></p>");
9
+ <% end %>
10
+ <% end %>
11
+
12
+ <% @result_hash[:lines].each do |result| %>
13
+ $(".output-content").append('<pre><%= escape_javascript result %></pre>');
14
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <h3>下载文件</h3>
2
+ <div class="">
3
+ <%= form_tag web_sandbox_console.download_path, method: :get do %>
4
+ <table>
5
+ <tr>
6
+ <td>文件名</td>
7
+ <td>
8
+ <%= text_field_tag :file_name, '', style: "width: 400px;height: 35px;font-size: 15px;" %>
9
+ </td>
10
+ <td><%= submit_tag '提交', data: { disable_with: "已发送,处理中..." }, class: 'view-file-button' %></td>
11
+ </table>
12
+ <% end %>
13
+ </div>
14
+
15
+ <h4>说明:</h4>
16
+ <div>
17
+ <pre class="no-border">
18
+ 1. 只可下载log目录下文件
19
+ 2. 下载时文件名需要带上后缀如:production.log
20
+ 3. csv文件下载后会被删除,不能重复下载
21
+ </pre>
22
+ </div>
@@ -1,4 +1,4 @@
1
- $(".output-content").append('<p>============ 本次输出如下(仅代表最后行输出)===============</p>');
1
+ $(".output-content").append("<p>============ <%= Time.current %> 本次输出如下 ===============</p>");
2
2
  <% @results.each do |result| %>
3
3
  $(".output-content").append('<p><%= escape_javascript result %></p>');
4
4
  <% end %>
@@ -1,15 +1,42 @@
1
- 控制台 index
1
+ <h3>执行代码</h3>
2
2
 
3
- <div class="console">
3
+ <div class="wrapper-console">
4
4
  <%= form_tag(web_sandbox_console.eval_code_path, remote: true, class: 'form') do %>
5
- <%= text_area_tag :code, '', class: 'text_area_box'%>
6
- <input type="reset" value="重置" class='reset-button'>
7
- <%= submit_tag '提交', data: { disable_with: "已发送,处理中..." }, class: 'send-button' %>
5
+ <div class= "left">
6
+ <%= text_area_tag :code, '# 这里输入ruby代码...', class: 'text_area_box'%>
7
+ </div>
8
+
9
+ <div class="right">
10
+ <div style="position: absolute;bottom: 0;">
11
+ <% if session[:pass_auth] %>
12
+ <p class="tip">授权成功</p>
13
+ <p class="tip">(当前修改将写入数据库)</p>
14
+ <% end %>
15
+ <%= submit_tag '提交', data: { disable_with: "已发送,处理中..." }, class: 'send-button margin-t-10' %>
16
+ <%= submit_tag '异步执行', data: { disable_with: "已发送,处理中..." }, class: 'send-button margin-t-10' %>
17
+ </div>
18
+ </div>
8
19
  <% end %>
9
-
10
20
  </div>
11
21
 
12
- <%= button_tag '清除输出', type: 'button', onclick: "$('.output-content').empty();", class: 'clear-button' %>
22
+ <div style="margin-top: 50px;">
23
+ <%= button_tag '清除输出', type: 'button', onclick: "$('.output-content').empty();", class: 'clear-button' %>
24
+ <div>
13
25
 
14
26
  <div class="output-content">
15
27
  </div>
28
+
29
+
30
+ <script>
31
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
32
+ mode: "text/x-ruby",
33
+ matchBrackets: true,
34
+ indentUnit: 2,
35
+ indentWithTabs: true,
36
+ theme: 'lucario',
37
+ tabSize: 2,
38
+ lineNumbers: true,
39
+ lineWrapping: true, /* 自动换行 */
40
+ fixedGutter: false
41
+ });
42
+ </script>