diff --git a/migration/md_account.11.cr b/migration/md_account.11.cr new file mode 100644 index 0000000..3aece3e --- /dev/null +++ b/migration/md_account.11.cr @@ -0,0 +1,20 @@ +class CreateMangaDexAccount < MG::Base + def up : String + <<-SQL + CREATE TABLE md_account ( + username TEXT NOT NULL PRIMARY KEY, + token TEXT NOT NULL, + expire INTEGER NOT NULL, + FOREIGN KEY (username) REFERENCES users (username) + ON UPDATE CASCADE + ON DELETE CASCADE + ); + SQL + end + + def down : String + <<-SQL + DROP TABLE md_account; + SQL + end +end diff --git a/public/js/mangadex.js b/public/js/mangadex.js new file mode 100644 index 0000000..3271c4b --- /dev/null +++ b/public/js/mangadex.js @@ -0,0 +1,61 @@ +const component = () => { + return { + username: '', + password: '', + expires: undefined, + loading: true, + loggingIn: false, + + init() { + this.loading = true; + $.ajax({ + type: 'GET', + url: `${base_url}api/admin/mangadex/expires`, + contentType: "application/json", + }) + .done(data => { + console.log(data); + if (data.error) { + alert('danger', `Failed to retrieve MangaDex token status. Error: ${data.error}`); + return; + } + this.expires = data.expires; + this.loading = false; + }) + .fail((jqXHR, status) => { + alert('danger', `Failed to retrieve MangaDex token status. Error: [${jqXHR.status}] ${jqXHR.statusText}`); + }); + }, + login() { + if (!(this.username && this.password)) return; + this.loggingIn = true; + $.ajax({ + type: 'POST', + url: `${base_url}api/admin/mangadex/login`, + contentType: "application/json", + dataType: 'json', + data: JSON.stringify({ + username: this.username, + password: this.password + }) + }) + .done(data => { + console.log(data); + if (data.error) { + alert('danger', `Failed to log in. Error: ${data.error}`); + return; + } + this.expires = data.expires; + }) + .fail((jqXHR, status) => { + alert('danger', `Failed to log in. Error: [${jqXHR.status}] ${jqXHR.statusText}`); + }) + .always(() => { + this.loggingIn = false; + }); + }, + get expired() { + return this.expires && moment().diff(moment.unix(this.expires)) > 0; + } + }; +}; diff --git a/shard.lock b/shard.lock index 024ee1a..662715b 100644 --- a/shard.lock +++ b/shard.lock @@ -54,7 +54,7 @@ shards: mangadex: git: https://github.com/hkalexling/mangadex.git - version: 0.5.0+git.commit.323110c56c2d5134ce4162b27a9b24ec34137fcb + version: 0.6.0+git.commit.d87a1d7a6e84d122814b38618954dcc73fc5553b mg: git: https://github.com/hkalexling/mg.git diff --git a/src/routes/admin.cr b/src/routes/admin.cr index fd63ec8..616d452 100644 --- a/src/routes/admin.cr +++ b/src/routes/admin.cr @@ -73,5 +73,9 @@ struct AdminRouter get "/admin/missing" do |env| layout "missing-items" end + + get "/admin/mangadex" do |env| + layout "mangadex" + end end end diff --git a/src/routes/api.cr b/src/routes/api.cr index ff6a087..211f557 100644 --- a/src/routes/api.cr +++ b/src/routes/api.cr @@ -905,6 +905,57 @@ struct APIRouter end end + Koa.describe "Logs the current user into their MangaDex account, and " \ + "saves the token." + Koa.tag "admin" + post "/api/admin/mangadex/login" do |env| + begin + username = env.params.json["username"].as String + password = env.params.json["password"].as String + mango_username = get_username env + + client = MangaDex::Client.from_config + client.auth username, password + + Storage.default.save_md_token mango_username, client.token.not_nil!, + client.token_expires + + send_json env, { + "success" => true, + "error" => nil, + "expires" => client.token_expires.to_unix, + }.to_json + rescue e + Logger.error e + send_json env, { + "success" => false, + "error" => e.message, + }.to_json + end + end + + Koa.describe "Returns the expiration date of the mangadex token if the " \ + "current user has logged in to MangaDex" + Koa.tag "admin" + get "/api/admin/mangadex/expires" do |env| + begin + username = get_username env + _, expires = Storage.default.get_md_token username + + send_json env, { + "success" => true, + "error" => nil, + "expires" => expires.try &.to_unix, + }.to_json + rescue e + Logger.error e + send_json env, { + "success" => false, + "error" => e.message, + }.to_json + end + end + doc = Koa.generate @@api_json = doc.to_json if doc diff --git a/src/storage.cr b/src/storage.cr index 937200f..88a5142 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -514,6 +514,37 @@ class Storage delete_missing "titles", id end + def save_md_token(username : String, token : String, expire : Time) + MainFiber.run do + get_db do |db| + count = db.query_one "select count(*) from md_account where " \ + "username = (?)", username, as: Int64 + if count == 0 + db.exec "insert into md_account values (?, ?, ?)", username, token, + expire.to_unix + else + db.exec "update md_account set token = (?), expire = (?) " \ + "where username = (?)", token, expire.to_unix, username + end + end + end + end + + def get_md_token(username) : Tuple(String?, Time?) + token = nil + expires = nil + MainFiber.run do + get_db do |db| + db.query_one? "select token, expire from md_account where " \ + "username = (?)", username do |res| + token = res.read String + expires = Time.unix res.read Int64 + end + end + end + {token, expires} + end + def close MainFiber.run do unless @db.nil? diff --git a/src/views/admin.html.ecr b/src/views/admin.html.ecr index fb64d3e..a6e9b31 100644 --- a/src/views/admin.html.ecr +++ b/src/views/admin.html.ecr @@ -33,6 +33,7 @@ +
  • Connect to MangaDex

  • diff --git a/src/views/components/jquery-ui.html.ecr b/src/views/components/jquery-ui.html.ecr new file mode 100644 index 0000000..2141e25 --- /dev/null +++ b/src/views/components/jquery-ui.html.ecr @@ -0,0 +1 @@ + diff --git a/src/views/components/moment.html.ecr b/src/views/components/moment.html.ecr new file mode 100644 index 0000000..fdec5cf --- /dev/null +++ b/src/views/components/moment.html.ecr @@ -0,0 +1 @@ + diff --git a/src/views/download-manager.html.ecr b/src/views/download-manager.html.ecr index 552405a..2b6e434 100644 --- a/src/views/download-manager.html.ecr +++ b/src/views/download-manager.html.ecr @@ -63,7 +63,7 @@ <% content_for "script" do %> - + <%= render_component "moment" %> <% end %> diff --git a/src/views/download.html.ecr b/src/views/download.html.ecr index 7a96858..a0489c0 100644 --- a/src/views/download.html.ecr +++ b/src/views/download.html.ecr @@ -110,8 +110,8 @@ <% content_for "script" do %> - - + <%= render_component "moment" %> + <%= render_component "jquery-ui" %> <% end %> diff --git a/src/views/mangadex.html.ecr b/src/views/mangadex.html.ecr new file mode 100644 index 0000000..dc7a378 --- /dev/null +++ b/src/views/mangadex.html.ecr @@ -0,0 +1,39 @@ +
    +

    Connect to MangaDex

    +
    +
    +

    This step is optional but highly recommended if you are using the MangaDex downloader. Connecting to MangaDex allows you to:

    +
      +
    • Search MangaDex by search terms in addition to manga IDs
    • +
    • Automatically download new chapters when they are available
    • +
    +
    + +
    +

    + You have logged in to MangaDex! + You have logged in to MangaDex but the token has expired. + The expiration date of your token is . + If the integration is not working, you + You + can log in again and the token will be updated. +

    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +<% content_for "script" do %> + <%= render_component "moment" %> + + +<% end %> diff --git a/src/views/plugin-download.html.ecr b/src/views/plugin-download.html.ecr index 68eb156..692e22f 100644 --- a/src/views/plugin-download.html.ecr +++ b/src/views/plugin-download.html.ecr @@ -68,7 +68,7 @@ var pid = "<%= plugin.not_nil!.info.id %>"; <% end %> - + <%= render_component "jquery-ui" %>