sqlui 0.1.60 → 0.1.62
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.release-version +1 -1
- data/app/database_config.rb +2 -2
- data/app/server.rb +40 -32
- data/app/sqlui_config.rb +5 -5
- data/app/views/databases.erb +4 -4
- data/app/views/error.erb +1 -1
- data/app/views/sqlui.erb +8 -6
- data/client/resources/sqlui.css +14 -10
- data/client/resources/sqlui.js +364 -212
- data/client/resources/vertical-resizer.svg +5 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ba6df74cbbe88d22279ec0a6a7cc9bf1ed191d695b84fcf424ee9b03a6b64cb
|
4
|
+
data.tar.gz: a9f53957b16860755a7d6d21980e573af93054bff997a205d791553b8b8dae59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9375a77c825b5036079d8e3d4b5df74e4370be1966441bc3a8a4a887e878fd58d79500187116c2492e3d27358084c0aaa70bd45f48aba5e387ac37c0d51e678
|
7
|
+
data.tar.gz: ac699985a8459edb63c8aeeca34c861471d468b9680fd767d8c69f14610c27d04d55feeaa07d2c0256fa0ee6148e4f659f9060b399847173fc4cbe67d3c9b6ea
|
data/.release-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.62
|
data/app/database_config.rb
CHANGED
@@ -14,8 +14,8 @@ class DatabaseConfig
|
|
14
14
|
@display_name = Args.fetch_non_empty_string(hash, :display_name).strip
|
15
15
|
@description = Args.fetch_non_empty_string(hash, :description).strip
|
16
16
|
@url_path = Args.fetch_non_empty_string(hash, :url_path).strip
|
17
|
-
raise ArgumentError, 'url_path should start with a /'
|
18
|
-
raise ArgumentError, 'url_path should not end with a /' if @url_path.
|
17
|
+
raise ArgumentError, 'url_path should not start with a /' if @url_path.start_with?('/')
|
18
|
+
raise ArgumentError, 'url_path should not end with a /' if @url_path.end_with?('/')
|
19
19
|
|
20
20
|
@saved_path = Args.fetch_non_empty_string(hash, :saved_path).strip
|
21
21
|
|
data/app/server.rb
CHANGED
@@ -25,6 +25,9 @@ class Server < Sinatra::Base
|
|
25
25
|
def self.init_and_run(config, resources_dir)
|
26
26
|
logger.info("Starting SQLUI v#{Version::SQLUI}")
|
27
27
|
logger.info("Airbrake enabled: #{config.airbrake[:server]&.[](:enabled) || false}")
|
28
|
+
|
29
|
+
WEBrick::HTTPRequest.const_set('MAX_URI_LENGTH', 2 * 1024 * 1024)
|
30
|
+
|
28
31
|
if config.airbrake[:server]&.[](:enabled)
|
29
32
|
require 'airbrake'
|
30
33
|
require 'airbrake/rack'
|
@@ -59,51 +62,53 @@ class Server < Sinatra::Base
|
|
59
62
|
set :raise_errors, false
|
60
63
|
set :show_exceptions, false
|
61
64
|
|
62
|
-
favicon_hash = Digest::MD5.hexdigest(File.read(File.join(resources_dir, 'favicon.svg')))
|
63
|
-
css_hash = Digest::MD5.hexdigest(File.read(File.join(resources_dir, 'sqlui.css')))
|
64
|
-
js_hash = Digest::MD5.hexdigest(File.read(File.join(resources_dir, 'sqlui.js')))
|
65
|
-
|
66
65
|
get '/-/health' do
|
67
66
|
status 200
|
68
67
|
body 'OK'
|
69
68
|
end
|
70
69
|
|
71
70
|
get '/?' do
|
72
|
-
redirect config.
|
71
|
+
redirect config.base_url_path, 301
|
73
72
|
end
|
74
73
|
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
resource_path_map = {}
|
75
|
+
Dir.glob(File.join(resources_dir, '*')).each do |file|
|
76
|
+
hash = Digest::MD5.hexdigest(File.read(file))
|
77
|
+
basename = File.basename(file)
|
78
|
+
url_path = "#{config.base_url_path}/#{basename}"
|
79
|
+
case File.extname(basename)
|
80
|
+
when '.svg'
|
81
|
+
content_type = 'image/svg+xml; charset=utf-8'
|
82
|
+
when '.css'
|
83
|
+
content_type = 'text/css; charset=utf-8'
|
84
|
+
when '.js'
|
85
|
+
content_type = 'text/javascript; charset=utf-8'
|
86
|
+
else
|
87
|
+
raise "unsupported resource file extension: #{File.extname(basename)}"
|
88
|
+
end
|
89
|
+
resource_path_map[basename] = "#{url_path}?#{hash}"
|
90
|
+
get url_path do
|
91
|
+
headers 'Content-Type' => content_type
|
92
|
+
headers 'Cache-Control' => 'max-age=31536000'
|
93
|
+
send_file file
|
94
|
+
end
|
78
95
|
end
|
79
96
|
|
80
|
-
get "#{config.
|
97
|
+
get "#{config.base_url_path}/?" do
|
81
98
|
headers 'Cache-Control' => 'no-cache'
|
82
|
-
erb :databases, locals: { config: config }
|
99
|
+
erb :databases, locals: { config: config, resource_path_map: resource_path_map }
|
83
100
|
end
|
84
101
|
|
85
102
|
config.database_configs.each do |database|
|
86
|
-
get "#{database.url_path}/?" do
|
103
|
+
get "#{config.base_url_path}/#{database.url_path}/?" do
|
87
104
|
redirect "#{database.url_path}/query", 301
|
88
105
|
end
|
89
106
|
|
90
|
-
|
91
|
-
headers 'Content-Type' => 'text/css; charset=utf-8'
|
92
|
-
headers 'Cache-Control' => 'max-age=31536000'
|
93
|
-
send_file File.join(resources_dir, 'sqlui.css')
|
94
|
-
end
|
95
|
-
|
96
|
-
get "#{database.url_path}/sqlui.js" do
|
97
|
-
headers 'Content-Type' => 'text/javascript; charset=utf-8'
|
98
|
-
headers 'Cache-Control' => 'max-age=31536000'
|
99
|
-
send_file File.join(resources_dir, 'sqlui.js')
|
100
|
-
end
|
101
|
-
|
102
|
-
post "#{database.url_path}/metadata" do
|
107
|
+
post "#{config.base_url_path}/#{database.url_path}/metadata" do
|
103
108
|
metadata = database.with_client do |client|
|
104
109
|
{
|
105
110
|
server: "#{config.name} - #{database.display_name}",
|
106
|
-
|
111
|
+
base_url_path: config.base_url_path,
|
107
112
|
schemas: DatabaseMetadata.lookup(client, database),
|
108
113
|
tables: database.tables,
|
109
114
|
columns: database.columns,
|
@@ -131,7 +136,7 @@ class Server < Sinatra::Base
|
|
131
136
|
body metadata.to_json
|
132
137
|
end
|
133
138
|
|
134
|
-
post "#{database.url_path}/query" do
|
139
|
+
post "#{config.base_url_path}/#{database.url_path}/query" do
|
135
140
|
data = request.body.read
|
136
141
|
request.body.rewind # since Airbrake will read the body on error
|
137
142
|
params.merge!(JSON.parse(data, symbolize_names: true))
|
@@ -215,7 +220,7 @@ class Server < Sinatra::Base
|
|
215
220
|
end
|
216
221
|
end
|
217
222
|
|
218
|
-
get "#{database.url_path}/download_csv" do
|
223
|
+
get "#{config.base_url_path}/#{database.url_path}/download_csv" do
|
219
224
|
break client_error('missing sql') unless params[:sql]
|
220
225
|
|
221
226
|
sql = Base64.decode64(params[:sql]).force_encoding('UTF-8')
|
@@ -250,7 +255,7 @@ class Server < Sinatra::Base
|
|
250
255
|
end
|
251
256
|
end
|
252
257
|
|
253
|
-
get(
|
258
|
+
get(/#{Regexp.escape("#{config.base_url_path}/#{database.url_path}/")}(query|graph|structure|saved)/) do
|
254
259
|
status 200
|
255
260
|
headers 'Cache-Control' => 'no-cache'
|
256
261
|
client_config = config.airbrake[:client] || {}
|
@@ -259,9 +264,7 @@ class Server < Sinatra::Base
|
|
259
264
|
airbrake_enabled: client_config[:enabled] || false,
|
260
265
|
airbrake_project_id: client_config[:project_id] || '',
|
261
266
|
airbrake_project_key: client_config[:project_key] || '',
|
262
|
-
|
263
|
-
css_hash: css_hash,
|
264
|
-
favicon_hash: favicon_hash
|
267
|
+
resource_path_map: resource_path_map
|
265
268
|
}
|
266
269
|
end
|
267
270
|
end
|
@@ -276,7 +279,12 @@ class Server < Sinatra::Base
|
|
276
279
|
body json
|
277
280
|
else
|
278
281
|
message = "#{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}"
|
279
|
-
erb :error, locals: {
|
282
|
+
erb :error, locals: {
|
283
|
+
resource_path_map: resource_path_map,
|
284
|
+
title: "SQLUI #{message}",
|
285
|
+
message: message,
|
286
|
+
stacktrace: stacktrace
|
287
|
+
}
|
280
288
|
end
|
281
289
|
end
|
282
290
|
|
data/app/sqlui_config.rb
CHANGED
@@ -8,7 +8,7 @@ require_relative 'deep'
|
|
8
8
|
|
9
9
|
# App config including database configs.
|
10
10
|
class SqluiConfig
|
11
|
-
attr_reader :name, :port, :environment, :
|
11
|
+
attr_reader :name, :port, :environment, :base_url_path, :database_configs, :airbrake
|
12
12
|
|
13
13
|
def initialize(filename, overrides = {})
|
14
14
|
config = YAML.safe_load(ERB.new(File.read(filename)).result, aliases: true).deep_merge!(overrides)
|
@@ -19,10 +19,10 @@ class SqluiConfig
|
|
19
19
|
@name = Args.fetch_non_empty_string(config, :name).strip
|
20
20
|
@port = Args.fetch_non_empty_int(config, :port)
|
21
21
|
@environment = Args.fetch_non_empty_string(config, :environment).strip
|
22
|
-
@
|
23
|
-
raise ArgumentError, '
|
24
|
-
if @
|
25
|
-
raise ArgumentError, '
|
22
|
+
@base_url_path = Args.fetch_non_empty_string(config, :base_url_path).strip
|
23
|
+
raise ArgumentError, 'base_url_path should start with a /' unless @base_url_path.start_with?('/')
|
24
|
+
if @base_url_path.length > 1 && @base_url_path.end_with?('/')
|
25
|
+
raise ArgumentError, 'base_url_path should not end with a /'
|
26
26
|
end
|
27
27
|
|
28
28
|
databases = Args.fetch_non_empty_hash(config, :databases)
|
data/app/views/databases.erb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<head>
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<title>SQLUI <%= config.name %> Databases</title>
|
6
|
-
<link rel="icon" type="image/x-icon" href="
|
6
|
+
<link rel="icon" type="image/x-icon" href="<%= resource_path_map['favicon.svg'] %>">
|
7
7
|
|
8
8
|
<style>
|
9
9
|
body {
|
@@ -92,9 +92,9 @@
|
|
92
92
|
<div class="database">
|
93
93
|
<div class="name-and-links">
|
94
94
|
<h2 class='name'><%= database_config.display_name %></h2>
|
95
|
-
<a class='query-link' href="<%= database_config.url_path
|
96
|
-
<a class='saved-link' href="<%= database_config.url_path
|
97
|
-
<a class='structure-link' href="<%= database_config.url_path
|
95
|
+
<a class='query-link' href="<%= "#{config.base_url_path}/#{database_config.url_path}/query" %>">query</a>
|
96
|
+
<a class='saved-link' href="<%= "#{config.base_url_path}/#{database_config.url_path}/saved" %>">saved</a>
|
97
|
+
<a class='structure-link' href="<%= "#{config.base_url_path}/#{database_config.url_path}/structure" %>">structure</a>
|
98
98
|
</div>
|
99
99
|
<p class='description'>
|
100
100
|
<%= database_config.description %>
|
data/app/views/error.erb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
<head>
|
5
5
|
<meta charset="utf-8">
|
6
6
|
<title><%= title %></title>
|
7
|
-
<link rel="icon" type="image/x-icon" href="
|
7
|
+
<link rel="icon" type="image/x-icon" href="<%= resource_path_map['favicon.svg'] %>">
|
8
8
|
</head>
|
9
9
|
<body style="font-family: monospace; font-size: 16px;">
|
10
10
|
|
data/app/views/sqlui.erb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
<head>
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<title>SQLUI</title>
|
6
|
-
<link rel="icon" type="image/x-icon" href="
|
6
|
+
<link rel="icon" type="image/x-icon" href="<%= resource_path_map['favicon.svg'] %>">
|
7
7
|
<!-- Initialize Airbrake before loading the main app JS so that we can catch errors as early as possible. -->
|
8
8
|
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@airbrake/browser"></script>
|
9
9
|
<script type="text/javascript">
|
@@ -19,9 +19,9 @@
|
|
19
19
|
window.airbrake?.notify(error)
|
20
20
|
}
|
21
21
|
</script>
|
22
|
-
<script type="text/javascript" src="sqlui.js
|
22
|
+
<script type="text/javascript" src="<%= resource_path_map['sqlui.js'] %>"></script>
|
23
23
|
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
24
|
-
<link rel="stylesheet" href="sqlui.css
|
24
|
+
<link rel="stylesheet" href="<%= resource_path_map['sqlui.css'] %>">
|
25
25
|
</head>
|
26
26
|
|
27
27
|
<body>
|
@@ -43,10 +43,12 @@
|
|
43
43
|
</div>
|
44
44
|
|
45
45
|
<div id="submit-box" class="tab-content-element graph-element query-element" style="display: none;">
|
46
|
-
<input id="cancel-button"
|
47
|
-
<div
|
46
|
+
<input id="cancel-button" type="button" value="cancel"></input>
|
47
|
+
<div id="editor-resizer">
|
48
|
+
<img src="<%= resource_path_map['vertical-resizer.svg'] %>" />
|
49
|
+
</div>
|
48
50
|
<div style="position: relative;">
|
49
|
-
<div
|
51
|
+
<div id="submit-button-wrapper">
|
50
52
|
<input id="submit-button-all" class="submit-button" type="button"></input>
|
51
53
|
<input id="submit-button-current" class="submit-button submit-button-show" type="button"></input>
|
52
54
|
<input id="submit-dropdown-button" class="submit-dropdown-button" type="button", value="▼"></input>
|
data/client/resources/sqlui.css
CHANGED
@@ -120,27 +120,35 @@ p {
|
|
120
120
|
padding: 5px;
|
121
121
|
}
|
122
122
|
|
123
|
-
|
123
|
+
#editor-resizer {
|
124
124
|
display: flex;
|
125
|
-
flex: 1
|
125
|
+
flex: 1;
|
126
|
+
cursor: row-resize;
|
127
|
+
flex-direction: column;
|
128
|
+
align-items: center;
|
126
129
|
}
|
127
130
|
|
128
|
-
|
131
|
+
#cancel-button {
|
129
132
|
cursor: pointer;
|
130
|
-
margin: 0;
|
131
133
|
background: none;
|
132
134
|
color: #333;
|
133
135
|
border: 1px solid #888;
|
134
136
|
height: 32px;
|
135
137
|
font-size: 18px;
|
138
|
+
margin: 0 220px 0 0; /* To center the resizer icon. Ok, it's a hack. Get over it. */
|
136
139
|
}
|
137
140
|
|
138
|
-
|
141
|
+
#cancel-button-spacer {
|
142
|
+
width: 150px;
|
143
|
+
}
|
144
|
+
|
145
|
+
#submit-button-wrapper {
|
139
146
|
cursor: pointer;
|
140
147
|
border: 1px solid #888;
|
141
148
|
height: 32px;
|
142
149
|
display: flex;
|
143
150
|
flex-direction: row;
|
151
|
+
background: white;
|
144
152
|
}
|
145
153
|
|
146
154
|
.submit-button, .submit-dropdown-button {
|
@@ -217,7 +225,7 @@ p {
|
|
217
225
|
|
218
226
|
.submit-dropdown-content-button:active,
|
219
227
|
.submit-button:active,
|
220
|
-
|
228
|
+
#cancel-button:active,
|
221
229
|
.submit-dropdown-button:active {
|
222
230
|
background-color: #e6e6e6;
|
223
231
|
outline: none
|
@@ -254,10 +262,6 @@ table tbody tr td {
|
|
254
262
|
height: 21px;
|
255
263
|
}
|
256
264
|
|
257
|
-
#result-table td, #result-table th {
|
258
|
-
cursor: default;
|
259
|
-
}
|
260
|
-
|
261
265
|
#result-table tbody tr td abbr a {
|
262
266
|
color: #555;
|
263
267
|
cursor: pointer;
|