mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 16:15:59 +08:00
Merge branch 'jb_connect_login_refresh'
This commit is contained in:
commit
392aad64a4
@ -332,6 +332,8 @@ set(SLIC3R_GUI_SOURCES
|
|||||||
Utils/Http.hpp
|
Utils/Http.hpp
|
||||||
Utils/FixModelByWin10.cpp
|
Utils/FixModelByWin10.cpp
|
||||||
Utils/FixModelByWin10.hpp
|
Utils/FixModelByWin10.hpp
|
||||||
|
Utils/Jwt.cpp
|
||||||
|
Utils/Jwt.hpp
|
||||||
Utils/Moonraker.cpp
|
Utils/Moonraker.cpp
|
||||||
Utils/Moonraker.hpp
|
Utils/Moonraker.hpp
|
||||||
Utils/OctoPrint.cpp
|
Utils/OctoPrint.cpp
|
||||||
|
@ -1448,6 +1448,13 @@ bool GUI_App::on_init_inner()
|
|||||||
this->check_updates(false);
|
this->check_updates(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Bind(wxEVT_ACTIVATE_APP, [this](const wxActivateEvent &evt) {
|
||||||
|
if (plater_) {
|
||||||
|
if (auto user_account = plater_->get_user_account())
|
||||||
|
user_account->on_activate_app(evt.GetActive());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
|
@ -6473,7 +6473,6 @@ void Plater::force_print_bed_update()
|
|||||||
|
|
||||||
void Plater::on_activate(bool active)
|
void Plater::on_activate(bool active)
|
||||||
{
|
{
|
||||||
this->p->user_account->on_activate_window(active);
|
|
||||||
if (active) {
|
if (active) {
|
||||||
this->p->show_delayed_error_message();
|
this->p->show_delayed_error_message();
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
bool on_connect_printers_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed);
|
bool on_connect_printers_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed);
|
||||||
bool on_connect_uiid_map_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed);
|
bool on_connect_uiid_map_success(const std::string& data, AppConfig* app_config, bool& out_printers_changed);
|
||||||
|
|
||||||
void on_activate_window(bool active) { m_communication->on_activate_window(active); }
|
void on_activate_app(bool active) { m_communication->on_activate_app(active); }
|
||||||
|
|
||||||
std::string get_username() const { return m_username; }
|
std::string get_username() const { return m_username; }
|
||||||
std::string get_access_token();
|
std::string get_access_token();
|
||||||
|
@ -475,6 +475,10 @@ void UserAccountCommunication::enqueue_refresh()
|
|||||||
BOOST_LOG_TRIVIAL(error) << "Connect Printers endpoint connection failed - Not Logged in.";
|
BOOST_LOG_TRIVIAL(error) << "Connect Printers endpoint connection failed - Not Logged in.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (m_session->is_enqueued(UserAccountActionID::USER_ACCOUNT_ACTION_REFRESH_TOKEN)) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "User Account: Token refresh already enqueued, skipping...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_session->enqueue_refresh({});
|
m_session->enqueue_refresh({});
|
||||||
}
|
}
|
||||||
wakeup_session_thread();
|
wakeup_session_thread();
|
||||||
@ -506,12 +510,19 @@ void UserAccountCommunication::init_session_thread()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAccountCommunication::on_activate_window(bool active)
|
void UserAccountCommunication::on_activate_app(bool active)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
|
std::lock_guard<std::mutex> lck(m_thread_stop_mutex);
|
||||||
m_window_is_active = active;
|
m_window_is_active = active;
|
||||||
}
|
}
|
||||||
|
auto now = std::time(nullptr);
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "UserAccountCommunication activate: active " << active;
|
||||||
|
if (active && m_next_token_refresh_at > 0 && m_next_token_refresh_at - now < 60) {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Enqueue access token refresh on activation";
|
||||||
|
m_token_timer->Stop();
|
||||||
|
enqueue_refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserAccountCommunication::wakeup_session_thread()
|
void UserAccountCommunication::wakeup_session_thread()
|
||||||
@ -527,12 +538,16 @@ void UserAccountCommunication::set_refresh_time(int seconds)
|
|||||||
{
|
{
|
||||||
assert(m_token_timer);
|
assert(m_token_timer);
|
||||||
m_token_timer->Stop();
|
m_token_timer->Stop();
|
||||||
int miliseconds = std::max(seconds * 1000 - 66666, 60000);
|
const auto prior_expiration_secs = 5 * 60;
|
||||||
m_token_timer->StartOnce(miliseconds);
|
int milliseconds = std::max((seconds - prior_expiration_secs) * 1000, 60000);
|
||||||
|
m_next_token_refresh_at = std::time(nullptr) + milliseconds / 1000;
|
||||||
|
m_token_timer->StartOnce(milliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UserAccountCommunication::on_token_timer(wxTimerEvent& evt)
|
void UserAccountCommunication::on_token_timer(wxTimerEvent& evt)
|
||||||
{
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "UserAccountCommunication: Token refresh timer fired";
|
||||||
enqueue_refresh();
|
enqueue_refresh();
|
||||||
}
|
}
|
||||||
void UserAccountCommunication::on_polling_timer(wxTimerEvent& evt)
|
void UserAccountCommunication::on_polling_timer(wxTimerEvent& evt)
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <wx/timer.h>
|
#include <ctime>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
@ -60,7 +60,7 @@ public:
|
|||||||
// Exchanges code for tokens and shared_session_key
|
// Exchanges code for tokens and shared_session_key
|
||||||
void on_login_code_recieved(const std::string& url_message);
|
void on_login_code_recieved(const std::string& url_message);
|
||||||
|
|
||||||
void on_activate_window(bool active);
|
void on_activate_app(bool active);
|
||||||
|
|
||||||
void set_username(const std::string& username);
|
void set_username(const std::string& username);
|
||||||
void set_remember_session(bool b);
|
void set_remember_session(bool b);
|
||||||
@ -97,6 +97,7 @@ private:
|
|||||||
|
|
||||||
wxTimer* m_token_timer;
|
wxTimer* m_token_timer;
|
||||||
wxEvtHandler* m_timer_evt_handler;
|
wxEvtHandler* m_timer_evt_handler;
|
||||||
|
std::time_t m_next_token_refresh_at{0};
|
||||||
|
|
||||||
void wakeup_session_thread();
|
void wakeup_session_thread();
|
||||||
void init_session_thread();
|
void init_session_thread();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
#include "../Utils/Http.hpp"
|
#include "../Utils/Http.hpp"
|
||||||
|
#include "../Utils/Jwt.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
@ -46,15 +47,24 @@ void UserActionPost::perform(/*UNUSED*/ wxEvtHandler* evt_handler, /*UNUSED*/ co
|
|||||||
if (success_callback)
|
if (success_callback)
|
||||||
success_callback(body);
|
success_callback(body);
|
||||||
});
|
});
|
||||||
http.perform_sync();
|
http.perform_sync(HttpRetryOpt::default_retry());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserActionGetWithEvent::perform(wxEvtHandler* evt_handler, const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) const
|
void UserActionGetWithEvent::perform(wxEvtHandler* evt_handler, const std::string& access_token, UserActionSuccessFn success_callback, UserActionFailFn fail_callback, const std::string& input) const
|
||||||
{
|
{
|
||||||
std::string url = m_url + input;
|
std::string url = m_url + input;
|
||||||
auto http = Http::get(std::move(url));
|
auto http = Http::get(std::move(url));
|
||||||
if (!access_token.empty())
|
if (!access_token.empty()) {
|
||||||
http.header("Authorization", "Bearer " + access_token);
|
http.header("Authorization", "Bearer " + access_token);
|
||||||
|
#ifndef _NDEBUG
|
||||||
|
// In debug mode, also verify the token expiration
|
||||||
|
// This is here to help with "dev" accounts with shorten (sort of faked) expiration time
|
||||||
|
// The /api/v1/me will accept these tokens even if these are fake-marked as expired
|
||||||
|
if (!Utils::verify_exp(access_token) && fail_callback) {
|
||||||
|
fail_callback("Token Expired");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
http.on_error([evt_handler, fail_callback, action_name = &m_action_name, fail_evt_type = m_fail_evt_type](std::string body, std::string error, unsigned status) {
|
http.on_error([evt_handler, fail_callback, action_name = &m_action_name, fail_evt_type = m_fail_evt_type](std::string body, std::string error, unsigned status) {
|
||||||
if (fail_callback)
|
if (fail_callback)
|
||||||
fail_callback(body);
|
fail_callback(body);
|
||||||
@ -69,9 +79,17 @@ void UserActionGetWithEvent::perform(wxEvtHandler* evt_handler, const std::strin
|
|||||||
wxQueueEvent(evt_handler, new UserAccountSuccessEvent(succ_evt_type, body));
|
wxQueueEvent(evt_handler, new UserAccountSuccessEvent(succ_evt_type, body));
|
||||||
});
|
});
|
||||||
|
|
||||||
http.perform_sync();
|
http.perform_sync(HttpRetryOpt::default_retry());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UserAccountSession::is_enqueued(UserAccountActionID action_id) const {
|
||||||
|
return std::any_of(
|
||||||
|
std::begin(m_priority_action_queue), std::end(m_priority_action_queue),
|
||||||
|
[action_id](const ActionQueueData& item) { return item.action_id == action_id; }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void UserAccountSession::process_action_queue()
|
void UserAccountSession::process_action_queue()
|
||||||
{
|
{
|
||||||
if (!m_proccessing_enabled)
|
if (!m_proccessing_enabled)
|
||||||
@ -84,7 +102,7 @@ void UserAccountSession::process_action_queue()
|
|||||||
while (!m_priority_action_queue.empty()) {
|
while (!m_priority_action_queue.empty()) {
|
||||||
m_actions[m_priority_action_queue.front().action_id]->perform(p_evt_handler, m_access_token, m_priority_action_queue.front().success_callback, m_priority_action_queue.front().fail_callback, m_priority_action_queue.front().input);
|
m_actions[m_priority_action_queue.front().action_id]->perform(p_evt_handler, m_access_token, m_priority_action_queue.front().success_callback, m_priority_action_queue.front().fail_callback, m_priority_action_queue.front().input);
|
||||||
if (!m_priority_action_queue.empty())
|
if (!m_priority_action_queue.empty())
|
||||||
m_priority_action_queue.pop();
|
m_priority_action_queue.pop_front();
|
||||||
}
|
}
|
||||||
// regular queue has to wait until priority fills tokens
|
// regular queue has to wait until priority fills tokens
|
||||||
if (!this->is_initialized())
|
if (!this->is_initialized())
|
||||||
@ -115,7 +133,7 @@ void UserAccountSession::init_with_code(const std::string& code, const std::stri
|
|||||||
|
|
||||||
m_proccessing_enabled = true;
|
m_proccessing_enabled = true;
|
||||||
// fail fn might be cancel_queue here
|
// fail fn might be cancel_queue here
|
||||||
m_priority_action_queue.push({ UserAccountActionID::USER_ACCOUNT_ACTION_CODE_FOR_TOKEN
|
m_priority_action_queue.push_back({ UserAccountActionID::USER_ACCOUNT_ACTION_CODE_FOR_TOKEN
|
||||||
, std::bind(&UserAccountSession::token_success_callback, this, std::placeholders::_1)
|
, std::bind(&UserAccountSession::token_success_callback, this, std::placeholders::_1)
|
||||||
, std::bind(&UserAccountSession::code_exchange_fail_callback, this, std::placeholders::_1)
|
, std::bind(&UserAccountSession::code_exchange_fail_callback, this, std::placeholders::_1)
|
||||||
, post_fields });
|
, post_fields });
|
||||||
@ -123,6 +141,7 @@ void UserAccountSession::init_with_code(const std::string& code, const std::stri
|
|||||||
|
|
||||||
void UserAccountSession::token_success_callback(const std::string& body)
|
void UserAccountSession::token_success_callback(const std::string& body)
|
||||||
{
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Access token refreshed";
|
||||||
// Data we need
|
// Data we need
|
||||||
std::string access_token, refresh_token, shared_session_key;
|
std::string access_token, refresh_token, shared_session_key;
|
||||||
int expires_in = 300;
|
int expires_in = 300;
|
||||||
@ -176,6 +195,7 @@ void UserAccountSession::token_success_callback(const std::string& body)
|
|||||||
|
|
||||||
void UserAccountSession::code_exchange_fail_callback(const std::string& body)
|
void UserAccountSession::code_exchange_fail_callback(const std::string& body)
|
||||||
{
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Access token refresh failed, body: " << body;
|
||||||
clear();
|
clear();
|
||||||
cancel_queue();
|
cancel_queue();
|
||||||
// Unlike refresh_fail_callback, no event was triggered so far, do it. (USER_ACCOUNT_ACTION_CODE_FOR_TOKEN does not send events)
|
// Unlike refresh_fail_callback, no event was triggered so far, do it. (USER_ACCOUNT_ACTION_CODE_FOR_TOKEN does not send events)
|
||||||
@ -186,7 +206,7 @@ void UserAccountSession::enqueue_test_with_refresh()
|
|||||||
{
|
{
|
||||||
// on test fail - try refresh
|
// on test fail - try refresh
|
||||||
m_proccessing_enabled = true;
|
m_proccessing_enabled = true;
|
||||||
m_priority_action_queue.push({ UserAccountActionID::USER_ACCOUNT_ACTION_TEST_ACCESS_TOKEN, nullptr, std::bind(&UserAccountSession::enqueue_refresh, this, std::placeholders::_1), {} });
|
m_priority_action_queue.push_back({ UserAccountActionID::USER_ACCOUNT_ACTION_TEST_ACCESS_TOKEN, nullptr, std::bind(&UserAccountSession::enqueue_refresh, this, std::placeholders::_1), {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -197,7 +217,7 @@ void UserAccountSession::enqueue_refresh(const std::string& body)
|
|||||||
"&client_id=" + client_id() +
|
"&client_id=" + client_id() +
|
||||||
"&refresh_token=" + m_refresh_token;
|
"&refresh_token=" + m_refresh_token;
|
||||||
|
|
||||||
m_priority_action_queue.push({ UserAccountActionID::USER_ACCOUNT_ACTION_REFRESH_TOKEN
|
m_priority_action_queue.push_back({ UserAccountActionID::USER_ACCOUNT_ACTION_REFRESH_TOKEN
|
||||||
, std::bind(&UserAccountSession::token_success_callback, this, std::placeholders::_1)
|
, std::bind(&UserAccountSession::token_success_callback, this, std::placeholders::_1)
|
||||||
, std::bind(&UserAccountSession::refresh_fail_callback, this, std::placeholders::_1)
|
, std::bind(&UserAccountSession::refresh_fail_callback, this, std::placeholders::_1)
|
||||||
, post_fields });
|
, post_fields });
|
||||||
@ -216,9 +236,7 @@ void UserAccountSession::refresh_fail_callback(const std::string& body)
|
|||||||
|
|
||||||
void UserAccountSession::cancel_queue()
|
void UserAccountSession::cancel_queue()
|
||||||
{
|
{
|
||||||
while (!m_priority_action_queue.empty()) {
|
m_priority_action_queue.clear();
|
||||||
m_priority_action_queue.pop();
|
|
||||||
}
|
|
||||||
while (!m_action_queue.empty()) {
|
while (!m_action_queue.empty()) {
|
||||||
m_action_queue.pop();
|
m_action_queue.pop();
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,8 @@ public:
|
|||||||
void enqueue_refresh(const std::string& body);
|
void enqueue_refresh(const std::string& body);
|
||||||
|
|
||||||
void process_action_queue();
|
void process_action_queue();
|
||||||
bool is_initialized() { return !m_access_token.empty() || !m_refresh_token.empty(); }
|
bool is_initialized() const { return !m_access_token.empty() || !m_refresh_token.empty(); }
|
||||||
|
bool is_enqueued(UserAccountActionID action_id) const;
|
||||||
std::string get_access_token() const { return m_access_token; }
|
std::string get_access_token() const { return m_access_token; }
|
||||||
std::string get_refresh_token() const { return m_refresh_token; }
|
std::string get_refresh_token() const { return m_refresh_token; }
|
||||||
std::string get_shared_session_key() const { return m_shared_session_key; }
|
std::string get_shared_session_key() const { return m_shared_session_key; }
|
||||||
@ -179,7 +180,7 @@ private:
|
|||||||
long long m_next_token_timeout;
|
long long m_next_token_timeout;
|
||||||
|
|
||||||
std::queue<ActionQueueData> m_action_queue;
|
std::queue<ActionQueueData> m_action_queue;
|
||||||
std::queue<ActionQueueData> m_priority_action_queue;
|
std::deque<ActionQueueData> m_priority_action_queue;
|
||||||
std::map<UserAccountActionID, std::unique_ptr<UserAction>> m_actions;
|
std::map<UserAccountActionID, std::unique_ptr<UserAction>> m_actions;
|
||||||
|
|
||||||
wxEvtHandler* p_evt_handler;
|
wxEvtHandler* p_evt_handler;
|
||||||
|
@ -499,6 +499,7 @@ ConnectRequestHandler::ConnectRequestHandler()
|
|||||||
m_actions["PRINT"] = std::bind(&ConnectRequestHandler::on_connect_action_print, this, std::placeholders::_1);
|
m_actions["PRINT"] = std::bind(&ConnectRequestHandler::on_connect_action_print, this, std::placeholders::_1);
|
||||||
m_actions["REQUEST_OPEN_IN_BROWSER"] = std::bind(&ConnectRequestHandler::on_connect_action_request_open_in_browser, this, std::placeholders::_1);
|
m_actions["REQUEST_OPEN_IN_BROWSER"] = std::bind(&ConnectRequestHandler::on_connect_action_request_open_in_browser, this, std::placeholders::_1);
|
||||||
m_actions["ERROR"] = std::bind(&ConnectRequestHandler::on_connect_action_error, this, std::placeholders::_1);
|
m_actions["ERROR"] = std::bind(&ConnectRequestHandler::on_connect_action_error, this, std::placeholders::_1);
|
||||||
|
m_actions["LOG"] = std::bind(&ConnectRequestHandler::on_connect_action_log, this, std::placeholders::_1);
|
||||||
}
|
}
|
||||||
ConnectRequestHandler::~ConnectRequestHandler()
|
ConnectRequestHandler::~ConnectRequestHandler()
|
||||||
{
|
{
|
||||||
@ -539,7 +540,7 @@ void ConnectRequestHandler::handle_message(const std::string& message)
|
|||||||
|
|
||||||
void ConnectRequestHandler::on_connect_action_error(const std::string &message_data)
|
void ConnectRequestHandler::on_connect_action_error(const std::string &message_data)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(error) << "WebKit runtime error: " << message_data;
|
BOOST_LOG_TRIVIAL(error) << "WebView runtime error: " << message_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectRequestHandler::resend_config()
|
void ConnectRequestHandler::resend_config()
|
||||||
@ -547,6 +548,11 @@ void ConnectRequestHandler::resend_config()
|
|||||||
on_connect_action_request_config({});
|
on_connect_action_request_config({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConnectRequestHandler::on_connect_action_log(const std::string& message_data)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "WebView log: " << message_data;
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectRequestHandler::on_connect_action_request_config(const std::string& message_data)
|
void ConnectRequestHandler::on_connect_action_request_config(const std::string& message_data)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -636,29 +642,93 @@ wxString ConnectWebViewPanel::get_login_script(bool refresh)
|
|||||||
window.__access_token_version = 0;
|
window.__access_token_version = 0;
|
||||||
)",
|
)",
|
||||||
#else
|
#else
|
||||||
|
refresh
|
||||||
|
?
|
||||||
R"(
|
R"(
|
||||||
console.log('Preparing login');
|
if (window._prusaSlicer_initLogin !== undefined) {
|
||||||
function errorHandler(err) {
|
console.log('Refreshing login');
|
||||||
|
if (window._prusaSlicer !== undefined)
|
||||||
|
_prusaSlicer.postMessage({action: 'LOG', message: 'Refreshing login'});
|
||||||
|
_prusaSlicer_initLogin('%s', 'refresh');
|
||||||
|
} else {
|
||||||
|
console.log('Refreshing login skipped as no _prusaSlicer_initLogin defined (yet?)');
|
||||||
|
if (window._prusaSlicer === undefined) {
|
||||||
|
console.log('Message handler _prusaSlicer not defined yet');
|
||||||
|
} else {
|
||||||
|
_prusaSlicer.postMessage({action: 'LOG', message: 'Refreshing login skipped as no _prusaSlicer_initLogin defined (yet?)'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)"
|
||||||
|
:
|
||||||
|
R"(
|
||||||
|
function _prusaSlicer_log(msg) {
|
||||||
|
console.log(msg);
|
||||||
|
if (window._prusaSlicer !== undefined)
|
||||||
|
_prusaSlicer.postMessage({action: 'LOG', message: msg});
|
||||||
|
}
|
||||||
|
function _prusaSlicer_errorHandler(err) {
|
||||||
const msg = {
|
const msg = {
|
||||||
action: 'ERROR',
|
action: 'ERROR',
|
||||||
error: JSON.stringify(err),
|
error: typeof(err) === 'string' ? err : JSON.stringify(err),
|
||||||
critical: false
|
critical: false
|
||||||
};
|
};
|
||||||
console.error('Login error occurred', msg);
|
console.error('Login error occurred', msg);
|
||||||
window._prusaSlicer.postMessage(msg);
|
window._prusaSlicer.postMessage(msg);
|
||||||
};
|
};
|
||||||
window.fetch('/slicer/login', {method: 'POST', headers: {Authorization: 'Bearer %s'}})
|
|
||||||
.then(function (resp) {
|
function _prusaSlicer_delay(ms) {
|
||||||
console.log('Login resp', resp);
|
return new Promise((resolve, reject) => {
|
||||||
resp.text()
|
setTimeout(resolve, ms);
|
||||||
.then(function (json) { console.log('Login resp body', json); return json; })
|
|
||||||
.then(function (body) {
|
|
||||||
if (resp.status >= 400) errorHandler({status: resp.status, body});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(function (err){
|
|
||||||
errorHandler({message: err.message, stack: err.stack});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function _prusaSlicer_initLogin(token, reason) {
|
||||||
|
const parts = token.split('.');
|
||||||
|
const claims = JSON.parse(atob(parts[1]));
|
||||||
|
const now = new Date().getTime() / 1000;
|
||||||
|
if (claims.exp <= now) {
|
||||||
|
_prusaSlicer_log('Skipping initLogin as token is expired');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let retry = false;
|
||||||
|
let backoff = 1000;
|
||||||
|
const maxBackoff = 64000;
|
||||||
|
do {
|
||||||
|
|
||||||
|
let error = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
_prusaSlicer_log('Slicer Login request (' + reason + ') ' + token.substring(token.length - 8));
|
||||||
|
let resp = await fetch('/slicer/login', {method: 'POST', headers: {Authorization: 'Bearer ' + token}});
|
||||||
|
let body = await resp.text();
|
||||||
|
_prusaSlicer_log('Slicer Login resp ' + resp.status + ' (' +reason + ' ' + token.substring(token.length - 8) + ') body: ' + body);
|
||||||
|
if (resp.status >= 500 || resp.status == 408) {
|
||||||
|
retry = true;
|
||||||
|
} else {
|
||||||
|
retry = false;
|
||||||
|
if (resp.status >= 400)
|
||||||
|
_prusaSlicer_errorHandler({status: resp.status, body});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_prusaSlicer_log('Slicer Login failed: ' + e.toString());
|
||||||
|
console.error('Slicer Login failed', e.toString());
|
||||||
|
retry = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retry) {
|
||||||
|
await _prusaSlicer_delay(backoff + 1000 * Math.random());
|
||||||
|
if (backoff < maxBackoff) {
|
||||||
|
backoff *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (retry);
|
||||||
|
}
|
||||||
|
if (window._prusaSlicer_initialLoad === undefined) {
|
||||||
|
console.log('Initial login');
|
||||||
|
_prusaSlicer_initLogin('%s', 'init-load');
|
||||||
|
window._prusaSlicer_initialLoad = true;
|
||||||
|
}
|
||||||
)",
|
)",
|
||||||
#endif
|
#endif
|
||||||
access_token
|
access_token
|
||||||
@ -678,10 +748,16 @@ void ConnectWebViewPanel::on_user_token(UserAccountSuccessEvent& e)
|
|||||||
e.Skip();
|
e.Skip();
|
||||||
auto access_token = wxGetApp().plater()->get_user_account()->get_access_token();
|
auto access_token = wxGetApp().plater()->get_user_account()->get_access_token();
|
||||||
assert(!access_token.empty());
|
assert(!access_token.empty());
|
||||||
wxString javascript = get_login_script(true);
|
wxString javascript = get_login_script(false);
|
||||||
|
|
||||||
|
m_browser->RemoveAllUserScripts();
|
||||||
|
m_browser->AddUserScript(javascript);
|
||||||
|
|
||||||
|
javascript = get_login_script(true);
|
||||||
//m_browser->AddUserScript(javascript, wxWEBVIEW_INJECT_AT_DOCUMENT_END);
|
//m_browser->AddUserScript(javascript, wxWEBVIEW_INJECT_AT_DOCUMENT_END);
|
||||||
BOOST_LOG_TRIVIAL(debug) << "RunScript " << javascript << "\n";
|
BOOST_LOG_TRIVIAL(debug) << "RunScript " << javascript << "\n";
|
||||||
m_browser->RunScriptAsync(javascript);
|
m_browser->RunScriptAsync(javascript);
|
||||||
|
resend_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectWebViewPanel::on_script_message(wxWebViewEvent& evt)
|
void ConnectWebViewPanel::on_script_message(wxWebViewEvent& evt)
|
||||||
@ -691,6 +767,7 @@ void ConnectWebViewPanel::on_script_message(wxWebViewEvent& evt)
|
|||||||
}
|
}
|
||||||
void ConnectWebViewPanel::on_navigation_request(wxWebViewEvent &evt)
|
void ConnectWebViewPanel::on_navigation_request(wxWebViewEvent &evt)
|
||||||
{
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Navigation requested to: " << into_u8(evt.GetURL());
|
||||||
if (evt.GetURL() == m_default_url) {
|
if (evt.GetURL() == m_default_url) {
|
||||||
m_reached_default_url = true;
|
m_reached_default_url = true;
|
||||||
return;
|
return;
|
||||||
|
@ -184,6 +184,7 @@ public:
|
|||||||
void resend_config();
|
void resend_config();
|
||||||
protected:
|
protected:
|
||||||
// action callbacs stored in m_actions
|
// action callbacs stored in m_actions
|
||||||
|
virtual void on_connect_action_log(const std::string& message_data);
|
||||||
virtual void on_connect_action_error(const std::string& message_data);
|
virtual void on_connect_action_error(const std::string& message_data);
|
||||||
virtual void on_connect_action_request_config(const std::string& message_data);
|
virtual void on_connect_action_request_config(const std::string& message_data);
|
||||||
virtual void on_connect_action_request_open_in_browser(const std::string& message_data);
|
virtual void on_connect_action_request_open_in_browser(const std::string& message_data);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <random>
|
||||||
#include <boost/filesystem/fstream.hpp> // IWYU pragma: keep
|
#include <boost/filesystem/fstream.hpp> // IWYU pragma: keep
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
@ -154,7 +155,7 @@ struct Http::priv
|
|||||||
|
|
||||||
std::string curl_error(CURLcode curlcode);
|
std::string curl_error(CURLcode curlcode);
|
||||||
std::string body_size_error();
|
std::string body_size_error();
|
||||||
void http_perform();
|
void http_perform(const HttpRetryOpt& retry_opts = HttpRetryOpt::no_retry());
|
||||||
};
|
};
|
||||||
|
|
||||||
Http::priv::priv(const std::string &url)
|
Http::priv::priv(const std::string &url)
|
||||||
@ -340,8 +341,20 @@ std::string Http::priv::body_size_error()
|
|||||||
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
|
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http::priv::http_perform()
|
bool is_transient_error(CURLcode res, long http_status)
|
||||||
{
|
{
|
||||||
|
if (res == CURLE_OK || res == CURLE_HTTP_RETURNED_ERROR)
|
||||||
|
return http_status == 408 || http_status >= 500;
|
||||||
|
return res == CURLE_COULDNT_CONNECT || res == CURLE_COULDNT_RESOLVE_HOST ||
|
||||||
|
res == CURLE_OPERATION_TIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Http::priv::http_perform(const HttpRetryOpt& retry_opts)
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
static thread_local std::mt19937 generator;
|
||||||
|
std::uniform_int_distribution<std::chrono::milliseconds::rep> randomized_delay(retry_opts.initial_delay.count(), (retry_opts.initial_delay.count() * 3) / 2);
|
||||||
|
|
||||||
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
::curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
|
::curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
|
||||||
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
|
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
|
||||||
@ -375,7 +388,28 @@ void Http::priv::http_perform()
|
|||||||
::curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, postfields.size());
|
::curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, postfields.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLcode res = ::curl_easy_perform(curl);
|
bool retry;
|
||||||
|
CURLcode res;
|
||||||
|
long http_status = 0;
|
||||||
|
std::chrono::milliseconds delay = std::chrono::milliseconds(randomized_delay(generator));
|
||||||
|
size_t num_retries = 0;
|
||||||
|
do {
|
||||||
|
res = ::curl_easy_perform(curl);
|
||||||
|
|
||||||
|
if (res == CURLE_OK)
|
||||||
|
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
|
||||||
|
retry = delay >= 0ms && is_transient_error(res, http_status);
|
||||||
|
if (retry && retry_opts.max_retries > 0 && num_retries >= retry_opts.max_retries)
|
||||||
|
retry = false;
|
||||||
|
if (retry) {
|
||||||
|
num_retries++;
|
||||||
|
BOOST_LOG_TRIVIAL(error)
|
||||||
|
<< "HTTP Transient error (code=" << res << ", http_status=" << http_status
|
||||||
|
<< "), retrying in " << delay.count() / 1000.0f << " s";
|
||||||
|
std::this_thread::sleep_for(delay);
|
||||||
|
delay = std::min(delay * 2, retry_opts.max_delay);
|
||||||
|
}
|
||||||
|
} while (retry);
|
||||||
|
|
||||||
putFile.reset();
|
putFile.reset();
|
||||||
|
|
||||||
@ -397,8 +431,6 @@ void Http::priv::http_perform()
|
|||||||
if (errorfn) { errorfn(std::move(buffer), curl_error(res), 0); }
|
if (errorfn) { errorfn(std::move(buffer), curl_error(res), 0); }
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
long http_status = 0;
|
|
||||||
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
|
|
||||||
|
|
||||||
if (http_status >= 400) {
|
if (http_status >= 400) {
|
||||||
if (errorfn) { errorfn(std::move(buffer), std::string(), http_status); }
|
if (errorfn) { errorfn(std::move(buffer), std::string(), http_status); }
|
||||||
@ -420,6 +452,23 @@ Http::Http(const std::string &url) : p(new priv(url)) {}
|
|||||||
|
|
||||||
// Public
|
// Public
|
||||||
|
|
||||||
|
const HttpRetryOpt& HttpRetryOpt::default_retry()
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
static HttpRetryOpt val = {500ms, 64s, 0};
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HttpRetryOpt& HttpRetryOpt::no_retry()
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
static HttpRetryOpt val = {0ms};
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Http::Http(Http &&other) : p(std::move(other.p)) {}
|
Http::Http(Http &&other) : p(std::move(other.p)) {}
|
||||||
|
|
||||||
Http::~Http()
|
Http::~Http()
|
||||||
@ -609,13 +658,13 @@ Http& Http::cookie_jar(const std::string& file_path)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Http::Ptr Http::perform()
|
Http::Ptr Http::perform(const HttpRetryOpt& retry_opts)
|
||||||
{
|
{
|
||||||
auto self = std::make_shared<Http>(std::move(*this));
|
auto self = std::make_shared<Http>(std::move(*this));
|
||||||
|
|
||||||
if (self->p) {
|
if (self->p) {
|
||||||
auto io_thread = std::thread([self](){
|
auto io_thread = std::thread([self, &retry_opts](){
|
||||||
self->p->http_perform();
|
self->p->http_perform(retry_opts);
|
||||||
});
|
});
|
||||||
self->p->io_thread = std::move(io_thread);
|
self->p->io_thread = std::move(io_thread);
|
||||||
}
|
}
|
||||||
@ -623,9 +672,9 @@ Http::Ptr Http::perform()
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http::perform_sync()
|
void Http::perform_sync(const HttpRetryOpt& retry_opts)
|
||||||
{
|
{
|
||||||
if (p) { p->http_perform(); }
|
if (p) { p->http_perform(retry_opts); }
|
||||||
}
|
}
|
||||||
|
|
||||||
void Http::cancel()
|
void Http::cancel()
|
||||||
|
@ -11,10 +11,20 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
struct HttpRetryOpt
|
||||||
|
{
|
||||||
|
std::chrono::milliseconds initial_delay;
|
||||||
|
std::chrono::milliseconds max_delay;
|
||||||
|
size_t max_retries{0};
|
||||||
|
|
||||||
|
static const HttpRetryOpt& no_retry();
|
||||||
|
static const HttpRetryOpt& default_retry();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Represetns a Http request
|
/// Represetns a Http request
|
||||||
class Http : public std::enable_shared_from_this<Http> {
|
class Http : public std::enable_shared_from_this<Http> {
|
||||||
@ -133,9 +143,9 @@ public:
|
|||||||
Http& set_referer(const std::string& referer);
|
Http& set_referer(const std::string& referer);
|
||||||
|
|
||||||
// Starts performing the request in a background thread
|
// Starts performing the request in a background thread
|
||||||
Ptr perform();
|
Ptr perform(const HttpRetryOpt& retry_opts = HttpRetryOpt::no_retry());
|
||||||
// Starts performing the request on the current thread
|
// Starts performing the request on the current thread
|
||||||
void perform_sync();
|
void perform_sync(const HttpRetryOpt &retry_opts = HttpRetryOpt::no_retry());
|
||||||
// Cancels a request in progress
|
// Cancels a request in progress
|
||||||
void cancel();
|
void cancel();
|
||||||
|
|
||||||
|
58
src/slic3r/Utils/Jwt.cpp
Normal file
58
src/slic3r/Utils/Jwt.cpp
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#include "Jwt.hpp"
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
#include <sstream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <boost/beast/core/detail/base64.hpp>
|
||||||
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
|
|
||||||
|
namespace Slic3r::Utils {
|
||||||
|
|
||||||
|
bool verify_exp(const std::string& token)
|
||||||
|
{
|
||||||
|
size_t payload_start = token.find('.');
|
||||||
|
if (payload_start == std::string::npos)
|
||||||
|
return false;
|
||||||
|
payload_start += 1; // payload starts after dot
|
||||||
|
|
||||||
|
const size_t payload_end = token.find('.', payload_start);
|
||||||
|
if (payload_end == std::string::npos)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t encoded_length = payload_end - payload_start;
|
||||||
|
size_t decoded_length = boost::beast::detail::base64::decoded_size(encoded_length);
|
||||||
|
|
||||||
|
auto json_b64 = token.substr(payload_start, encoded_length);
|
||||||
|
std::replace(json_b64.begin(), json_b64.end(), '-', '+');
|
||||||
|
std::replace(json_b64.begin(), json_b64.end(), '_', '/');
|
||||||
|
|
||||||
|
size_t padding = encoded_length % 4;
|
||||||
|
encoded_length += padding;
|
||||||
|
while (padding--) json_b64 += '=';
|
||||||
|
|
||||||
|
|
||||||
|
std::string json;
|
||||||
|
json.resize(decoded_length + 2);
|
||||||
|
size_t read_bytes, written_bytes;
|
||||||
|
std::tie(written_bytes, read_bytes) = boost::beast::detail::base64::decode(json.data(), json_b64.data(), json_b64.length());
|
||||||
|
json.resize(written_bytes);
|
||||||
|
if (written_bytes == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
namespace pt = boost::property_tree;
|
||||||
|
|
||||||
|
pt::ptree payload;
|
||||||
|
std::istringstream iss(json);
|
||||||
|
pt::json_parser::read_json(iss, payload);
|
||||||
|
|
||||||
|
auto exp_opt = payload.get_optional<double>("exp");
|
||||||
|
if (!exp_opt)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto now = time(nullptr);
|
||||||
|
return exp_opt.get() > now;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
10
src/slic3r/Utils/Jwt.hpp
Normal file
10
src/slic3r/Utils/Jwt.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Slic3r::Utils {
|
||||||
|
|
||||||
|
bool verify_exp(const std::string& token);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user