From 042df2bf1fea55a62bcebd61c89eb8df7ecc12bb Mon Sep 17 00:00:00 2001 From: Alex Ling Date: Fri, 14 Feb 2020 00:57:39 +0000 Subject: [PATCH] - --- src/auth_handler.cr | 13 ++++++-- src/config.cr | 6 ++-- src/library.cr | 2 +- src/mango.cr | 76 +++++++++++++++++++++++++++++++------------- src/storage.cr | 16 ++++++++-- src/views/admin.ecr | 24 ++++++++++++-- src/views/index.ecr | 2 ++ src/views/layout.ecr | 39 ++++++++++++++++++++--- src/views/title.ecr | 1 + src/views/user.ecr | 23 ++++++++++++-- 10 files changed, 162 insertions(+), 40 deletions(-) diff --git a/src/auth_handler.cr b/src/auth_handler.cr index 4224dd5..70cddac 100644 --- a/src/auth_handler.cr +++ b/src/auth_handler.cr @@ -1,6 +1,15 @@ require "kemal" require "./storage" +def request_path_startswith(env, ary) + ary.each do |prefix| + if env.request.path.starts_with? prefix + return true + end + end + return false +end + class AuthHandler < Kemal::Handler exclude ["/login"] exclude ["/login"], "POST" @@ -18,9 +27,9 @@ class AuthHandler < Kemal::Handler return env.redirect "/login" end - if env.request.path.starts_with? "/admin" + if request_path_startswith env, ["/admin", "/api/admin"] unless storage.verify_admin cookie.value - env.response.status_code = 401 + return env.response.status_code = 401 end end diff --git a/src/config.cr b/src/config.cr index aad2bc3..be9be41 100644 --- a/src/config.cr +++ b/src/config.cr @@ -9,10 +9,10 @@ class Config property port = 9000 @[YAML::Field(key: "library_path")] - property library_path = File.expand_path "~/mango-library", home: true + property library_path = File.expand_path "~/mango/library", home: true @[YAML::Field(key: "db_path")] - property db_path = File.expand_path "~/mango-library/mango.db", home: true + property db_path = File.expand_path "~/mango/mango.db", home: true def self.load cfg_path = File.expand_path "~/.config/mango/config.yml", home: true @@ -22,7 +22,7 @@ class Config puts "The config file #{cfg_path} does not exist." \ "Do you want mango to dump the default config there? [Y/n]" input = gets - if !input.nil? && input.downcase == "n" + if input && input.downcase == "n" abort "Aborting..." end default = self.allocate diff --git a/src/library.cr b/src/library.cr index 6315770..7d1f6ec 100644 --- a/src/library.cr +++ b/src/library.cr @@ -79,7 +79,7 @@ class Library def initialize(dir : String) @dir = dir unless Dir.exists? dir - abort "ERROR: The library directory #{dir} does not exist" + Dir.mkdir_p dir end @titles = (Dir.entries dir) .select! { |path| File.directory? File.join dir, path } diff --git a/src/mango.cr b/src/mango.cr index 8cffbc6..fb292cf 100644 --- a/src/mango.cr +++ b/src/mango.cr @@ -8,7 +8,6 @@ config = Config.load library = Library.new config.library_path storage = Storage.new config.db_path - macro layout(name) render "src/views/#{{{name}}}.ecr", "src/views/layout.ecr" end @@ -23,11 +22,14 @@ macro get_username(env) storage.verify_token cookie.value end -def hash_to_query(hash) - hash.map { |k, v| "#{k}=#{v}" } - .join("&") +macro send_json(env, json) + {{env}}.response.content_type = "application/json" + {{json}} end +def hash_to_query(hash) + hash.map { |k, v| "#{k}=#{v}" }.join("&") +end get "/" do |env| titles = library.titles @@ -53,6 +55,7 @@ get "/admin/user" do |env| layout "user" end + get "/admin/user/edit" do |env| username = env.params.query["username"]? admin = env.params.query["admin"]? @@ -76,9 +79,16 @@ post "/admin/user/edit" do |env| if username.size < 3 raise "Username should contain at least 3 characters" end + if (username =~ /^[A-Za-z0-9_]+$/).nil? + raise "Username should contain alphanumeric characters "\ + "and underscores only" + end if password.size < 6 raise "Password should contain at least 6 characters" end + if (password =~ /^[[:ascii:]]+$/).nil? + raise "password should contain ASCII characters only" + end storage.new_user username, password, admin @@ -104,8 +114,18 @@ post "/admin/user/edit/:original_username" do |env| if username.size < 3 raise "Username should contain at least 3 characters" end - if password.size != 0 && password.size < 6 - raise "Password should contain at least 6 characters" + if (username =~ /^[A-Za-z0-9_]+$/).nil? + raise "Username should contain alphanumeric characters "\ + "and underscores only" + end + + if password.size != 0 + if password.size < 6 + raise "Password should contain at least 6 characters" + end + if (password =~ /^[[:ascii:]]+$/).nil? + raise "password should contain ASCII characters only" + end end storage.update_user original_username, username, password, admin @@ -121,14 +141,13 @@ post "/admin/user/edit/:original_username" do |env| end end + get "/reader/:title/:entry" do |env| # We should save the reading progress, and ask the user if she wants to # start over or resume. For now we just start from page 0 begin - title = library.get_title env.params.url["title"] - raise "" if title.nil? - entry = title.get_entry env.params.url["entry"] - raise "" if entry.nil? + title = (library.get_title env.params.url["title"]).not_nil! + entry = (title.get_entry env.params.url["entry"]).not_nil! env.redirect "/reader/#{title.title}/#{entry.title}/0" rescue env.response.status_code = 404 @@ -139,10 +158,8 @@ get "/reader/:title/:entry/:page" do |env| imgs_each_page = 5 # here each :page contains `imgs_each_page` images begin - title = library.get_title env.params.url["title"] - raise "" if title.nil? - entry = title.get_entry env.params.url["entry"] - raise "" if entry.nil? + title = (library.get_title env.params.url["title"]).not_nil! + entry = (title.get_entry env.params.url["entry"]).not_nil! page = env.params.url["page"].to_i raise "" if page * imgs_each_page >= entry.pages @@ -163,8 +180,7 @@ end get "/logout" do |env| begin - cookie = env.request.cookies.find { |c| c.name == "token" } - raise "" if cookie.nil? + cookie = env.request.cookies.find { |c| c.name == "token" }.not_nil! storage.logout cookie.value rescue ensure @@ -176,8 +192,7 @@ post "/login" do |env| begin username = env.params.body["username"] password = env.params.body["password"] - token = storage.verify_user username, password - raise "" if token.nil? + token = storage.verify_user(username, password).not_nil! cookie = HTTP::Cookie.new "token", token env.response.cookies << cookie @@ -215,8 +230,7 @@ get "/api/book/:title" do |env| t = library.get_title title raise "Title `#{title}` not found" if t.nil? - env.response.content_type = "application/json" - t.to_json + send_json env, t.to_json rescue e STDERR.puts e env.response.status_code = 500 @@ -225,8 +239,26 @@ get "/api/book/:title" do |env| end get "/api/book" do |env| - env.response.content_type = "application/json" - library.to_json + send_json env, library.to_json +end + +post "/api/admin/scan" do |env| + start = Time.utc + library = Library.new config.library_path + ms = (Time.utc - start).total_milliseconds + send_json env, \ + {"milliseconds" => ms, "titles" => library.titles.size}.to_json +end + +post "/api/admin/user/delete/:username" do |env| + begin + username = env.params.url["username"] + storage.delete_user username + rescue e + send_json env, {"success" => false, "error" => e.message}.to_json + else + send_json env, {"success" => true}.to_json + end end add_handler AuthHandler.new storage diff --git a/src/storage.cr b/src/storage.cr index 35c063b..d9683b3 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -20,6 +20,10 @@ class Storage def initialize(path) @path = path + dir = File.dirname path + unless Dir.exists? dir + Dir.mkdir_p dir + end DB.open "sqlite3://#{path}" do |db| begin db.exec "create table users" \ @@ -34,7 +38,7 @@ class Storage random_pw = random_str hash = hash_password random_pw db.exec "insert into users values (?, ?, ?, ?)", - "admin", hash, "", 1 + "admin", hash, nil, 1 puts "Initial user created. You can log in with " \ "#{{"username" => "admin", "password" => random_pw}}" end @@ -99,7 +103,7 @@ class Storage DB.open "sqlite3://#{@path}" do |db| hash = hash_password password db.exec "insert into users values (?, ?, ?, ?)", - username, hash, "", admin + username, hash, nil, admin end end @@ -119,11 +123,17 @@ class Storage end end + def delete_user(username) + DB.open "sqlite3://#{@path}" do |db| + db.exec "delete from users where username = (?)", username + end + end + def logout(token) DB.open "sqlite3://#{@path}" do |db| begin db.exec "update users set token = (?) where token = (?)", \ - "", token + nil, token rescue end end diff --git a/src/views/admin.ecr b/src/views/admin.ecr index 0b5cc64..ac2de52 100644 --- a/src/views/admin.ecr +++ b/src/views/admin.ecr @@ -1,7 +1,13 @@
@@ -9,8 +15,22 @@ <% content_for "script" do %> diff --git a/src/views/title.ecr b/src/views/title.ecr index 669219a..fc7d77e 100644 --- a/src/views/title.ecr +++ b/src/views/title.ecr @@ -1,4 +1,5 @@

<%= title.title %>

+

<%= title.entries.size %> entries found

<%- title.entries.each do |e| -%>
diff --git a/src/views/user.ecr b/src/views/user.ecr index a322b9e..0f006a8 100644 --- a/src/views/user.ecr +++ b/src/views/user.ecr @@ -1,3 +1,4 @@ +
@@ -27,8 +28,24 @@ <% content_for "script" do %> <% end %>