servel 0.28.0 → 0.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/_gallery.haml +7 -6
- data/app/css/common.css +8 -0
- data/app/css/gallery.css +63 -39
- data/app/js/gallery.js +34 -7
- data/app/js/index.js +1 -45
- data/app/js/listing.js +1 -1
- data/bin/servel +1 -1
- data/lib/servel.rb +41 -1
- data/lib/servel/cli.rb +16 -18
- data/lib/servel/config_parser.rb +45 -0
- data/lib/servel/version.rb +1 -1
- data/servel.gemspec +1 -0
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36dac91c1fba1bbaa2ab70a7dc39404996216867968c93438f503ccc8dd15631
|
4
|
+
data.tar.gz: fedf2fb5c899e69de6715c40b64c4d7c652f6f01c6bec40c99cea1780ce6c16a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b52f97da999645b081ad452866fb032e841f073dbb9e3be5c28b4501062cca015d131872c237fc14cf5920421a0671ef8114d984c7bc320c4183f407da66cb3
|
7
|
+
data.tar.gz: 3d621057dd5499b01a69251a08e3d18c3dab29435fde99313f43484d0ae53ba443ca9aec6c16b71369a8e0a25dd5319bf029104cf7c385bf32913cc4d928cc92
|
data/app/_gallery.haml
CHANGED
@@ -6,9 +6,10 @@
|
|
6
6
|
#page-back.paginator ◀
|
7
7
|
#page-next.paginator ▶
|
8
8
|
#content
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
#scroller
|
10
|
+
%img#image
|
11
|
+
%video#video{controls: false, loop: true}
|
12
|
+
%audio#audio{controls: true}
|
13
|
+
#text
|
14
|
+
%a#text-anchor{href: '#'}
|
15
|
+
#text-content
|
data/app/css/common.css
CHANGED
data/app/css/gallery.css
CHANGED
@@ -1,25 +1,85 @@
|
|
1
1
|
#gallery {
|
2
2
|
background: #333;
|
3
3
|
display: none;
|
4
|
-
flex-direction: column;
|
5
4
|
}
|
6
5
|
|
7
6
|
body.has-gallery.gallery #gallery {
|
8
7
|
display: flex;
|
9
8
|
}
|
10
9
|
|
10
|
+
#controls {
|
11
|
+
display: flex;
|
12
|
+
}
|
13
|
+
|
14
|
+
#page-jump-listing {
|
15
|
+
flex-grow: 1;
|
16
|
+
}
|
17
|
+
|
18
|
+
.paginator {
|
19
|
+
display: flex;
|
20
|
+
align-items: center;
|
21
|
+
justify-content: center;
|
22
|
+
height: 75px;
|
23
|
+
|
24
|
+
font-size: 2em;
|
25
|
+
font-weight: bold;
|
26
|
+
text-align: center;
|
27
|
+
background-color: #1f1f1f;
|
28
|
+
color: #858585;
|
29
|
+
|
30
|
+
cursor: pointer;
|
31
|
+
}
|
32
|
+
|
33
|
+
.paginator:hover {
|
34
|
+
background-color: #0f0f0f;
|
35
|
+
color: #c1c1c1;
|
36
|
+
}
|
37
|
+
|
11
38
|
#content {
|
12
39
|
display: flex;
|
13
40
|
align-items: center;
|
14
41
|
justify-content: center;
|
15
42
|
flex-grow: 1;
|
43
|
+
min-width: 0;
|
44
|
+
min-height: 0;
|
45
|
+
}
|
46
|
+
|
47
|
+
#scroller {
|
48
|
+
width: 100%;
|
49
|
+
max-height: 100%;
|
50
|
+
overflow: auto;
|
51
|
+
}
|
52
|
+
|
53
|
+
body.portrait #gallery {
|
54
|
+
flex-direction: column;
|
55
|
+
}
|
56
|
+
|
57
|
+
body.portrait .paginator {
|
58
|
+
width: 100px;
|
59
|
+
}
|
60
|
+
|
61
|
+
body.portrait #page-jump-listing {
|
62
|
+
width: auto;
|
63
|
+
}
|
64
|
+
|
65
|
+
body.landscape #gallery {
|
66
|
+
flex-direction: row;
|
67
|
+
}
|
68
|
+
|
69
|
+
body.landscape #controls {
|
70
|
+
flex-direction: column;
|
71
|
+
flex-shrink: 0;
|
72
|
+
width: 100px;
|
73
|
+
}
|
74
|
+
|
75
|
+
body.landscape #page-jump-listing {
|
76
|
+
writing-mode: vertical-rl;
|
77
|
+
transform: rotate(180deg);
|
16
78
|
}
|
17
79
|
|
18
80
|
#image, #video, #audio, #text {
|
19
81
|
display: none;
|
20
|
-
max-width: 100%;
|
21
82
|
margin: 0 auto 0 auto;
|
22
|
-
padding-top: 75px;
|
23
83
|
}
|
24
84
|
|
25
85
|
#video:focus, #audio:focus {
|
@@ -51,36 +111,6 @@ body.has-gallery.gallery #gallery {
|
|
51
111
|
display: block;
|
52
112
|
}
|
53
113
|
|
54
|
-
#controls {
|
55
|
-
position: fixed;
|
56
|
-
left: 0;
|
57
|
-
right: 0;
|
58
|
-
display: flex;
|
59
|
-
z-index: 10;
|
60
|
-
}
|
61
|
-
|
62
|
-
.paginator {
|
63
|
-
display: flex;
|
64
|
-
align-items: center;
|
65
|
-
justify-content: center;
|
66
|
-
width: 100px;
|
67
|
-
height: 75px;
|
68
|
-
|
69
|
-
font-size: 2em;
|
70
|
-
font-weight: bold;
|
71
|
-
text-align: center;
|
72
|
-
background-color: #000000;
|
73
|
-
color: #ffffff;
|
74
|
-
|
75
|
-
opacity: 0.4;
|
76
|
-
cursor: pointer;
|
77
|
-
}
|
78
|
-
|
79
|
-
#page-jump-listing {
|
80
|
-
flex-grow: 1;
|
81
|
-
width: auto;
|
82
|
-
}
|
83
|
-
|
84
114
|
@media screen and (max-width: 767px) {
|
85
115
|
#gallery.video, #gallery.audio, #gallery.text {
|
86
116
|
padding: 0;
|
@@ -101,9 +131,3 @@ body.has-gallery.gallery #gallery {
|
|
101
131
|
display: none;
|
102
132
|
}
|
103
133
|
}
|
104
|
-
|
105
|
-
@media screen and (min-width: 768px) {
|
106
|
-
.paginator:hover {
|
107
|
-
opacity: 0.7;
|
108
|
-
}
|
109
|
-
}
|
data/app/js/gallery.js
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
3
|
var Gallery = (function() {
|
4
|
-
var LAYOUT_MODES = ["fit-both", "fit-width", "clamp-width"];
|
4
|
+
var LAYOUT_MODES = ["fit-both", "fit-width", "clamp-width", "full-size"];
|
5
5
|
|
6
|
+
var $body;
|
6
7
|
var $gallery;
|
8
|
+
var $image;
|
7
9
|
var $video;
|
8
10
|
var $audio;
|
9
11
|
var currentIndex;
|
@@ -155,15 +157,38 @@ var Gallery = (function() {
|
|
155
157
|
}
|
156
158
|
|
157
159
|
function layout() {
|
158
|
-
var
|
159
|
-
|
160
|
+
var vw = document.documentElement.clientWidth;
|
161
|
+
var vh = document.documentElement.clientHeight;
|
162
|
+
|
163
|
+
var viewportOrientation = vw > vh ? "landscape" : "portrait";
|
164
|
+
$body.classList.remove("landscape", "portrait");
|
165
|
+
$body.classList.add(viewportOrientation);
|
166
|
+
|
167
|
+
$gallery.style.height = vh + "px";
|
168
|
+
|
169
|
+
var scrollerMaxHeight = viewportOrientation == "landscape" ? vh : vh - 75;
|
160
170
|
|
161
171
|
var layoutMode = LAYOUT_MODES[layoutModeIndex];
|
162
|
-
var maxHeight = layoutMode == "fit-both" ? viewportHeight : "none";
|
163
|
-
var maxWidth = layoutMode == "clamp-width" ? "1000px" : "100%";
|
164
172
|
|
165
|
-
|
166
|
-
|
173
|
+
if(layoutMode == "fit-both") {
|
174
|
+
var maxWidth = "100%";
|
175
|
+
var maxHeight = (scrollerMaxHeight + "px");
|
176
|
+
}
|
177
|
+
else if(layoutMode == "fit-width") {
|
178
|
+
var maxWidth = "100%";
|
179
|
+
var maxHeight = "none";
|
180
|
+
}
|
181
|
+
else if(layoutMode == "clamp-width") {
|
182
|
+
var maxWidth = "1000px";
|
183
|
+
var maxHeight = "none";
|
184
|
+
}
|
185
|
+
else if(layoutMode == "full-size") {
|
186
|
+
var maxWidth = "none";
|
187
|
+
var maxHeight = "none";
|
188
|
+
}
|
189
|
+
|
190
|
+
$image.style.maxWidth = maxWidth;
|
191
|
+
$image.style.maxHeight = maxHeight;
|
167
192
|
$video.style.maxWidth = maxWidth;
|
168
193
|
$video.style.maxHeight = maxHeight;
|
169
194
|
$audio.style.maxWidth = maxWidth;
|
@@ -181,7 +206,9 @@ var Gallery = (function() {
|
|
181
206
|
}
|
182
207
|
|
183
208
|
function init() {
|
209
|
+
$body = $("body");
|
184
210
|
$gallery = $("#gallery");
|
211
|
+
$image = $("#image");
|
185
212
|
$video = $("#video");
|
186
213
|
$audio = $("#audio");
|
187
214
|
|
data/app/js/index.js
CHANGED
@@ -19,52 +19,8 @@ var Index = (function() {
|
|
19
19
|
document.body.classList.add("gallery");
|
20
20
|
}
|
21
21
|
|
22
|
-
function jumpScroll() {
|
23
|
-
if(galleryVisible()) {
|
24
|
-
jumpListing();
|
25
|
-
window.scrollTo(0, 0);
|
26
|
-
}
|
27
|
-
else {
|
28
|
-
jumpGallery();
|
29
|
-
window.scrollTo(0, document.body.scrollHeight);
|
30
|
-
}
|
31
|
-
}
|
32
|
-
|
33
|
-
function atTop() {
|
34
|
-
return window.scrollY == 0;
|
35
|
-
}
|
36
|
-
|
37
|
-
function bottomScrollDown(event) {
|
38
|
-
return galleryVisible() && atBottom() && event.deltaY > 0;
|
39
|
-
}
|
40
|
-
|
41
|
-
function topScrollUp(event) {
|
42
|
-
return listingVisible() && atTop() && event.deltaY < 0;
|
43
|
-
}
|
44
|
-
|
45
|
-
function initEvents() {
|
46
|
-
var scrollSize = 0;
|
47
|
-
|
48
|
-
window.addEventListener("wheel", function(event) {
|
49
|
-
if(bottomScrollDown(event) || topScrollUp(event)) {
|
50
|
-
scrollSize += Math.abs(event.deltaY);
|
51
|
-
|
52
|
-
if(scrollSize >= 1500) {
|
53
|
-
scrollSize = 0;
|
54
|
-
jumpScroll();
|
55
|
-
}
|
56
|
-
}
|
57
|
-
else {
|
58
|
-
scrollSize = 0;
|
59
|
-
}
|
60
|
-
});
|
61
|
-
}
|
62
|
-
|
63
22
|
function init() {
|
64
|
-
|
65
|
-
else jumpListing();
|
66
|
-
|
67
|
-
initEvents();
|
23
|
+
jumpListing();
|
68
24
|
}
|
69
25
|
|
70
26
|
return {
|
data/app/js/listing.js
CHANGED
@@ -27,7 +27,7 @@ var Listing = (function() {
|
|
27
27
|
function renderRow(file) {
|
28
28
|
return HTMLSafe`
|
29
29
|
<tr>
|
30
|
-
<td class="name">
|
30
|
+
<td class="name text-selectable">
|
31
31
|
<span class="icon">${file.icon}</span>
|
32
32
|
<a href="${file.href}" class="default ${file.class}" data-url="${file.href}" data-type="${file.mediaType}">${file.name}</a>
|
33
33
|
</td>
|
data/bin/servel
CHANGED
data/lib/servel.rb
CHANGED
@@ -1,20 +1,59 @@
|
|
1
1
|
require 'rack'
|
2
2
|
require 'rack/handler/puma'
|
3
|
+
require 'puma/configuration'
|
3
4
|
require 'hamlit'
|
4
5
|
require 'active_support/all'
|
5
6
|
require 'lru_redux'
|
7
|
+
require 'tty-config'
|
6
8
|
|
7
9
|
require 'thread'
|
8
10
|
require 'pathname'
|
9
11
|
require 'json'
|
12
|
+
require 'digest'
|
10
13
|
|
11
14
|
module Servel
|
12
|
-
def self.build_app(
|
15
|
+
def self.build_app(listings:, username: nil, password: nil)
|
16
|
+
app = build_listings_app(build_path_map(listings))
|
17
|
+
|
18
|
+
if username && username != ""
|
19
|
+
build_authentication_app(app: app, username: username, password: password)
|
20
|
+
else
|
21
|
+
app
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.build_authentication_app(app:, username:, password:)
|
26
|
+
hashed_username = Digest::SHA512.digest(username)
|
27
|
+
hashed_password = Digest::SHA512.digest(password)
|
28
|
+
|
29
|
+
Rack::Auth::Basic.new(app) do |submitted_username, submitted_password|
|
30
|
+
hashed_submitted_username = Digest::SHA512.digest(submitted_username)
|
31
|
+
hashed_submitted_password = Digest::SHA512.digest(submitted_password)
|
32
|
+
|
33
|
+
Rack::Utils.secure_compare(
|
34
|
+
"#{hashed_username}#{hashed_password}",
|
35
|
+
"#{hashed_submitted_username}#{hashed_submitted_password}"
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.build_path_map(listings)
|
41
|
+
listings.map do |listing|
|
42
|
+
listing = { listing => nil } if listing.is_a?(String)
|
43
|
+
|
44
|
+
root, url_root = listing.keys.first, listing.values.first || "/"
|
45
|
+
[Pathname.new(root).realpath, url_root]
|
46
|
+
end.to_h
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.build_listings_app(path_map)
|
13
50
|
url_map = path_map.map { |root, url_root| [url_root, Servel::App.new(root)] }.to_h
|
14
51
|
url_map["/"] = Servel::HomeApp.new(path_map.values) unless url_map.keys.include?("/")
|
15
52
|
|
16
53
|
Rack::URLMap.new(url_map)
|
17
54
|
end
|
55
|
+
|
56
|
+
|
18
57
|
end
|
19
58
|
|
20
59
|
require "servel/version"
|
@@ -25,4 +64,5 @@ require "servel/haml_context"
|
|
25
64
|
require "servel/index"
|
26
65
|
require "servel/app"
|
27
66
|
require "servel/home_app"
|
67
|
+
require "servel/config_parser"
|
28
68
|
require "servel/cli"
|
data/lib/servel/cli.rb
CHANGED
@@ -1,18 +1,16 @@
|
|
1
|
-
class Servel::CLI
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
1
|
+
class Servel::CLI
|
2
|
+
ALLOWED_PUMA_OPTIONS = [
|
3
|
+
:Host,
|
4
|
+
:Port,
|
5
|
+
:binds
|
6
|
+
]
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@config = Servel::ConfigParser.new.config
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
app = Servel.build_app(**@config.slice(:listings, :username, :password))
|
14
|
+
Rack::Handler::Puma.run(app, **@config.slice(*ALLOWED_PUMA_OPTIONS))
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Servel::ConfigParser
|
2
|
+
attr_reader :config
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@tty_config = TTY::Config.new
|
6
|
+
@tty_config.filename = "servel"
|
7
|
+
@tty_config.append_path(Dir.pwd)
|
8
|
+
@tty_config.append_path((Pathname.new(Dir.home) + ".servel").to_s)
|
9
|
+
@tty_config.append_path(Dir.home)
|
10
|
+
@tty_config.env_prefix = "servel"
|
11
|
+
@tty_config.autoload_env
|
12
|
+
@tty_config.read if @tty_config.exist?
|
13
|
+
|
14
|
+
@tty_config.append(*parse_argv, to: :listings)
|
15
|
+
parse_binds
|
16
|
+
|
17
|
+
@config = @tty_config.to_h.transform_keys(&:to_sym)
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_argv
|
21
|
+
ARGV.map do |arg|
|
22
|
+
root, url_root = arg.split(":" , 2)
|
23
|
+
{ root => url_root }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_binds
|
28
|
+
return parse_ssl_bind if @tty_config.fetch(:cert) && @tty_config.fetch(:key)
|
29
|
+
|
30
|
+
host = @tty_config.fetch(:host)
|
31
|
+
port = @tty_config.fetch(:port)
|
32
|
+
@tty_config.set(:Host, value: host) if host
|
33
|
+
@tty_config.set(:Port, value: port) if port
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_ssl_bind
|
37
|
+
host = @tty_config.fetch(:host, default: ::Puma::ConfigDefault::DefaultTCPHost)
|
38
|
+
port = @tty_config.fetch(:port, default: ::Puma::ConfigDefault::DefaultTCPPort)
|
39
|
+
key = Pathname.new(@tty_config.fetch(:key)).expand_path
|
40
|
+
cert = Pathname.new(@tty_config.fetch(:cert)).expand_path
|
41
|
+
|
42
|
+
query = URI.encode_www_form(key: key, cert: cert)
|
43
|
+
@tty_config.append("ssl://#{host}:#{port}?#{query}", to: :binds)
|
44
|
+
end
|
45
|
+
end
|
data/lib/servel/version.rb
CHANGED
data/servel.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: servel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.33.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brenton "B-Train" Fletcher
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: tty-config
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
description: Serves files and directories over HTTP.
|
112
126
|
email:
|
113
127
|
- i@bloople.net
|
@@ -143,6 +157,7 @@ files:
|
|
143
157
|
- lib/servel.rb
|
144
158
|
- lib/servel/app.rb
|
145
159
|
- lib/servel/cli.rb
|
160
|
+
- lib/servel/config_parser.rb
|
146
161
|
- lib/servel/entry.rb
|
147
162
|
- lib/servel/entry_factory.rb
|
148
163
|
- lib/servel/haml_context.rb
|
@@ -170,8 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
170
185
|
- !ruby/object:Gem::Version
|
171
186
|
version: '0'
|
172
187
|
requirements: []
|
173
|
-
|
174
|
-
rubygems_version: 2.7.6
|
188
|
+
rubygems_version: 3.0.3
|
175
189
|
signing_key:
|
176
190
|
specification_version: 4
|
177
191
|
summary: Serves files and directories over HTTP.
|