diff --git a/Client/CMakeLists.txt b/Client/CMakeLists.txt index a0aa2345..c0100c49 100644 --- a/Client/CMakeLists.txt +++ b/Client/CMakeLists.txt @@ -8,6 +8,8 @@ set(CMAKE_AUTOUIC ON) find_package( OpenSSL REQUIRED ) +include_directories( Headers ) + find_package(Qt6 COMPONENTS Core @@ -15,22 +17,44 @@ find_package(Qt6 Widgets Network WebSockets + Sql REQUIRED ) +SET( HEADERS + Headers/main.h + Headers/Utils/Logs.h + Headers/UI/Dialogs/DialogConnect.h + Headers/Utils/Valid.h + Headers/MainAdaptix.h + Headers/Client/Storage.h + Headers/Client/AuthProfile.h + Headers/Utils/FileSystem.h +) + add_executable(AdaptixClient + ${HEADERS} + Source/main.cpp Resources/Resources.qrc + Source/Utils/Logs.cpp + Source/UI/Dialogs/DialogConnect.cpp + Source/Utils/Valid.cpp + Source/MainAdaptix.cpp + Source/Client/Storage.cpp + Source/Client/AuthProfile.cpp + Source/Utils/FileSystem.cpp ) target_link_libraries(AdaptixClient - Qt::Core - Qt::Gui - Qt::Widgets + Qt6::Core + Qt6::Gui + Qt6::Widgets Qt6::Network Qt6::WebSockets + Qt6::Sql OpenSSL::Crypto ) diff --git a/Client/Headers/Client/AuthProfile.h b/Client/Headers/Client/AuthProfile.h new file mode 100644 index 00000000..9a479c52 --- /dev/null +++ b/Client/Headers/Client/AuthProfile.h @@ -0,0 +1,31 @@ +#ifndef ADAPTIXCLIENT_AUTHPROFILE_H +#define ADAPTIXCLIENT_AUTHPROFILE_H +#include + +class AuthProfile { + QString project; + QString username; + QString password; + QString host; + QString port; + QString endpoint; + QString accessToken; + QString refreshToken; + +public: + bool valid; + + AuthProfile(); + AuthProfile(QString project, QString username, QString password, QString host, QString port, QString endpoint); + ~AuthProfile(); + + QString GetProject() { return project; }; + QString GetUsername() { return username; }; + QString GetPassword() { return password; }; + QString GetHost() { return host; }; + QString GetPort() { return port; }; + QString GetEndpoint() { return endpoint; }; +}; + + +#endif //ADAPTIXCLIENT_AUTHPROFILE_H diff --git a/Client/Headers/Client/Storage.h b/Client/Headers/Client/Storage.h new file mode 100644 index 00000000..03534452 --- /dev/null +++ b/Client/Headers/Client/Storage.h @@ -0,0 +1,27 @@ +#ifndef ADAPTIXCLIENT_STORAGE_H +#define ADAPTIXCLIENT_STORAGE_H + +#include +#include + +class Storage { + +private: + QSqlDatabase db; + QString appDirPath; + + void checkDatabase(); + +public: + QString dbFilePath; + + Storage(); + ~Storage(); + + void AddProject(AuthProfile profile); + void RemoveProject(QString project); + bool ExistsProject(QString project); + QVector ListProjects(); +}; + +#endif //ADAPTIXCLIENT_STORAGE_H \ No newline at end of file diff --git a/Client/Headers/MainAdaptix.h b/Client/Headers/MainAdaptix.h new file mode 100644 index 00000000..f79f49d0 --- /dev/null +++ b/Client/Headers/MainAdaptix.h @@ -0,0 +1,22 @@ +#ifndef ADAPTIXCLIENT_MAINADAPTIX_H +#define ADAPTIXCLIENT_MAINADAPTIX_H + +#include +#include + +class MainAdaptix : public QWidget { + +public: + Storage* storage = nullptr; + + explicit MainAdaptix(); + ~MainAdaptix() override; + + void Start(); + void Exit(); + void SetApplicationTheme(); +}; + +extern MainAdaptix* GlobalClient; + +#endif //ADAPTIXCLIENT_MAINADAPTIX_H diff --git a/Client/Headers/UI/Dialogs/DialogConnect.h b/Client/Headers/UI/Dialogs/DialogConnect.h new file mode 100644 index 00000000..f695c2c9 --- /dev/null +++ b/Client/Headers/UI/Dialogs/DialogConnect.h @@ -0,0 +1,52 @@ +#ifndef ADAPTIXCLIENT_DIALOGCONNECT_H +#define ADAPTIXCLIENT_DIALOGCONNECT_H + +#include +#include + +class DialogConnect : public QDialog +{ + bool toConnect = false; + bool isNewProject = true; + + QWidget* ConnectWidget = nullptr; + QGridLayout* gridLayout = nullptr; + QLabel* label_UserInfo = nullptr; + QLabel* label_User = nullptr; + QLabel* label_Password = nullptr; + QLabel* label_ServerDetails = nullptr; + QLabel* label_Project = nullptr; + QLabel* label_Host = nullptr; + QLabel* label_Port = nullptr; + QLabel* label_Endpoint = nullptr; + QLineEdit* lineEdit_User = nullptr; + QLineEdit* lineEdit_Password = nullptr; + QLineEdit* lineEdit_Project = nullptr; + QLineEdit* lineEdit_Host = nullptr; + QLineEdit* lineEdit_Port = nullptr; + QLineEdit* lineEdit_Endpoint = nullptr; + QPushButton* ButtonConnect = nullptr; + QTableWidget* tableWidget = nullptr; + QMenu* menuContex = nullptr; + + void CreateUI(); + bool CheckValidInput(); + +public: + + QVector listProjects; + + ~DialogConnect() override; + explicit DialogConnect(); + + AuthProfile StartDialog(); + +private slots: + void onButton_Connect(); + void handleContextMenu( const QPoint &pos ) const; + void itemSelected(); + void itemRemove() const; + +}; + +#endif //ADAPTIXCLIENT_DIALOGCONNECT_H diff --git a/Client/Headers/Utils/FileSystem.h b/Client/Headers/Utils/FileSystem.h new file mode 100644 index 00000000..6b059143 --- /dev/null +++ b/Client/Headers/Utils/FileSystem.h @@ -0,0 +1,11 @@ +#ifndef ADAPTIXCLIENT_FILESYSTEM_H +#define ADAPTIXCLIENT_FILESYSTEM_H + +#include +#include +#include +#include + +QString ReadFileString(const QString &filePath, bool* result); + +#endif //ADAPTIXCLIENT_FILESYSTEM_H diff --git a/Client/Headers/Utils/Logs.h b/Client/Headers/Utils/Logs.h new file mode 100644 index 00000000..25a4951b --- /dev/null +++ b/Client/Headers/Utils/Logs.h @@ -0,0 +1,18 @@ +#ifndef ADAPTIXCLIENT_LOGS_H +#define ADAPTIXCLIENT_LOGS_H + +#include +#include +#include +#include + +void LogInfo(const char* format, ...); + +void LogSuccess(const char* format, ...); + +void LogError(const char* format, ...); + +void MessageError( QString message ); + + +#endif //ADAPTIXCLIENT_LOGS_H diff --git a/Client/Headers/Utils/Valid.h b/Client/Headers/Utils/Valid.h new file mode 100644 index 00000000..c02aadb4 --- /dev/null +++ b/Client/Headers/Utils/Valid.h @@ -0,0 +1,9 @@ +#ifndef ADAPTIXCLIENT_VALID_H +#define ADAPTIXCLIENT_VALID_H + +#include +#include + +bool IsValidURI(const QString &uri); + +#endif //ADAPTIXCLIENT_VALID_H \ No newline at end of file diff --git a/Client/Headers/main.h b/Client/Headers/main.h new file mode 100644 index 00000000..b8c72461 --- /dev/null +++ b/Client/Headers/main.h @@ -0,0 +1,27 @@ +#ifndef ADAPTIXCLIENT_MAIN_H +#define ADAPTIXCLIENT_MAIN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + + +#endif //ADAPTIXCLIENT_MAIN_H diff --git a/Client/Resources/Resources.qrc b/Client/Resources/Resources.qrc index c5b08b9f..7d2ed057 100755 --- a/Client/Resources/Resources.qrc +++ b/Client/Resources/Resources.qrc @@ -8,4 +8,10 @@ fonts/DroidSansMono.ttf + + themes/dark.css + themes/light_arc.css + themes/color_nord.css + + diff --git a/Client/Resources/themes/color_nord.css b/Client/Resources/themes/color_nord.css new file mode 100644 index 00000000..5b429d87 --- /dev/null +++ b/Client/Resources/themes/color_nord.css @@ -0,0 +1,58 @@ +/* Nord Theme */ +QWidget { + background-color: #2E3440; /* Polar Night background */ + color: #D8DEE9; /* Snow Storm text */ +} + +/* QPushButton */ +QPushButton { + background-color: #3B4252; /* Darker Polar Night for buttons */ + border: 1px solid #4C566A; /* Frost border */ + padding: 8px; + border-radius: 4px; + color: #D8DEE9; +} + +QPushButton:hover { + background-color: #4C566A; /* Frost on hover */ +} + +/* QLineEdit */ +QLineEdit { + background-color: #3B4252; + border: 1px solid #4C566A; + padding: 8px; + color: #D8DEE9; +} + +/* QTableWidget */ +QTableWidget { + background-color: #3B4252; + border: 1px solid #4C566A; + gridline-color: #434C5E; /* Grid lines */ + color: #D8DEE9; + border-radius: 4px; + padding: 4px; +} + +/* QTableWidget Selected Row */ +QTableWidget::item:selected { + background-color: #88C0D0; /* Frost for selected row */ + color: #2E3440; /* Polar Night for contrast */ +} + +/* QMenu */ +QMenu { + background-color: #2E3440; + border: 1px solid #4C566A; +} + +QMenu::item { + padding: 5px 10px; + color: #D8DEE9; +} + +QMenu::item:selected { + background-color: #88C0D0; + color: #2E3440; +} diff --git a/Client/Resources/themes/dark.css b/Client/Resources/themes/dark.css new file mode 100644 index 00000000..98890d02 --- /dev/null +++ b/Client/Resources/themes/dark.css @@ -0,0 +1,58 @@ +/* Dark Theme */ +QWidget { + background-color: #222222; /* Dark gray background */ + color: #E0E0E0; /* Light gray text */ +} + +/* QPushButton */ +QPushButton { + background-color: #333333; /* Dark gray for buttons */ + border: 1px solid #4A4A4A; /* Medium gray border */ + padding: 8px; + border-radius: 4px; + color: #E0E0E0; /* Light gray text */ +} + +QPushButton:hover { + background-color: #4A4A4A; /* Slightly lighter gray on hover */ +} + +/* QLineEdit */ +QLineEdit { + background-color: #2A2A2A; /* Darker gray for input fields */ + border: 1px solid #4A4A4A; /* Medium gray border */ + padding: 8px; + color: #E0E0E0; /* Light gray text */ +} + +/* QTableWidget */ +QTableWidget { + background-color: #2A2A2A; /* Darker gray background for table */ + border: 1px solid #4A4A4A; /* Medium gray border */ + gridline-color: #3C3C3C; /* Slightly lighter gray for grid lines */ + color: #E0E0E0; /* Light gray text */ + border-radius: 4px; + padding: 4px; +} + +/* QTableWidget Selected Row */ +QTableWidget::item:selected { + background-color: #4A4A4A; /* Slightly lighter gray for selected row */ + color: #FFFFFF; /* White text for contrast */ +} + +/* QMenu */ +QMenu { + background-color: #1E1E1E; /* Dark gray background */ + border: 1px solid #4A4A4A; /* Medium gray border */ +} + +QMenu::item { + padding: 5px 10px; + color: #E0E0E0; /* Light gray text */ +} + +QMenu::item:selected { + background-color: #333333; /* Dark gray for selected item */ + color: #FFFFFF; /* White text for contrast */ +} diff --git a/Client/Resources/themes/light_arc.css b/Client/Resources/themes/light_arc.css new file mode 100644 index 00000000..87de7aa3 --- /dev/null +++ b/Client/Resources/themes/light_arc.css @@ -0,0 +1,55 @@ +/* Arc Theme */ +QWidget { + background-color: #ECECEC; + color: #2E3440; +} + +/* QPushButton */ +QPushButton { + background-color: #5294E2; + color: #FFFFFF; + border: none; + padding: 8px; + border-radius: 4px; +} + +QPushButton:hover { + background-color: #3B75D1; +} + +/* QLineEdit */ +QLineEdit { + background-color: #FFFFFF; + border: 1px solid #D0D0D0; + padding: 8px; + color: #2E3440; +} + +/* QTableWidget */ +QTableWidget { + background-color: #FFFFFF; + border: 1px solid #D0D0D0; + gridline-color: #E0E0E0; + color: #2E3440; +} + +/* QMenu */ +QMenu { + background-color: #ECECEC; + border: 1px solid #D0D0D0; +} + +QMenu::item { + padding: 5px 10px; +} + +QMenu::item:selected { + background-color: #5294E2; + color: #FFFFFF; +} + +/* QTableWidget Selected Row */ +QTableWidget::item:selected { + background-color: #4A566A; /* Darker gray for selected row */ + color: #FFFFFF; /* White text for selected row */ +} diff --git a/Client/Source/Client/AuthProfile.cpp b/Client/Source/Client/AuthProfile.cpp new file mode 100644 index 00000000..5ece66aa --- /dev/null +++ b/Client/Source/Client/AuthProfile.cpp @@ -0,0 +1,18 @@ +#include + +AuthProfile::AuthProfile() { + this->valid = false; +} + +AuthProfile::AuthProfile(QString project, QString username, QString password, QString host, QString port, QString endpoint) { + this->project = project; + this->username = username; + this->password = password; + this->host = host; + this->port = port; + this->endpoint = endpoint; + this->valid = true; +} + +AuthProfile::~AuthProfile() {} + diff --git a/Client/Source/Client/Storage.cpp b/Client/Source/Client/Storage.cpp new file mode 100644 index 00000000..2d67e876 --- /dev/null +++ b/Client/Source/Client/Storage.cpp @@ -0,0 +1,117 @@ +#include + +Storage::Storage() { + QString homeDirPath = QDir::homePath(); + bool appDirExists = false; + appDirPath = QDir(homeDirPath).filePath(".adaptix"); + + QDir appDir(appDirPath); + if ( appDir.exists() ) { + appDirExists = true; + } else { + if (appDir.mkpath(appDirPath)) { + appDirExists = true; + } + else { + LogError("Adaptix directory %s not created!\n", appDirPath.toStdString().c_str()); + } + } + + if( appDirExists ) { + dbFilePath = QDir(appDirPath).filePath("storage.db"); + db = QSqlDatabase::addDatabase( "QSQLITE" ); + db.setDatabaseName(dbFilePath); + + if ( db.open() ) { + this->checkDatabase(); + } + else { + LogError("Adaptix Database did not opened: %s\n", db.lastError().text().toStdString().c_str()); + } + } +} + +Storage::~Storage() { + if (db.isOpen()) { + db.close(); + } +} + +void Storage::checkDatabase() { + + auto query = QSqlQuery(); + query.prepare("CREATE TABLE IF NOT EXISTS Projects ( " + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "project TEXT UNIQUE, " + "host TEXT, " + "port INTEGER, " + "endpoint TEXT, " + "username TEXT, " + "password TEXT );" + ); + + if ( !query.exec() ) { + LogError("Table PROJECTS not created: %s\n", query.lastError().text().toStdString().c_str()); + } +} + +void Storage::AddProject(AuthProfile profile) { + QSqlQuery query; + query.prepare( "INSERT INTO Projects (project, host, port, endpoint, username, password) VALUES (:Project, :Host, :Port, :Endpoint, :Username, :Password);"); + + query.bindValue(":Project", profile.GetProject().toStdString().c_str()); + query.bindValue(":Host", profile.GetHost().toStdString().c_str()); + query.bindValue(":Port", profile.GetPort().toStdString().c_str()); + query.bindValue(":Endpoint", profile.GetEndpoint().toStdString().c_str()); + query.bindValue(":Username", profile.GetUsername().toStdString().c_str()); + query.bindValue(":Password", profile.GetPassword().toStdString().c_str()); + + if ( !query.exec() ) { + LogError("The project has not been added to the database.: %s\n", query.lastError().text().toStdString().c_str()); + } +} + +QVector Storage::ListProjects() { + auto list = QVector(); + QSqlQuery query; + + query.prepare( "SELECT * FROM Projects;" ); + if ( query.exec() ) { + while ( query.next() ) { + AuthProfile profile(query.value( "project" ).toString(), + query.value( "username" ).toString(), + query.value( "password" ).toString(), + query.value( "host" ).toString(), + query.value( "port" ).toString(), + query.value( "endpoint" ).toString() ); + list.push_back(profile); + } + } + else { + LogError("Failed to query projects from database: %s\n", query.lastError().text().toStdString().c_str()); + } + + return list; +} + +bool Storage::ExistsProject(QString project) { + QSqlQuery query; + + query.prepare("SELECT 1 FROM Projects WHERE project = :Project LIMIT 1;"); + query.bindValue(":Project", project); + if (!query.exec()) { + LogError("Failed to query projects from database: %s\n", query.lastError().text().toStdString().c_str()); + return false; + } + + return query.next(); +} + +void Storage::RemoveProject(QString project) { + QSqlQuery query; + query.prepare("DELETE FROM Projects WHERE project = :Project"); + query.bindValue(":Project", project); + if (!query.exec()) { + LogError("Failed to delete project from database: %s\n", query.lastError().text().toStdString().c_str()); + } +} diff --git a/Client/Source/MainAdaptix.cpp b/Client/Source/MainAdaptix.cpp new file mode 100644 index 00000000..b6b3995c --- /dev/null +++ b/Client/Source/MainAdaptix.cpp @@ -0,0 +1,49 @@ +#include +#include + +MainAdaptix::MainAdaptix() { + storage = new Storage(); + this->SetApplicationTheme(); +} + +MainAdaptix::~MainAdaptix(){ + delete storage; +}; + +void MainAdaptix::Start() { + auto dialogConnect = new DialogConnect(); + auto authProfile = dialogConnect->StartDialog(); + if ( ! authProfile.valid ) { + this->Exit(); + return; + } + + QApplication::exec(); +} + +void MainAdaptix::Exit() { + QApplication::exit(0); +} + +void MainAdaptix::SetApplicationTheme() { + + QGuiApplication::setWindowIcon( QIcon( ":/LogoLin" ) ); + + int FontID = QFontDatabase::addApplicationFont( ":/fonts/DroidSansMono" ); + QString FontFamily = QFontDatabase::applicationFontFamilies( FontID ).at( 0 ); + auto Font = QFont( FontFamily ); + + Font.setPointSize( 10 ); + QApplication::setFont( Font ); + + QString theme = ":/themes/Dark"; +// QString theme = ":/themes/Arc (Light)"; +// QString theme = ":/themes/Nord"; + + bool result = false; + QString style = ReadFileString(theme, &result); + if (result) { + QApplication *app = qobject_cast(QCoreApplication::instance()); + app->setStyleSheet(style); + } +} diff --git a/Client/Source/UI/Dialogs/DialogConnect.cpp b/Client/Source/UI/Dialogs/DialogConnect.cpp new file mode 100644 index 00000000..975a8707 --- /dev/null +++ b/Client/Source/UI/Dialogs/DialogConnect.cpp @@ -0,0 +1,256 @@ +#include +#include + +DialogConnect::DialogConnect() { + this->CreateUI(); + + connect( tableWidget, &QTableWidget::itemPressed, this, &DialogConnect::itemSelected ); + connect( tableWidget, &QTableWidget::customContextMenuRequested, this, &DialogConnect::handleContextMenu ); + + connect( lineEdit_Project, &QLineEdit::returnPressed, this, [&](){onButton_Connect();} ); + connect( lineEdit_Host, &QLineEdit::returnPressed, this, [&](){onButton_Connect();} ); + connect( lineEdit_Port, &QLineEdit::returnPressed, this, [&](){onButton_Connect();} ); + connect( lineEdit_Endpoint, &QLineEdit::returnPressed, this, [&](){onButton_Connect();} ); + connect( lineEdit_User, &QLineEdit::returnPressed, this, [&](){onButton_Connect();} ); + connect( lineEdit_Password, &QLineEdit::returnPressed, this, [&](){onButton_Connect();} ); + connect( ButtonConnect, &QPushButton::clicked, this, [&](){onButton_Connect();} ); + + tableWidget->show(); +} + +DialogConnect::~DialogConnect() { + delete ConnectWidget; + delete gridLayout; + delete label_UserInfo; + delete label_User; + delete label_Password; + delete label_ServerDetails; + delete label_Project; + delete label_Host; + delete label_Port; + delete label_Endpoint; + delete lineEdit_User; + delete lineEdit_Password; + delete lineEdit_Project; + delete lineEdit_Host; + delete lineEdit_Port; + delete lineEdit_Endpoint; + delete ButtonConnect; + delete tableWidget; + delete menuContex; +} + +void DialogConnect::CreateUI() { + + resize( 740, 340 ); + setMinimumSize( QSize( 740, 340 ) ); + setMaximumSize( QSize( 740, 340 ) ); + + + ConnectWidget = this ; + ConnectWidget->setWindowTitle("Connect"); + + + label_UserInfo = new QLabel( ConnectWidget ); + label_UserInfo->setAlignment(Qt::AlignCenter); + label_UserInfo->setText( "User information" ); + + label_User = new QLabel( ConnectWidget ); + label_User->setText( "User:" ); + + label_Password = new QLabel( ConnectWidget ); + label_Password->setText( "Password:" ); + + label_ServerDetails = new QLabel( ConnectWidget ); + label_ServerDetails->setAlignment(Qt::AlignCenter); + label_ServerDetails->setText( "Server details" ); + + label_Project = new QLabel( ConnectWidget ); + label_Project->setText( "Project:" ); + + label_Host = new QLabel( ConnectWidget ); + label_Host->setText( "Host:" ); + + label_Port = new QLabel( ConnectWidget ); + label_Port->setText( "Port:" ); + + label_Endpoint = new QLabel( ConnectWidget ); + label_Endpoint->setText( "Endpoint:" ); + + + lineEdit_User = new QLineEdit( ConnectWidget ); + + lineEdit_Password = new QLineEdit( ConnectWidget ); + lineEdit_Password->setEchoMode( QLineEdit::Password ); + + lineEdit_Project = new QLineEdit( ConnectWidget ); + + lineEdit_Host = new QLineEdit( ConnectWidget ); + + lineEdit_Port = new QLineEdit( ConnectWidget ); + lineEdit_Port->setValidator( new QRegularExpressionValidator( QRegularExpression( "[0-9]*" ), lineEdit_Port ) ); + + lineEdit_Endpoint = new QLineEdit( ConnectWidget ); + + + ButtonConnect = new QPushButton( ConnectWidget ); + ButtonConnect->setText( "Connect" ); + ButtonConnect->setFocus(); + + + menuContex = new QMenu( this ); + menuContex->addAction( "Remove", this, &DialogConnect::itemRemove ); + + + tableWidget = new QTableWidget(ConnectWidget); + tableWidget->setColumnCount( 3 ); + tableWidget->setMinimumSize(QSize( 390, 0) ); + tableWidget->setContextMenuPolicy( Qt::CustomContextMenu ); + tableWidget->addAction( menuContex->menuAction() ); + tableWidget->setAutoFillBackground( false ); + tableWidget->horizontalHeader()->setSectionResizeMode( QHeaderView::Stretch ); + tableWidget->setShowGrid( false ); + tableWidget->setSortingEnabled( true ); + tableWidget->setWordWrap( true ); + tableWidget->setCornerButtonEnabled( false ); + tableWidget->horizontalHeader()->setVisible( true ); + tableWidget->horizontalHeader()->setCascadingSectionResizes( true ); + tableWidget->horizontalHeader()->setHighlightSections( false ); + tableWidget->verticalHeader()->setVisible( false ); + tableWidget->setSelectionBehavior( QAbstractItemView::SelectRows ); + tableWidget->setSelectionMode( QAbstractItemView::SingleSelection ); + tableWidget->verticalHeader()->setDefaultSectionSize( 12 ); + tableWidget->setFocusPolicy( Qt::NoFocus ); + + tableWidget->setHorizontalHeaderItem( 0, new QTableWidgetItem( "Username" ) ); + tableWidget->setHorizontalHeaderItem( 1, new QTableWidgetItem( "Project" ) ); + tableWidget->setHorizontalHeaderItem( 2, new QTableWidgetItem( "Host" ) ); + + + gridLayout = new QGridLayout( ConnectWidget ); + gridLayout->addWidget( tableWidget, 0, 2, 10, 1 ); + gridLayout->addWidget( label_UserInfo, 0, 1, 1, 1 ); + gridLayout->addWidget( label_User, 1, 0, 1, 1 ); + gridLayout->addWidget( label_Password, 2, 0, 1, 1 ); + gridLayout->addWidget( label_ServerDetails, 4, 1, 1, 1 ); + gridLayout->addWidget( label_Project, 5, 0, 1, 1 ); + gridLayout->addWidget( label_Host, 6, 0, 1, 1 ); + gridLayout->addWidget( label_Port, 7, 0, 1, 1 ); + gridLayout->addWidget( label_Endpoint, 8, 0, 1, 1 ); + gridLayout->addWidget( lineEdit_User, 1, 1, 1, 1 ); + gridLayout->addWidget( lineEdit_Password, 2, 1, 1, 1 ); + gridLayout->addWidget( lineEdit_Project, 5, 1, 1, 1 ); + gridLayout->addWidget( lineEdit_Host, 6, 1, 1, 1 ); + gridLayout->addWidget( lineEdit_Port, 7, 1, 1, 1 ); + gridLayout->addWidget( lineEdit_Endpoint, 8, 1, 1, 1 ); + gridLayout->addWidget( ButtonConnect, 9, 1, 1, 1 ); +} + +AuthProfile DialogConnect::StartDialog() { + this->listProjects = GlobalClient->storage->ListProjects(); + for ( auto profile : this->listProjects ) { + tableWidget->setRowCount( tableWidget->rowCount() + 1 ); + + auto item_Username = new QTableWidgetItem(); + auto item_Project = new QTableWidgetItem(); + auto item_Host = new QTableWidgetItem(); + + item_Username->setText( profile.GetUsername() ); + item_Username->setFlags( item_Username->flags() ^ Qt::ItemIsEditable ); + + item_Project->setText( profile.GetProject() ); + item_Project->setFlags( item_Project->flags() ^ Qt::ItemIsEditable ); + + item_Host->setText( profile.GetHost() ); + item_Host->setFlags( item_Host->flags() ^ Qt::ItemIsEditable ); + + tableWidget->setItem( tableWidget->rowCount() - 1, 0, item_Username ); + tableWidget->setItem( tableWidget->rowCount() - 1, 1, item_Project ); + tableWidget->setItem( tableWidget->rowCount() - 1, 2, item_Host ); + } + + this->exec(); + + AuthProfile authProfile; + if( this->toConnect ) { + AuthProfile newProfile(lineEdit_Project->text(), lineEdit_User->text(),lineEdit_Password->text(),lineEdit_Host->text(),lineEdit_Port->text(), lineEdit_Endpoint->text()); + + if( ! GlobalClient->storage->ExistsProject(lineEdit_Project->text()) ) + GlobalClient->storage->AddProject(newProfile); + + return newProfile; + } + return authProfile; +} + +void DialogConnect::itemRemove() const { + QString Project = tableWidget->item(tableWidget->currentRow(), 1)->text(); + GlobalClient->storage->RemoveProject(Project); + tableWidget->removeRow(tableWidget->currentRow()); +} + +void DialogConnect::itemSelected() { + QString project = tableWidget->item(tableWidget->currentRow(), 1)->text(); + this->isNewProject = false; + for ( auto profile : this->listProjects ) { + if ( profile.GetProject() == project ) { + lineEdit_Project->setText( profile.GetProject() ); + lineEdit_Host->setText( profile.GetHost() ); + lineEdit_Port->setText( profile.GetPort() ); + lineEdit_Endpoint->setText( profile.GetEndpoint() ); + lineEdit_User->setText( profile.GetUsername() ); + lineEdit_Password->setText( profile.GetPassword() ); + } + } +} + +void DialogConnect::handleContextMenu(const QPoint &pos ) const { + QPoint globalPos = tableWidget->mapToGlobal( pos ); + menuContex->exec( globalPos ); +} + +bool DialogConnect::CheckValidInput() { + if ( lineEdit_Project->text().isEmpty() ) { + MessageError("Project is empty"); + return false; + } + if ( lineEdit_Host->text().isEmpty() ) { + MessageError("Host is empty"); + return false; + } + if ( lineEdit_Port->text().isEmpty() ) { + MessageError("Port is empty"); + return false; + } + if ( lineEdit_Endpoint->text().isEmpty() ) { + MessageError("Endpoint is empty"); + return false; + } + if ( !IsValidURI(lineEdit_Endpoint->text()) ) { + MessageError("Endpoint is invalid URI (Example: /api/qwe)"); + return false; + } + if ( lineEdit_User->text().isEmpty() ) { + MessageError("Username is empty"); + return false; + } + if ( lineEdit_Password->text().isEmpty() ) { + MessageError("Password is empty"); + return false; + } + + if( GlobalClient->storage->ExistsProject(lineEdit_Project->text()) && isNewProject ) { + MessageError("Project already exists"); + return false; + } + + return true; +} + +void DialogConnect::onButton_Connect() { + if( this->CheckValidInput() ) { + this->toConnect = true; + close(); + } +} + diff --git a/Client/Source/Utils/FileSystem.cpp b/Client/Source/Utils/FileSystem.cpp new file mode 100644 index 00000000..702054d9 --- /dev/null +++ b/Client/Source/Utils/FileSystem.cpp @@ -0,0 +1,16 @@ +#include + +QString ReadFileString(const QString &filePath, bool* result) { + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + *result = false; + return QString(); + } + + QTextStream in(&file); + QString content = in.readAll(); + file.close(); + + *result = true; + return content; +} \ No newline at end of file diff --git a/Client/Source/Utils/Logs.cpp b/Client/Source/Utils/Logs.cpp new file mode 100644 index 00000000..9da2c444 --- /dev/null +++ b/Client/Source/Utils/Logs.cpp @@ -0,0 +1,36 @@ +#include + +void LogInfo(const char* format, ...) { + va_list args; + va_start(args, format); + QString message = QString::vasprintf(format, args); + va_end(args); + + qInfo().nospace() << message; +} + +void LogSuccess(const char* format, ...) { + va_list args; + va_start(args, format); + QString message = QString::vasprintf(format, args); + va_end(args); + + qDebug().nospace() << "\033[1;32m[+]\033[0m " << message; +} + +void LogError(const char* format, ...) { + va_list args; + va_start(args, format); + QString message = QString::vasprintf(format, args); + va_end(args); + + qCritical().nospace() << "\033[1;31m[-]\033[0m " << message; +} + +void MessageError( QString message ) { + auto box = QMessageBox(); + box.setWindowTitle( "Error" ); + box.setText( message ); + box.setIcon( QMessageBox::Critical ); + box.exec(); +} \ No newline at end of file diff --git a/Client/Source/Utils/Valid.cpp b/Client/Source/Utils/Valid.cpp new file mode 100644 index 00000000..0bb43711 --- /dev/null +++ b/Client/Source/Utils/Valid.cpp @@ -0,0 +1,7 @@ +#include + +bool IsValidURI(const QString &uri) { + QRegularExpression regex(R"(^\/(?!\/)([a-zA-Z0-9\-_]+\/?)*[a-zA-Z0-9\-_]$)"); + QRegularExpressionMatch match = regex.match(uri); + return match.hasMatch(); +} \ No newline at end of file diff --git a/Client/Source/main.cpp b/Client/Source/main.cpp index e813f8ce..2b8595a9 100644 --- a/Client/Source/main.cpp +++ b/Client/Source/main.cpp @@ -1,20 +1,11 @@ -#include -#include -#include +#include +#include + +MainAdaptix* GlobalClient = nullptr; int main(int argc, char *argv[]) { QApplication a(argc, argv); - auto FontID = QFontDatabase::addApplicationFont( ":/fonts/DroidSansMono" ); - auto Family = QFontDatabase::applicationFontFamilies( FontID ).at( 0 ); - auto Font = QFont( Family ); - Font.setPointSize( 10 ); - QApplication::setFont( Font ); - QGuiApplication::setWindowIcon( QIcon( ":/LogoLin" ) ); - - QPushButton button("Hello world!", nullptr); - button.resize(200, 100); - button.show(); - - return QApplication::exec(); + GlobalClient = new MainAdaptix; + GlobalClient->Start(); }