markdownr 0.5.9 → 0.5.11
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/bin/Dockerfile.markdownr +5 -0
- data/bin/build-and-push-to-docker +12 -0
- data/bin/markdownr +10 -0
- data/lib/markdown_server/app.rb +107 -1
- data/lib/markdown_server/version.rb +1 -1
- data/views/admin_login.erb +24 -0
- data/views/layout.erb +4 -1
- data/views/setup_info.erb +28 -0
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 49c6809e18e2fe51a4e65446e185529edd529e61bb77e6eea79696b734ebdb8b
|
|
4
|
+
data.tar.gz: 9d533023489082e69413b4e718c27efa05076f1da863f25d9d68de26bd3ec090
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a2602173ee41f8d6034e410a8deba4fb11f5f3149e39c410dbc6366349dc93ba0bec519c6860e4970a7f14ed20dd5f8eeadf03d6452e821993e0d0450bebf19e
|
|
7
|
+
data.tar.gz: a5dc9a0bb156371fddcbdcdab626ee36b82079a175c56d967f7d4ed1f746d5f191bf709efbb7c0d4cb4f8419d072f51b1d92c8e39e90c8e643c220ebe57d4cbf
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
IMAGE="$DOCKER_HUB_USERNAME/markdownr:latest"
|
|
5
|
+
|
|
6
|
+
echo "$DOCKER_HUB_TOKEN" | docker login -u "$DOCKER_HUB_USERNAME" --password-stdin
|
|
7
|
+
|
|
8
|
+
docker build --no-cache -f bin/Dockerfile.markdownr -t "$IMAGE" .
|
|
9
|
+
|
|
10
|
+
docker push "$IMAGE"
|
|
11
|
+
|
|
12
|
+
docker logout
|
data/bin/markdownr
CHANGED
|
@@ -23,6 +23,14 @@ OptionParser.new do |opts|
|
|
|
23
23
|
options[:allow_robots] = true
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
opts.on("--behind-proxy", "Trust X-Forwarded-For for client IP (use when behind a reverse proxy like Caddy)") do
|
|
27
|
+
options[:behind_proxy] = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
opts.on("-i", "--index-file FILENAME", "Render this file instead of the directory listing when present (e.g. index.md, moc.md)") do |f|
|
|
31
|
+
options[:index_file] = f
|
|
32
|
+
end
|
|
33
|
+
|
|
26
34
|
opts.on("--no-link-tooltips", "Disable preview tooltips for local markdown links") do
|
|
27
35
|
options[:link_tooltips] = false
|
|
28
36
|
end
|
|
@@ -46,8 +54,10 @@ unless File.directory?(dir)
|
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
MarkdownServer::App.set :root_dir, dir
|
|
57
|
+
MarkdownServer::App.set :behind_proxy, options[:behind_proxy] || false
|
|
49
58
|
MarkdownServer::App.set :custom_title, options[:title]
|
|
50
59
|
MarkdownServer::App.set :allow_robots, options[:allow_robots] || false
|
|
60
|
+
MarkdownServer::App.set :index_file, options[:index_file]
|
|
51
61
|
MarkdownServer::App.set :link_tooltips, options.fetch(:link_tooltips, true)
|
|
52
62
|
MarkdownServer::App.set :hard_wrap, options.fetch(:hard_wrap, true)
|
|
53
63
|
MarkdownServer::App.set :port, options[:port]
|
data/lib/markdown_server/app.rb
CHANGED
|
@@ -9,6 +9,7 @@ require "cgi"
|
|
|
9
9
|
require "pathname"
|
|
10
10
|
require "set"
|
|
11
11
|
require "net/http"
|
|
12
|
+
require "base64"
|
|
12
13
|
|
|
13
14
|
module MarkdownServer
|
|
14
15
|
class App < Sinatra::Base
|
|
@@ -20,11 +21,15 @@ module MarkdownServer
|
|
|
20
21
|
set :root_dir, Dir.pwd
|
|
21
22
|
set :custom_title, nil
|
|
22
23
|
set :allow_robots, false
|
|
24
|
+
set :index_file, nil
|
|
23
25
|
set :link_tooltips, true
|
|
24
26
|
set :hard_wrap, true
|
|
25
27
|
set :show_exceptions, false
|
|
26
28
|
set :protection, false
|
|
27
29
|
set :host_authorization, { permitted_hosts: [] }
|
|
30
|
+
set :behind_proxy, false
|
|
31
|
+
set :session_secret, ENV.fetch("MARKDOWNR_SESSION_SECRET", SecureRandom.hex(64))
|
|
32
|
+
set :sessions, key: "markdownr_session", same_site: :strict, httponly: true
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
helpers do
|
|
@@ -721,6 +726,23 @@ module MarkdownServer
|
|
|
721
726
|
info_html + infl_html + usage_html + conc_html
|
|
722
727
|
end
|
|
723
728
|
|
|
729
|
+
def inline_directory_html(dir_path, relative_dir)
|
|
730
|
+
entries = Dir.entries(dir_path).reject { |e| e.start_with?(".") || EXCLUDED.include?(e) }
|
|
731
|
+
items = entries.map do |name|
|
|
732
|
+
stat = File.stat(File.join(dir_path, name)) rescue next
|
|
733
|
+
is_dir = stat.directory?
|
|
734
|
+
href = "/browse/" + (relative_dir.empty? ? "" : relative_dir + "/") +
|
|
735
|
+
encode_path_component(name) + (is_dir ? "/" : "")
|
|
736
|
+
{ name: name, is_dir: is_dir, href: href }
|
|
737
|
+
end.compact.sort_by { |i| [i[:is_dir] ? 0 : 1, i[:name].downcase] }
|
|
738
|
+
|
|
739
|
+
rows = items.map do |i|
|
|
740
|
+
%(<li><a href="#{h(i[:href])}"><span class="icon">#{icon_for(i[:name], i[:is_dir])}</span> ) +
|
|
741
|
+
%(#{h(i[:name])}#{i[:is_dir] ? "/" : ""}</a></li>)
|
|
742
|
+
end.join
|
|
743
|
+
%(<ul class="dir-listing">#{rows}</ul>)
|
|
744
|
+
end
|
|
745
|
+
|
|
724
746
|
def compile_regexes(query)
|
|
725
747
|
words = query.split(/\s+/).reject(&:empty?)
|
|
726
748
|
return nil if words.empty?
|
|
@@ -729,6 +751,43 @@ module MarkdownServer
|
|
|
729
751
|
raise RegexpError, e.message
|
|
730
752
|
end
|
|
731
753
|
|
|
754
|
+
def client_ip
|
|
755
|
+
if settings.behind_proxy
|
|
756
|
+
fwd = env["HTTP_X_FORWARDED_FOR"].to_s.split(",").map(&:strip).first
|
|
757
|
+
fwd && !fwd.empty? ? fwd : env["REMOTE_ADDR"]
|
|
758
|
+
else
|
|
759
|
+
env["REMOTE_ADDR"]
|
|
760
|
+
end
|
|
761
|
+
end
|
|
762
|
+
|
|
763
|
+
def setup_config
|
|
764
|
+
@setup_config ||= begin
|
|
765
|
+
path = File.join(root_dir, ".setup.yml")
|
|
766
|
+
(File.exist?(path) && YAML.safe_load(File.read(path))) || {}
|
|
767
|
+
rescue StandardError
|
|
768
|
+
{}
|
|
769
|
+
end
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
def admin?
|
|
773
|
+
return true if session[:admin]
|
|
774
|
+
|
|
775
|
+
adm = setup_config["admin"]
|
|
776
|
+
return false unless adm.is_a?(Hash)
|
|
777
|
+
|
|
778
|
+
return true if adm["ip"].to_s.strip == client_ip
|
|
779
|
+
|
|
780
|
+
if adm["user"] && adm["pw"]
|
|
781
|
+
auth = request.env["HTTP_AUTHORIZATION"].to_s
|
|
782
|
+
if auth.start_with?("Basic ")
|
|
783
|
+
user, pw = Base64.decode64(auth[6..]).split(":", 2)
|
|
784
|
+
return true if user == adm["user"].to_s && pw == adm["pw"].to_s
|
|
785
|
+
end
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
false
|
|
789
|
+
end
|
|
790
|
+
|
|
732
791
|
end
|
|
733
792
|
|
|
734
793
|
# Routes
|
|
@@ -746,6 +805,41 @@ module MarkdownServer
|
|
|
746
805
|
redirect "/browse/"
|
|
747
806
|
end
|
|
748
807
|
|
|
808
|
+
get "/setup-info" do
|
|
809
|
+
@title = "Setup Info"
|
|
810
|
+
@client_ip = client_ip
|
|
811
|
+
@is_admin = admin?
|
|
812
|
+
@served_path = root_dir if @is_admin
|
|
813
|
+
@public_config = setup_config.reject { |k, _| k == "admin" }
|
|
814
|
+
erb :setup_info
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
get "/admin/login" do
|
|
818
|
+
@title = "Admin Login"
|
|
819
|
+
@error = session.delete(:login_error)
|
|
820
|
+
@return_to = params[:return_to]
|
|
821
|
+
erb :admin_login
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
post "/admin/login" do
|
|
825
|
+
adm = setup_config["admin"]
|
|
826
|
+
if adm.is_a?(Hash) &&
|
|
827
|
+
params[:username] == adm["user"].to_s &&
|
|
828
|
+
params[:password] == adm["pw"].to_s
|
|
829
|
+
session[:admin] = true
|
|
830
|
+
return_to = params[:return_to].to_s
|
|
831
|
+
redirect(return_to.start_with?("/") ? return_to : "/")
|
|
832
|
+
else
|
|
833
|
+
session[:login_error] = "Invalid username or password."
|
|
834
|
+
redirect "/admin/login"
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
get "/admin/logout" do
|
|
839
|
+
session.clear
|
|
840
|
+
redirect "/"
|
|
841
|
+
end
|
|
842
|
+
|
|
749
843
|
get "/browse/?*" do
|
|
750
844
|
requested = params["splat"].first.to_s
|
|
751
845
|
requested = requested.chomp("/")
|
|
@@ -757,7 +851,14 @@ module MarkdownServer
|
|
|
757
851
|
end
|
|
758
852
|
|
|
759
853
|
if File.directory?(real_path)
|
|
760
|
-
|
|
854
|
+
index = settings.index_file
|
|
855
|
+
index_path = index && File.join(real_path, index)
|
|
856
|
+
if index_path && File.file?(index_path)
|
|
857
|
+
index_rel = requested.empty? ? index : "#{requested}/#{index}"
|
|
858
|
+
render_file(index_path, index_rel)
|
|
859
|
+
else
|
|
860
|
+
render_directory(real_path, requested)
|
|
861
|
+
end
|
|
761
862
|
else
|
|
762
863
|
render_file(real_path, requested)
|
|
763
864
|
end
|
|
@@ -1022,6 +1123,11 @@ module MarkdownServer
|
|
|
1022
1123
|
@meta, body = parse_frontmatter(content)
|
|
1023
1124
|
@current_wiki_dir = File.dirname(real_path)
|
|
1024
1125
|
@content = render_markdown(body)
|
|
1126
|
+
relative_dir = File.dirname(relative_path)
|
|
1127
|
+
relative_dir = "" if relative_dir == "."
|
|
1128
|
+
listing = -> { inline_directory_html(File.dirname(real_path), relative_dir) }
|
|
1129
|
+
@content.gsub!("<p>{{directory}}</p>") { listing.call }
|
|
1130
|
+
@content.gsub!("<p>{{admin-directory}}</p>") { admin? ? listing.call : "" }
|
|
1025
1131
|
@toc = extract_toc(@content)
|
|
1026
1132
|
@has_toc = @toc.length > 1
|
|
1027
1133
|
erb :markdown
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<div class="title-bar">
|
|
2
|
+
<h1 class="page-title">Admin Login</h1>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<div class="md-content" style="max-width: 360px;">
|
|
6
|
+
<% if @error %>
|
|
7
|
+
<p style="color: #b33; margin-bottom: 1rem;"><%= h(@error) %></p>
|
|
8
|
+
<% end %>
|
|
9
|
+
|
|
10
|
+
<form method="post" action="/admin/login">
|
|
11
|
+
<input type="hidden" name="return_to" value="<%= h(@return_to.to_s) %>">
|
|
12
|
+
<table class="meta-table" style="margin-bottom: 1rem;">
|
|
13
|
+
<tr>
|
|
14
|
+
<th class="blb-th">Username</th>
|
|
15
|
+
<td><input type="text" name="username" autofocus style="width:100%;padding:2px 4px;"></td>
|
|
16
|
+
</tr>
|
|
17
|
+
<tr>
|
|
18
|
+
<th class="blb-th">Password</th>
|
|
19
|
+
<td><input type="password" name="password" style="width:100%;padding:2px 4px;"></td>
|
|
20
|
+
</tr>
|
|
21
|
+
</table>
|
|
22
|
+
<button type="submit" style="padding:4px 16px;">Log in</button>
|
|
23
|
+
</form>
|
|
24
|
+
</div>
|
data/views/layout.erb
CHANGED
|
@@ -1219,6 +1219,7 @@
|
|
|
1219
1219
|
var touchStartX = 0;
|
|
1220
1220
|
var touchStartY = 0;
|
|
1221
1221
|
var touchCurrentX = 0;
|
|
1222
|
+
var touchStartedOnTable = false;
|
|
1222
1223
|
var isDragging = false;
|
|
1223
1224
|
|
|
1224
1225
|
function openDrawer() {
|
|
@@ -1277,6 +1278,7 @@
|
|
|
1277
1278
|
touchStartY = e.touches[0].clientY;
|
|
1278
1279
|
touchCurrentX = touchStartX;
|
|
1279
1280
|
isDragging = false;
|
|
1281
|
+
touchStartedOnTable = !!e.target.closest('table');
|
|
1280
1282
|
}, { passive: true });
|
|
1281
1283
|
|
|
1282
1284
|
document.addEventListener('touchmove', function(e) {
|
|
@@ -1315,7 +1317,8 @@
|
|
|
1315
1317
|
if (dy > 80) return; // Too vertical
|
|
1316
1318
|
|
|
1317
1319
|
if (!isOpen && dx < -threshold) {
|
|
1318
|
-
// Swipe left — open drawer
|
|
1320
|
+
// Swipe left — open drawer (not when swipe started on a table)
|
|
1321
|
+
if (touchStartedOnTable) return;
|
|
1319
1322
|
openDrawer();
|
|
1320
1323
|
} else if (isOpen && dx > threshold) {
|
|
1321
1324
|
// Swipe right — close drawer
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<div class="title-bar">
|
|
2
|
+
<h1 class="page-title">Setup Info</h1>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<div class="md-content">
|
|
6
|
+
<% if @is_admin %>
|
|
7
|
+
<p><strong>You are an administrator.</strong></p>
|
|
8
|
+
<% end %>
|
|
9
|
+
|
|
10
|
+
<table class="meta-table">
|
|
11
|
+
<tr><th>Your IP</th><td><%= h(@client_ip) %></td></tr>
|
|
12
|
+
<% if @served_path %>
|
|
13
|
+
<tr><th>Serving</th><td><%= h(@served_path) %></td></tr>
|
|
14
|
+
<% end %>
|
|
15
|
+
</table>
|
|
16
|
+
|
|
17
|
+
<% unless @public_config.empty? %>
|
|
18
|
+
<h3>Configuration</h3>
|
|
19
|
+
<table class="meta-table">
|
|
20
|
+
<% @public_config.each do |key, value| %>
|
|
21
|
+
<tr>
|
|
22
|
+
<th><%= h(key.to_s) %></th>
|
|
23
|
+
<td><%= h(value.to_s) %></td>
|
|
24
|
+
</tr>
|
|
25
|
+
<% end %>
|
|
26
|
+
</table>
|
|
27
|
+
<% end %>
|
|
28
|
+
</div>
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: markdownr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Dunn
|
|
@@ -100,15 +100,19 @@ executables:
|
|
|
100
100
|
extensions: []
|
|
101
101
|
extra_rdoc_files: []
|
|
102
102
|
files:
|
|
103
|
+
- bin/Dockerfile.markdownr
|
|
104
|
+
- bin/build-and-push-to-docker
|
|
103
105
|
- bin/markdownr
|
|
104
106
|
- lib/markdown_server.rb
|
|
105
107
|
- lib/markdown_server/app.rb
|
|
106
108
|
- lib/markdown_server/version.rb
|
|
109
|
+
- views/admin_login.erb
|
|
107
110
|
- views/directory.erb
|
|
108
111
|
- views/layout.erb
|
|
109
112
|
- views/markdown.erb
|
|
110
113
|
- views/raw.erb
|
|
111
114
|
- views/search.erb
|
|
115
|
+
- views/setup_info.erb
|
|
112
116
|
homepage: https://github.com/brianmd/markdown-server
|
|
113
117
|
licenses:
|
|
114
118
|
- MIT
|