Storage Crab Desktop follows a layered architecture with clear separation between core logic and UI components.
┌─────────────────────────────────────────────────────────┐
│ main.cpp │
│ (QApplication entry) │
└──────────────────────┬──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ LoginWindow │
│ (Email/Password → ApiDispatcher::login) │
└──────────────────────┬──────────────────────────────────┘
│ onLoginSuccessfull
▼
┌─────────────────────────────────────────────────────────┐
│ UserDashboard │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │ Upload │ │ Download │ │ Share │ │ Decrypt │ │
│ │ Dialog │ │ Shared │ │ Code │ │ Dialog │ │
│ └──────────┘ └──────────┘ └──────────┘ └─────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ UploadedFilePanel (list) │ │
│ │ [filename] [size] [share] [download] [delete] │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
│
┌───────────────────────┼──────────────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌─────────────┐
│ core │ │ ui_elements │ │ Backend │
│ (static lib) │◄───│ (static lib) │ │ API Server │
└────┬─────────┘ └──────────────┘ └─────────────┘
│
├─ ApiDispatcher (singleton)
├─ TokenPair (thread-safe)
├─ CryptoService / FileCrypto
├─ AES / RSA
└─ RequestResult / FileData
Static library containing all non-UI logic. Depends on Qt6::Core and Qt6::Concurrent for async operations.
File: include/api/api_dispatcher.hpp
Central class managing all API communication. Implemented as a thread-safe singleton.
class ApiDispatcher final : public QObject {
static ApiDispatcher& instance(); // Singleton access
void storeTokens(access, refresh);
RequestResultFuture login(email, password_hash);
RequestResultFuture me();
RequestResultFuture getFiles();
RequestResultFuture uploadFile(fileName, path);
RequestResultFuture downloadFile(fileID, destination);
RequestResultFuture downloadSharedFile(code, destination);
RequestResultFuture deleteFile(fileID);
RequestResultFuture shareFile(fileID);
signals:
void sessionExpired(); // Emitted when refresh fails
private slots:
void onRefreshTimer(); // Auto-refresh every 9 minutes
};All methods return QFuture<API::RequestResult> for asynchronous execution via QtConcurrent::run().
File: include/api/token_pair.h
Stores access and refresh tokens with mutex protection for thread-safe access.
namespace API {
class TokenPair {
void store(access, refresh);
std::string getAccess() const;
std::string getRefresh() const;
private:
std::string accessToken;
std::string refreshToken;
mutable std::mutex mutex;
};
}File: include/api/request_result.hpp
Wraps API response with success status and JSON body.
struct RequestResult : public ResultBase {
nlohmann::json body;
static RequestResult success(const nlohmann::json &body);
static RequestResult error(const nlohmann::json &body);
static RequestResult error_msg(const std::string& msg);
std::string extractErrorDetails() const;
};File: include/api/file_data.hpp
Represents a file record from the server.
struct FileData {
std::string name; // File name
std::string path; // Remote storage path
size_t size; // Size in bytes
size_t id; // File ID
};File: include/utils/watch_future.hpp
Utility for handling async API calls with success/failure callbacks on the UI thread.
template <typename FutureT, typename SuccessFn, typename FailureFn>
void watchFuture(QObject* parent, QFuture<FutureT> future,
SuccessFn onSuccess, FailureFn onFailure);Usage pattern:
auto future = dispatch([&] { return ApiDispatcher::instance().getFiles(); });
watchFuture(this, future,
[](const API::RequestResult& result) { /* success */ },
[](const API::RequestResult& result) { /* failure */ }
);Static library containing all Qt widgets and windows. Links against core.
| Window/Dialog | Purpose | Key Signals/Slots |
|---|---|---|
LoginWindow |
User authentication | onLoginButtonClicked() → ApiDispatcher::login() |
UserDashboard |
Main file management view | onUploadButtonClicked(), onLogoutButtonClicked() |
UploadDialog |
Configure encryption and upload | Returns EncryptOptions + file path |
DecryptDialog |
Select encrypted file + key to decrypt | Returns file path + key path |
ShareCodeDialog |
Display share code with copy button | Shows code, emits refresh signal |
DownloadSharedDialog |
Enter share code to download | Returns share code |
UploadedFilePanel |
Single file row widget | shareButtonPressed, downloadButtonPressed, deleteButtonPressed |
LoginWindow
└─ login success → UserDashboard (created, shown)
└─ login success → LoginWindow (hidden)
UserDashboard
└─ upload → UploadDialog → ApiDispatcher::uploadFile()
└─ download → ApiDispatcher::downloadFile()
└─ delete → ApiDispatcher::deleteFile()
└─ share → ApiDispatcher::shareFile() → ShareCodeDialog
└─ decrypt → DecryptDialog → FileCrypto::decryptFile()
└─ download shared → DownloadSharedDialog → ApiDispatcher::downloadSharedFile()
└─ logout → LoginWindow (shown), UserDashboard (closed)
ApiDispatcher
└─ sessionExpired() → LoginWindow (shown), Dashboard (closed)
File: include/cryptography/algorithm_types.h
enum class AlgorithmType { AES = 0, Hybrid };
enum class AESType { AES_128 = 16, AES_192 = 24, AES_256 = 32 };File: include/cryptography/crypto_service.h
Orchestrates encryption/decryption operations.
class CryptoService {
struct EncryptResult { ByteArray encryptedContent; std::string encryptedFileName; nlohmann::json config; };
struct DecryptResult { ByteArray content; std::string decryptedFileName; };
static EncryptResult Encrypt(content, fileName, aesType, algorithmType, rsaKeySize, encryptName);
static DecryptResult Decrypt(content, fileName, config, decryptName);
};File: include/cryptography/file_crypto.h
Handles reading/writing files and generating key files.
class FileCrypto {
struct EncryptOptions { filePath, keyPath, aesType, algorithm, rsaKeySize, encryptFileName; };
struct DecryptOptions { filePath, keyPath, decryptFileName; };
struct Result : ResultBase { path, fileName; };
static Result encryptFile(options);
static Result decryptFile(options);
};File: include/cryptography/aes.h
Implements AES encryption with configurable key sizes.
namespace Cryptography::AES {
struct KeyData : Key { AESKey key; ByteArray iv; AESType type; };
KeyData generateKey(AESType type);
KeyData parseKey(const nlohmann::json& config);
ByteArray encrypt(const KeyData& config, const ByteArray& input);
ByteArray decrypt(const KeyData& config, const ByteArray& ciphertext);
}File: include/cryptography/rsa.h
Uses OpenSSL for RSA key generation and encryption.
namespace Cryptography::RSA {
struct KeyData : Key { EVP_PKEY* keyPair; };
KeyData generateKey(size_t key_size);
KeyData parseKey(const nlohmann::json& config);
ByteArray encrypt(const KeyData& config, const ByteArray& input);
ByteArray decrypt(const KeyData& config, const ByteArray& ciphertext);
}User enters credentials
│
▼
LoginWindow::onLoginButtonClicked()
│
▼
ApiDispatcher::login(email, password_hash)
│ POST /api/token/ {email, password}
▼
Backend returns { access, refresh }
│
▼
ApiDispatcher::storeTokens(access, refresh)
│
▼
TokenRefreshTimer starts (9 minute interval)
│
▼
Login successful → UserDashboard shown
QTimer fires (every 9 minutes)
│
▼
ApiDispatcher::onRefreshTimer()
│ POST /api/token/refresh/ {refresh: refreshToken}
▼
If success: store new access token
If fail: emit sessionExpired() → return to LoginWindow
UserDashboard::onUploadButtonClicked()
│
▼
UploadDialog (select file, encryption options)
│
▼
FileCrypto::encryptFile(options) → writes encrypted file + .crbkey file
│
▼
ApiDispatcher::uploadFile(fileName, encryptedFilePath)
│ POST /api/upload/ multipart/form-data {json: metadata, file: encryptedFile}
▼
Server stores file, returns file record
│
▼
UserDashboard::addFile(fileData) → adds UploadedFilePanel
| Target | Type | Depends On |
|---|---|---|
core |
Static library | Qt6::Core, Qt6::Concurrent, curlpp, nlohmann_json, OpenSSL |
ui_elements |
Static library | core, Qt6::Widgets |
storage-crab-desktop |
Executable | core, ui_elements |