715 lines
26 KiB
C++
715 lines
26 KiB
C++
#include <UI/Dialogs/DialogConnect.h>
|
|
#include <Client/AuthProfile.h>
|
|
#include <Client/Storage.h>
|
|
#include <MainAdaptix.h>
|
|
#include <Utils/NonBlockingDialogs.h>
|
|
#include <QVBoxLayout>
|
|
#include <QFile>
|
|
#include <QIODevice>
|
|
#include <QFrame>
|
|
#include <QFileDialog>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
|
|
static QString defaultProjectDir(const QString &projectName)
|
|
{
|
|
return QDir::home().filePath("AdaptixProjects/" + projectName.trimmed());
|
|
}
|
|
|
|
bool DialogConnect::parseUrl(QString &host, QString &port, QString &endpoint) const
|
|
{
|
|
QUrl url(lineEdit_Url->text().trimmed());
|
|
if (!url.isValid() || url.host().isEmpty())
|
|
return false;
|
|
|
|
host = url.host();
|
|
port = url.port(-1) != -1 ? QString::number(url.port()) : "443";
|
|
endpoint = url.path().isEmpty() ? "/" : url.path();
|
|
return true;
|
|
}
|
|
|
|
QString DialogConnect::buildUrl(const QString &host, const QString &port, const QString &endpoint) const
|
|
{
|
|
QUrl url;
|
|
url.setScheme("https");
|
|
url.setHost(host);
|
|
if (!port.isEmpty() && port != "443")
|
|
url.setPort(port.toInt());
|
|
url.setPath(endpoint.isEmpty() ? "/" : endpoint);
|
|
return url.toString();
|
|
}
|
|
|
|
DialogConnect::DialogConnect()
|
|
{
|
|
createUI();
|
|
loadProjects();
|
|
|
|
connect(cardWidget, &QListWidget::itemPressed, this, &DialogConnect::onProfileSelected);
|
|
connect(cardWidget, &QListWidget::customContextMenuRequested, this, &DialogConnect::handleContextMenu);
|
|
connect(buttonNewProfile, &QPushButton::clicked, this, &DialogConnect::onButton_NewProfile);
|
|
connect(buttonLoad, &QPushButton::clicked, this, &DialogConnect::onButton_Load);
|
|
connect(buttonSave, &QPushButton::clicked, this, &DialogConnect::onButton_Save);
|
|
connect(lineEdit_Project, &QLineEdit::textChanged, this, &DialogConnect::onProjectNameChanged);
|
|
connect(lineEdit_ProjectDir, &QLineEdit::textEdited, this, &DialogConnect::onProjectDirEdited);
|
|
connect(buttonConnect, &QPushButton::clicked, this, &DialogConnect::onButton_Connect);
|
|
|
|
auto connectReturnPressed = [this](const QLineEdit* edit) {
|
|
connect(edit, &QLineEdit::returnPressed, this, &DialogConnect::onButton_Connect);
|
|
};
|
|
connectReturnPressed(lineEdit_Project);
|
|
connectReturnPressed(lineEdit_Url);
|
|
connectReturnPressed(lineEdit_User);
|
|
connectReturnPressed(lineEdit_Password);
|
|
|
|
auto action = lineEdit_ProjectDir->addAction(QIcon(":/icons/folder"), QLineEdit::TrailingPosition);
|
|
connect(action, &QAction::triggered, this, &DialogConnect::onSelectProjectDir);
|
|
|
|
connect(subsSelectBtn, &QPushButton::clicked, this, &DialogConnect::showSubsPopup);
|
|
connect(dataListWidget, &QListWidget::itemChanged, this, &DialogConnect::onSubsSelectionChanged);
|
|
connect(agentListWidget, &QListWidget::itemChanged, this, &DialogConnect::onSubsSelectionChanged);
|
|
}
|
|
|
|
DialogConnect::~DialogConnect() = default;
|
|
|
|
void DialogConnect::createUI()
|
|
{
|
|
resize(720, 420);
|
|
setFixedSize(720, 420);
|
|
setWindowTitle("Connect");
|
|
setProperty("Main", "base");
|
|
|
|
groupUserInfo = new QGroupBox("User Info", this);
|
|
auto userLayout = new QGridLayout(groupUserInfo);
|
|
userLayout->setContentsMargins(10, 10, 10, 10);
|
|
userLayout->setHorizontalSpacing(12);
|
|
userLayout->setVerticalSpacing(8);
|
|
|
|
label_User = new QLabel("User:", this);
|
|
label_Password = new QLabel("Password:", this);
|
|
|
|
lineEdit_User = new QLineEdit(this);
|
|
lineEdit_User->setToolTip("Enter your username");
|
|
|
|
lineEdit_Password = new QLineEdit(this);
|
|
lineEdit_Password->setEchoMode(QLineEdit::Password);
|
|
lineEdit_Password->setToolTip("Enter your password");
|
|
|
|
userLayout->addWidget(label_User, 0, 0);
|
|
userLayout->addWidget(lineEdit_User, 0, 1);
|
|
userLayout->addWidget(label_Password, 1, 0);
|
|
userLayout->addWidget(lineEdit_Password, 1, 1);
|
|
userLayout->setColumnMinimumWidth(0, 100);
|
|
|
|
groupServerDetails = new QGroupBox("Server Details", this);
|
|
auto serverLayout = new QGridLayout(groupServerDetails);
|
|
serverLayout->setContentsMargins(10, 10, 10, 10);
|
|
serverLayout->setHorizontalSpacing(12);
|
|
serverLayout->setVerticalSpacing(8);
|
|
|
|
label_Url = new QLabel("URL:", this);
|
|
|
|
lineEdit_Url = new QLineEdit(this);
|
|
lineEdit_Url->setPlaceholderText("https://address:4321/endpoint");
|
|
lineEdit_Url->setToolTip("Enter full server URL (e.g., https://host:port/endpoint)");
|
|
|
|
serverLayout->addWidget(label_Url, 0, 0);
|
|
serverLayout->addWidget(lineEdit_Url, 0, 1);
|
|
serverLayout->setColumnMinimumWidth(0, 100);
|
|
|
|
groupProject = new QGroupBox("Project", this);
|
|
auto projectLayout = new QGridLayout(groupProject);
|
|
projectLayout->setContentsMargins(10, 10, 10, 10);
|
|
projectLayout->setHorizontalSpacing(12);
|
|
projectLayout->setVerticalSpacing(8);
|
|
|
|
label_Project = new QLabel("Name:", this);
|
|
label_ProjectDir = new QLabel("Directory:", this);
|
|
|
|
lineEdit_Project = new QLineEdit(this);
|
|
lineEdit_Project->setToolTip("Enter project name");
|
|
|
|
lineEdit_ProjectDir = new QLineEdit(this);
|
|
lineEdit_ProjectDir->setToolTip("Enter path to project directory (auto-generated if empty)");
|
|
|
|
subsSelectBtn = new QPushButton(QIcon(":/icons/settings_account"), "", this);
|
|
subsSelectBtn->setFixedWidth(30);
|
|
subsSelectBtn->setToolTip("Select subscriptions");
|
|
|
|
auto historyLabel = new QLabel("History:", this);
|
|
QFont boldFont = historyLabel->font();
|
|
boldFont.setBold(true);
|
|
historyLabel->setFont(boldFont);
|
|
|
|
dataListWidget = new QListWidget();
|
|
dataListWidget->setSelectionMode(QAbstractItemView::NoSelection);
|
|
|
|
auto makeSectionHeader = [](const QString &title) -> QListWidgetItem* {
|
|
auto *item = new QListWidgetItem(title);
|
|
QFont f = item->font();
|
|
f.setBold(true);
|
|
item->setFont(f);
|
|
item->setFlags(Qt::ItemIsEnabled);
|
|
item->setData(Qt::UserRole, true);
|
|
return item;
|
|
};
|
|
|
|
dataListWidget->addItem(makeSectionHeader("Data"));
|
|
for (const QString &cat : {"chat_history", "downloads_history", "screenshot_history", "credentials_history", "targets_history"}) {
|
|
auto *item = new QListWidgetItem(cat);
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Checked);
|
|
dataListWidget->addItem(item);
|
|
}
|
|
dataListWidget->addItem(makeSectionHeader("Agent"));
|
|
for (const QString &cat : {"console_history", "tasks_history"}) {
|
|
auto *item = new QListWidgetItem(cat);
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Checked);
|
|
dataListWidget->addItem(item);
|
|
}
|
|
|
|
auto realtimeLabel = new QLabel("RealTime:", this);
|
|
QFont boldFont2 = realtimeLabel->font();
|
|
boldFont2.setBold(true);
|
|
realtimeLabel->setFont(boldFont2);
|
|
|
|
agentListWidget = new QListWidget();
|
|
agentListWidget->setSelectionMode(QAbstractItemView::NoSelection);
|
|
|
|
agentListWidget->addItem(makeSectionHeader("Data"));
|
|
for (const QString &cat : {"chat_realtime", "downloads_realtime", "screenshot_realtime", "credentials_realtime", "targets_realtime", "notifications", "tunnels"}) {
|
|
auto *item = new QListWidgetItem(cat);
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Checked);
|
|
agentListWidget->addItem(item);
|
|
}
|
|
agentListWidget->addItem(makeSectionHeader("Agent"));
|
|
for (const QString &cat : {"tasks_manager"}) {
|
|
auto *item = new QListWidgetItem(cat);
|
|
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
|
item->setCheckState(Qt::Checked);
|
|
agentListWidget->addItem(item);
|
|
}
|
|
|
|
multiuserCheck = new QCheckBox("Console Team Mode", this);
|
|
multiuserCheck->setChecked(true);
|
|
multiuserCheck->setToolTip("See console output from all operators");
|
|
|
|
auto agentsOnlyActiveCheck = new QCheckBox("Only active agents", this);
|
|
agentsOnlyActiveCheck->setChecked(false);
|
|
agentsOnlyActiveCheck->setToolTip("Synchronize only active agents");
|
|
|
|
auto tasksOnlyJobsCheck = new QCheckBox("Only JOB tasks", this);
|
|
tasksOnlyJobsCheck->setChecked(false);
|
|
tasksOnlyJobsCheck->setToolTip("Synchronize only jobs");
|
|
|
|
auto leftCol = new QVBoxLayout();
|
|
leftCol->setSpacing(6);
|
|
leftCol->addWidget(historyLabel);
|
|
leftCol->addWidget(dataListWidget);
|
|
|
|
auto rightCol = new QVBoxLayout();
|
|
rightCol->setSpacing(6);
|
|
rightCol->addWidget(realtimeLabel);
|
|
rightCol->addWidget(agentListWidget);
|
|
rightCol->addWidget(multiuserCheck);
|
|
rightCol->addWidget(agentsOnlyActiveCheck);
|
|
rightCol->addWidget(tasksOnlyJobsCheck);
|
|
|
|
auto popupLayout = new QHBoxLayout();
|
|
popupLayout->setContentsMargins(8, 8, 8, 8);
|
|
popupLayout->setSpacing(12);
|
|
popupLayout->addLayout(leftCol, 1);
|
|
popupLayout->addLayout(rightCol, 1);
|
|
|
|
subsPopupDialog = new QDialog(this, Qt::Popup | Qt::FramelessWindowHint);
|
|
subsPopupDialog->setLayout(popupLayout);
|
|
subsPopupDialog->setProperty("Main", "base");
|
|
subsPopupDialog->setMinimumWidth(600);
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
|
connect(agentsOnlyActiveCheck, &QCheckBox::checkStateChanged, this, &DialogConnect::onSubsSelectionChanged);
|
|
connect(tasksOnlyJobsCheck, &QCheckBox::checkStateChanged, this, &DialogConnect::onSubsSelectionChanged);
|
|
#else
|
|
connect(agentsOnlyActiveCheck, &QCheckBox::stateChanged, this, &DialogConnect::onSubsSelectionChanged);
|
|
connect(tasksOnlyJobsCheck, &QCheckBox::stateChanged, this, &DialogConnect::onSubsSelectionChanged);
|
|
#endif
|
|
agentsOnlyActiveCheck->setObjectName("agentsOnlyActiveCheck");
|
|
tasksOnlyJobsCheck->setObjectName("tasksOnlyJobsCheck");
|
|
|
|
projectLayout->addWidget(label_Project, 0, 0, 1, 1);
|
|
projectLayout->addWidget(lineEdit_Project, 0, 1, 1, 1);
|
|
projectLayout->addWidget(subsSelectBtn, 0, 2, 1, 1);
|
|
projectLayout->addWidget(label_ProjectDir, 1, 0, 1, 1);
|
|
projectLayout->addWidget(lineEdit_ProjectDir,1, 1, 1, 2);
|
|
projectLayout->setColumnMinimumWidth(0, 100);
|
|
|
|
buttonConnect = new QPushButton(this);
|
|
buttonConnect->setDefault(true);
|
|
buttonConnect->setText("Connect");
|
|
buttonConnect->setFixedWidth(160);
|
|
buttonConnect->setFocus();
|
|
|
|
auto actionButtonsLayout = new QHBoxLayout();
|
|
actionButtonsLayout->addStretch();
|
|
actionButtonsLayout->addWidget(buttonConnect);
|
|
actionButtonsLayout->addStretch();
|
|
|
|
auto formLayout = new QVBoxLayout();
|
|
formLayout->setContentsMargins(10, 10, 10, 10);
|
|
formLayout->setSpacing(10);
|
|
formLayout->addWidget(groupUserInfo);
|
|
formLayout->addWidget(groupServerDetails);
|
|
formLayout->addWidget(groupProject);
|
|
formLayout->addStretch(1);
|
|
formLayout->addLayout(actionButtonsLayout);
|
|
|
|
auto formWidget = new QWidget(this);
|
|
formWidget->setLayout(formLayout);
|
|
|
|
menuContext = new QMenu(this);
|
|
menuContext->addAction("Remove", this, &DialogConnect::itemRemove);
|
|
|
|
label_Profiles = new QLabel(this);
|
|
label_Profiles->setAlignment(Qt::AlignCenter);
|
|
label_Profiles->setText("Profiles");
|
|
|
|
cardWidget = new CardListWidget(this);
|
|
cardWidget->setFixedWidth(240);
|
|
cardWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
cardWidget->addAction(menuContext->menuAction());
|
|
cardWidget->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
cardWidget->setFocusPolicy(Qt::NoFocus);
|
|
|
|
buttonNewProfile = new QPushButton(this);
|
|
buttonNewProfile->setText("New Profile");
|
|
buttonNewProfile->setMinimumSize(QSize(10, 30));
|
|
|
|
buttonLoad = new QPushButton(QIcon(":/icons/file_open"), "", this);
|
|
buttonLoad->setIconSize(QSize(20, 20));
|
|
buttonLoad->setFixedSize(QSize(30, 30));
|
|
buttonLoad->setToolTip("Load profile from file");
|
|
|
|
buttonSave = new QPushButton(QIcon(":/icons/save_as"), "", this);
|
|
buttonSave->setIconSize(QSize(20, 20));
|
|
buttonSave->setFixedSize(QSize(30, 30));
|
|
buttonSave->setToolTip("Save profile to file");
|
|
|
|
auto profileButtonsLayout = new QHBoxLayout();
|
|
profileButtonsLayout->addWidget(buttonNewProfile);
|
|
profileButtonsLayout->addWidget(buttonLoad);
|
|
profileButtonsLayout->addWidget(buttonSave);
|
|
profileButtonsLayout->setSpacing(5);
|
|
profileButtonsLayout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
auto profileButtonsWidget = new QWidget(this);
|
|
profileButtonsWidget->setLayout(profileButtonsLayout);
|
|
|
|
auto separatorLine = new QFrame(this);
|
|
separatorLine->setFrameShape(QFrame::VLine);
|
|
separatorLine->setFrameShadow(QFrame::Sunken);
|
|
|
|
gridLayout = new QGridLayout(this);
|
|
gridLayout->setContentsMargins(5, 5, 5, 5);
|
|
gridLayout->addWidget(formWidget, 0, 0, 3, 1);
|
|
gridLayout->addWidget(separatorLine, 0, 1, 3, 1);
|
|
gridLayout->addWidget(label_Profiles, 0, 2, 1, 1);
|
|
gridLayout->addWidget(cardWidget, 1, 2, 1, 1);
|
|
gridLayout->addWidget(profileButtonsWidget, 2, 2, 1, 1);
|
|
|
|
gridLayout->setRowStretch( 0, 0 );
|
|
gridLayout->setRowStretch( 1, 1 );
|
|
gridLayout->setRowStretch( 2, 0 );
|
|
gridLayout->setColumnStretch( 0, 1 );
|
|
gridLayout->setColumnStretch( 1, 0 );
|
|
gridLayout->setColumnStretch( 2, 0 );
|
|
gridLayout->setColumnStretch( 3, 0 );
|
|
}
|
|
|
|
void DialogConnect::loadProjects()
|
|
{
|
|
cardWidget->clear();
|
|
listProjects = GlobalClient->storage->ListProjects();
|
|
for (auto& profile : listProjects) {
|
|
QString subtitle = profile.GetUsername() + " @ " + profile.GetHost();
|
|
cardWidget->addCard(profile.GetProject(), subtitle);
|
|
}
|
|
}
|
|
|
|
AuthProfile* DialogConnect::StartDialog()
|
|
{
|
|
toConnect = false;
|
|
exec();
|
|
if (!toConnect)
|
|
return nullptr;
|
|
|
|
QString projectDir = lineEdit_ProjectDir->text().trimmed();
|
|
if (projectDir.isEmpty())
|
|
projectDir = defaultProjectDir(lineEdit_Project->text());
|
|
|
|
QString host, port, endpoint;
|
|
parseUrl(host, port, endpoint);
|
|
|
|
auto* newProfile = new AuthProfile(lineEdit_Project->text(), lineEdit_User->text(), lineEdit_Password->text(), host, port, endpoint, projectDir);
|
|
|
|
QStringList selectedSubs;
|
|
for (int i = 0; i < dataListWidget->count(); ++i) {
|
|
auto *item = dataListWidget->item(i);
|
|
if (item && item->data(Qt::UserRole).toBool())
|
|
continue;
|
|
if (item->checkState() == Qt::Checked)
|
|
selectedSubs.append(item->text());
|
|
}
|
|
for (int i = 0; i < agentListWidget->count(); ++i) {
|
|
auto *item = agentListWidget->item(i);
|
|
if (item && item->data(Qt::UserRole).toBool())
|
|
continue;
|
|
if (item->checkState() == Qt::Checked)
|
|
selectedSubs.append(item->text());
|
|
}
|
|
|
|
auto agentsOnlyActiveCheck = subsPopupDialog ? subsPopupDialog->findChild<QCheckBox*>("agentsOnlyActiveCheck") : nullptr;
|
|
if (agentsOnlyActiveCheck && agentsOnlyActiveCheck->isChecked())
|
|
selectedSubs.append("agents_only_active");
|
|
|
|
auto tasksOnlyJobsCheck = subsPopupDialog ? subsPopupDialog->findChild<QCheckBox*>("tasksOnlyJobsCheck") : nullptr;
|
|
if (tasksOnlyJobsCheck && tasksOnlyJobsCheck->isChecked())
|
|
selectedSubs.append("tasks_only_jobs");
|
|
|
|
newProfile->SetSubscriptions(selectedSubs);
|
|
newProfile->SetRegisteredCategories(selectedSubs);
|
|
|
|
updateSubsDisplay();
|
|
newProfile->SetConsoleMultiuser(multiuserCheck->isChecked());
|
|
|
|
if (GlobalClient->storage->ExistsProject(lineEdit_Project->text()))
|
|
GlobalClient->storage->UpdateProject(*newProfile);
|
|
else
|
|
GlobalClient->storage->AddProject(*newProfile);
|
|
|
|
return newProfile;
|
|
}
|
|
|
|
void DialogConnect::updateSubsDisplay()
|
|
{
|
|
QStringList selectedSubs;
|
|
for (int i = 0; i < dataListWidget->count(); ++i) {
|
|
auto *item = dataListWidget->item(i);
|
|
if (item && item->data(Qt::UserRole).toBool())
|
|
continue;
|
|
if (item->checkState() == Qt::Checked)
|
|
selectedSubs.append(item->text());
|
|
}
|
|
for (int i = 0; i < agentListWidget->count(); ++i) {
|
|
auto *item = agentListWidget->item(i);
|
|
if (item && item->data(Qt::UserRole).toBool())
|
|
continue;
|
|
if (item->checkState() == Qt::Checked)
|
|
selectedSubs.append(item->text());
|
|
}
|
|
|
|
auto agentsOnlyActiveCheck = subsPopupDialog ? subsPopupDialog->findChild<QCheckBox*>("agentsOnlyActiveCheck") : nullptr;
|
|
if (agentsOnlyActiveCheck && agentsOnlyActiveCheck->isChecked())
|
|
selectedSubs.append("agents_only_active");
|
|
|
|
auto tasksOnlyJobsCheck = subsPopupDialog ? subsPopupDialog->findChild<QCheckBox*>("tasksOnlyJobsCheck") : nullptr;
|
|
if (tasksOnlyJobsCheck && tasksOnlyJobsCheck->isChecked())
|
|
selectedSubs.append("tasks_only_jobs");
|
|
|
|
subsSelectBtn->setToolTip(selectedSubs.isEmpty() ? "Select subscriptions" : selectedSubs.join(", "));
|
|
}
|
|
|
|
void DialogConnect::itemRemove()
|
|
{
|
|
auto* item = cardWidget->currentItem();
|
|
if (!item)
|
|
return;
|
|
|
|
QString project = item->data(CardListWidget::TitleRole).toString();
|
|
Storage::RemoveAllListenerProfiles(project);
|
|
Storage::RemoveAllAgentProfiles(project);
|
|
GlobalClient->storage->RemoveProject(project);
|
|
delete cardWidget->takeItem(cardWidget->row(item));
|
|
loadProjects();
|
|
}
|
|
|
|
void DialogConnect::onProfileSelected()
|
|
{
|
|
auto* item = cardWidget->currentItem();
|
|
if (!item)
|
|
return;
|
|
|
|
const QString project = item->data(CardListWidget::TitleRole).toString();
|
|
isNewProject = false;
|
|
projectDirTouched = true;
|
|
|
|
for (auto& p : listProjects) {
|
|
if (p.GetProject() == project) {
|
|
lineEdit_Project->setText(p.GetProject());
|
|
lineEdit_ProjectDir->setText(p.GetProjectDir());
|
|
lineEdit_Url->setText(buildUrl(p.GetHost(), p.GetPort(), p.GetEndpoint()));
|
|
lineEdit_User->setText(p.GetUsername());
|
|
lineEdit_Password->setText(p.GetPassword());
|
|
|
|
QStringList subs = p.GetSubscriptions();
|
|
dataListWidget->blockSignals(true);
|
|
for (int i = 0; i < dataListWidget->count(); ++i) {
|
|
auto *subItem = dataListWidget->item(i);
|
|
if (subItem && subItem->data(Qt::UserRole).toBool())
|
|
continue;
|
|
subItem->setCheckState(subs.contains(subItem->text()) ? Qt::Checked : Qt::Unchecked);
|
|
}
|
|
dataListWidget->blockSignals(false);
|
|
agentListWidget->blockSignals(true);
|
|
for (int i = 0; i < agentListWidget->count(); ++i) {
|
|
auto *subItem = agentListWidget->item(i);
|
|
if (subItem && subItem->data(Qt::UserRole).toBool())
|
|
continue;
|
|
subItem->setCheckState(subs.contains(subItem->text()) ? Qt::Checked : Qt::Unchecked);
|
|
}
|
|
agentListWidget->blockSignals(false);
|
|
multiuserCheck->setChecked(p.GetConsoleMultiuser());
|
|
|
|
auto agentsOnlyActiveCheck = subsPopupDialog ? subsPopupDialog->findChild<QCheckBox*>("agentsOnlyActiveCheck") : nullptr;
|
|
if (agentsOnlyActiveCheck)
|
|
agentsOnlyActiveCheck->setChecked(subs.contains("agents_only_active"));
|
|
|
|
auto tasksOnlyJobsCheck = subsPopupDialog ? subsPopupDialog->findChild<QCheckBox*>("tasksOnlyJobsCheck") : nullptr;
|
|
if (tasksOnlyJobsCheck)
|
|
tasksOnlyJobsCheck->setChecked(subs.contains("tasks_only_jobs"));
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DialogConnect::handleContextMenu(const QPoint &pos)
|
|
{
|
|
QPoint globalPos = cardWidget->mapToGlobal( pos );
|
|
menuContext->exec( globalPos );
|
|
}
|
|
|
|
bool DialogConnect::checkValidInput() const
|
|
{
|
|
const auto checkEmpty = [](const QLineEdit* edit, const QString& msg) {
|
|
if (edit->text().isEmpty()) {
|
|
MessageError(msg);
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
if (!checkEmpty(lineEdit_Project, "Project is empty")) return false;
|
|
if (!checkEmpty(lineEdit_Url, "URL is empty")) return false;
|
|
if (!checkEmpty(lineEdit_User, "Username is empty")) return false;
|
|
if (!checkEmpty(lineEdit_Password, "Password is empty")) return false;
|
|
|
|
QString host, port, endpoint;
|
|
if (!parseUrl(host, port, endpoint)) {
|
|
MessageError("Invalid URL format (Example: https://host:port/endpoint)");
|
|
return false;
|
|
}
|
|
|
|
if (GlobalClient->storage->ExistsProject(lineEdit_Project->text()) && isNewProject) {
|
|
MessageError("Project already exists");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DialogConnect::onButton_Connect()
|
|
{
|
|
if (checkValidInput()) {
|
|
toConnect = true;
|
|
close();
|
|
}
|
|
}
|
|
|
|
void DialogConnect::clearFields()
|
|
{
|
|
lineEdit_User->clear();
|
|
lineEdit_Password->clear();
|
|
lineEdit_Project->clear();
|
|
lineEdit_ProjectDir->clear();
|
|
lineEdit_Url->clear();
|
|
cardWidget->clearSelection();
|
|
|
|
dataListWidget->blockSignals(true);
|
|
for (int i = 0; i < dataListWidget->count(); ++i)
|
|
dataListWidget->item(i)->setCheckState(Qt::Checked);
|
|
dataListWidget->blockSignals(false);
|
|
agentListWidget->blockSignals(true);
|
|
for (int i = 0; i < agentListWidget->count(); ++i)
|
|
agentListWidget->item(i)->setCheckState(Qt::Checked);
|
|
agentListWidget->blockSignals(false);
|
|
multiuserCheck->setChecked(true);
|
|
|
|
lineEdit_Project->setFocus();
|
|
}
|
|
|
|
void DialogConnect::onProjectNameChanged(const QString &text)
|
|
{
|
|
if (projectDirTouched)
|
|
return;
|
|
|
|
const QString name = text.trimmed();
|
|
lineEdit_ProjectDir->setText(name.isEmpty() ? QString() : defaultProjectDir(name));
|
|
}
|
|
|
|
void DialogConnect::onProjectDirEdited(const QString &) { projectDirTouched = true; }
|
|
|
|
void DialogConnect::onSelectProjectDir()
|
|
{
|
|
QString current = lineEdit_ProjectDir->text().trimmed();
|
|
if (current.isEmpty())
|
|
current = defaultProjectDir(lineEdit_Project->text());
|
|
|
|
QString dir = QFileDialog::getExistingDirectory(this, "Select project directory", current);
|
|
if (!dir.isEmpty()) {
|
|
lineEdit_ProjectDir->setText(dir);
|
|
projectDirTouched = true;
|
|
}
|
|
}
|
|
|
|
void DialogConnect::onButton_NewProfile()
|
|
{
|
|
isNewProject = true;
|
|
projectDirTouched = false;
|
|
clearFields();
|
|
}
|
|
|
|
void DialogConnect::onButton_Load()
|
|
{
|
|
QString baseDir = QDir::home().filePath("AdaptixProjects");
|
|
QDir(baseDir).mkpath(".");
|
|
|
|
QString projectDir = lineEdit_ProjectDir->text().trimmed();
|
|
if (!projectDir.isEmpty() && QDir(projectDir).exists())
|
|
baseDir = projectDir;
|
|
|
|
NonBlockingDialogs::getOpenFileName(this, "Load Profile", baseDir, "Adaptix Profile files (*.adaptixProfile)",
|
|
[this](const QString& filePath) {
|
|
if (filePath.isEmpty())
|
|
return;
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::ReadOnly)) {
|
|
MessageError("Failed to open file for reading");
|
|
return;
|
|
}
|
|
|
|
QJsonParseError parseError;
|
|
QJsonDocument document = QJsonDocument::fromJson(file.readAll(), &parseError);
|
|
file.close();
|
|
|
|
if (parseError.error != QJsonParseError::NoError || !document.isObject()) {
|
|
MessageError("Error JSON parse");
|
|
return;
|
|
}
|
|
|
|
QJsonObject json = document.object();
|
|
const QStringList requiredFields = {"project", "host", "port", "endpoint", "username", "password"};
|
|
for (const QString& field : requiredFields) {
|
|
if (!json.contains(field) || !json[field].isString()) {
|
|
MessageError(QString("Required parameter '%1' is missing").arg(field));
|
|
return;
|
|
}
|
|
}
|
|
|
|
const QString project = json["project"].toString();
|
|
isNewProject = !GlobalClient->storage->ExistsProject(project);
|
|
lineEdit_Project->setText(project);
|
|
|
|
QString projectDirFinal;
|
|
if (json.contains("projectDir") && json["projectDir"].isString()) {
|
|
projectDirFinal = json["projectDir"].toString();
|
|
projectDirTouched = true;
|
|
} else {
|
|
projectDirFinal = defaultProjectDir(project);
|
|
projectDirTouched = false;
|
|
}
|
|
lineEdit_ProjectDir->setText(projectDirFinal);
|
|
|
|
lineEdit_User->setText(json["username"].toString());
|
|
lineEdit_Password->setText(json["password"].toString());
|
|
lineEdit_Url->setText(buildUrl(json["host"].toString(), json["port"].toString(), json["endpoint"].toString()));
|
|
|
|
AuthProfile loadedProfile(project, json["username"].toString(), json["password"].toString(), json["host"].toString(), json["port"].toString(), json["endpoint"].toString(), projectDirFinal);
|
|
|
|
if (isNewProject)
|
|
GlobalClient->storage->AddProject(loadedProfile);
|
|
else
|
|
GlobalClient->storage->UpdateProject(loadedProfile);
|
|
|
|
loadProjects();
|
|
|
|
for (int i = 0; i < cardWidget->count(); ++i) {
|
|
if (cardWidget->item(i)->data(CardListWidget::TitleRole).toString() == project) {
|
|
cardWidget->setCurrentRow(i);
|
|
onProfileSelected();
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void DialogConnect::onButton_Save()
|
|
{
|
|
if (!checkValidInput())
|
|
return;
|
|
|
|
const QString projectName = lineEdit_Project->text().trimmed();
|
|
QString projectDir = lineEdit_ProjectDir->text().trimmed();
|
|
if (projectDir.isEmpty())
|
|
projectDir = defaultProjectDir(projectName);
|
|
|
|
QString host, port, endpoint;
|
|
parseUrl(host, port, endpoint);
|
|
|
|
QJsonObject json;
|
|
json["project"] = projectName;
|
|
json["host"] = host;
|
|
json["port"] = port;
|
|
json["endpoint"] = endpoint;
|
|
json["username"] = lineEdit_User->text().trimmed();
|
|
json["password"] = lineEdit_Password->text();
|
|
json["projectDir"] = projectDir;
|
|
|
|
QString baseDir = QDir::homePath();
|
|
QString projectDirText = lineEdit_ProjectDir->text().trimmed();
|
|
if (!projectDirText.isEmpty())
|
|
baseDir = projectDirText;
|
|
|
|
QString initialPath = QDir(baseDir).filePath(projectName + ".adaptixProfile");
|
|
|
|
NonBlockingDialogs::getSaveFileName(this, "Save Profile", initialPath, "Adaptix Profile files (*.adaptixProfile)",
|
|
[json](const QString& filePath) {
|
|
if (filePath.isEmpty())
|
|
return;
|
|
|
|
QFile file(filePath);
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
|
MessageError("Failed to open file for writing");
|
|
return;
|
|
}
|
|
|
|
file.write(QJsonDocument(json).toJson(QJsonDocument::Indented));
|
|
file.close();
|
|
MessageSuccess("Profile saved successfully");
|
|
});
|
|
}
|
|
|
|
void DialogConnect::showSubsPopup()
|
|
{
|
|
QPoint pos = subsSelectBtn->mapToGlobal(QPoint(0, subsSelectBtn->height()));
|
|
subsPopupDialog->move(pos);
|
|
subsPopupDialog->show();
|
|
subsPopupDialog->raise();
|
|
subsPopupDialog->activateWindow();
|
|
}
|
|
|
|
void DialogConnect::onSubsSelectionChanged()
|
|
{
|
|
updateSubsDisplay();
|
|
} |