mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-13 19:01:21 +00:00
Introducing Orca Cloud: https://cloud.orcaslicer.com (#13414)
* Add OrcaCloud sync platform and preset bundle sharing system Introduce OrcaCloud, a cloud sync platform for user presets, alongside a preset bundle system that enables sharing printer/filament/process profiles as local exportable bundles or subscribed cloud bundles. OrcaCloud platform: - Auth to Orca Cloud - Encrypted token storage (file-based or system keychain) - User preset sync with - Profile migration from default/bambu folders on first login - Homepage integration with entrance to cloud.orcaslicer.com Preset bundles: - Local bundle import/export with bundle_structure.json metadata - Subscribed cloud bundles with version-based update checking - Thread-safe concurrent bundle access with read-write mutex - Canonical bundle preset naming (_local/<id>/... and _subscribed/<id>/...) - Bundle presets are read-only; grouped under subheaders in combo boxes - PresetBundleDialog with auto-sync toggle, refresh, update notifications - Hyperlinked bundle names to cloud bundle pages Co-authored-by: Sabriel Koh <sabrielkcr@gmail.com> Co-authored-by: Derrick <derrick992110@gmail.com> Co-authored-by: Mykola Nahirnyi <mnahirnyi@amcbridge.com> Co-authored-by: Ian Chua <iancrb00@gmail.com> Co-authored-by: Draginraptor <draginraptor@gmail.com> Co-authored-by: ExPikaPaka <112851715+ExPikaPaka@users.noreply.github.com> Co-authored-by: Ian Bassi <ian.bassi@outlook.com> Co-authored-by: Ocraftyone <Ocraftyone@users.noreply.github.com> Co-authored-by: yw4z <ywsyildiz@gmail.com> Co-authored-by: peterm-m <101202951+peterm-m@users.noreply.github.com> * Fixed an issue on Windows it failed to login Orca Cloud with Google account
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
#include <boost/cast.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -40,13 +42,78 @@ END_EVENT_TABLE()
|
||||
|
||||
int ZUserLogin::web_sequence_id = 20000;
|
||||
|
||||
ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_ANY, "OrcaSlicer")
|
||||
namespace {
|
||||
|
||||
int reserve_loopback_port()
|
||||
{
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::ip::tcp::acceptor acceptor(io_service, {boost::asio::ip::tcp::v4(), 0});
|
||||
return static_cast<int>(acceptor.local_endpoint().port());
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::string rewrite_loopback_url(std::string url, int port)
|
||||
{
|
||||
if (port <= 0) {
|
||||
return url;
|
||||
}
|
||||
|
||||
const std::string old_port = std::to_string(LOCALHOST_PORT);
|
||||
const std::string new_port = std::to_string(port);
|
||||
|
||||
boost::replace_all(url, std::string(LOCALHOST_URL) + old_port, std::string(LOCALHOST_URL) + new_port);
|
||||
boost::replace_all(url, "http://127.0.0.1:" + old_port, "http://127.0.0.1:" + new_port);
|
||||
boost::replace_all(url, "http%3A%2F%2Flocalhost%3A" + old_port, "http%3A%2F%2Flocalhost%3A" + new_port);
|
||||
boost::replace_all(url, "http%3A%2F%2F127.0.0.1%3A" + old_port, "http%3A%2F%2F127.0.0.1%3A" + new_port);
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int ZUserLogin::ensure_loopback_port()
|
||||
{
|
||||
if (m_loopback_port <= 0) {
|
||||
m_loopback_port = reserve_loopback_port();
|
||||
}
|
||||
int port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
|
||||
wxGetApp().start_http_server(port, m_cloud_agent->get_id());
|
||||
return port;
|
||||
}
|
||||
|
||||
ZUserLogin::ZUserLogin(std::shared_ptr<ICloudServiceAgent> cloud_agent)
|
||||
: wxDialog((wxWindow*) (wxGetApp().mainframe), wxID_ANY, "OrcaSlicer"), m_cloud_agent(cloud_agent)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
const auto bblnetwork_enabled =wxGetApp().app_config->get_bool("installed_networking");
|
||||
// Url
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (!agent && bblnetwork_enabled) {
|
||||
|
||||
if (!m_cloud_agent) {
|
||||
wxBoxSizer* m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1));
|
||||
m_line_top->SetBackgroundColour(wxColour(166, 169, 170));
|
||||
m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0);
|
||||
|
||||
auto* m_message = new wxStaticText(this, wxID_ANY,
|
||||
_L("Cloud agent is not available. Please restart OrcaSlicer and try again."),
|
||||
wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_message->SetForegroundColour(*wxBLACK);
|
||||
m_message->Wrap(FromDIP(360));
|
||||
m_sizer_main->Add(m_message, 0, wxALIGN_CENTER | wxALL, FromDIP(15));
|
||||
|
||||
m_sizer_main->Add(0, 0, 1, wxBOTTOM, 10);
|
||||
SetSizer(m_sizer_main);
|
||||
m_sizer_main->SetSizeHints(this);
|
||||
Layout();
|
||||
Fit();
|
||||
CentreOnParent();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto bblnetwork_enabled = wxGetApp().app_config->get_bool("installed_networking");
|
||||
if (m_cloud_agent->get_id() == BBL_CLOUD_PROVIDER && !bblnetwork_enabled) {
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
@@ -74,20 +141,16 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
|
||||
Layout();
|
||||
Fit();
|
||||
CentreOnParent();
|
||||
}
|
||||
else {
|
||||
// Get the login URL from the cloud service agent
|
||||
} else {
|
||||
// Get the login URL from the injected cloud service agent
|
||||
wxString strlang = wxGetApp().current_language_code_safe();
|
||||
strlang.Replace("_", "-");
|
||||
TargetUrl = wxString::FromUTF8(agent->get_cloud_login_url(strlang.ToStdString()));
|
||||
m_networkOk = TargetUrl.StartsWith("file://");
|
||||
TargetUrl = wxString::FromUTF8(m_cloud_agent->get_cloud_login_url(strlang.ToStdString()));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "login url = " << TargetUrl.ToStdString();
|
||||
|
||||
m_bbl_user_agent = wxString::Format("BBL-Slicer/v%s", wxGetApp().get_bbl_client_version());
|
||||
|
||||
// set the frame icon
|
||||
|
||||
// Create the webview
|
||||
m_browser = WebView::CreateWebView(this, TargetUrl);
|
||||
if (m_browser == nullptr) {
|
||||
@@ -97,12 +160,6 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
|
||||
m_browser->Hide();
|
||||
m_browser->SetSize(0, 0);
|
||||
|
||||
// Log backend information
|
||||
// wxLogMessage(wxWebView::GetBackendVersionInfo().ToString());
|
||||
// wxLogMessage("Backend: %s Version: %s",
|
||||
// m_browser->GetClassInfo()->GetClassName(),wxWebView::GetBackendVersionInfo().ToString());
|
||||
// wxLogMessage("User Agent: %s", m_browser->GetUserAgent());
|
||||
|
||||
// Connect the webview events
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATING, &ZUserLogin::OnNavigationRequest, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATED, &ZUserLogin::OnNavigationComplete, this, m_browser->GetId());
|
||||
@@ -113,19 +170,15 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
|
||||
Bind(wxEVT_WEBVIEW_FULLSCREEN_CHANGED, &ZUserLogin::OnFullScreenChanged, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &ZUserLogin::OnScriptMessage, this, m_browser->GetId());
|
||||
|
||||
// Connect the idle events
|
||||
// Bind(wxEVT_IDLE, &ZUserLogin::OnIdle, this);
|
||||
// Bind(wxEVT_CLOSE_WINDOW, &ZUserLogin::OnClose, this);
|
||||
|
||||
// UI
|
||||
SetTitle(_L("Login"));
|
||||
// Set a more sensible size for web browsing
|
||||
wxSize pSize = FromDIP(wxSize(650, 840));
|
||||
SetSize(pSize);
|
||||
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
|
||||
Move(tmpPT);
|
||||
}
|
||||
@@ -221,8 +274,7 @@ void ZUserLogin::OnDocumentLoaded(wxWebViewEvent &evt)
|
||||
{
|
||||
// Only notify if the document is the main frame, not a subframe
|
||||
wxString tmpUrl = evt.GetURL();
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
std::string strHost = agent->get_cloud_service_host();
|
||||
std::string strHost = m_cloud_agent->get_cloud_service_host();
|
||||
|
||||
if (tmpUrl.StartsWith("file://") || tmpUrl.Contains(strHost)) {
|
||||
m_networkOk = true;
|
||||
@@ -270,11 +322,10 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
|
||||
json j = json::parse(into_u8(str_input));
|
||||
wxString strCmd = j["command"];
|
||||
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (agent && strCmd == "get_login_cmd" && agent->get_cloud_agent()) {
|
||||
if (m_cloud_agent && strCmd == "get_login_cmd") {
|
||||
// Return login config (backend_url, apikey, pkce)
|
||||
// WebView handles provider selection internally
|
||||
std::string login_cmd = agent->build_login_cmd();
|
||||
std::string login_cmd = m_cloud_agent->build_login_cmd();
|
||||
m_loopback_port = 0;
|
||||
try {
|
||||
json cfg = json::parse(login_cmd);
|
||||
@@ -329,13 +380,11 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
|
||||
|
||||
// Handle message after modal dialog ends to avoid deadlock
|
||||
// Use wxTheApp->CallAfter to ensure it runs after modal loop exits
|
||||
wxTheApp->CallAfter([message_json]() {
|
||||
wxGetApp().handle_script_message(message_json);
|
||||
});
|
||||
const auto provider = m_cloud_agent->get_id();
|
||||
wxTheApp->CallAfter([message_json, provider]() { wxGetApp().handle_script_message(message_json, provider); });
|
||||
}
|
||||
else if (strCmd == "get_localhost_url") {
|
||||
int loopback_port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
|
||||
wxGetApp().start_http_server(loopback_port);
|
||||
int loopback_port = ensure_loopback_port();
|
||||
std::string sequence_id = j["sequence_id"].get<std::string>();
|
||||
CallAfter([this, sequence_id] {
|
||||
json ack_j;
|
||||
@@ -351,8 +400,8 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt)
|
||||
else if (strCmd == "thirdparty_login") {
|
||||
if (j["data"].contains("url")) {
|
||||
std::string jump_url = j["data"]["url"].get<std::string>();
|
||||
int loopback_port = m_loopback_port > 0 ? m_loopback_port : LOCALHOST_PORT;
|
||||
wxGetApp().start_http_server(loopback_port);
|
||||
int loopback_port = ensure_loopback_port();
|
||||
jump_url = rewrite_loopback_url(jump_url, loopback_port);
|
||||
CallAfter([this, jump_url] {
|
||||
wxString url = wxString::FromUTF8(jump_url);
|
||||
wxLaunchDefaultBrowser(url);
|
||||
|
||||
Reference in New Issue
Block a user