feat: preview edit custom

This commit is contained in:
arm64v8a
2023-01-02 16:13:12 +09:00
parent 5e1d0cdce4
commit 62aee79383
13 changed files with 269 additions and 168 deletions

View File

@@ -449,8 +449,28 @@ namespace NekoRay {
}
// chain rules: this
auto mapping_port = MkPort();
auto ext_mapping_port = 0;
auto ext_socks_port = 0;
auto thisExternalStat = ent->bean->NeedExternal(isFirstProfile, dataStore->running_spmode == SystemProxyMode::VPN);
// determine port
if (thisExternalStat > 0) {
if (ent->type == "custom") {
auto bean = ent->CustomBean();
if (InRange(bean->mapping_port, 0, 65535)) {
ext_mapping_port = bean->mapping_port;
} else {
ext_mapping_port = MkPort();
}
if (InRange(bean->socks_port, 0, 65535)) {
ext_socks_port = bean->socks_port;
} else {
ext_socks_port = MkPort();
}
} else {
ext_mapping_port = MkPort();
ext_socks_port = MkPort();
}
}
if (thisExternalStat == 2) dataStore->need_keep_vpn_off = true;
if (thisExternalStat == 1) {
// mapping
@@ -459,7 +479,7 @@ namespace NekoRay {
{"type", "direct"},
{"tag", tagOut + "-mapping"},
{"listen", "127.0.0.1"},
{"listen_port", mapping_port},
{"listen_port", ext_mapping_port},
{"override_address", ent->bean->serverAddress},
{"override_port", ent->bean->serverPort},
};
@@ -468,7 +488,7 @@ namespace NekoRay {
{"protocol", "dokodemo-door"},
{"tag", tagOut + "-mapping"},
{"listen", "127.0.0.1"},
{"port", mapping_port},
{"port", ext_mapping_port},
{"settings", QJsonObject{
// to
{"address", ent->bean->serverAddress},
@@ -497,12 +517,9 @@ namespace NekoRay {
// Outbound
QJsonObject outbound;
fmt::CoreObjOutboundBuildResult coreR;
fmt::ExternalBuildResult extR;
if (thisExternalStat > 0) {
auto ext_socks_port = MkPort();
extR = ent->bean->BuildExternal(mapping_port, ext_socks_port, thisExternalStat);
const auto extR = ent->bean->BuildExternal(ext_mapping_port, ext_socks_port, thisExternalStat);
if (extR.program.isEmpty()) {
status->result->error = QObject::tr("Core not found: %1").arg(ent->bean->DisplayType());
return {};
@@ -530,14 +547,14 @@ namespace NekoRay {
}
// EXTERNAL PROCESS
auto extC = new sys::ExternalProcess();
QSharedPointer<sys::ExternalProcess> extC(new sys::ExternalProcess());
extC->tag = ent->bean->DisplayType();
extC->program = extR.program;
extC->arguments = extR.arguments;
extC->env = extR.env;
status->result->ext += extC;
status->result->exts.emplace_back(extR, extC);
} else {
coreR = IS_NEKO_BOX ? ent->bean->BuildCoreObjSingBox() : ent->bean->BuildCoreObjV2Ray();
const auto coreR = IS_NEKO_BOX ? ent->bean->BuildCoreObjSingBox() : ent->bean->BuildCoreObjV2Ray();
if (coreR.outbound.isEmpty()) {
status->result->error = "unsupported outbound";
return {};

View File

@@ -14,7 +14,7 @@ namespace NekoRay {
QSharedPointer<traffic::TrafficData> outboundStat; // main
QStringList ignoreConnTag;
QList<sys::ExternalProcess *> ext;
std::list<std::pair<fmt::ExternalBuildResult, QSharedPointer<sys::ExternalProcess>>> exts; // extR to extC
};
class BuildConfigStatus {

View File

@@ -20,7 +20,7 @@
auto TempFile = QFileInfo(f).absoluteFilePath();
namespace NekoRay::fmt {
// 0: no external
// 0: Internal
// 1: Mapping External
// 2: Direct External
@@ -85,13 +85,10 @@ namespace NekoRay::fmt {
for (int i = 0; i < result.arguments.length(); i++) {
auto arg = result.arguments[i];
if (arg.contains("%mapping_port%")) {
arg = arg.replace("%mapping_port%", Int2String(mapping_port));
} else if (arg.contains("%socks_port%")) {
arg = arg.replace("%socks_port%", Int2String(socks_port));
} else {
continue;
}
arg = arg.replace("%mapping_port%", Int2String(mapping_port));
arg = arg.replace("%socks_port%", Int2String(socks_port));
arg = arg.replace("%server_addr%", serverAddress);
arg = arg.replace("%server_port%", Int2String(serverPort));
result.arguments[i] = arg;
}

View File

@@ -9,12 +9,16 @@ namespace NekoRay::fmt {
QList<QString> command;
QString config_suffix;
QString config_simple;
int mapping_port = 0;
int socks_port = 0;
CustomBean() : AbstractBean(0) {
_add(new configItem("core", &core, itemType::string));
_add(new configItem("cmd", &command, itemType::stringList));
_add(new configItem("cs", &config_simple, itemType::string));
_add(new configItem("cs_suffix", &config_suffix, itemType::string));
_add(new configItem("mapping_port", &mapping_port, itemType::integer));
_add(new configItem("socks_port", &socks_port, itemType::integer));
};
QString DisplayType() override {

View File

@@ -5,12 +5,17 @@
#include <QApplication>
namespace NekoRay::sys {
ExternalProcess::ExternalProcess() : QProcess() {}
ExternalProcess::ExternalProcess() : QProcess() {
// qDebug() << "[Debug] ExternalProcess()" << this << running_ext;
}
ExternalProcess::~ExternalProcess() {
// qDebug() << "[Debug] ~ExternalProcess()" << this << running_ext;
}
void ExternalProcess::Start() {
if (started) return;
started = true;
if (managed) running_ext.push_back(this);
if (show_log) {
connect(this, &QProcess::readyReadStandardOutput, this, [&]() {
@@ -19,14 +24,11 @@ namespace NekoRay::sys {
connect(this, &QProcess::readyReadStandardError, this, [&]() {
MW_show_log_ext_vt100(readAllStandardError().trimmed());
});
}
if (managed) {
connect(this, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) {
if (!killed) {
crashed = true;
MW_show_log_ext(tag, "errorOccurred:" + errorString());
MW_dialog_message("ExternalProcess", "Crashed");
if (managed) MW_dialog_message("ExternalProcess", "Crashed");
}
});
connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) {
@@ -37,7 +39,7 @@ namespace NekoRay::sys {
crashed = true;
MW_show_log_ext(tag, "[Error] Program exited accidentally: " + errorString());
Kill();
MW_dialog_message("ExternalProcess", "Crashed");
if (managed) MW_dialog_message("ExternalProcess", "Crashed");
}
}
});
@@ -51,7 +53,6 @@ namespace NekoRay::sys {
void ExternalProcess::Kill() {
if (killed) return;
killed = true;
if (managed) running_ext.removeAll(this);
if (!crashed) {
QProcess::kill();
@@ -59,7 +60,7 @@ namespace NekoRay::sys {
}
}
CoreProcess::CoreProcess(const QString &core_path, const QStringList &args) {
CoreProcess::CoreProcess(const QString &core_path, const QStringList &args) : ExternalProcess() {
ExternalProcess::managed = false;
ExternalProcess::show_log = false;
ExternalProcess::program = core_path;

View File

@@ -12,10 +12,11 @@ namespace NekoRay::sys {
QStringList arguments;
QStringList env;
bool managed = true; // running_ext & stateChanged
bool managed = true; // MW_dialog_message
bool show_log = true;
ExternalProcess();
~ExternalProcess();
// start & kill is one time
@@ -41,6 +42,6 @@ namespace NekoRay::sys {
int restart_id = -1;
};
// start & kill change this list
inline QList<ExternalProcess *> running_ext;
// 手动管理
inline QList<QSharedPointer<ExternalProcess>> running_ext;
} // namespace NekoRay::sys

View File

@@ -601,10 +601,6 @@
<source>Config Suffix</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Please read the documentation. If you don&apos;t understand, use a share link instead.</source>
<translation type="unfinished"></translation>
@@ -617,6 +613,22 @@
<source>Please pick a core.</source>
<translation>لطفا یک هسته انتخاب کنید.</translation>
</message>
<message>
<source>Random if it&apos;s empty or zero.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Preview</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Preview replace</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Preview config</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditNaive</name>

View File

@@ -591,10 +591,6 @@
<source>Command</source>
<translation></translation>
</message>
<message>
<source>Generator</source>
<translation></translation>
</message>
<message>
<source>Json Editor</source>
<translation>JSON </translation>
@@ -615,6 +611,22 @@
<source>Config Suffix</source>
<translation></translation>
</message>
<message>
<source>Random if it&apos;s empty or zero.</source>
<translation>使</translation>
</message>
<message>
<source>Preview</source>
<translation></translation>
</message>
<message>
<source>Preview replace</source>
<translation></translation>
</message>
<message>
<source>Preview config</source>
<translation></translation>
</message>
</context>
<context>
<name>EditNaive</name>

View File

@@ -234,12 +234,11 @@ void DialogEditProfile::typeSelected(const QString &newType) {
delete old;
// 左边 bean inner editor
innerEditor->get_edit_dialog = [&]() {
return (QWidget *) this;
};
innerEditor->editor_cache_updated = [=] {
editor_cache_updated_impl();
};
innerEditor->get_edit_dialog = [&]() { return (QWidget *) this; };
innerEditor->get_edit_text_name = [&]() { return ui->name->text(); };
innerEditor->get_edit_text_serverAddress = [&]() { return ui->address->text(); };
innerEditor->get_edit_text_serverPort = [&]() { return ui->port->text(); };
innerEditor->editor_cache_updated = [=] { editor_cache_updated_impl(); };
innerEditor->onStart(ent);
// 左边 common

View File

@@ -4,6 +4,11 @@
#include "qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
#include "fmt/CustomBean.hpp"
#include "fmt/Preset.hpp"
#include "db/ConfigBuilder.hpp"
#include "db/Database.hpp"
#include <QMessageBox>
#include <QClipboard>
EditCustom::EditCustom(QWidget *parent) : QWidget(parent), ui(new Ui::EditCustom) {
ui->setupUi(this);
@@ -20,6 +25,14 @@ EditCustom::~EditCustom() {
delete ui;
}
#define SAVE_CUSTOM_BEAN \
P_SAVE_COMBO(core) \
bean->command = ui->command->text().split(" "); \
P_SAVE_STRING_QTEXTEDIT(config_simple) \
P_SAVE_COMBO(config_suffix) \
P_SAVE_INT(mapping_port) \
P_SAVE_INT(socks_port)
void EditCustom::onStart(QSharedPointer<NekoRay::ProxyEntity> _ent) {
this->ent = _ent;
auto bean = this->ent->CustomBean();
@@ -50,6 +63,8 @@ void EditCustom::onStart(QSharedPointer<NekoRay::ProxyEntity> _ent) {
ui->command->setText(bean->command.join(" "));
P_LOAD_STRING(config_simple)
P_LOAD_COMBO(config_suffix)
P_LOAD_INT(mapping_port)
P_LOAD_INT(socks_port)
// custom external
if (!bean->core.isEmpty()) {
@@ -72,17 +87,46 @@ void EditCustom::onStart(QSharedPointer<NekoRay::ProxyEntity> _ent) {
ui->config_suffix_l->hide();
}
// Generators
ui->generator->setVisible(false);
// Preview
connect(ui->preview, &QPushButton::clicked, this, [=] {
// CustomBean::BuildExternal
QStringList th;
auto mapping_port = ui->mapping_port->text().toInt();
auto socks_port = ui->socks_port->text().toInt();
th << "%mapping_port% => " + (mapping_port <= 0 ? "Random" : Int2String(mapping_port));
th << "%socks_port% => " + (socks_port <= 0 ? "Random" : Int2String(socks_port));
th << "%server_addr% => " + get_edit_text_serverAddress();
th << "%server_port% => " + get_edit_text_serverPort();
MessageBoxInfo(tr("Preview replace"), th.join("\n"));
// EditCustom::onEnd
auto tmpEnt = NekoRay::ProfileManager::NewProxyEntity("custom");
auto bean = tmpEnt->CustomBean();
SAVE_CUSTOM_BEAN
if (bean->core.isEmpty()) return;
//
auto result = NekoRay::BuildConfig(tmpEnt, false, false);
if (!result->error.isEmpty()) {
MessageBoxInfo(software_name, result->error);
return;
}
for (const auto &ext: result->exts) {
auto extR = ext.first;
auto command = QStringList{extR.program};
command += extR.arguments;
auto btn = QMessageBox::information(this, tr("Preview config"),
QString("Command: %1\n\n%2").arg(QStringList2Command(command), extR.config_export),
"OK", "Copy", "", 0, 0);
if (btn == 1) {
QApplication::clipboard()->setText(extR.config_export);
}
}
});
}
bool EditCustom::onEnd() {
auto bean = this->ent->CustomBean();
P_SAVE_COMBO(core)
bean->command = ui->command->text().split(" ");
P_SAVE_STRING_QTEXTEDIT(config_simple)
P_SAVE_COMBO(config_suffix)
SAVE_CUSTOM_BEAN
if (bean->core.isEmpty()) {
MessageBoxWarning(software_name, tr("Please pick a core."));

View File

@@ -7,129 +7,136 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>400</height>
<height>450</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">EditCustom</string>
</property>
<layout class="QHBoxLayout" name="layout">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="core_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Core</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="core">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="as_json">
<property name="text">
<string>Json Editor</string>
</property>
</widget>
</item>
</layout>
<widget class="QLabel" name="core_l">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Core</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="command_l">
<property name="text">
<string>Command</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="command">
<property name="placeholderText">
<string notr="true">%config%</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="config_suffix_l">
<property name="text">
<string>Config Suffix</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="config_suffix">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string notr="true"/>
</property>
</item>
<item>
<property name="text">
<string notr="true">json</string>
</property>
</item>
</widget>
</item>
</layout>
<widget class="QComboBox" name="core">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="config_simple">
<property name="minimumSize">
<size>
<width>0</width>
<height>300</height>
</size>
<widget class="QPushButton" name="as_json">
<property name="text">
<string>Json Editor</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="generator">
<property name="title">
<string>Generator</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="fake" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="command_l">
<property name="text">
<string>Command</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="command">
<property name="placeholderText">
<string notr="true">%config%</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="config_suffix_l">
<property name="text">
<string>Config Suffix</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="config_suffix">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<property name="text">
<string notr="true">json</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">yml</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="toolTip">
<string>Random if it's empty or zero.</string>
</property>
<property name="text">
<string notr="true">Mapping Port</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="mapping_port"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="toolTip">
<string>Random if it's empty or zero.</string>
</property>
<property name="text">
<string notr="true">Socks Port</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="socks_port"/>
</item>
<item>
<widget class="QPushButton" name="preview">
<property name="text">
<string>Preview</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="config_simple">
<property name="minimumSize">
<size>
<width>0</width>
<height>300</height>
</size>
</property>
</widget>
</item>
</layout>
@@ -138,6 +145,11 @@
<tabstop>core</tabstop>
<tabstop>as_json</tabstop>
<tabstop>command</tabstop>
<tabstop>config_suffix</tabstop>
<tabstop>mapping_port</tabstop>
<tabstop>socks_port</tabstop>
<tabstop>preview</tabstop>
<tabstop>config_simple</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@@ -12,6 +12,9 @@ public:
virtual bool onEnd() = 0;
std::function<QWidget *()> get_edit_dialog;
std::function<QString()> get_edit_text_name;
std::function<QString()> get_edit_text_serverAddress;
std::function<QString()> get_edit_text_serverPort;
// cached editor

View File

@@ -100,15 +100,15 @@ void MainWindow::speedtest_current_group(int mode) {
req.set_url(NekoRay::dataStore->test_url.toStdString());
//
QList<NekoRay::sys::ExternalProcess *> ext;
std::list<std::pair<NekoRay::fmt::ExternalBuildResult, QSharedPointer<NekoRay::sys::ExternalProcess>>> exts;
if (mode == libcore::TestMode::UrlTest || mode == libcore::FullTest) {
auto c = NekoRay::BuildConfig(profile, true, false);
// external test ???
if (!c->ext.isEmpty()) {
ext = c->ext;
for (auto extC: ext) {
extC->Start();
// TODO refactor external test
if (!c->exts.empty()) {
exts = c->exts;
for (const auto &ext: exts) {
ext.second->Start();
}
QThread::msleep(500);
}
@@ -128,9 +128,8 @@ void MainWindow::speedtest_current_group(int mode) {
bool rpcOK;
auto result = defaultClient->Test(&rpcOK, req);
for (auto extC: ext) {
extC->Kill();
extC->deleteLater();
for (const auto &ext: exts) {
ext.second->Kill();
}
if (!rpcOK) return;
@@ -236,8 +235,9 @@ void MainWindow::neko_start(int _id) {
NekoRay::traffic::trafficLooper->loop_enabled = true;
#endif
for (auto extC: result->ext) {
extC->Start();
for (const auto &ext: result->exts) {
NekoRay::sys::running_ext.push_back(ext.second);
ext.second->Start();
}
NekoRay::dataStore->UpdateStartedId(ent->id);
@@ -254,7 +254,6 @@ void MainWindow::neko_stop(bool crash) {
while (!NekoRay::sys::running_ext.isEmpty()) {
auto extC = NekoRay::sys::running_ext.takeFirst();
extC->Kill();
extC->deleteLater();
}
#ifndef NKR_NO_GRPC