mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-03 11:25:29 -04:00
Use singleton in various classes
This commit is contained in:
parent
09b297cd8e
commit
1bec9f0108
@ -26,6 +26,16 @@ class Config
|
|||||||
"manga_rename_rule" => "{title}",
|
"manga_rename_rule" => "{title}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@singlet : Config?
|
||||||
|
|
||||||
|
def self.current
|
||||||
|
@@singlet.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_current
|
||||||
|
@@singlet = self
|
||||||
|
end
|
||||||
|
|
||||||
def self.load(path : String?)
|
def self.load(path : String?)
|
||||||
path = "~/.config/mango/config.yml" if path.nil?
|
path = "~/.config/mango/config.yml" if path.nil?
|
||||||
cfg_path = File.expand_path path, home: true
|
cfg_path = File.expand_path path, home: true
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
require "./config"
|
|
||||||
require "./library"
|
|
||||||
require "./storage"
|
|
||||||
require "./logger"
|
|
||||||
|
|
||||||
class Context
|
|
||||||
property config : Config
|
|
||||||
property library : Library
|
|
||||||
property storage : Storage
|
|
||||||
property logger : Logger
|
|
||||||
property queue : MangaDex::Queue
|
|
||||||
|
|
||||||
def initialize(@config, @logger, @library, @storage, @queue)
|
|
||||||
end
|
|
||||||
|
|
||||||
{% for lvl in Logger::LEVELS %}
|
|
||||||
def {{lvl.id}}(msg)
|
|
||||||
@logger.{{lvl.id}} msg
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
end
|
|
@ -2,20 +2,17 @@ require "kemal"
|
|||||||
require "../logger"
|
require "../logger"
|
||||||
|
|
||||||
class LogHandler < Kemal::BaseLogHandler
|
class LogHandler < Kemal::BaseLogHandler
|
||||||
def initialize(@logger : Logger)
|
|
||||||
end
|
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
elapsed_time = Time.measure { call_next env }
|
elapsed_time = Time.measure { call_next env }
|
||||||
elapsed_text = elapsed_text elapsed_time
|
elapsed_text = elapsed_text elapsed_time
|
||||||
msg = "#{env.response.status_code} #{env.request.method}" \
|
msg = "#{env.response.status_code} #{env.request.method}" \
|
||||||
" #{env.request.resource} #{elapsed_text}"
|
" #{env.request.resource} #{elapsed_text}"
|
||||||
@logger.debug msg
|
Logger.debug msg
|
||||||
env
|
env
|
||||||
end
|
end
|
||||||
|
|
||||||
def write(msg)
|
def write(msg)
|
||||||
@logger.debug msg
|
Logger.debug msg
|
||||||
end
|
end
|
||||||
|
|
||||||
private def elapsed_text(elapsed)
|
private def elapsed_text(elapsed)
|
||||||
|
@ -97,7 +97,7 @@ class Title
|
|||||||
encoded_title : String, mtime : Time
|
encoded_title : String, mtime : Time
|
||||||
|
|
||||||
def initialize(@dir : String, @parent_id, storage,
|
def initialize(@dir : String, @parent_id, storage,
|
||||||
@logger : Logger, @library : Library)
|
@library : Library)
|
||||||
@id = storage.get_id @dir, true
|
@id = storage.get_id @dir, true
|
||||||
@title = File.basename dir
|
@title = File.basename dir
|
||||||
@encoded_title = URI.encode @title
|
@encoded_title = URI.encode @title
|
||||||
@ -109,7 +109,7 @@ class Title
|
|||||||
next if fn.starts_with? "."
|
next if fn.starts_with? "."
|
||||||
path = File.join dir, fn
|
path = File.join dir, fn
|
||||||
if File.directory? path
|
if File.directory? path
|
||||||
title = Title.new path, @id, storage, @logger, library
|
title = Title.new path, @id, storage, library
|
||||||
next if title.entries.size == 0 && title.titles.size == 0
|
next if title.entries.size == 0 && title.titles.size == 0
|
||||||
@library.title_hash[title.id] = title
|
@library.title_hash[title.id] = title
|
||||||
@title_ids << title.id
|
@title_ids << title.id
|
||||||
@ -118,9 +118,9 @@ class Title
|
|||||||
if [".zip", ".cbz"].includes? File.extname path
|
if [".zip", ".cbz"].includes? File.extname path
|
||||||
zip_exception = validate_zip path
|
zip_exception = validate_zip path
|
||||||
unless zip_exception.nil?
|
unless zip_exception.nil?
|
||||||
@logger.warn "File #{path} is corrupted or is not a valid zip " \
|
Logger.warn "File #{path} is corrupted or is not a valid zip " \
|
||||||
"archive. Ignoring it."
|
"archive. Ignoring it."
|
||||||
@logger.debug "Zip error: #{zip_exception}"
|
Logger.debug "Zip error: #{zip_exception}"
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
entry = Entry.new path, self, @id, storage
|
entry = Entry.new path, self, @id, storage
|
||||||
@ -367,9 +367,19 @@ end
|
|||||||
|
|
||||||
class Library
|
class Library
|
||||||
property dir : String, title_ids : Array(String), scan_interval : Int32,
|
property dir : String, title_ids : Array(String), scan_interval : Int32,
|
||||||
logger : Logger, storage : Storage, title_hash : Hash(String, Title)
|
storage : Storage, title_hash : Hash(String, Title)
|
||||||
|
|
||||||
def initialize(@dir, @scan_interval, @logger, @storage)
|
def self.default
|
||||||
|
unless @@default
|
||||||
|
@@default = new
|
||||||
|
end
|
||||||
|
@@default.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@storage = Storage.default
|
||||||
|
@dir = Config.current.library_path
|
||||||
|
@scan_interval = Config.current.scan_interval
|
||||||
# explicitly initialize @titles to bypass the compiler check. it will
|
# explicitly initialize @titles to bypass the compiler check. it will
|
||||||
# be filled with actual Titles in the `scan` call below
|
# be filled with actual Titles in the `scan` call below
|
||||||
@title_ids = [] of String
|
@title_ids = [] of String
|
||||||
@ -381,7 +391,7 @@ class Library
|
|||||||
start = Time.local
|
start = Time.local
|
||||||
scan
|
scan
|
||||||
ms = (Time.local - start).total_milliseconds
|
ms = (Time.local - start).total_milliseconds
|
||||||
@logger.info "Scanned #{@title_ids.size} titles in #{ms}ms"
|
Logger.info "Scanned #{@title_ids.size} titles in #{ms}ms"
|
||||||
sleep @scan_interval * 60
|
sleep @scan_interval * 60
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -410,8 +420,8 @@ class Library
|
|||||||
|
|
||||||
def scan
|
def scan
|
||||||
unless Dir.exists? @dir
|
unless Dir.exists? @dir
|
||||||
@logger.info "The library directory #{@dir} does not exist. " \
|
Logger.info "The library directory #{@dir} does not exist. " \
|
||||||
"Attempting to create it"
|
"Attempting to create it"
|
||||||
Dir.mkdir_p @dir
|
Dir.mkdir_p @dir
|
||||||
end
|
end
|
||||||
@title_ids.clear
|
@title_ids.clear
|
||||||
@ -419,13 +429,13 @@ class Library
|
|||||||
.select { |fn| !fn.starts_with? "." }
|
.select { |fn| !fn.starts_with? "." }
|
||||||
.map { |fn| File.join @dir, fn }
|
.map { |fn| File.join @dir, fn }
|
||||||
.select { |path| File.directory? path }
|
.select { |path| File.directory? path }
|
||||||
.map { |path| Title.new path, "", @storage, @logger, self }
|
.map { |path| Title.new path, "", @storage, self }
|
||||||
.select { |title| !(title.entries.empty? && title.titles.empty?) }
|
.select { |title| !(title.entries.empty? && title.titles.empty?) }
|
||||||
.sort { |a, b| a.title <=> b.title }
|
.sort { |a, b| a.title <=> b.title }
|
||||||
.each do |title|
|
.each do |title|
|
||||||
@title_hash[title.id] = title
|
@title_hash[title.id] = title
|
||||||
@title_ids << title.id
|
@title_ids << title.id
|
||||||
end
|
end
|
||||||
@logger.debug "Scan completed"
|
Logger.debug "Scan completed"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -8,7 +8,15 @@ class Logger
|
|||||||
|
|
||||||
@@severity : Log::Severity = :info
|
@@severity : Log::Severity = :info
|
||||||
|
|
||||||
def initialize(level : String)
|
def self.default
|
||||||
|
unless @@default
|
||||||
|
@@default = new
|
||||||
|
end
|
||||||
|
@@default.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
level = Config.current.log_level
|
||||||
{% begin %}
|
{% begin %}
|
||||||
case level.downcase
|
case level.downcase
|
||||||
when "off"
|
when "off"
|
||||||
@ -50,9 +58,16 @@ class Logger
|
|||||||
@backend.write Log::Entry.new "", Log::Severity::None, msg, nil
|
@backend.write Log::Entry.new "", Log::Severity::None, msg, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.log(msg)
|
||||||
|
default.log msg
|
||||||
|
end
|
||||||
|
|
||||||
{% for lvl in LEVELS %}
|
{% for lvl in LEVELS %}
|
||||||
def {{lvl.id}}(msg)
|
def {{lvl.id}}(msg)
|
||||||
@log.{{lvl.id}} { msg }
|
@log.{{lvl.id}} { msg }
|
||||||
end
|
end
|
||||||
|
def self.{{lvl.id}}(msg)
|
||||||
|
default.not_nil!.{{lvl.id}} msg
|
||||||
|
end
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
@ -133,7 +133,15 @@ module MangaDex
|
|||||||
end
|
end
|
||||||
|
|
||||||
class API
|
class API
|
||||||
def initialize(@base_url = "https://mangadex.org/api/")
|
def self.default
|
||||||
|
unless @@default
|
||||||
|
@@default = new
|
||||||
|
end
|
||||||
|
@@default.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@base_url = Config.current.mangadex["api_url"].to_s || "https://mangadex.org/api/"
|
||||||
@lang = {} of String => String
|
@lang = {} of String => String
|
||||||
CSV.each_row {{read_file "src/assets/lang_codes.csv"}} do |row|
|
CSV.each_row {{read_file "src/assets/lang_codes.csv"}} do |row|
|
||||||
@lang[row[1]] = row[0]
|
@lang[row[1]] = row[0]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
require "./api"
|
require "./api"
|
||||||
require "sqlite3"
|
require "sqlite3"
|
||||||
|
require "zip"
|
||||||
|
|
||||||
module MangaDex
|
module MangaDex
|
||||||
class PageJob
|
class PageJob
|
||||||
@ -79,12 +80,20 @@ module MangaDex
|
|||||||
|
|
||||||
class Queue
|
class Queue
|
||||||
property downloader : Downloader?
|
property downloader : Downloader?
|
||||||
|
@path : String = Config.current.mangadex["download_queue_db_path"].to_s
|
||||||
|
|
||||||
def initialize(@path : String, @logger : Logger)
|
def self.default
|
||||||
dir = File.dirname path
|
unless @@default
|
||||||
|
@@default = new
|
||||||
|
end
|
||||||
|
@@default.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
dir = File.dirname @path
|
||||||
unless Dir.exists? dir
|
unless Dir.exists? dir
|
||||||
@logger.info "The queue DB directory #{dir} does not exist. " \
|
Logger.info "The queue DB directory #{dir} does not exist. " \
|
||||||
"Attepmting to create it"
|
"Attepmting to create it"
|
||||||
Dir.mkdir_p dir
|
Dir.mkdir_p dir
|
||||||
end
|
end
|
||||||
DB.open "sqlite3://#{@path}" do |db|
|
DB.open "sqlite3://#{@path}" do |db|
|
||||||
@ -101,7 +110,7 @@ module MangaDex
|
|||||||
db.exec "create index if not exists status_idx " \
|
db.exec "create index if not exists status_idx " \
|
||||||
"on queue (status)"
|
"on queue (status)"
|
||||||
rescue e
|
rescue e
|
||||||
@logger.error "Error when checking tables in DB: #{e}"
|
Logger.error "Error when checking tables in DB: #{e}"
|
||||||
raise e
|
raise e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -254,11 +263,22 @@ module MangaDex
|
|||||||
|
|
||||||
class Downloader
|
class Downloader
|
||||||
property stopped = false
|
property stopped = false
|
||||||
|
@wait_seconds : Int32 = Config.current.mangadex["download_wait_seconds"]
|
||||||
|
.to_i32
|
||||||
|
@retries : Int32 = Config.current.mangadex["download_retries"].to_i32
|
||||||
|
@library_path : String = Config.current.library_path
|
||||||
@downloading = false
|
@downloading = false
|
||||||
|
|
||||||
def initialize(@queue : Queue, @api : API, @library_path : String,
|
def self.default
|
||||||
@wait_seconds : Int32, @retries : Int32,
|
unless @@default
|
||||||
@logger : Logger)
|
@@default = new
|
||||||
|
end
|
||||||
|
@@default.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@queue = Queue.default
|
||||||
|
@api = API.default
|
||||||
@queue.downloader = self
|
@queue.downloader = self
|
||||||
|
|
||||||
spawn do
|
spawn do
|
||||||
@ -270,7 +290,7 @@ module MangaDex
|
|||||||
next if job.nil?
|
next if job.nil?
|
||||||
download job
|
download job
|
||||||
rescue e
|
rescue e
|
||||||
@logger.error e
|
Logger.error e
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -282,7 +302,7 @@ module MangaDex
|
|||||||
begin
|
begin
|
||||||
chapter = @api.get_chapter(job.id)
|
chapter = @api.get_chapter(job.id)
|
||||||
rescue e
|
rescue e
|
||||||
@logger.error e
|
Logger.error e
|
||||||
@queue.set_status JobStatus::Error, job
|
@queue.set_status JobStatus::Error, job
|
||||||
unless e.message.nil?
|
unless e.message.nil?
|
||||||
@queue.add_message e.message.not_nil!, job
|
@queue.add_message e.message.not_nil!, job
|
||||||
@ -310,16 +330,16 @@ module MangaDex
|
|||||||
ext = File.extname fn
|
ext = File.extname fn
|
||||||
fn = "#{i.to_s.rjust len, '0'}#{ext}"
|
fn = "#{i.to_s.rjust len, '0'}#{ext}"
|
||||||
page_job = PageJob.new url, fn, writer, @retries
|
page_job = PageJob.new url, fn, writer, @retries
|
||||||
@logger.debug "Downloading #{url}"
|
Logger.debug "Downloading #{url}"
|
||||||
loop do
|
loop do
|
||||||
sleep @wait_seconds.seconds
|
sleep @wait_seconds.seconds
|
||||||
download_page page_job
|
download_page page_job
|
||||||
break if page_job.success ||
|
break if page_job.success ||
|
||||||
page_job.tries_remaning <= 0
|
page_job.tries_remaning <= 0
|
||||||
page_job.tries_remaning -= 1
|
page_job.tries_remaning -= 1
|
||||||
@logger.warn "Failed to download page #{url}. " \
|
Logger.warn "Failed to download page #{url}. " \
|
||||||
"Retrying... Remaining retries: " \
|
"Retrying... Remaining retries: " \
|
||||||
"#{page_job.tries_remaning}"
|
"#{page_job.tries_remaning}"
|
||||||
end
|
end
|
||||||
|
|
||||||
channel.send page_job
|
channel.send page_job
|
||||||
@ -330,8 +350,8 @@ module MangaDex
|
|||||||
page_jobs = [] of PageJob
|
page_jobs = [] of PageJob
|
||||||
chapter.pages.size.times do
|
chapter.pages.size.times do
|
||||||
page_job = channel.receive
|
page_job = channel.receive
|
||||||
@logger.debug "[#{page_job.success ? "success" : "failed"}] " \
|
Logger.debug "[#{page_job.success ? "success" : "failed"}] " \
|
||||||
"#{page_job.url}"
|
"#{page_job.url}"
|
||||||
page_jobs << page_job
|
page_jobs << page_job
|
||||||
if page_job.success
|
if page_job.success
|
||||||
@queue.add_success job
|
@queue.add_success job
|
||||||
@ -339,14 +359,14 @@ module MangaDex
|
|||||||
@queue.add_fail job
|
@queue.add_fail job
|
||||||
msg = "Failed to download page #{page_job.url}"
|
msg = "Failed to download page #{page_job.url}"
|
||||||
@queue.add_message msg, job
|
@queue.add_message msg, job
|
||||||
@logger.error msg
|
Logger.error msg
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
fail_count = page_jobs.count { |j| !j.success }
|
fail_count = page_jobs.count { |j| !j.success }
|
||||||
@logger.debug "Download completed. " \
|
Logger.debug "Download completed. " \
|
||||||
"#{fail_count}/#{page_jobs.size} failed"
|
"#{fail_count}/#{page_jobs.size} failed"
|
||||||
writer.close
|
writer.close
|
||||||
@logger.debug "cbz File created at #{zip_path}"
|
Logger.debug "cbz File created at #{zip_path}"
|
||||||
|
|
||||||
zip_exception = validate_zip zip_path
|
zip_exception = validate_zip zip_path
|
||||||
if !zip_exception.nil?
|
if !zip_exception.nil?
|
||||||
@ -363,7 +383,7 @@ module MangaDex
|
|||||||
end
|
end
|
||||||
|
|
||||||
private def download_page(job : PageJob)
|
private def download_page(job : PageJob)
|
||||||
@logger.debug "downloading #{job.url}"
|
Logger.debug "downloading #{job.url}"
|
||||||
headers = HTTP::Headers{
|
headers = HTTP::Headers{
|
||||||
"User-agent" => "Mangadex.cr",
|
"User-agent" => "Mangadex.cr",
|
||||||
}
|
}
|
||||||
@ -377,7 +397,7 @@ module MangaDex
|
|||||||
end
|
end
|
||||||
job.success = true
|
job.success = true
|
||||||
rescue e
|
rescue e
|
||||||
@logger.error e
|
Logger.error e
|
||||||
job.success = false
|
job.success = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
17
src/mango.cr
17
src/mango.cr
@ -1,5 +1,5 @@
|
|||||||
|
require "./config"
|
||||||
require "./server"
|
require "./server"
|
||||||
require "./context"
|
|
||||||
require "./mangadex/*"
|
require "./mangadex/*"
|
||||||
require "option_parser"
|
require "option_parser"
|
||||||
|
|
||||||
@ -24,18 +24,7 @@ OptionParser.parse do |parser|
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
config = Config.load config_path
|
Config.load(config_path).set_current
|
||||||
logger = Logger.new config.log_level
|
|
||||||
storage = Storage.new config.db_path, logger
|
|
||||||
library = Library.new config.library_path, config.scan_interval, logger, storage
|
|
||||||
queue = MangaDex::Queue.new config.mangadex["download_queue_db_path"].to_s,
|
|
||||||
logger
|
|
||||||
api = MangaDex::API.new config.mangadex["api_url"].to_s
|
|
||||||
MangaDex::Downloader.new queue, api, config.library_path,
|
|
||||||
config.mangadex["download_wait_seconds"].to_i,
|
|
||||||
config.mangadex["download_retries"].to_i, logger
|
|
||||||
|
|
||||||
context = Context.new config, logger, library, storage, queue
|
server = Server.new
|
||||||
|
|
||||||
server = Server.new context
|
|
||||||
server.start
|
server.start
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
require "./router"
|
require "./router"
|
||||||
|
|
||||||
class AdminRouter < Router
|
class AdminRouter < Router
|
||||||
def setup
|
def initialize
|
||||||
get "/admin" do |env|
|
get "/admin" do |env|
|
||||||
layout "admin"
|
layout "admin"
|
||||||
end
|
end
|
||||||
@ -96,7 +96,7 @@ class AdminRouter < Router
|
|||||||
end
|
end
|
||||||
|
|
||||||
get "/admin/downloads" do |env|
|
get "/admin/downloads" do |env|
|
||||||
base_url = @context.config.mangadex["base_url"]
|
base_url = Config.current.mangadex["base_url"]
|
||||||
layout "download-manager"
|
layout "download-manager"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -3,7 +3,7 @@ require "../mangadex/*"
|
|||||||
require "../upload"
|
require "../upload"
|
||||||
|
|
||||||
class APIRouter < Router
|
class APIRouter < Router
|
||||||
def setup
|
def initialize
|
||||||
get "/api/page/:tid/:eid/:page" do |env|
|
get "/api/page/:tid/:eid/:page" do |env|
|
||||||
begin
|
begin
|
||||||
tid = env.params.url["tid"]
|
tid = env.params.url["tid"]
|
||||||
@ -123,7 +123,7 @@ class APIRouter < Router
|
|||||||
get "/api/admin/mangadex/manga/:id" do |env|
|
get "/api/admin/mangadex/manga/:id" do |env|
|
||||||
begin
|
begin
|
||||||
id = env.params.url["id"]
|
id = env.params.url["id"]
|
||||||
api = MangaDex::API.new @context.config.mangadex["api_url"].to_s
|
api = MangaDex::API.default
|
||||||
manga = api.get_manga id
|
manga = api.get_manga id
|
||||||
send_json env, manga.to_info_json
|
send_json env, manga.to_info_json
|
||||||
rescue e
|
rescue e
|
||||||
@ -230,7 +230,7 @@ class APIRouter < Router
|
|||||||
end
|
end
|
||||||
|
|
||||||
ext = File.extname filename
|
ext = File.extname filename
|
||||||
upload = Upload.new @context.config.upload_path, @context.logger
|
upload = Upload.new Config.current.upload_path
|
||||||
url = upload.path_to_url upload.save "img", ext, part.body
|
url = upload.path_to_url upload.save "img", ext, part.body
|
||||||
|
|
||||||
if url.nil?
|
if url.nil?
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
require "./router"
|
require "./router"
|
||||||
|
|
||||||
class MainRouter < Router
|
class MainRouter < Router
|
||||||
def setup
|
def initialize
|
||||||
get "/login" do |env|
|
get "/login" do |env|
|
||||||
render "src/views/login.ecr"
|
render "src/views/login.ecr"
|
||||||
end
|
end
|
||||||
@ -59,7 +59,7 @@ class MainRouter < Router
|
|||||||
end
|
end
|
||||||
|
|
||||||
get "/download" do |env|
|
get "/download" do |env|
|
||||||
base_url = @context.config.mangadex["base_url"]
|
base_url = Config.current.mangadex["base_url"]
|
||||||
layout "download"
|
layout "download"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
require "./router"
|
require "./router"
|
||||||
|
|
||||||
class ReaderRouter < Router
|
class ReaderRouter < Router
|
||||||
def setup
|
def initialize
|
||||||
get "/reader/:title/:entry" do |env|
|
get "/reader/:title/:entry" do |env|
|
||||||
begin
|
begin
|
||||||
title = (@context.library.get_title env.params.url["title"]).not_nil!
|
title = (@context.library.get_title env.params.url["title"]).not_nil!
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
require "../context"
|
|
||||||
|
|
||||||
class Router
|
class Router
|
||||||
def initialize(@context : Context)
|
@context : Context = Context.default
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -1,11 +1,38 @@
|
|||||||
require "kemal"
|
require "kemal"
|
||||||
require "./context"
|
require "./library"
|
||||||
require "./handlers/*"
|
require "./handlers/*"
|
||||||
require "./util"
|
require "./util"
|
||||||
require "./routes/*"
|
require "./routes/*"
|
||||||
|
|
||||||
|
class Context
|
||||||
|
property library : Library
|
||||||
|
property storage : Storage
|
||||||
|
property queue : MangaDex::Queue
|
||||||
|
|
||||||
|
def self.default
|
||||||
|
unless @@default
|
||||||
|
@@default = new
|
||||||
|
end
|
||||||
|
@@default.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@storage = Storage.default
|
||||||
|
@library = Library.default
|
||||||
|
@queue = MangaDex::Queue.default
|
||||||
|
end
|
||||||
|
|
||||||
|
{% for lvl in Logger::LEVELS %}
|
||||||
|
def {{lvl.id}}(msg)
|
||||||
|
Logger.{{lvl.id}} msg
|
||||||
|
end
|
||||||
|
{% end %}
|
||||||
|
end
|
||||||
|
|
||||||
class Server
|
class Server
|
||||||
def initialize(@context : Context)
|
@context : Context = Context.default
|
||||||
|
|
||||||
|
def initialize
|
||||||
error 403 do |env|
|
error 403 do |env|
|
||||||
message = "HTTP 403: You are not authorized to visit #{env.request.path}"
|
message = "HTTP 403: You are not authorized to visit #{env.request.path}"
|
||||||
layout "message"
|
layout "message"
|
||||||
@ -19,15 +46,15 @@ class Server
|
|||||||
layout "message"
|
layout "message"
|
||||||
end
|
end
|
||||||
|
|
||||||
MainRouter.new(@context).setup
|
MainRouter.new
|
||||||
AdminRouter.new(@context).setup
|
AdminRouter.new
|
||||||
ReaderRouter.new(@context).setup
|
ReaderRouter.new
|
||||||
APIRouter.new(@context).setup
|
APIRouter.new
|
||||||
|
|
||||||
Kemal.config.logging = false
|
Kemal.config.logging = false
|
||||||
add_handler LogHandler.new @context.logger
|
add_handler LogHandler.new
|
||||||
add_handler AuthHandler.new @context.storage
|
add_handler AuthHandler.new @context.storage
|
||||||
add_handler UploadHandler.new @context.config.upload_path
|
add_handler UploadHandler.new Config.current.upload_path
|
||||||
{% if flag?(:release) %}
|
{% if flag?(:release) %}
|
||||||
# when building for relase, embed the static files in binary
|
# when building for relase, embed the static files in binary
|
||||||
@context.debug "We are in release mode. Using embedded static files."
|
@context.debug "We are in release mode. Using embedded static files."
|
||||||
@ -41,7 +68,7 @@ class Server
|
|||||||
{% if flag?(:release) %}
|
{% if flag?(:release) %}
|
||||||
Kemal.config.env = "production"
|
Kemal.config.env = "production"
|
||||||
{% end %}
|
{% end %}
|
||||||
Kemal.config.port = @context.config.port
|
Kemal.config.port = Config.current.port
|
||||||
Kemal.run
|
Kemal.run
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -13,14 +13,23 @@ def verify_password(hash, pw)
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Storage
|
class Storage
|
||||||
def initialize(@path : String, @logger : Logger)
|
@path : String = Config.current.db_path
|
||||||
dir = File.dirname path
|
|
||||||
|
def self.default
|
||||||
|
unless @@default
|
||||||
|
@@default = new
|
||||||
|
end
|
||||||
|
@@default.not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
dir = File.dirname @path
|
||||||
unless Dir.exists? dir
|
unless Dir.exists? dir
|
||||||
@logger.info "The DB directory #{dir} does not exist. " \
|
Logger.info "The DB directory #{dir} does not exist. " \
|
||||||
"Attepmting to create it"
|
"Attepmting to create it"
|
||||||
Dir.mkdir_p dir
|
Dir.mkdir_p dir
|
||||||
end
|
end
|
||||||
DB.open "sqlite3://#{path}" do |db|
|
DB.open "sqlite3://#{@path}" do |db|
|
||||||
begin
|
begin
|
||||||
# We create the `ids` table first. even if the uses has an
|
# We create the `ids` table first. even if the uses has an
|
||||||
# early version installed and has the `user` table only,
|
# early version installed and has the `user` table only,
|
||||||
@ -34,19 +43,19 @@ class Storage
|
|||||||
"(username text, password text, token text, admin integer)"
|
"(username text, password text, token text, admin integer)"
|
||||||
rescue e
|
rescue e
|
||||||
unless e.message.not_nil!.ends_with? "already exists"
|
unless e.message.not_nil!.ends_with? "already exists"
|
||||||
@logger.fatal "Error when checking tables in DB: #{e}"
|
Logger.fatal "Error when checking tables in DB: #{e}"
|
||||||
raise e
|
raise e
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@logger.debug "Creating DB file at #{@path}"
|
Logger.debug "Creating DB file at #{@path}"
|
||||||
db.exec "create unique index username_idx on users (username)"
|
db.exec "create unique index username_idx on users (username)"
|
||||||
db.exec "create unique index token_idx on users (token)"
|
db.exec "create unique index token_idx on users (token)"
|
||||||
random_pw = random_str
|
random_pw = random_str
|
||||||
hash = hash_password random_pw
|
hash = hash_password random_pw
|
||||||
db.exec "insert into users values (?, ?, ?, ?)",
|
db.exec "insert into users values (?, ?, ?, ?)",
|
||||||
"admin", hash, nil, 1
|
"admin", hash, nil, 1
|
||||||
@logger.log "Initial user created. You can log in with " \
|
Logger.log "Initial user created. You can log in with " \
|
||||||
"#{{"username" => "admin", "password" => random_pw}}"
|
"#{{"username" => "admin", "password" => random_pw}}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -58,18 +67,18 @@ class Storage
|
|||||||
"users where username = (?)",
|
"users where username = (?)",
|
||||||
username, as: {String, String?}
|
username, as: {String, String?}
|
||||||
unless verify_password hash, password
|
unless verify_password hash, password
|
||||||
@logger.debug "Password does not match the hash"
|
Logger.debug "Password does not match the hash"
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@logger.debug "User #{username} verified"
|
Logger.debug "User #{username} verified"
|
||||||
return token if token
|
return token if token
|
||||||
token = random_str
|
token = random_str
|
||||||
@logger.debug "Updating token for #{username}"
|
Logger.debug "Updating token for #{username}"
|
||||||
db.exec "update users set token = (?) where username = (?)",
|
db.exec "update users set token = (?) where username = (?)",
|
||||||
token, username
|
token, username
|
||||||
return token
|
return token
|
||||||
rescue e
|
rescue e
|
||||||
@logger.error "Error when verifying user #{username}: #{e}"
|
Logger.error "Error when verifying user #{username}: #{e}"
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -82,7 +91,7 @@ class Storage
|
|||||||
username = db.query_one "select username from users where " \
|
username = db.query_one "select username from users where " \
|
||||||
"token = (?)", token, as: String
|
"token = (?)", token, as: String
|
||||||
rescue e
|
rescue e
|
||||||
@logger.debug "Unable to verify token"
|
Logger.debug "Unable to verify token"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
username
|
username
|
||||||
@ -95,7 +104,7 @@ class Storage
|
|||||||
is_admin = db.query_one "select admin from users where " \
|
is_admin = db.query_one "select admin from users where " \
|
||||||
"token = (?)", token, as: Bool
|
"token = (?)", token, as: Bool
|
||||||
rescue e
|
rescue e
|
||||||
@logger.debug "Unable to verify user as admin"
|
Logger.debug "Unable to verify user as admin"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
is_admin
|
is_admin
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
require "./util"
|
require "./util"
|
||||||
|
|
||||||
class Upload
|
class Upload
|
||||||
def initialize(@dir : String, @logger : Logger)
|
def initialize(@dir : String)
|
||||||
unless Dir.exists? @dir
|
unless Dir.exists? @dir
|
||||||
@logger.info "The uploads directory #{@dir} does not exist. " \
|
Logger.info "The uploads directory #{@dir} does not exist. " \
|
||||||
"Attempting to create it"
|
"Attempting to create it"
|
||||||
Dir.mkdir_p @dir
|
Dir.mkdir_p @dir
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -19,7 +19,7 @@ class Upload
|
|||||||
file_path = File.join full_dir, filename
|
file_path = File.join full_dir, filename
|
||||||
|
|
||||||
unless Dir.exists? full_dir
|
unless Dir.exists? full_dir
|
||||||
@logger.debug "creating directory #{full_dir}"
|
Logger.debug "creating directory #{full_dir}"
|
||||||
Dir.mkdir_p full_dir
|
Dir.mkdir_p full_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ class Upload
|
|||||||
end
|
end
|
||||||
|
|
||||||
if ary.empty?
|
if ary.empty?
|
||||||
@logger.warn "File #{path} is not in the upload directory #{@dir}"
|
Logger.warn "File #{path} is not in the upload directory #{@dir}"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user