Skip to content

Commit

Permalink
Merge branch 'v0.1.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
hkalexling committed Feb 23, 2020
2 parents dd49f75 + ac620e1 commit bf0f527
Show file tree
Hide file tree
Showing 16 changed files with 401 additions and 325 deletions.
4 changes: 2 additions & 2 deletions docker-compose.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ services:
ports:
- 9000:9000
volumes:
- ./mango:/root/mango
- ./config:/root/.config/mango
- ~/mango:/root/mango
- ~/.config/mango:/root/.config/mango
4 changes: 2 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const minify = require("gulp-babel-minify");
const minifyCss = require('gulp-minify-css');

gulp.task('minify-js', () => {
return gulp.src('public/js/*.js')
.pipe(uglify())
.pipe(minify())
.pipe(gulp.dest('dist/js'));
});

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"license": "MIT",
"devDependencies": {
"gulp": "^4.0.2",
"gulp-minify-css": "^1.2.4",
"gulp-uglify": "^3.0.2"
"gulp-babel-minify": "^0.5.1",
"gulp-minify-css": "^1.2.4"
},
"scripts": {
"uglify": "gulp"
Expand Down
17 changes: 10 additions & 7 deletions public/js/title.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
function showModal(title, zipPath, pages, percentage, title, entry) {
function showModal(encodedPath, pages, percentage, encodedeTitle, encodedEntryTitle, titleID, entryID) {
const zipPath = decodeURIComponent(encodedPath);
const title = decodeURIComponent(encodedeTitle);
const entry = decodeURIComponent(encodedEntryTitle);
$('#modal button, #modal a').each(function(){
$(this).removeAttr('hidden');
});
Expand All @@ -16,20 +19,20 @@ function showModal(title, zipPath, pages, percentage, title, entry) {
$('#path-text').text(zipPath);
$('#pages-text').text(pages + ' pages');

$('#beginning-btn').attr('href', '/reader/' + title + '/' + entry + '/1');
$('#continue-btn').attr('href', '/reader/' + title + '/' + entry);
$('#beginning-btn').attr('href', '/reader/' + titleID + '/' + entryID + '/1');
$('#continue-btn').attr('href', '/reader/' + titleID + '/' + entryID);

$('#read-btn').click(function(){
updateProgress(title, entry, pages);
updateProgress(titleID, entryID, pages);
});
$('#unread-btn').click(function(){
updateProgress(title, entry, 0);
updateProgress(titleID, entryID, 0);
});

UIkit.modal($('#modal')).show();
}
function updateProgress(title, entry, page) {
$.post('/api/progress/' + title + '/' + entry + '/' + page, function(data) {
function updateProgress(titleID, entryID, page) {
$.post('/api/progress/' + titleID + '/' + entryID + '/' + page, function(data) {
if (data.success) {
location.reload();
}
Expand Down
46 changes: 30 additions & 16 deletions src/library.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "zip"
require "mime"
require "json"
require "uri"

struct Image
property data : Bytes
Expand All @@ -14,19 +15,23 @@ end

class Entry
JSON.mapping zip_path: String, book_title: String, title: String, \
size: String, pages: Int32, cover_url: String
size: String, pages: Int32, cover_url: String, id: String, \
title_id: String, encoded_path: String, encoded_title: String

def initialize(path, @book_title)
def initialize(path, @book_title, @title_id, storage)
@zip_path = path
@encoded_path = URI.encode path
@title = File.basename path, File.extname path
@encoded_title = URI.encode @title
@size = (File.size path).humanize_bytes
@pages = Zip::File.new(path).entries
.select { |e|
["image/jpeg", "image/png"].includes? \
MIME.from_filename? e.filename
}
.size
@cover_url = "/api/page/#{@book_title}/#{title}/1"
@id = storage.get_id @zip_path, false
@cover_url = "/api/page/#{@title_id}/#{@id}/1"
end
def read_page(page_num)
Zip::File.open @zip_path do |file|
Expand All @@ -51,20 +56,27 @@ class Entry
end

class Title
JSON.mapping dir: String, entries: Array(Entry), title: String
JSON.mapping dir: String, entries: Array(Entry), title: String,
id: String, encoded_title: String

def initialize(dir : String)
def initialize(dir : String, storage)
@dir = dir
@id = storage.get_id @dir, true
@title = File.basename dir
@encoded_title = URI.encode @title
@entries = (Dir.entries dir)
.select { |path| [".zip", ".cbz"].includes? File.extname path }
.map { |path| Entry.new File.join(dir, path), @title }
.map { |path|
Entry.new File.join(dir, path), @title, @id, storage
}
.select { |e| e.pages > 0 }
.sort { |a, b| a.title <=> b.title }
end
def get_entry(name)
@entries.find { |e| e.title == name }
def get_entry(eid)
@entries.find { |e| e.id == eid }
end
# For backward backward compatibility with v0.1.0, we save entry titles
# instead of IDs in info.json
def save_progress(username, entry, page)
info = TitleInfo.new @dir
if info.progress[username]?.nil?
Expand All @@ -75,7 +87,7 @@ class Title
info.progress[username][entry] = page
info.save @dir
end
def load_progress(username, entry : String)
def load_progress(username, entry)
info = TitleInfo.new @dir
if info.progress[username]?.nil?
return 0
Expand All @@ -85,10 +97,10 @@ class Title
end
info.progress[username][entry]
end
def load_percetage(username, entry : String)
def load_percetage(username, entry)
info = TitleInfo.new @dir
page = load_progress username, entry
entry_obj = get_entry entry
entry_obj = @entries.find{|e| e.title == entry}
return 0 if entry_obj.nil?
page / entry_obj.pages
end
Expand Down Expand Up @@ -136,9 +148,10 @@ class TitleInfo
end

class Library
JSON.mapping dir: String, titles: Array(Title), scan_interval: Int32, logger: MLogger
JSON.mapping dir: String, titles: Array(Title), scan_interval: Int32,
logger: MLogger, storage: Storage

def initialize(@dir, @scan_interval, @logger)
def initialize(@dir, @scan_interval, @logger, @storage)
# explicitly initialize @titles to bypass the compiler check. it will
# be filled with actual Titles in the `scan` call below
@titles = [] of Title
Expand All @@ -154,8 +167,8 @@ class Library
end
end
end
def get_title(name)
@titles.find { |t| t.title == name }
def get_title(tid)
@titles.find { |t| t.id == tid }
end
def scan
unless Dir.exists? @dir
Expand All @@ -165,8 +178,9 @@ class Library
end
@titles = (Dir.entries @dir)
.select { |path| File.directory? File.join @dir, path }
.map { |path| Title.new File.join @dir, path }
.map { |path| Title.new File.join(@dir, path), @storage }
.select { |title| !title.entries.empty? }
.sort { |a, b| a.title <=> b.title }
@logger.debug "Scan completed"
@logger.debug "Scanned library: \n#{self.to_pretty_json}"
end
Expand Down
2 changes: 1 addition & 1 deletion src/mango.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ end

config = Config.load config_path
logger = MLogger.new config
library = Library.new config.library_path, config.scan_interval, logger
storage = Storage.new config.db_path, logger
library = Library.new config.library_path, config.scan_interval, logger, storage

context = Context.new config, logger, library, storage

Expand Down
103 changes: 103 additions & 0 deletions src/routes/admin.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
require "./router"

class AdminRouter < Router
def setup
get "/admin" do |env|
layout "admin"
end

get "/admin/user" do |env|
users = @context.storage.list_users
username = get_username env
layout "user"
end

get "/admin/user/edit" do |env|
username = env.params.query["username"]?
admin = env.params.query["admin"]?
if admin
admin = admin == "true"
end
error = env.params.query["error"]?
current_user = get_username env
new_user = username.nil? && admin.nil?
layout "user-edit"
end

post "/admin/user/edit" do |env|
# creating new user
begin
username = env.params.body["username"]
password = env.params.body["password"]
# if `admin` is unchecked, the body hash
# would not contain `admin`
admin = !env.params.body["admin"]?.nil?

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

@context.storage.new_user username, password, admin

env.redirect "/admin/user"
rescue e
@context.error e
redirect_url = URI.new \
path: "/admin/user/edit",\
query: hash_to_query({"error" => e.message})
env.redirect redirect_url.to_s
end
end

post "/admin/user/edit/:original_username" do |env|
# editing existing user
begin
username = env.params.body["username"]
password = env.params.body["password"]
# if `admin` is unchecked, the body
# hash would not contain `admin`
admin = !env.params.body["admin"]?.nil?
original_username = env.params.url["original_username"]

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 != 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

@context.storage.update_user \
original_username, username, password, admin

env.redirect "/admin/user"
rescue e
@context.error e
redirect_url = URI.new \
path: "/admin/user/edit",\
query: hash_to_query({"username" => original_username, \
"admin" => admin, "error" => e.message})
env.redirect redirect_url.to_s
end
end
end
end
92 changes: 92 additions & 0 deletions src/routes/api.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
require "./router"

class APIRouter < Router
def setup
get "/api/page/:tid/:eid/:page" do |env|
begin
tid = env.params.url["tid"]
eid = env.params.url["eid"]
page = env.params.url["page"].to_i

title = @context.library.get_title tid
raise "Title ID `#{tid}` not found" if title.nil?
entry = title.get_entry eid
raise "Entry ID `#{eid}` of `#{title.title}` not found" if \
entry.nil?
img = entry.read_page page
raise "Failed to load page #{page} of " \
"`#{title.title}/#{entry.title}`" if img.nil?

send_img env, img
rescue e
@context.error e
env.response.status_code = 500
e.message
end
end

get "/api/book/:title" do |env|
begin
tid = env.params.url["tid"]
title = @context.library.get_title tid
raise "Title ID `#{tid}` not found" if title.nil?

send_json env, title.to_json
rescue e
@context.error e
env.response.status_code = 500
e.message
end
end

get "/api/book" do |env|
send_json env, @context.library.to_json
end

post "/api/admin/scan" do |env|
start = Time.utc
@context.library.scan
ms = (Time.utc - start).total_milliseconds
send_json env, {
"milliseconds" => ms,
"titles" => @context.library.titles.size
}.to_json
end

post "/api/admin/user/delete/:username" do |env|
begin
username = env.params.url["username"]
@context.storage.delete_user username
rescue e
@context.error e
send_json env, {
"success" => false,
"error" => e.message
}.to_json
else
send_json env, {"success" => true}.to_json
end
end

post "/api/progress/:title/:entry/:page" do |env|
begin
username = get_username env
title = (@context.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 "incorrect page value" if page < 0 || page > entry.pages
title.save_progress username, entry.title, page
rescue e
@context.error e
send_json env, {
"success" => false,
"error" => e.message
}.to_json
else
send_json env, {"success" => true}.to_json
end
end
end
end
Loading

0 comments on commit bf0f527

Please sign in to comment.