web_sandbox_console 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +45 -11
- data/app/assets/images/web_sandbox_console/logo-icon.png +0 -0
- data/app/assets/javascripts/web_sandbox_console/application.js +0 -1
- data/app/assets/stylesheets/web_sandbox_console/application.css +9 -6
- data/app/assets/stylesheets/web_sandbox_console/common.css +218 -0
- data/app/controllers/web_sandbox_console/application_controller.rb +4 -0
- data/app/controllers/web_sandbox_console/authorization_controller.rb +0 -2
- data/app/controllers/web_sandbox_console/home_controller.rb +32 -6
- data/app/views/layouts/web_sandbox_console/application.html.erb +75 -11
- data/app/views/web_sandbox_console/authorization/auth_page.html.erb +3 -5
- data/app/views/web_sandbox_console/home/do_view_file.js.erb +9 -0
- data/app/views/web_sandbox_console/home/download_page.html.erb +22 -0
- data/app/views/web_sandbox_console/home/index.html.erb +1 -5
- data/app/views/web_sandbox_console/home/view_file.html.erb +2 -3
- data/config/routes.rb +4 -0
- data/lib/generators/web_sandbox_console/templates/web_sandbox_console.rb +12 -5
- data/lib/generators/web_sandbox_console/templates/web_sandbox_console.yml +5 -0
- data/lib/generators/web_sandbox_console/web_sandbox_console_generator.rb +5 -0
- data/lib/web_sandbox_console/configuration.rb +1 -1
- data/lib/web_sandbox_console/safe_ruby.rb +39 -0
- data/lib/web_sandbox_console/sandbox.rb +19 -5
- data/lib/web_sandbox_console/version.rb +1 -1
- data/lib/web_sandbox_console/view_file.rb +66 -16
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92242034900727caa3f1920df25ef3fc88b886b647d0444431708016785d422a
|
4
|
+
data.tar.gz: 1216442a9f5408aa82218e3dc8ad73e5e896a03b2c9caaa59bb90dc64c6cc09a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0bc65c60e3e7657b4475b54c93fbf533c9468ffdb5b3af468bf3d4a9f7aef37dc04c938076b00908e37c41d672a99a38a7de81683649004483af21e162fec721
|
7
|
+
data.tar.gz: 98d09ce9a324f83fb7be0ca158d8090252b6b7ef359b32ff595ad43aa96052b7d40334d679d8a2a6bf9312756d421391418fb11cae18c6cb42b9a593c1699ef7
|
data/README.md
CHANGED
@@ -19,7 +19,7 @@ $ bundle
|
|
19
19
|
```
|
20
20
|
|
21
21
|
此时,如果是在本地,你访问 `http://localhost:3000/web_sandbox_console` 就能看到web控制台了,下面这个样子。
|
22
|
-

|
23
23
|
|
24
24
|
## 配置
|
25
25
|
```
|
@@ -33,10 +33,15 @@ $ bundle
|
|
33
33
|
$ rails g web_sandbox_console
|
34
34
|
```
|
35
35
|
|
36
|
-
|
36
|
+
这会在项目路径下,创建两个配置文件
|
37
37
|
```ruby
|
38
38
|
# config/initializers/web_sandbox_console.rb
|
39
39
|
|
40
|
+
require 'yaml'
|
41
|
+
|
42
|
+
config_file_path = "#{Rails.root}/config/web_sandbox_console.yml"
|
43
|
+
config_hash = File.exists?(config_file_path) ? YAML.load_file(config_file_path).with_indifferent_access[:web_sandbox_console] : {}
|
44
|
+
|
40
45
|
# web_sandbox_console 配置文件
|
41
46
|
# 以下配置 都是可选的 缺少的情况下用默认值 或者 不生效
|
42
47
|
WebSandboxConsole.setup do |config|
|
@@ -46,8 +51,10 @@ WebSandboxConsole.setup do |config|
|
|
46
51
|
# 配置 ip 白名单
|
47
52
|
# config.ip_whitelist = %w(192.168.23.12 192.145.2.0/24)
|
48
53
|
|
49
|
-
#
|
50
|
-
#
|
54
|
+
# 配置 基本认证 在 config/web_sandbox_console.yml中配置
|
55
|
+
# PS: 1. 即使config/web_sandbox_console.yml文件不存在,也不会有任何使用上的影响,效果相当于没有开启
|
56
|
+
# 2. 下面这行不用注释掉,只要不配置yml文件就行
|
57
|
+
config.http_basic_auth = config_hash[:http_basic_auth]
|
51
58
|
|
52
59
|
# # 配置 黑名单 类方法
|
53
60
|
# config.class_method_blacklist = {File: %i(delete read write),Dir: %i(new delete mkdir)}
|
@@ -59,19 +66,32 @@ WebSandboxConsole.setup do |config|
|
|
59
66
|
# 默认都是项目路径下的
|
60
67
|
# config.view_file_blacklist = %w(config/secrets.yml vendor/)
|
61
68
|
|
62
|
-
# 配置 文件权限,是否仅能查看log
|
69
|
+
# 配置 文件权限,是否仅能查看log文件,默认开启
|
63
70
|
#config.only_view_log_file = false
|
64
71
|
|
65
72
|
# 通过非对称加密方式 升级权限,授权通过后,可获得执行数据权限(PS: 数据操作不再回滚)
|
66
|
-
#
|
73
|
+
# PS:配置同 http_basic_auth
|
74
|
+
config.public_key = config_hash[:public_key]
|
67
75
|
|
68
76
|
# # 配置 日志路径 默认路径位于项目下
|
69
77
|
# config.console_log_path = "log/web_sandbox_console.log"
|
70
78
|
end
|
71
79
|
```
|
72
80
|
|
81
|
+
主要用于配置 http_basic_auth 和 public_key,如需使用基本授权、升级授权,参照example文件创建yml文件即可
|
82
|
+
```ruby
|
83
|
+
# config/web_sandbox_console.yml.example
|
84
|
+
|
85
|
+
web_sandbox_console:
|
86
|
+
http_basic_auth:
|
87
|
+
name: dmy
|
88
|
+
password: 123456
|
89
|
+
public_key: "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMbJOE1vQT1jFpaH1GPYzdRJN/\nLh8VePmzXs5BYOLHB0xIjArL1NlXMbCJ+AS2rv3/oHIOdHhEuZw0tmm9DhG100R8\nRjBpsEKCDI88jl9qRkFmD3CVk8XQXv6c2IkRZCYSTvgDkmnKAlORksfw+p0cR2AQ\nlAtAsNsNviKYBzXKfQIDAQAB\n-----END PUBLIC KEY-----\n"
|
90
|
+
|
91
|
+
```
|
92
|
+
|
73
93
|
## 深入了解
|
74
|
-
|
94
|
+
主要包含三大功能块:代码执行、文件查看、日志下载,下面分别介绍
|
75
95
|
|
76
96
|
### 代码执行
|
77
97
|
1. 提交和异步执行
|
@@ -79,16 +99,17 @@ end
|
|
79
99
|
> 提交后代码会立即执行;如果点击异步执行,则代码会在后台异步执行,这对于需要执行非常耗时的代码,强烈建议异步执行;
|
80
100
|
|
81
101
|
2. 升级权限
|
82
|
-

|
103
|
+
|
83
104
|
```
|
84
105
|
通常你能做的操作就是,查查数据等,数据的所有操作都不会写入到数据库,这样很安全;但是每当你需要修改数据时,还是需要让运维处理;为了满足可以数据写入、和安全性的要求;开发了升级权限这个功能。
|
85
106
|
|
86
|
-
|
107
|
+
升级权限你需要在配置文件中配置公钥,自己保存私钥;整个过程采用非对称加密的方式,进行授权,还是比较安全的。
|
87
108
|
升级授权成功后,代码执行将不再回滚,会直接写入数据库。
|
88
109
|
```
|
89
110
|
|
90
111
|
升级权限流程如下:
|
91
|
-
> - `config/
|
112
|
+
> - `config/web_sandbox_console.yml`中配置公钥
|
92
113
|
> - 进入授权页面,点击获取令牌 `web_sandbox_console/auth_page`
|
93
114
|
> - 用私钥对令牌加密,然后用base64加密
|
94
115
|
> - 将加密的打印结果(注意是puts 文本),输入加密密文框,提交
|
@@ -106,7 +127,7 @@ puts encode_text
|
|
106
127
|
```
|
107
128
|
|
108
129
|
### 文件查看
|
109
|
-

|
110
131
|
1. 目录和文件
|
111
132
|
|
112
133
|
> 你可以查看一个目录下有哪些文件夹或文件,你也可以直接查看文件的内容,默认返回一个文件的前100行
|
@@ -128,6 +149,19 @@ PS: a. 在过滤文件(过滤内容/过滤时间)的时候,此时指定行
|
|
128
149
|
|
129
150
|
> 默认情况,只能查看日志文件或目录,当然你也可以去配置做调整。
|
130
151
|
|
152
|
+
### 日志下载
|
153
|
+

|
154
|
+
你可以直接输入文件名(需要带后缀)下载日志
|
155
|
+
|
156
|
+
### 关于数据导出
|
157
|
+
在gem 中你可用`CSV.open`的方式导出数据,出于安全考虑,这里open方法是复写之后的,内部没有调用`File.open`方法;
|
158
|
+
|
159
|
+
有一下几点需注意:
|
160
|
+
> 1. 你写入你任何路径,最终都只会在log目录下创建csv文件
|
161
|
+
> 2. 文件名以你路径中(/分隔)的最后一个名称为准
|
162
|
+
> 3. csv 文件下载后,会自动删除掉,因此只能下载一次
|
163
|
+
|
164
|
+
|
131
165
|
## Contributing
|
132
166
|
Contribution directions go here.
|
133
167
|
|
Binary file
|
@@ -19,7 +19,7 @@
|
|
19
19
|
}
|
20
20
|
|
21
21
|
.tip {
|
22
|
-
color:
|
22
|
+
color: #E35520;
|
23
23
|
font-size: 12px;
|
24
24
|
}
|
25
25
|
|
@@ -64,7 +64,8 @@
|
|
64
64
|
}
|
65
65
|
|
66
66
|
.output-content{
|
67
|
-
height:
|
67
|
+
min-height: 800px;
|
68
|
+
height: 100%;
|
68
69
|
font-size: 14px;
|
69
70
|
background-color: #272822;
|
70
71
|
color: #F8F8F2;
|
@@ -83,14 +84,14 @@
|
|
83
84
|
}
|
84
85
|
|
85
86
|
.clear-button{
|
86
|
-
background:
|
87
|
+
background: #20aee3;
|
87
88
|
height: 30px;
|
88
89
|
width: 100px;
|
89
90
|
font-size: 15px;
|
90
91
|
color: white;
|
91
92
|
cursor: pointer;
|
92
93
|
border-radius: 5px;
|
93
|
-
border:
|
94
|
+
border: #20aee3;
|
94
95
|
}
|
95
96
|
|
96
97
|
.send-button{
|
@@ -103,19 +104,21 @@
|
|
103
104
|
}
|
104
105
|
|
105
106
|
.reset-button{
|
106
|
-
background:
|
107
|
+
background: #20aee3;
|
107
108
|
color: white;
|
108
109
|
height: 30px;
|
109
110
|
font-size: 15px;
|
110
111
|
color: 15px;
|
111
112
|
cursor: pointer;
|
112
|
-
border:
|
113
|
+
border: #20aee3;
|
113
114
|
}
|
114
115
|
|
115
116
|
button,input{
|
116
117
|
outline:none;
|
117
118
|
}
|
118
119
|
|
120
|
+
|
121
|
+
|
119
122
|
button:focus,input:focus,textarea:focus {
|
120
123
|
outline: none;
|
121
124
|
border: 1px solid #272822;
|
@@ -0,0 +1,218 @@
|
|
1
|
+
body {
|
2
|
+
background: #fff;
|
3
|
+
font-family: "Montserrat", sans-serif;
|
4
|
+
margin: 0;
|
5
|
+
overflow-x: hidden;
|
6
|
+
color: #67757c;
|
7
|
+
font-weight: 300;
|
8
|
+
font-size: 14px;
|
9
|
+
}
|
10
|
+
|
11
|
+
* {
|
12
|
+
outline: none;
|
13
|
+
}
|
14
|
+
|
15
|
+
a{ text-decoration:none}
|
16
|
+
|
17
|
+
#main-wrapper {
|
18
|
+
width: 100%;
|
19
|
+
overflow: hidden;
|
20
|
+
}
|
21
|
+
|
22
|
+
.topbar {
|
23
|
+
position: fixed;
|
24
|
+
width: 100%;
|
25
|
+
z-index: 50;
|
26
|
+
background: #ffffff;
|
27
|
+
}
|
28
|
+
|
29
|
+
.topbar .top-navbar {
|
30
|
+
min-height: 70px;
|
31
|
+
padding: 0px;
|
32
|
+
}
|
33
|
+
|
34
|
+
.topbar .top-navbar .navbar-header {
|
35
|
+
line-height: 72px;
|
36
|
+
padding-left: 10px;
|
37
|
+
border-right: 1px solid rgba(120, 130, 140, 0.13);
|
38
|
+
}
|
39
|
+
|
40
|
+
.navbar-header {
|
41
|
+
width: 200px;
|
42
|
+
flex-shrink: 0;
|
43
|
+
}
|
44
|
+
|
45
|
+
.topbar .top-navbar .navbar-header .navbar-brand {
|
46
|
+
margin-right: 0px;
|
47
|
+
padding-bottom: 0px;
|
48
|
+
padding-top: 0px;
|
49
|
+
}
|
50
|
+
|
51
|
+
.navbar-brand {
|
52
|
+
display: inline-block;
|
53
|
+
padding-top: .3125rem;
|
54
|
+
padding-bottom: .3125rem;
|
55
|
+
margin-right: 1rem;
|
56
|
+
font-size: 1.25rem;
|
57
|
+
line-height: inherit;
|
58
|
+
white-space: nowrap;
|
59
|
+
color: rgba(0,0,0,.9);
|
60
|
+
}
|
61
|
+
|
62
|
+
.navbar-light .navbar-brand {
|
63
|
+
color: rgba(0,0,0,.9);
|
64
|
+
}
|
65
|
+
|
66
|
+
.topbar .navbar-collapse {
|
67
|
+
padding: 0px;
|
68
|
+
border-bottom: 1px solid rgba(120, 130, 140, 0.13);
|
69
|
+
display: flex!important;
|
70
|
+
align-items: center;
|
71
|
+
}
|
72
|
+
|
73
|
+
.left-sidebar {
|
74
|
+
position: fixed;
|
75
|
+
width: 210px;
|
76
|
+
height: 100%;
|
77
|
+
top: 0px;
|
78
|
+
z-index: 20;
|
79
|
+
padding-top: 70px;
|
80
|
+
background: #fff;
|
81
|
+
border-right: 1px solid rgba(120, 130, 140, 0.13);
|
82
|
+
}
|
83
|
+
|
84
|
+
.ps {
|
85
|
+
-ms-touch-action: auto;
|
86
|
+
touch-action: auto;
|
87
|
+
overflow: hidden !important;
|
88
|
+
-ms-overflow-style: none;
|
89
|
+
}
|
90
|
+
|
91
|
+
.scroll-sidebar {
|
92
|
+
height: 100%;
|
93
|
+
}
|
94
|
+
|
95
|
+
.sidebar-nav {
|
96
|
+
background: #fff;
|
97
|
+
padding: 15px 0 0 0px;
|
98
|
+
}
|
99
|
+
|
100
|
+
.sidebar-nav ul {
|
101
|
+
margin: 0px;
|
102
|
+
padding: 0px;
|
103
|
+
}
|
104
|
+
|
105
|
+
.sidebar-nav > ul > li {
|
106
|
+
margin-bottom: 8px;
|
107
|
+
margin-top: 8px;
|
108
|
+
}
|
109
|
+
|
110
|
+
.sidebar-nav ul li {
|
111
|
+
list-style: none;
|
112
|
+
}
|
113
|
+
|
114
|
+
.sidebar-nav > ul > li > a {
|
115
|
+
border-left: 3px solid transparent;
|
116
|
+
}
|
117
|
+
|
118
|
+
.sidebar-nav > ul > li > a:hover {
|
119
|
+
color: #20aee3;
|
120
|
+
font-size: 16px;
|
121
|
+
}
|
122
|
+
|
123
|
+
.menu-active {
|
124
|
+
color: #20aee3;
|
125
|
+
font-size: 16px;
|
126
|
+
}
|
127
|
+
|
128
|
+
.sidebar-nav ul li a {
|
129
|
+
color: #8d97ad;
|
130
|
+
padding: 10px 35px 10px 15px;
|
131
|
+
display: block;
|
132
|
+
font-size: 15px;
|
133
|
+
font-weight: 400;
|
134
|
+
}
|
135
|
+
|
136
|
+
.waves-effect {
|
137
|
+
position: relative;
|
138
|
+
cursor: pointer;
|
139
|
+
display: inline-block;
|
140
|
+
overflow: hidden;
|
141
|
+
-webkit-user-select: none;
|
142
|
+
-moz-user-select: none;
|
143
|
+
-ms-user-select: none;
|
144
|
+
user-select: none;
|
145
|
+
-webkit-tap-highlight-color: transparent;
|
146
|
+
vertical-align: middle;
|
147
|
+
z-index: 1;
|
148
|
+
will-change: opacity, transform;
|
149
|
+
}
|
150
|
+
|
151
|
+
.topbar .top-navbar .navbar-header .navbar-brand b {
|
152
|
+
line-height: 70px;
|
153
|
+
display: inline-block;
|
154
|
+
font-weight: bolder;
|
155
|
+
}
|
156
|
+
|
157
|
+
img {
|
158
|
+
vertical-align: middle;
|
159
|
+
border-style: none;
|
160
|
+
}
|
161
|
+
|
162
|
+
.sidebar-nav > ul > li > a i {
|
163
|
+
color: #787f91;
|
164
|
+
}
|
165
|
+
|
166
|
+
.sidebar-nav > ul > li > a i {
|
167
|
+
width: 38px;
|
168
|
+
font-size: 24px;
|
169
|
+
display: inline-block;
|
170
|
+
vertical-align: middle;
|
171
|
+
color: #787f91;
|
172
|
+
}
|
173
|
+
|
174
|
+
.fa {
|
175
|
+
display: inline-block;
|
176
|
+
font: normal normal normal 14px/1 FontAwesome;
|
177
|
+
font-size: inherit;
|
178
|
+
text-rendering: auto;
|
179
|
+
-webkit-font-smoothing: antialiased;
|
180
|
+
-moz-osx-font-smoothing: grayscale;
|
181
|
+
}
|
182
|
+
|
183
|
+
.page-wrapper {
|
184
|
+
background: #f6f9fa;
|
185
|
+
padding-bottom: 60px;
|
186
|
+
position: relative;
|
187
|
+
margin-left: 200px;
|
188
|
+
/*padding-top: 70px;*/
|
189
|
+
z-index: 100
|
190
|
+
}
|
191
|
+
|
192
|
+
.container-fluid {
|
193
|
+
padding: 25px 25px;
|
194
|
+
}
|
195
|
+
|
196
|
+
.container-fluid {
|
197
|
+
width: 100%;
|
198
|
+
margin-right: auto;
|
199
|
+
margin-left: auto;
|
200
|
+
padding-right: 15px;
|
201
|
+
padding-left: 15px;
|
202
|
+
width: 100%;
|
203
|
+
}
|
204
|
+
|
205
|
+
.navbar-nav {
|
206
|
+
display: -ms-flexbox;
|
207
|
+
display: flex;
|
208
|
+
-ms-flex-direction: column;
|
209
|
+
flex-direction: column;
|
210
|
+
padding-left: 0;
|
211
|
+
margin-bottom: 0;
|
212
|
+
list-style: none;
|
213
|
+
flex-direction: row;
|
214
|
+
}
|
215
|
+
|
216
|
+
.mr-auto {
|
217
|
+
margin-right: auto!important;
|
218
|
+
}
|
@@ -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
|
@@ -2,8 +2,6 @@ require_dependency "web_sandbox_console/application_controller"
|
|
2
2
|
|
3
3
|
module WebSandboxConsole
|
4
4
|
class AuthorizationController < 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
|
before_action :restrict_fetch_token_times, only: :fetch_token
|
8
6
|
|
9
7
|
# 获取令牌
|
@@ -2,9 +2,7 @@ require_dependency "web_sandbox_console/application_controller"
|
|
2
2
|
|
3
3
|
module WebSandboxConsole
|
4
4
|
class HomeController < ApplicationController
|
5
|
-
|
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
|
|
@@ -24,9 +22,37 @@ module WebSandboxConsole
|
|
24
22
|
|
25
23
|
# 查看文件
|
26
24
|
def do_view_file
|
27
|
-
results
|
28
|
-
@lines
|
29
|
-
@total_line_num
|
25
|
+
results = ViewFile.new(params).view
|
26
|
+
@lines = results[:lines]
|
27
|
+
@total_line_num = results[:total_line_num]
|
28
|
+
@touch_grep_protect = results[:touch_grep_protect]
|
29
|
+
@content_is_trimed = results[:content_is_trimed]
|
30
|
+
end
|
31
|
+
|
32
|
+
# 下载文件页面
|
33
|
+
def download_page
|
30
34
|
end
|
35
|
+
|
36
|
+
# 下载文件
|
37
|
+
def download
|
38
|
+
if params[:file_name].blank?
|
39
|
+
flash[:notice] = "文件名不能为空"
|
40
|
+
return redirect_to download_page_path
|
41
|
+
end
|
42
|
+
|
43
|
+
file_full_path = "#{Rails.root}/log/#{params[:file_name]}"
|
44
|
+
unless File.exists?(file_full_path)
|
45
|
+
flash[:notice] = '文件不存在,请检查文件名;或在其它服务器请多次尝试'
|
46
|
+
return redirect_to download_page_path
|
47
|
+
end
|
48
|
+
|
49
|
+
# 打包
|
50
|
+
`tar czf #{file_full_path}.tar.gz #{file_full_path}`
|
51
|
+
# 如果是csv文件,需删除
|
52
|
+
File.delete(file_full_path) if file_full_path.split(".").last == 'csv'
|
53
|
+
|
54
|
+
send_file "#{file_full_path}.tar.gz"
|
55
|
+
end
|
56
|
+
|
31
57
|
end
|
32
58
|
end
|
@@ -1,16 +1,80 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
|
-
<head>
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
</head>
|
9
|
-
<body>
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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;">运维控制台</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 <%= action_name == 'auth_page' ? 'menu-active' : '' %>">说明文档</span>
|
63
|
+
<% end %>
|
64
|
+
</li>
|
65
|
+
</ul>
|
66
|
+
</nav>
|
67
|
+
|
68
|
+
</div>
|
69
|
+
</aside>
|
70
|
+
|
71
|
+
<div class="page-wrapper" style="min-height: 800px;">
|
72
|
+
<div class="container-fluid">
|
73
|
+
<%= yield %>
|
74
|
+
</div>
|
75
|
+
</div>
|
76
|
+
</div>
|
77
|
+
</body>
|
14
78
|
</html>
|
15
79
|
|
16
80
|
<script type="text/javascript">
|
@@ -1,12 +1,10 @@
|
|
1
|
-
|
1
|
+
<h3>升级授权</h3>
|
2
|
+
|
2
3
|
<p>
|
3
|
-
<%= link_to '
|
4
|
-
<%= link_to '获取令牌', web_sandbox_console.fetch_token_path(:format => "js"), remote: true %>
|
4
|
+
<%= link_to '获取令牌', fetch_token_path,format: 'json', remote: true %>
|
5
5
|
<span> PS: 注意获取令牌一天最多20次,有效期仅5分钟</span>
|
6
6
|
</p>
|
7
7
|
|
8
|
-
|
9
|
-
<h2>授权申请</h1>
|
10
8
|
<div class="">
|
11
9
|
<%= form_tag web_sandbox_console.auth_path, method: :post do %>
|
12
10
|
<table>
|
@@ -2,6 +2,15 @@ $(".output-content").empty();
|
|
2
2
|
<% line_num_show = @total_line_num ? "原始文件总行数:#{@total_line_num}" : "" %>
|
3
3
|
|
4
4
|
$(".output-content").append("<p>============ <%= Time.current %> 查找结果如下 <%= line_num_show %> ===============</p>");
|
5
|
+
|
6
|
+
<% if @touch_grep_protect %>
|
7
|
+
$(".output-content").append("<p>PS: 由于过滤执行时间太长,已触发过滤保护,返回内容为最后1000行</p>");
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<% if @content_is_trimed %>
|
11
|
+
$(".output-content").append("<p>PS: 当前返回内容太多,仅展示满足条件的1000行</p>");
|
12
|
+
<% end %>
|
13
|
+
|
5
14
|
<% @lines.each do |result| %>
|
6
15
|
$(".output-content").append('<pre><%= escape_javascript result %></pre>');
|
7
16
|
<% 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>
|
18
|
+
1. 只可下载log目录下文件
|
19
|
+
2. 下载时文件名需要带上后缀如:production.log
|
20
|
+
3. csv文件下载后会被删除,不能重复下载
|
21
|
+
</pre>
|
22
|
+
</div>
|
@@ -1,8 +1,4 @@
|
|
1
|
-
|
2
|
-
<%= link_to '查看文件', web_sandbox_console.view_file_path %>
|
3
|
-
<% unless session[:pass_auth] %>
|
4
|
-
<%= link_to '升级授权', web_sandbox_console.auth_page_path %>
|
5
|
-
<% end %>
|
1
|
+
<h3>执行代码</h3>
|
6
2
|
|
7
3
|
<div class="console margin-b-20">
|
8
4
|
<%= form_tag(web_sandbox_console.eval_code_path, remote: true, class: 'form') do %>
|
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
<%= link_to '控制台', web_sandbox_console.root_path %>
|
1
|
+
<h3>查看文件</h3>
|
3
2
|
<div class="view_file">
|
4
3
|
<%= form_tag do_view_file_path, method: :post, remote: true do %>
|
5
4
|
<table>
|
@@ -28,7 +27,7 @@
|
|
28
27
|
</tr>
|
29
28
|
</table>
|
30
29
|
<% end %>
|
31
|
-
<span class="tip"
|
30
|
+
<span class="tip">注意:过滤时将忽略筛选行数;过滤内容中不能出现单引号</span>
|
32
31
|
</div>
|
33
32
|
|
34
33
|
<div class="output-content">
|
data/config/routes.rb
CHANGED
@@ -9,5 +9,9 @@ WebSandboxConsole::Engine.routes.draw do
|
|
9
9
|
get :fetch_token, to: "authorization#fetch_token"
|
10
10
|
get :auth_page, to: "authorization#auth_page"
|
11
11
|
post :auth, to: "authorization#auth"
|
12
|
+
|
13
|
+
# 文件下载
|
14
|
+
get :download_page, to: "home#download_page"
|
15
|
+
get :download, to: "home#download"
|
12
16
|
end
|
13
17
|
|
@@ -1,3 +1,8 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
config_file_path = "#{Rails.root}/config/web_sandbox_console.yml"
|
4
|
+
config_hash = File.exists?(config_file_path) ? YAML.load_file(config_file_path).with_indifferent_access[:web_sandbox_console] : {}
|
5
|
+
|
1
6
|
# web_sandbox_console 配置文件
|
2
7
|
# 以下配置 都是可选的 缺少的情况下用默认值 或者 不生效
|
3
8
|
WebSandboxConsole.setup do |config|
|
@@ -7,8 +12,10 @@ WebSandboxConsole.setup do |config|
|
|
7
12
|
# 配置 ip 白名单
|
8
13
|
# config.ip_whitelist = %w(192.168.23.12 192.145.2.0/24)
|
9
14
|
|
10
|
-
#
|
11
|
-
#
|
15
|
+
# 配置 基本认证 在 config/web_sandbox_console.yml中配置
|
16
|
+
# PS: 1. 即使config/web_sandbox_console.yml文件不存在,也不会有任何使用上的影响,效果相当于没有开启
|
17
|
+
# 2. 下面这行不用注释掉,只要不配置yml文件就行
|
18
|
+
config.http_basic_auth = config_hash[:http_basic_auth]
|
12
19
|
|
13
20
|
# # 配置 黑名单 类方法
|
14
21
|
# config.class_method_blacklist = {File: %i(delete read write),Dir: %i(new delete mkdir)}
|
@@ -20,13 +27,13 @@ WebSandboxConsole.setup do |config|
|
|
20
27
|
# 默认都是项目路径下的
|
21
28
|
# config.view_file_blacklist = %w(config/secrets.yml vendor/)
|
22
29
|
|
23
|
-
# 配置 文件权限,是否仅能查看log
|
30
|
+
# 配置 文件权限,是否仅能查看log文件,默认开启
|
24
31
|
#config.only_view_log_file = false
|
25
32
|
|
26
33
|
# 通过非对称加密方式 升级权限,授权通过后,可获得执行数据权限(PS: 数据操作不再回滚)
|
27
|
-
#
|
34
|
+
# PS:配置同 http_basic_auth
|
35
|
+
config.public_key = config_hash[:public_key]
|
28
36
|
|
29
37
|
# # 配置 日志路径 默认路径位于项目下
|
30
38
|
# config.console_log_path = "log/web_sandbox_console.log"
|
31
39
|
end
|
32
|
-
|
@@ -0,0 +1,5 @@
|
|
1
|
+
web_sandbox_console:
|
2
|
+
http_basic_auth:
|
3
|
+
name: dmy
|
4
|
+
password: 123456
|
5
|
+
public_key: "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMbJOE1vQT1jFpaH1GPYzdRJN/\nLh8VePmzXs5BYOLHB0xIjArL1NlXMbCJ+AS2rv3/oHIOdHhEuZw0tmm9DhG100R8\nRjBpsEKCDI88jl9qRkFmD3CVk8XQXv6c2IkRZCYSTvgDkmnKAlORksfw+p0cR2AQ\nlAtAsNsNviKYBzXKfQIDAQAB\n-----END PUBLIC KEY-----\n"
|
@@ -5,4 +5,9 @@ class WebSandboxConsoleGenerator < Rails::Generators::Base
|
|
5
5
|
# 源文件 目标位置
|
6
6
|
copy_file "web_sandbox_console.rb", "config/initializers/web_sandbox_console.rb"
|
7
7
|
end
|
8
|
+
|
9
|
+
# 生成配置yaml文件
|
10
|
+
def copy_config_file
|
11
|
+
copy_file "web_sandbox_console.yml", "config/web_sandbox_console.yml.example"
|
12
|
+
end
|
8
13
|
end
|
@@ -5,6 +5,8 @@ module WebSandboxConsole
|
|
5
5
|
sanitize_constants
|
6
6
|
sanitize_instance_methods
|
7
7
|
sanitize_class_methods
|
8
|
+
sanitize_logger_new
|
9
|
+
sanitize_csv
|
8
10
|
end
|
9
11
|
|
10
12
|
# 净化 类方法
|
@@ -66,5 +68,42 @@ module WebSandboxConsole
|
|
66
68
|
hash1
|
67
69
|
end
|
68
70
|
|
71
|
+
# 发现代码 中有 Logger.new(Rails.root.join('log', 'hubar')) 写法, 会 触发 File.open方法
|
72
|
+
# 封装后避免调用 File.open(禁用)
|
73
|
+
def sanitize_logger_new
|
74
|
+
Logger.instance_eval do
|
75
|
+
def new(logdev, shift_age = 0, shift_size = 1048576)
|
76
|
+
instance = allocate
|
77
|
+
instance.send(:initialize, logdev.to_s, shift_age, shift_size)
|
78
|
+
instance
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# 净化 csv
|
84
|
+
def sanitize_csv
|
85
|
+
require 'csv' unless defined? CSV
|
86
|
+
|
87
|
+
CSV.instance_eval do
|
88
|
+
# 重写方法 以写日志方式 写数据
|
89
|
+
def open(filename, mode="r", **options)
|
90
|
+
# 无论输入什么路径 都只会在log下创建文件
|
91
|
+
basename = File.basename(filename, ".*")
|
92
|
+
file_path = "#{Rails.root}/log/#{basename}.csv"
|
93
|
+
logger = Logger.new(file_path)
|
94
|
+
logger.formatter = proc {|severity, datetime, progname, msg| msg}
|
95
|
+
|
96
|
+
logger.instance_exec do
|
97
|
+
# 支持类型 csv 数据写入方式
|
98
|
+
def << (data_arr)
|
99
|
+
self.info data_arr.join(",") + "\n"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
yield(logger)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
69
108
|
end
|
70
109
|
end
|
@@ -46,10 +46,7 @@ module WebSandboxConsole
|
|
46
46
|
WebSandboxConsole.init_safe_env
|
47
47
|
result = nil
|
48
48
|
begin
|
49
|
-
|
50
|
-
result = eval(#{self.code.inspect})
|
51
|
-
raise ActiveRecord::Rollback unless #{self.pass_auth}
|
52
|
-
end
|
49
|
+
#{self.pass_auth ? no_rollback_code : rollback_code}
|
53
50
|
rescue Exception => e
|
54
51
|
WebSandboxConsole.log_p(e, "#{self.uuid}")
|
55
52
|
rescue SyntaxError => e
|
@@ -58,6 +55,23 @@ module WebSandboxConsole
|
|
58
55
|
WebSandboxConsole.log_p(result, "#{self.uuid}")
|
59
56
|
CODE
|
60
57
|
end
|
58
|
+
|
59
|
+
# 回滚code
|
60
|
+
def rollback_code
|
61
|
+
<<-EOF
|
62
|
+
ActiveRecord::Base.transaction(requires_new: true) do
|
63
|
+
result = eval(#{self.code.inspect})
|
64
|
+
raise ActiveRecord::Rollback
|
65
|
+
end
|
66
|
+
EOF
|
67
|
+
end
|
68
|
+
|
69
|
+
# 不回滚code
|
70
|
+
def no_rollback_code
|
71
|
+
<<-EOF
|
72
|
+
result = eval(#{self.code.inspect})
|
73
|
+
EOF
|
74
|
+
end
|
61
75
|
|
62
76
|
# 临时文件目录
|
63
77
|
def tmp_file_dir
|
@@ -102,7 +116,7 @@ module WebSandboxConsole
|
|
102
116
|
|
103
117
|
# 运行rails runner
|
104
118
|
def exec_rails_runner
|
105
|
-
@stdout = `bundle exec rails runner #{self.exe_tmp_file}`
|
119
|
+
@stdout = `RAILS_ENV=#{Rails.env} bundle exec rails runner #{self.exe_tmp_file}`
|
106
120
|
end
|
107
121
|
|
108
122
|
# 返回结果
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module WebSandboxConsole
|
2
2
|
class ViewFile
|
3
|
-
attr_accessor :file_or_dir
|
4
|
-
attr_accessor :start_line_num
|
5
|
-
attr_accessor :end_line_num
|
6
|
-
attr_accessor :sed_start_time
|
7
|
-
attr_accessor :sed_end_time
|
8
|
-
attr_accessor :grep_content
|
3
|
+
attr_accessor :file_or_dir # 文件或目录
|
4
|
+
attr_accessor :start_line_num # 起始行数
|
5
|
+
attr_accessor :end_line_num # 结束行数
|
6
|
+
attr_accessor :sed_start_time # 开始时间
|
7
|
+
attr_accessor :sed_end_time # 结束时间
|
8
|
+
attr_accessor :grep_content # 过滤内容
|
9
|
+
attr_accessor :touch_grep_protect # 是否触发过滤保护
|
10
|
+
attr_accessor :content_is_trimed # 内容是否有裁剪
|
9
11
|
|
10
12
|
def initialize(opts = {})
|
11
13
|
@file_or_dir = opts[:file_or_dir]
|
@@ -14,6 +16,8 @@ module WebSandboxConsole
|
|
14
16
|
@sed_start_time = parse_time(opts[:sed_start_time])
|
15
17
|
@sed_end_time = parse_time(opts[:sed_end_time])
|
16
18
|
@grep_content = opts[:grep_content]
|
19
|
+
@touch_grep_protect = false
|
20
|
+
@content_is_trimed = false
|
17
21
|
end
|
18
22
|
|
19
23
|
def view
|
@@ -31,6 +35,7 @@ module WebSandboxConsole
|
|
31
35
|
# 检查参数
|
32
36
|
def check_param
|
33
37
|
raise ViewFileError, '文件或目录参数不能为空' if file_or_dir.blank?
|
38
|
+
raise ViewFileError, "过滤内容中不能出现单引号" if grep_content.to_s.include?("'")
|
34
39
|
end
|
35
40
|
|
36
41
|
# 转换成项目路径
|
@@ -119,8 +124,15 @@ module WebSandboxConsole
|
|
119
124
|
else
|
120
125
|
special_line_content
|
121
126
|
end
|
122
|
-
|
123
|
-
|
127
|
+
# 修剪行数
|
128
|
+
lines = content_trim(lines)
|
129
|
+
|
130
|
+
{
|
131
|
+
lines: add_line_num(lines),
|
132
|
+
total_line_num: cal_file_total_line_num,
|
133
|
+
touch_grep_protect: @touch_grep_protect,
|
134
|
+
content_is_trimed: @content_is_trimed
|
135
|
+
}
|
124
136
|
end
|
125
137
|
end
|
126
138
|
|
@@ -129,33 +141,71 @@ module WebSandboxConsole
|
|
129
141
|
`wc -l < #{file_or_dir_path}`.to_i
|
130
142
|
end
|
131
143
|
|
132
|
-
# 最后 xx
|
144
|
+
# 最后 xx 行
|
133
145
|
def tail_any_line(num)
|
134
|
-
(
|
146
|
+
tail_any_line_content(num).split("\n")
|
147
|
+
end
|
148
|
+
|
149
|
+
# 最后多少行内容
|
150
|
+
def tail_any_line_content(num)
|
151
|
+
`tail -n #{num} #{file_or_dir_path}`
|
135
152
|
end
|
136
153
|
|
137
154
|
# 按指定行返回
|
138
155
|
def special_line_content
|
139
156
|
File.readlines(file_or_dir_path)[(start_line_num - 1)..(end_line_num - 1)]
|
140
157
|
end
|
158
|
+
|
159
|
+
# 过滤超时保护
|
160
|
+
def grep_timeout_protect
|
161
|
+
begin
|
162
|
+
Timeout::timeout(8) {yield}
|
163
|
+
rescue Timeout::Error => e
|
164
|
+
# 触发过滤保护
|
165
|
+
@touch_grep_protect = true
|
166
|
+
tail_any_line_content(1000)
|
167
|
+
end
|
168
|
+
end
|
141
169
|
|
142
170
|
# 过滤文件
|
143
171
|
def grep_file_content
|
144
172
|
content = if @sed_start_time && @grep_content.present?
|
145
|
-
`sed -n '/#{@sed_start_time}/,/#{@sed_end_time}/p' #{file_or_dir_path} |
|
173
|
+
grep_timeout_protect {`sed -n '/#{@sed_start_time}/,/#{@sed_end_time}/p' #{file_or_dir_path} | fgrep '#{@grep_content}'`}
|
146
174
|
elsif @sed_start_time
|
147
|
-
`sed -n '/#{@sed_start_time}/,/#{@sed_end_time}/p' #{file_or_dir_path}`
|
175
|
+
grep_timeout_protect {`sed -n '/#{@sed_start_time}/,/#{@sed_end_time}/p' #{file_or_dir_path}`}
|
148
176
|
else
|
149
|
-
`
|
177
|
+
grep_timeout_protect {`fgrep '#{@grep_content}' #{file_or_dir_path}`}
|
178
|
+
end
|
179
|
+
content.split("\n")
|
180
|
+
end
|
181
|
+
|
182
|
+
# 修剪过滤内容
|
183
|
+
def content_trim(lines)
|
184
|
+
if lines.length > 1000
|
185
|
+
@content_is_trimed = true
|
186
|
+
# 内容太多时 只返回前1000行
|
187
|
+
lines.first(1000)
|
188
|
+
else
|
189
|
+
lines
|
150
190
|
end
|
151
|
-
|
152
|
-
content.split(/[\r,\r\n]/)
|
153
191
|
end
|
154
192
|
|
155
193
|
# 添加行号
|
156
194
|
def add_line_num(lines)
|
157
195
|
start_num = is_big_file? ? 1 : start_line_num
|
158
|
-
lines.each_with_index.map
|
196
|
+
lines.each_with_index.map do |line, index|
|
197
|
+
line = "#{index + start_num}: #{line}"
|
198
|
+
hightlight_grep_content(line)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# 高亮内容
|
203
|
+
def hightlight_grep_content(content)
|
204
|
+
if @grep_content.present?
|
205
|
+
content.gsub(@grep_content.to_s, "<span style='color: #E35520;'> #{@grep_content}</span>").html_safe
|
206
|
+
else
|
207
|
+
content
|
208
|
+
end
|
159
209
|
end
|
160
210
|
|
161
211
|
private
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: web_sandbox_console
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- dongmingyan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -50,9 +50,11 @@ files:
|
|
50
50
|
- README.md
|
51
51
|
- Rakefile
|
52
52
|
- app/assets/config/web_sandbox_console_manifest.js
|
53
|
+
- app/assets/images/web_sandbox_console/logo-icon.png
|
53
54
|
- app/assets/javascripts/web_sandbox_console/application.js
|
54
55
|
- app/assets/javascripts/web_sandbox_console/home.js
|
55
56
|
- app/assets/stylesheets/web_sandbox_console/application.css
|
57
|
+
- app/assets/stylesheets/web_sandbox_console/common.css
|
56
58
|
- app/assets/stylesheets/web_sandbox_console/home.css
|
57
59
|
- app/controllers/web_sandbox_console/application_controller.rb
|
58
60
|
- app/controllers/web_sandbox_console/authorization_controller.rb
|
@@ -65,12 +67,14 @@ files:
|
|
65
67
|
- app/views/web_sandbox_console/authorization/auth_page.html.erb
|
66
68
|
- app/views/web_sandbox_console/authorization/fetch_token.js.erb
|
67
69
|
- app/views/web_sandbox_console/home/do_view_file.js.erb
|
70
|
+
- app/views/web_sandbox_console/home/download_page.html.erb
|
68
71
|
- app/views/web_sandbox_console/home/eval_code.js.erb
|
69
72
|
- app/views/web_sandbox_console/home/index.html.erb
|
70
73
|
- app/views/web_sandbox_console/home/view_file.html.erb
|
71
74
|
- config/routes.rb
|
72
75
|
- lib/generators/web_sandbox_console/USAGE
|
73
76
|
- lib/generators/web_sandbox_console/templates/web_sandbox_console.rb
|
77
|
+
- lib/generators/web_sandbox_console/templates/web_sandbox_console.yml
|
74
78
|
- lib/generators/web_sandbox_console/web_sandbox_console_generator.rb
|
75
79
|
- lib/tasks/web_sandbox_console_tasks.rake
|
76
80
|
- lib/web_sandbox_console.rb
|