mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 05:30:06 +08:00
refactor group/profile relation
This commit is contained in:
parent
ce6cfe3584
commit
b82d937964
@ -208,6 +208,8 @@ set(PROJECT_SOURCES
|
||||
src/stats/connectionLister/connectionLister.cpp
|
||||
src/configs/proxy/Json2Bean.cpp
|
||||
include/sys/windows/eventHandler.h
|
||||
src/dataStore/Group.cpp
|
||||
src/dataStore/ProxyEntity.cpp
|
||||
)
|
||||
|
||||
# Qt exe
|
||||
|
||||
@ -38,8 +38,6 @@ namespace NekoGui {
|
||||
|
||||
void DeleteProfile(int id);
|
||||
|
||||
void MoveProfile(const std::shared_ptr<ProxyEntity> &ent, int gid);
|
||||
|
||||
std::shared_ptr<ProxyEntity> GetProfile(int id);
|
||||
|
||||
bool AddGroup(const std::shared_ptr<Group> &ent);
|
||||
@ -78,6 +76,8 @@ namespace NekoGui {
|
||||
static std::shared_ptr<Group> LoadGroup(const QString &jsonPath);
|
||||
|
||||
static std::shared_ptr<RoutingChain> LoadRouteChain(const QString &jsonPath);
|
||||
|
||||
void deleteProfile(int id);
|
||||
};
|
||||
|
||||
extern ProfileManager *profileManager;
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "include/global/NekoGui.hpp"
|
||||
#include "ProxyEntity.hpp"
|
||||
#include "include/global/NekoGui.hpp"
|
||||
|
||||
namespace NekoGui {
|
||||
namespace NekoGui
|
||||
{
|
||||
class Group : public JsonStore {
|
||||
public:
|
||||
int id = -1;
|
||||
@ -19,14 +20,20 @@ namespace NekoGui {
|
||||
// list ui
|
||||
bool manually_column_width = false;
|
||||
QList<int> column_width;
|
||||
QList<int> order;
|
||||
QList<int> profiles;
|
||||
|
||||
Group();
|
||||
|
||||
// 按 id 顺序
|
||||
[[nodiscard]] QList<std::shared_ptr<ProxyEntity>> Profiles() const;
|
||||
[[nodiscard]] QList<int> Profiles() const;
|
||||
|
||||
// 按 显示 顺序
|
||||
[[nodiscard]] QList<std::shared_ptr<ProxyEntity>> ProfilesWithOrder() const;
|
||||
[[nodiscard]] QList<std::shared_ptr<ProxyEntity>> GetProfileEnts() const;
|
||||
|
||||
bool RemoveProfile(int id);
|
||||
|
||||
bool AddProfile(int id);
|
||||
|
||||
bool SwapProfiles(int idx1, int idx2);
|
||||
|
||||
bool HasProfile(int id) const;
|
||||
};
|
||||
} // namespace NekoGui
|
||||
}// namespace NekoGui
|
||||
|
||||
@ -55,7 +55,6 @@ namespace NekoGui {
|
||||
bool flag_tray = false;
|
||||
bool flag_debug = false;
|
||||
bool flag_restart_tun_on = false;
|
||||
bool flag_reorder = false;
|
||||
bool flag_dns_set = false;
|
||||
|
||||
// Saved
|
||||
|
||||
@ -10,64 +10,21 @@
|
||||
class MyTableWidget : public QTableWidget {
|
||||
public:
|
||||
explicit MyTableWidget(QWidget *parent = nullptr) : QTableWidget(parent) {
|
||||
// 拖拽设置
|
||||
this->setDragDropMode(QAbstractItemView::InternalMove); // 内部移动
|
||||
this->setDropIndicatorShown(true); // drop位置 提示
|
||||
this->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
};
|
||||
|
||||
QList<int> order; // id sorted (save)
|
||||
std::map<int, int> id2Row; // id2Row
|
||||
QList<int> row2Id; // row2Id: use this to refresh data
|
||||
|
||||
std::function<void()> callback_save_order;
|
||||
std::function<void(int id)> refresh_data;
|
||||
|
||||
void _save_order(bool saveToFile) {
|
||||
order.clear();
|
||||
id2Row.clear();
|
||||
for (int i = 0; i < this->rowCount(); i++) {
|
||||
auto id = row2Id[i];
|
||||
order += id;
|
||||
id2Row[id] = i;
|
||||
}
|
||||
if (callback_save_order != nullptr && saveToFile)
|
||||
{
|
||||
callback_save_order();
|
||||
}
|
||||
this->setDragDropMode(InternalMove);
|
||||
this->setDropIndicatorShown(true);
|
||||
this->setSelectionBehavior(SelectRows);
|
||||
}
|
||||
|
||||
void update_order(bool saveToFile) {
|
||||
if (order.isEmpty()) {
|
||||
_save_order(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Then save the order
|
||||
_save_order(saveToFile);
|
||||
};
|
||||
|
||||
std::function<void(int row1, int row2)> rowsSwapped;
|
||||
protected:
|
||||
void dropEvent(QDropEvent *event) override {
|
||||
if (order.isEmpty()) return;
|
||||
|
||||
int row_src, row_dst;
|
||||
row_src = this->currentRow();
|
||||
auto id_src = row2Id[row_src];
|
||||
QTableWidgetItem *item = this->itemAt(event->position().toPoint());
|
||||
if (item != nullptr) {
|
||||
row_dst = item->row();
|
||||
// Modify order
|
||||
row2Id.swapItemsAt(row_src, row_dst);
|
||||
order.removeAt(row_src);
|
||||
order.insert(row_dst, id_src);
|
||||
} else {
|
||||
return;
|
||||
if (const QTableWidgetItem *item = this->itemAt(event->position().toPoint()); item != nullptr) {
|
||||
const int row_dst = item->row();
|
||||
if (rowsSwapped)
|
||||
{
|
||||
rowsSwapped(row_dst, currentRow());
|
||||
clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
// Do update order & refresh
|
||||
clearSelection();
|
||||
update_order(true);
|
||||
refresh_data(-1);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -752,12 +752,10 @@ namespace NekoGui_sub {
|
||||
QList<std::shared_ptr<NekoGui::ProxyEntity>> update_del; // 更新前后都有的,需要删除的新配置
|
||||
QList<std::shared_ptr<NekoGui::ProxyEntity>> update_keep; // 更新前后都有的,被保留的旧配置
|
||||
|
||||
// 订阅解析前
|
||||
if (group != nullptr) {
|
||||
in = group->Profiles();
|
||||
in = group->GetProfileEnts();
|
||||
group->sub_last_update = QDateTime::currentMSecsSinceEpoch() / 1000;
|
||||
group->info = sub_user_info;
|
||||
group->order.clear();
|
||||
group->Save();
|
||||
//
|
||||
if (NekoGui::dataStore->sub_clear) {
|
||||
@ -771,7 +769,7 @@ namespace NekoGui_sub {
|
||||
rawUpdater->update(content);
|
||||
|
||||
if (group != nullptr) {
|
||||
out_all = group->Profiles();
|
||||
out_all = group->GetProfileEnts();
|
||||
|
||||
QString change_text;
|
||||
|
||||
@ -786,7 +784,6 @@ namespace NekoGui_sub {
|
||||
NekoGui::ProfileFilter::OnlyInSrc(in, out, only_in);
|
||||
NekoGui::ProfileFilter::OnlyInSrc(out, in, only_out);
|
||||
NekoGui::ProfileFilter::Common(in, out, update_keep, update_del, false);
|
||||
|
||||
QString notice_added;
|
||||
QString notice_deleted;
|
||||
if (only_out.size() < 1000)
|
||||
@ -810,22 +807,22 @@ namespace NekoGui_sub {
|
||||
|
||||
|
||||
// sort according to order in remote
|
||||
group->order = {};
|
||||
group->profiles.clear();
|
||||
for (const auto &ent: rawUpdater->updated_order) {
|
||||
auto deleted_index = update_del.indexOf(ent);
|
||||
if (deleted_index >= 0) {
|
||||
if (deleted_index >= update_keep.count()) continue; // should not happen
|
||||
auto ent2 = update_keep[deleted_index];
|
||||
group->order.append(ent2->id);
|
||||
const auto& ent2 = update_keep[deleted_index];
|
||||
group->profiles.append(ent2->id);
|
||||
} else {
|
||||
group->order.append(ent->id);
|
||||
group->profiles.append(ent->id);
|
||||
}
|
||||
}
|
||||
group->Save();
|
||||
|
||||
// cleanup
|
||||
for (const auto &ent: out_all) {
|
||||
if (!group->order.contains(ent->id)) {
|
||||
if (!group->HasProfile(ent->id)) {
|
||||
NekoGui::profileManager->DeleteProfile(ent->id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,9 +2,7 @@
|
||||
|
||||
#include "include/configs/proxy/includes.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QColor>
|
||||
|
||||
namespace NekoGui {
|
||||
|
||||
@ -55,7 +53,7 @@ namespace NekoGui {
|
||||
}
|
||||
// Clear Corrupted profile
|
||||
for (auto id: delProfile) {
|
||||
DeleteProfile(id);
|
||||
deleteProfile(id);
|
||||
}
|
||||
// Load Groups
|
||||
auto loadedOrder = groupsTabOrder;
|
||||
@ -91,71 +89,16 @@ namespace NekoGui {
|
||||
|
||||
// First setup
|
||||
if (groups.empty()) {
|
||||
auto defaultGroup = NekoGui::ProfileManager::NewGroup();
|
||||
auto defaultGroup = NewGroup();
|
||||
defaultGroup->name = QObject::tr("Default");
|
||||
NekoGui::profileManager->AddGroup(defaultGroup);
|
||||
profileManager->AddGroup(defaultGroup);
|
||||
}
|
||||
|
||||
if (routes.empty()) {
|
||||
auto defaultRoute = RoutingChain::GetDefaultChain();
|
||||
profileManager->AddRouteChain(defaultRoute);
|
||||
}
|
||||
|
||||
// Add default route chains
|
||||
routes[IranBypassChainID] = RoutingChain::GetIranDefaultChain();
|
||||
routes[ChinaBypassChainID] = RoutingChain::GetChinaDefaultChain();
|
||||
|
||||
//
|
||||
if (dataStore->flag_reorder) {
|
||||
{
|
||||
// remove all (contains orphan)
|
||||
for (const auto &profile: profiles) {
|
||||
QFile::remove(profile.second->fn);
|
||||
}
|
||||
}
|
||||
std::map<int, int> gidOld2New;
|
||||
{
|
||||
int i = 0;
|
||||
int ii = 0;
|
||||
QList<int> newProfilesIdOrder;
|
||||
std::map<int, std::shared_ptr<ProxyEntity>> newProfiles;
|
||||
for (auto gid: groupsTabOrder) {
|
||||
auto group = GetGroup(gid);
|
||||
gidOld2New[gid] = ii++;
|
||||
for (auto const &profile: group->ProfilesWithOrder()) {
|
||||
auto oldId = profile->id;
|
||||
auto newId = i++;
|
||||
profile->id = newId;
|
||||
profile->gid = gidOld2New[gid];
|
||||
profile->fn = QString("profiles/%1.json").arg(newId);
|
||||
profile->Save();
|
||||
newProfiles[newId] = profile;
|
||||
newProfilesIdOrder << newId;
|
||||
}
|
||||
group->order = {};
|
||||
group->Save();
|
||||
}
|
||||
profiles = newProfiles;
|
||||
profilesIdOrder = newProfilesIdOrder;
|
||||
}
|
||||
{
|
||||
QList<int> newGroupsIdOrder;
|
||||
std::map<int, std::shared_ptr<Group>> newGroups;
|
||||
for (auto oldGid: groupsTabOrder) {
|
||||
auto newId = gidOld2New[oldGid];
|
||||
auto group = groups[oldGid];
|
||||
QFile::remove(group->fn);
|
||||
group->id = newId;
|
||||
group->fn = QString("groups/%1.json").arg(newId);
|
||||
group->Save();
|
||||
newGroups[newId] = group;
|
||||
newGroupsIdOrder << newId;
|
||||
}
|
||||
groups = newGroups;
|
||||
groupsIdOrder = newGroupsIdOrder;
|
||||
groupsTabOrder = newGroupsIdOrder;
|
||||
}
|
||||
MessageBoxInfo(software_name, "Profiles and groups reorder complete.");
|
||||
routes[IranBypassChainID] = RoutingChain::GetIranDefaultChain();
|
||||
routes[ChinaBypassChainID] = RoutingChain::GetChinaDefaultChain();
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,54 +186,6 @@ namespace NekoGui {
|
||||
return ent;
|
||||
}
|
||||
|
||||
// ProxyEntity
|
||||
|
||||
ProxyEntity::ProxyEntity(NekoGui_fmt::AbstractBean *bean, const QString &type_) {
|
||||
if (type_ != nullptr) this->type = type_;
|
||||
|
||||
_add(new configItem("type", &type, itemType::string));
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("gid", &gid, itemType::integer));
|
||||
_add(new configItem("yc", &latency, itemType::integer));
|
||||
_add(new configItem("dl", &dl_speed, itemType::string));
|
||||
_add(new configItem("ul", &ul_speed, itemType::string));
|
||||
_add(new configItem("report", &full_test_report, itemType::string));
|
||||
|
||||
// 可以不关联 bean,只加载 ProxyEntity 的信息
|
||||
if (bean != nullptr) {
|
||||
this->bean = std::shared_ptr<NekoGui_fmt::AbstractBean>(bean);
|
||||
// 有虚函数就要在这里 dynamic_cast
|
||||
_add(new configItem("bean", dynamic_cast<JsonStore *>(bean), itemType::jsonStore));
|
||||
_add(new configItem("traffic", dynamic_cast<JsonStore *>(traffic_data.get()), itemType::jsonStore));
|
||||
}
|
||||
};
|
||||
|
||||
QString ProxyEntity::DisplayTestResult() const {
|
||||
QString result;
|
||||
if (latency < 0) {
|
||||
result = "Unavailable";
|
||||
} else if (latency > 0) {
|
||||
result = UNICODE_LRO + QString("%1 ms").arg(latency);
|
||||
}
|
||||
if (!dl_speed.isEmpty()) result += " ↓" + dl_speed;
|
||||
if (!ul_speed.isEmpty()) result += " ↑" + ul_speed;
|
||||
return result;
|
||||
}
|
||||
|
||||
QColor ProxyEntity::DisplayLatencyColor() const {
|
||||
if (latency < 0) {
|
||||
return Qt::red;
|
||||
} else if (latency > 0) {
|
||||
if (latency <= 200) {
|
||||
return Qt::darkGreen;
|
||||
} else {
|
||||
return Qt::darkYellow;
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// Profile
|
||||
|
||||
int ProfileManager::NewProfileID() const {
|
||||
@ -306,8 +201,16 @@ namespace NekoGui {
|
||||
return false;
|
||||
}
|
||||
|
||||
ent->gid = gid < 0 ? dataStore->current_group : gid;
|
||||
ent->id = NewProfileID();
|
||||
ent->gid = gid < 0 ? dataStore->current_group : gid;
|
||||
if (auto group = GetGroup(ent->gid); group != nullptr)
|
||||
{
|
||||
group->AddProfile(ent->id);
|
||||
group->Save();
|
||||
} else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
profiles[ent->id] = ent;
|
||||
profilesIdOrder.push_back(ent->id);
|
||||
|
||||
@ -319,26 +222,23 @@ namespace NekoGui {
|
||||
void ProfileManager::DeleteProfile(int id) {
|
||||
if (id < 0) return;
|
||||
if (dataStore->started_id == id) return;
|
||||
auto ent = GetProfile(id);
|
||||
if (ent == nullptr) return;
|
||||
if (auto group = GetGroup(ent->gid); group != nullptr)
|
||||
{
|
||||
group->RemoveProfile(id);
|
||||
group->Save();
|
||||
}
|
||||
deleteProfile(id);
|
||||
}
|
||||
|
||||
void ProfileManager::deleteProfile(int id)
|
||||
{
|
||||
profiles.erase(id);
|
||||
profilesIdOrder.removeAll(id);
|
||||
QFile(QString("profiles/%1.json").arg(id)).remove();
|
||||
}
|
||||
|
||||
void ProfileManager::MoveProfile(const std::shared_ptr<ProxyEntity> &ent, int gid) {
|
||||
if (gid == ent->gid || gid < 0) return;
|
||||
auto oldGroup = GetGroup(ent->gid);
|
||||
if (oldGroup != nullptr && !oldGroup->order.isEmpty()) {
|
||||
oldGroup->order.removeAll(ent->id);
|
||||
oldGroup->Save();
|
||||
}
|
||||
auto newGroup = GetGroup(gid);
|
||||
if (newGroup != nullptr && !newGroup->order.isEmpty()) {
|
||||
newGroup->order.push_back(ent->id);
|
||||
newGroup->Save();
|
||||
}
|
||||
ent->gid = gid;
|
||||
ent->Save();
|
||||
}
|
||||
|
||||
std::shared_ptr<ProxyEntity> ProfileManager::GetProfile(int id) {
|
||||
return profiles.count(id) ? profiles[id] : nullptr;
|
||||
@ -359,21 +259,6 @@ namespace NekoGui {
|
||||
}
|
||||
// Group
|
||||
|
||||
Group::Group() {
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("front_proxy_id", &front_proxy_id, itemType::integer));
|
||||
_add(new configItem("landing_proxy_id", &landing_proxy_id, itemType::integer));
|
||||
_add(new configItem("archive", &archive, itemType::boolean));
|
||||
_add(new configItem("skip_auto_update", &skip_auto_update, itemType::boolean));
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("order", &order, itemType::integerList));
|
||||
_add(new configItem("url", &url, itemType::string));
|
||||
_add(new configItem("info", &info, itemType::string));
|
||||
_add(new configItem("lastup", &sub_last_update, itemType::integer64));
|
||||
_add(new configItem("manually_column_width", &manually_column_width, itemType::boolean));
|
||||
_add(new configItem("column_width", &column_width, itemType::integerList));
|
||||
}
|
||||
|
||||
std::shared_ptr<Group> ProfileManager::LoadGroup(const QString &jsonPath) {
|
||||
auto ent = std::make_shared<Group>();
|
||||
ent->fn = jsonPath;
|
||||
@ -406,12 +291,10 @@ namespace NekoGui {
|
||||
|
||||
void ProfileManager::DeleteGroup(int gid) {
|
||||
if (groups.size() <= 1) return;
|
||||
QList<int> toDelete;
|
||||
for (const auto &[id, profile]: profiles) {
|
||||
if (profile->gid == gid) toDelete += id; // map访问中,不能操作
|
||||
}
|
||||
for (const auto &id: toDelete) {
|
||||
DeleteProfile(id);
|
||||
auto group = GetGroup(gid);
|
||||
if (group == nullptr) return;
|
||||
for (const auto id : group->Profiles()) {
|
||||
deleteProfile(id);
|
||||
}
|
||||
groups.erase(gid);
|
||||
groupsIdOrder.removeAll(gid);
|
||||
@ -427,48 +310,6 @@ namespace NekoGui {
|
||||
return GetGroup(dataStore->current_group);
|
||||
}
|
||||
|
||||
RouteRule::RouteRule() {
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("ip_version", &ip_version, itemType::string));
|
||||
_add(new configItem("network", &network, itemType::string));
|
||||
_add(new configItem("protocol", &protocol, itemType::string));
|
||||
_add(new configItem("inbound", &inbound, itemType::stringList));
|
||||
_add(new configItem("domain", &domain, itemType::stringList));
|
||||
_add(new configItem("domain_suffix", &domain_suffix, itemType::stringList));
|
||||
_add(new configItem("domain_keyword", &domain_keyword, itemType::stringList));
|
||||
_add(new configItem("domain_regex", &domain_regex, itemType::stringList));
|
||||
_add(new configItem("source_ip_cidr", &source_ip_cidr, itemType::stringList));
|
||||
_add(new configItem("source_ip_is_private", &source_ip_is_private, itemType::boolean));
|
||||
_add(new configItem("ip_cidr", &ip_cidr, itemType::stringList));
|
||||
_add(new configItem("ip_is_private", &ip_is_private, itemType::boolean));
|
||||
_add(new configItem("source_port", &source_port, itemType::stringList));
|
||||
_add(new configItem("source_port_range", &source_port_range, itemType::stringList));
|
||||
_add(new configItem("port", &port, itemType::stringList));
|
||||
_add(new configItem("port_range", &port_range, itemType::stringList));
|
||||
_add(new configItem("process_name", &process_name, itemType::stringList));
|
||||
_add(new configItem("process_path", &process_path, itemType::stringList));
|
||||
_add(new configItem("process_path_regex", &process_path_regex, itemType::stringList));
|
||||
_add(new configItem("rule_set", &rule_set, itemType::stringList));
|
||||
_add(new configItem("invert", &invert, itemType::boolean));
|
||||
_add(new configItem("outboundID", &outboundID, itemType::integer));
|
||||
_add(new configItem("actionType", &action, itemType::string));
|
||||
_add(new configItem("rejectMethod", &rejectMethod, itemType::string));
|
||||
_add(new configItem("noDrop", &no_drop, itemType::boolean));
|
||||
_add(new configItem("override_address", &override_address, itemType::string));
|
||||
_add(new configItem("override_port", &override_port, itemType::integer));
|
||||
_add(new configItem("sniffers", &sniffers, itemType::stringList));
|
||||
_add(new configItem("sniffOverrideDest", &sniffOverrideDest, itemType::boolean));
|
||||
_add(new configItem("strategy", &strategy, itemType::string));
|
||||
_add(new configItem("type", &type, itemType::integer));
|
||||
_add(new configItem("simple_action", &simpleAction, itemType::integer));
|
||||
}
|
||||
|
||||
RoutingChain::RoutingChain() {
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("rules", &castedRules, itemType::jsonStoreList));
|
||||
}
|
||||
|
||||
std::shared_ptr<RoutingChain> ProfileManager::NewRouteChain() {
|
||||
auto route = std::make_shared<RoutingChain>();
|
||||
return route;
|
||||
@ -518,25 +359,4 @@ namespace NekoGui {
|
||||
}
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<ProxyEntity>> Group::Profiles() const {
|
||||
QList<std::shared_ptr<ProxyEntity>> ret;
|
||||
for (const auto &[_, profile]: profileManager->profiles) {
|
||||
if (id == profile->gid) ret += profile;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<ProxyEntity>> Group::ProfilesWithOrder() const {
|
||||
if (order.isEmpty()) {
|
||||
return Profiles();
|
||||
} else {
|
||||
QList<std::shared_ptr<ProxyEntity>> ret;
|
||||
for (auto _id: order) {
|
||||
auto ent = profileManager->GetProfile(_id);
|
||||
if (ent != nullptr) ret += ent;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace NekoGui
|
||||
65
src/dataStore/Group.cpp
Normal file
65
src/dataStore/Group.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
#include <include/dataStore/Group.hpp>
|
||||
|
||||
#include "include/ui/profile/dialog_edit_profile.h"
|
||||
|
||||
namespace NekoGui
|
||||
{
|
||||
Group::Group() {
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("front_proxy_id", &front_proxy_id, itemType::integer));
|
||||
_add(new configItem("landing_proxy_id", &landing_proxy_id, itemType::integer));
|
||||
_add(new configItem("archive", &archive, itemType::boolean));
|
||||
_add(new configItem("skip_auto_update", &skip_auto_update, itemType::boolean));
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("profiles", &profiles, itemType::integerList));
|
||||
_add(new configItem("url", &url, itemType::string));
|
||||
_add(new configItem("info", &info, itemType::string));
|
||||
_add(new configItem("lastup", &sub_last_update, itemType::integer64));
|
||||
_add(new configItem("manually_column_width", &manually_column_width, itemType::boolean));
|
||||
_add(new configItem("column_width", &column_width, itemType::integerList));
|
||||
}
|
||||
|
||||
QList<int> Group::Profiles() const {
|
||||
return profiles;
|
||||
}
|
||||
|
||||
QList<std::shared_ptr<ProxyEntity>> Group::GetProfileEnts() const
|
||||
{
|
||||
auto res = QList<std::shared_ptr<ProxyEntity>>{};
|
||||
for (auto id : profiles)
|
||||
{
|
||||
res.append(profileManager->GetProfile(id));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
bool Group::AddProfile(int id)
|
||||
{
|
||||
if (HasProfile(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
profiles.append(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Group::RemoveProfile(int id)
|
||||
{
|
||||
if (!HasProfile(id)) return false;
|
||||
profiles.removeAll(id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Group::SwapProfiles(int idx1, int idx2)
|
||||
{
|
||||
if (profiles.size() <= idx1 || profiles.size() <= idx2) return false;
|
||||
profiles.swapItemsAt(idx1, idx2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Group::HasProfile(int id) const
|
||||
{
|
||||
return profiles.contains(id);
|
||||
}
|
||||
}
|
||||
48
src/dataStore/ProxyEntity.cpp
Normal file
48
src/dataStore/ProxyEntity.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include <include/dataStore/ProxyEntity.hpp>
|
||||
|
||||
namespace NekoGui
|
||||
{
|
||||
ProxyEntity::ProxyEntity(NekoGui_fmt::AbstractBean *bean, const QString &type_) {
|
||||
if (type_ != nullptr) this->type = type_;
|
||||
|
||||
_add(new configItem("type", &type, itemType::string));
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("gid", &gid, itemType::integer));
|
||||
_add(new configItem("yc", &latency, itemType::integer));
|
||||
_add(new configItem("dl", &dl_speed, itemType::string));
|
||||
_add(new configItem("ul", &ul_speed, itemType::string));
|
||||
_add(new configItem("report", &full_test_report, itemType::string));
|
||||
|
||||
if (bean != nullptr) {
|
||||
this->bean = std::shared_ptr<NekoGui_fmt::AbstractBean>(bean);
|
||||
_add(new configItem("bean", dynamic_cast<JsonStore *>(bean), itemType::jsonStore));
|
||||
_add(new configItem("traffic", dynamic_cast<JsonStore *>(traffic_data.get()), itemType::jsonStore));
|
||||
}
|
||||
};
|
||||
|
||||
QString ProxyEntity::DisplayTestResult() const {
|
||||
QString result;
|
||||
if (latency < 0) {
|
||||
result = "Unavailable";
|
||||
} else if (latency > 0) {
|
||||
result = UNICODE_LRO + QString("%1 ms").arg(latency);
|
||||
}
|
||||
if (!dl_speed.isEmpty()) result += " ↓" + dl_speed;
|
||||
if (!ul_speed.isEmpty()) result += " ↑" + ul_speed;
|
||||
return result;
|
||||
}
|
||||
|
||||
QColor ProxyEntity::DisplayLatencyColor() const {
|
||||
if (latency < 0) {
|
||||
return Qt::red;
|
||||
} else if (latency > 0) {
|
||||
if (latency <= 200) {
|
||||
return Qt::darkGreen;
|
||||
} else {
|
||||
return Qt::darkYellow;
|
||||
}
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,6 +22,42 @@ namespace NekoGui {
|
||||
return false;
|
||||
}
|
||||
|
||||
RouteRule::RouteRule() {
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("ip_version", &ip_version, itemType::string));
|
||||
_add(new configItem("network", &network, itemType::string));
|
||||
_add(new configItem("protocol", &protocol, itemType::string));
|
||||
_add(new configItem("inbound", &inbound, itemType::stringList));
|
||||
_add(new configItem("domain", &domain, itemType::stringList));
|
||||
_add(new configItem("domain_suffix", &domain_suffix, itemType::stringList));
|
||||
_add(new configItem("domain_keyword", &domain_keyword, itemType::stringList));
|
||||
_add(new configItem("domain_regex", &domain_regex, itemType::stringList));
|
||||
_add(new configItem("source_ip_cidr", &source_ip_cidr, itemType::stringList));
|
||||
_add(new configItem("source_ip_is_private", &source_ip_is_private, itemType::boolean));
|
||||
_add(new configItem("ip_cidr", &ip_cidr, itemType::stringList));
|
||||
_add(new configItem("ip_is_private", &ip_is_private, itemType::boolean));
|
||||
_add(new configItem("source_port", &source_port, itemType::stringList));
|
||||
_add(new configItem("source_port_range", &source_port_range, itemType::stringList));
|
||||
_add(new configItem("port", &port, itemType::stringList));
|
||||
_add(new configItem("port_range", &port_range, itemType::stringList));
|
||||
_add(new configItem("process_name", &process_name, itemType::stringList));
|
||||
_add(new configItem("process_path", &process_path, itemType::stringList));
|
||||
_add(new configItem("process_path_regex", &process_path_regex, itemType::stringList));
|
||||
_add(new configItem("rule_set", &rule_set, itemType::stringList));
|
||||
_add(new configItem("invert", &invert, itemType::boolean));
|
||||
_add(new configItem("outboundID", &outboundID, itemType::integer));
|
||||
_add(new configItem("actionType", &action, itemType::string));
|
||||
_add(new configItem("rejectMethod", &rejectMethod, itemType::string));
|
||||
_add(new configItem("noDrop", &no_drop, itemType::boolean));
|
||||
_add(new configItem("override_address", &override_address, itemType::string));
|
||||
_add(new configItem("override_port", &override_port, itemType::integer));
|
||||
_add(new configItem("sniffers", &sniffers, itemType::stringList));
|
||||
_add(new configItem("sniffOverrideDest", &sniffOverrideDest, itemType::boolean));
|
||||
_add(new configItem("strategy", &strategy, itemType::string));
|
||||
_add(new configItem("type", &type, itemType::integer));
|
||||
_add(new configItem("simple_action", &simpleAction, itemType::integer));
|
||||
}
|
||||
|
||||
RouteRule::RouteRule(const RouteRule& other) {
|
||||
name = other.name;
|
||||
ip_version = other.ip_version;
|
||||
@ -805,6 +841,12 @@ namespace NekoGui {
|
||||
}
|
||||
}
|
||||
|
||||
RoutingChain::RoutingChain() {
|
||||
_add(new configItem("id", &id, itemType::integer));
|
||||
_add(new configItem("name", &name, itemType::string));
|
||||
_add(new configItem("rules", &castedRules, itemType::jsonStoreList));
|
||||
}
|
||||
|
||||
RoutingChain::RoutingChain(const RoutingChain& other) : JsonStore(other) {
|
||||
id = other.id;
|
||||
name = QString(other.name);
|
||||
|
||||
@ -87,7 +87,6 @@ int main(int argc, char* argv[]) {
|
||||
if (NekoGui::dataStore->argv.contains("-debug")) NekoGui::dataStore->flag_debug = true;
|
||||
if (NekoGui::dataStore->argv.contains("-flag_restart_tun_on")) NekoGui::dataStore->flag_restart_tun_on = true;
|
||||
if (NekoGui::dataStore->argv.contains("-flag_restart_dns_set")) NekoGui::dataStore->flag_dns_set = true;
|
||||
if (NekoGui::dataStore->argv.contains("-flag_reorder")) NekoGui::dataStore->flag_reorder = true;
|
||||
#ifdef NKR_CPP_USE_APPDATA
|
||||
NekoGui::dataStore->flag_use_appdata = true; // Example: Package & MacOS
|
||||
#endif
|
||||
|
||||
@ -261,12 +261,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
ui->graph_tab->layout()->addWidget(speedChartWidget);
|
||||
|
||||
// table UI
|
||||
ui->proxyListTable->callback_save_order = [=] {
|
||||
ui->proxyListTable->rowsSwapped = [=](int row1, int row2)
|
||||
{
|
||||
auto group = NekoGui::profileManager->CurrentGroup();
|
||||
group->order = ui->proxyListTable->order;
|
||||
group->Save();
|
||||
group->SwapProfiles(row1, row2);
|
||||
refresh_proxy_list(group->profiles[row1]);
|
||||
refresh_proxy_list(group->profiles[row2]);
|
||||
};
|
||||
ui->proxyListTable->refresh_data = [=](int id) { refresh_proxy_list_impl_refresh_data(id); };
|
||||
if (auto button = ui->proxyListTable->findChild<QAbstractButton *>(QString(), Qt::FindDirectChildrenOnly)) {
|
||||
// Corner Button
|
||||
connect(button, &QAbstractButton::clicked, this, [=] { refresh_proxy_list_impl(-1, {GroupSortMethod::ById}); });
|
||||
@ -446,7 +447,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
urltest_current_group(get_now_selected_list());
|
||||
});
|
||||
connect(ui->actionUrl_Test_Group, &QAction::triggered, this, [=]() {
|
||||
urltest_current_group(NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder());
|
||||
urltest_current_group(NekoGui::profileManager->CurrentGroup()->GetProfileEnts());
|
||||
});
|
||||
connect(ui->actionSpeedtest_Current, &QAction::triggered, this, [=]()
|
||||
{
|
||||
@ -461,7 +462,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
});
|
||||
connect(ui->actionSpeedtest_Group, &QAction::triggered, this, [=]()
|
||||
{
|
||||
speedtest_current_group(NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder());
|
||||
speedtest_current_group(NekoGui::profileManager->CurrentGroup()->GetProfileEnts());
|
||||
});
|
||||
connect(ui->menu_stop_testing, &QAction::triggered, this, [=]() { stopTests(); });
|
||||
//
|
||||
@ -805,7 +806,6 @@ void MainWindow::on_menu_exit_triggered() {
|
||||
arguments.removeFirst();
|
||||
arguments.removeAll("-tray");
|
||||
arguments.removeAll("-flag_restart_tun_on");
|
||||
arguments.removeAll("-flag_reorder");
|
||||
arguments.removeAll("-flag_restart_dns_set");
|
||||
}
|
||||
auto program = QApplication::applicationFilePath();
|
||||
@ -1267,73 +1267,24 @@ void MainWindow::refresh_proxy_list(const int &id) {
|
||||
void MainWindow::refresh_proxy_list_impl(const int &id, GroupSortAction groupSortAction) {
|
||||
ui->proxyListTable->setUpdatesEnabled(false);
|
||||
if (id < 0) {
|
||||
ui->proxyListTable->row2Id.clear();
|
||||
ui->proxyListTable->setRowCount(0);
|
||||
auto oldOrder = QList<int>();
|
||||
oldOrder << ui->proxyListTable->order;
|
||||
auto group = NekoGui::profileManager->CurrentGroup();
|
||||
if (group == nullptr)
|
||||
auto currentGroup = NekoGui::profileManager->CurrentGroup();
|
||||
if (currentGroup == nullptr)
|
||||
{
|
||||
ui->proxyListTable->setUpdatesEnabled(true);
|
||||
MW_show_log("Could not find current group!");
|
||||
return;
|
||||
}
|
||||
|
||||
QSet<int> currProfs;
|
||||
// remove old ones
|
||||
ui->proxyListTable->order.clear();
|
||||
for (const int oldID : oldOrder)
|
||||
{
|
||||
if (NekoGui::profileManager->GetProfile(oldID) != nullptr)
|
||||
{
|
||||
ui->proxyListTable->order << oldID;
|
||||
currProfs.insert(oldID);
|
||||
}
|
||||
}
|
||||
|
||||
// add new ones
|
||||
for (const auto& profile : NekoGui::profileManager->profiles)
|
||||
{
|
||||
if (profile.second->gid == group->id && !currProfs.contains(profile.first))
|
||||
{
|
||||
ui->proxyListTable->order << profile.first;
|
||||
}
|
||||
}
|
||||
|
||||
switch (groupSortAction.method) {
|
||||
case GroupSortMethod::Raw: {
|
||||
QList<int> newGroupOrder;
|
||||
QSet<int> newGroupIds;
|
||||
for (const int oldId : group->order)
|
||||
{
|
||||
if (NekoGui::profileManager->GetProfile(oldId) != nullptr)
|
||||
{
|
||||
newGroupOrder << oldId;
|
||||
newGroupIds.insert(oldId);
|
||||
}
|
||||
}
|
||||
for (const auto& profile : NekoGui::profileManager->profiles)
|
||||
{
|
||||
if (profile.second->gid == group->id && !newGroupIds.contains(profile.first))
|
||||
{
|
||||
newGroupOrder << profile.first;
|
||||
}
|
||||
}
|
||||
group->order.clear();
|
||||
group->order << newGroupOrder;
|
||||
ui->proxyListTable->order = group->order;
|
||||
break;
|
||||
}
|
||||
case GroupSortMethod::ById: {
|
||||
// Clear Order
|
||||
ui->proxyListTable->order.clear();
|
||||
ui->proxyListTable->callback_save_order();
|
||||
break;
|
||||
}
|
||||
case GroupSortMethod::ByAddress:
|
||||
case GroupSortMethod::ByName:
|
||||
case GroupSortMethod::ByLatency:
|
||||
case GroupSortMethod::ByType: {
|
||||
std::sort(ui->proxyListTable->order.begin(), ui->proxyListTable->order.end(),
|
||||
std::sort(currentGroup->profiles.begin(), currentGroup->profiles.end(),
|
||||
[=](int a, int b) {
|
||||
QString ms_a;
|
||||
QString ms_b;
|
||||
@ -1380,26 +1331,7 @@ void MainWindow::refresh_proxy_list_impl(const int &id, GroupSortAction groupSor
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->proxyListTable->order.isEmpty())
|
||||
{
|
||||
for (const auto& ent : group->Profiles())
|
||||
{
|
||||
ui->proxyListTable->order << ent->id;
|
||||
}
|
||||
}
|
||||
|
||||
bool needSave = oldOrder.size() != ui->proxyListTable->order.size();
|
||||
for (int i=0;i<oldOrder.size() && !needSave;i++)
|
||||
{
|
||||
if (oldOrder[i] != ui->proxyListTable->order[i])
|
||||
{
|
||||
needSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
ui->proxyListTable->row2Id << ui->proxyListTable->order;
|
||||
ui->proxyListTable->setRowCount(ui->proxyListTable->order.size());
|
||||
ui->proxyListTable->update_order(needSave);
|
||||
ui->proxyListTable->setRowCount(currentGroup->profiles.count());
|
||||
}
|
||||
|
||||
// refresh data
|
||||
@ -1408,24 +1340,24 @@ void MainWindow::refresh_proxy_list_impl(const int &id, GroupSortAction groupSor
|
||||
|
||||
void MainWindow::refresh_proxy_list_impl_refresh_data(const int &id, bool stopping) {
|
||||
ui->proxyListTable->setUpdatesEnabled(false);
|
||||
auto currentGroup = NekoGui::profileManager->CurrentGroup();
|
||||
if (id >= 0)
|
||||
{
|
||||
if (ui->proxyListTable->id2Row.count(id) == 0)
|
||||
if (!currentGroup->HasProfile(id))
|
||||
{
|
||||
qDebug("Invalid proxy list id, data might be corrupted");
|
||||
ui->proxyListTable->setUpdatesEnabled(true);
|
||||
return;
|
||||
}
|
||||
auto rowID = ui->proxyListTable->id2Row[id];
|
||||
auto rowID = currentGroup->profiles.indexOf(id);
|
||||
auto profile = NekoGui::profileManager->GetProfile(id);
|
||||
refresh_table_item(rowID, profile, stopping);
|
||||
} else
|
||||
{
|
||||
ui->proxyListTable->blockSignals(true);
|
||||
for (int row = 0; row < ui->proxyListTable->rowCount(); row++) {
|
||||
auto profileId = ui->proxyListTable->row2Id[row];
|
||||
int row = 0;
|
||||
for (const auto profileId : currentGroup->profiles) {
|
||||
auto profile = NekoGui::profileManager->GetProfile(profileId);
|
||||
refresh_table_item(row, profile, stopping);
|
||||
refresh_table_item(row++, profile, stopping);
|
||||
}
|
||||
ui->proxyListTable->blockSignals(false);
|
||||
}
|
||||
@ -1523,8 +1455,8 @@ void MainWindow::on_menu_delete_repeat_triggered () {
|
||||
QList<std::shared_ptr<NekoGui::ProxyEntity>> out;
|
||||
QList<std::shared_ptr<NekoGui::ProxyEntity>> out_del;
|
||||
|
||||
NekoGui::ProfileFilter::Uniq (NekoGui::profileManager-> CurrentGroup ()-> Profiles (), out, true , false );
|
||||
NekoGui::ProfileFilter::OnlyInSrc_ByPointer (NekoGui::profileManager-> CurrentGroup ()-> Profiles (), out, out_del);
|
||||
NekoGui::ProfileFilter::Uniq (NekoGui::profileManager-> CurrentGroup ()-> GetProfileEnts (), out, true , false );
|
||||
NekoGui::ProfileFilter::OnlyInSrc_ByPointer (NekoGui::profileManager-> CurrentGroup ()-> GetProfileEnts (), out, out_del);
|
||||
|
||||
int remove_display_count = 0 ;
|
||||
QString remove_display;
|
||||
@ -1539,9 +1471,9 @@ void MainWindow::on_menu_delete_repeat_triggered () {
|
||||
if (!out_del.empty() &&
|
||||
QMessageBox::question ( this , tr ( " Confirmation " ), tr ( " Remove %1 item(s) ? " ). arg (out_del. length ()) + " \n " + remove_display) == QMessageBox::StandardButton::Yes) {
|
||||
for ( const auto &ent: out_del) {
|
||||
NekoGui::profileManager-> DeleteProfile (ent-> id );
|
||||
NekoGui::profileManager->DeleteProfile(ent-> id );
|
||||
}
|
||||
refresh_proxy_list ();
|
||||
refresh_proxy_list();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1868,7 +1800,7 @@ void MainWindow::on_menu_remove_invalid_triggered() {
|
||||
|
||||
auto currentGroup = NekoGui::profileManager->GetGroup(NekoGui::dataStore->current_group);
|
||||
if (currentGroup == nullptr) return;
|
||||
for (const auto &profile : currentGroup->Profiles()) {
|
||||
for (const auto &profile : currentGroup->GetProfileEnts()) {
|
||||
if (!IsValid(profile)) out_del += profile;
|
||||
}
|
||||
|
||||
@ -1927,7 +1859,8 @@ void MainWindow::on_menu_resolve_domain_triggered() {
|
||||
auto resolve_count = std::atomic<int>(0);
|
||||
NekoGui::dataStore->resolve_count = profiles.count();
|
||||
|
||||
for (const auto &profile: profiles) {
|
||||
for (const auto id: profiles) {
|
||||
auto profile = NekoGui::profileManager->GetProfile(id);
|
||||
profile->bean->ResolveDomainToIP([=] {
|
||||
profile->Save();
|
||||
if (--NekoGui::dataStore->resolve_count != 0) return;
|
||||
@ -1957,9 +1890,9 @@ QList<std::shared_ptr<NekoGui::ProxyEntity>> MainWindow::get_selected_or_group()
|
||||
QList<std::shared_ptr<NekoGui::ProxyEntity>> profiles;
|
||||
if (selected_or_group > 0) {
|
||||
profiles = get_now_selected_list();
|
||||
if (profiles.isEmpty() && selected_or_group == 2) profiles = NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder();
|
||||
if (profiles.isEmpty() && selected_or_group == 2) profiles = NekoGui::profileManager->CurrentGroup()->GetProfileEnts();
|
||||
} else {
|
||||
profiles = NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder();
|
||||
profiles = NekoGui::profileManager->CurrentGroup()->GetProfileEnts();
|
||||
}
|
||||
return profiles;
|
||||
}
|
||||
|
||||
@ -564,7 +564,7 @@ void DialogEditProfile::do_apply_to_group(const std::shared_ptr<NekoGui::Group>
|
||||
auto stream = GetStreamSettings(ent->bean.get());
|
||||
|
||||
auto copyStream = [=](void *p) {
|
||||
for (const auto &profile: group->Profiles()) {
|
||||
for (const auto &profile: group->GetProfileEnts()) {
|
||||
auto newStream = GetStreamSettings(profile->bean.get());
|
||||
if (newStream == nullptr) continue;
|
||||
if (stream == newStream) continue;
|
||||
@ -575,7 +575,7 @@ void DialogEditProfile::do_apply_to_group(const std::shared_ptr<NekoGui::Group>
|
||||
};
|
||||
|
||||
auto copyBean = [=](void *p) {
|
||||
for (const auto &profile: group->Profiles()) {
|
||||
for (const auto &profile: group->GetProfileEnts()) {
|
||||
if (profile == ent) continue;
|
||||
profile->bean->_setValue(ent->bean->_name(p), p);
|
||||
// qDebug() << profile->bean->ToJsonBytes();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user