mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 13:50:12 +08:00
add extra core &&
fix speedtest current
This commit is contained in:
parent
83413c19e8
commit
78a78cf38e
@ -148,6 +148,10 @@ set(PROJECT_SOURCES
|
|||||||
src/ui/profile/edit_custom.cpp
|
src/ui/profile/edit_custom.cpp
|
||||||
include/ui/profile/edit_custom.ui
|
include/ui/profile/edit_custom.ui
|
||||||
|
|
||||||
|
include/ui/profile/edit_extra_core.h
|
||||||
|
src/ui/profile/edit_extra_core.cpp
|
||||||
|
include/ui/profile/edit_extra_core.ui
|
||||||
|
|
||||||
include/ui/profile/edit_wireguard.h
|
include/ui/profile/edit_wireguard.h
|
||||||
src/ui/profile/edit_wireguard.cpp
|
src/ui/profile/edit_wireguard.cpp
|
||||||
include/ui/profile/edit_wireguard.ui
|
include/ui/profile/edit_wireguard.ui
|
||||||
|
|||||||
@ -138,11 +138,17 @@ func (x *ErrorResp) GetError() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LoadConfigReq struct {
|
type LoadConfigReq struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
CoreConfig string `protobuf:"bytes,1,opt,name=core_config,json=coreConfig,proto3" json:"core_config,omitempty"`
|
CoreConfig string `protobuf:"bytes,1,opt,name=core_config,json=coreConfig,proto3" json:"core_config,omitempty"`
|
||||||
DisableStats bool `protobuf:"varint,2,opt,name=disable_stats,json=disableStats,proto3" json:"disable_stats,omitempty"`
|
DisableStats bool `protobuf:"varint,2,opt,name=disable_stats,json=disableStats,proto3" json:"disable_stats,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
NeedExtraProcess bool `protobuf:"varint,3,opt,name=need_extra_process,json=needExtraProcess,proto3" json:"need_extra_process,omitempty"`
|
||||||
sizeCache protoimpl.SizeCache
|
ExtraProcessPath string `protobuf:"bytes,4,opt,name=extra_process_path,json=extraProcessPath,proto3" json:"extra_process_path,omitempty"`
|
||||||
|
ExtraProcessArgs string `protobuf:"bytes,5,opt,name=extra_process_args,json=extraProcessArgs,proto3" json:"extra_process_args,omitempty"`
|
||||||
|
ExtraProcessConf string `protobuf:"bytes,6,opt,name=extra_process_conf,json=extraProcessConf,proto3" json:"extra_process_conf,omitempty"`
|
||||||
|
ExtraProcessConfDir string `protobuf:"bytes,7,opt,name=extra_process_conf_dir,json=extraProcessConfDir,proto3" json:"extra_process_conf_dir,omitempty"`
|
||||||
|
ExtraNoOut bool `protobuf:"varint,8,opt,name=extra_no_out,json=extraNoOut,proto3" json:"extra_no_out,omitempty"`
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *LoadConfigReq) Reset() {
|
func (x *LoadConfigReq) Reset() {
|
||||||
@ -189,6 +195,48 @@ func (x *LoadConfigReq) GetDisableStats() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LoadConfigReq) GetNeedExtraProcess() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.NeedExtraProcess
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadConfigReq) GetExtraProcessPath() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExtraProcessPath
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadConfigReq) GetExtraProcessArgs() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExtraProcessArgs
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadConfigReq) GetExtraProcessConf() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExtraProcessConf
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadConfigReq) GetExtraProcessConfDir() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExtraProcessConfDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoadConfigReq) GetExtraNoOut() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.ExtraNoOut
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type URLTestResp struct {
|
type URLTestResp struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
OutboundTag string `protobuf:"bytes,1,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"`
|
OutboundTag string `protobuf:"bytes,1,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"`
|
||||||
@ -1193,12 +1241,29 @@ var file_libcore_proto_rawDesc = string([]byte{
|
|||||||
0x79, 0x52, 0x65, 0x71, 0x22, 0x0b, 0x0a, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73,
|
0x79, 0x52, 0x65, 0x71, 0x22, 0x0b, 0x0a, 0x09, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73,
|
||||||
0x70, 0x22, 0x21, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14,
|
0x70, 0x22, 0x21, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x14,
|
||||||
0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
|
0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
|
||||||
0x72, 0x72, 0x6f, 0x72, 0x22, 0x55, 0x0a, 0x0d, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66,
|
0x72, 0x72, 0x6f, 0x72, 0x22, 0xe4, 0x02, 0x0a, 0x0d, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e,
|
||||||
0x69, 0x67, 0x52, 0x65, 0x71, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f,
|
0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x63,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x72, 0x65,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x72,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
|
0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62,
|
||||||
0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x64,
|
0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c,
|
||||||
0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, 0x65, 0x0a, 0x0b, 0x55,
|
0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x2c, 0x0a, 0x12,
|
||||||
|
0x6e, 0x65, 0x65, 0x64, 0x5f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65,
|
||||||
|
0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6e, 0x65, 0x65, 0x64, 0x45, 0x78,
|
||||||
|
0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x65, 0x78,
|
||||||
|
0x74, 0x72, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68,
|
||||||
|
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f,
|
||||||
|
0x63, 0x65, 0x73, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x72,
|
||||||
|
0x61, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x18, 0x05,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65,
|
||||||
|
0x73, 0x73, 0x41, 0x72, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f,
|
||||||
|
0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x18, 0x06, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x10, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
|
||||||
|
0x43, 0x6f, 0x6e, 0x66, 0x12, 0x33, 0x0a, 0x16, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x70, 0x72,
|
||||||
|
0x6f, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x07,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x78, 0x74, 0x72, 0x61, 0x50, 0x72, 0x6f, 0x63, 0x65,
|
||||||
|
0x73, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x44, 0x69, 0x72, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x78, 0x74,
|
||||||
|
0x72, 0x61, 0x5f, 0x6e, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
|
0x0a, 0x65, 0x78, 0x74, 0x72, 0x61, 0x4e, 0x6f, 0x4f, 0x75, 0x74, 0x22, 0x65, 0x0a, 0x0b, 0x55,
|
||||||
0x52, 0x4c, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75,
|
0x52, 0x4c, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75,
|
||||||
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1d, 0x0a,
|
0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x12, 0x1d, 0x0a,
|
||||||
|
|||||||
@ -38,6 +38,12 @@ message ErrorResp {
|
|||||||
message LoadConfigReq {
|
message LoadConfigReq {
|
||||||
string core_config = 1;
|
string core_config = 1;
|
||||||
bool disable_stats = 2;
|
bool disable_stats = 2;
|
||||||
|
bool need_extra_process = 3;
|
||||||
|
string extra_process_path = 4;
|
||||||
|
string extra_process_args = 5;
|
||||||
|
string extra_process_conf = 6;
|
||||||
|
string extra_process_conf_dir = 7;
|
||||||
|
bool extra_no_out = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message URLTestResp {
|
message URLTestResp {
|
||||||
|
|||||||
15
core/server/internal/process/logger.go
Normal file
15
core/server/internal/process/logger.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package process
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
type pipeLogger struct {
|
||||||
|
prefix string
|
||||||
|
noOut bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pipeLogger) Write(b []byte) (int, error) {
|
||||||
|
if !p.noOut {
|
||||||
|
log.Println(p.prefix + ":" + string(b))
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
47
core/server/internal/process/process.go
Normal file
47
core/server/internal/process/process.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package process
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/sagernet/sing/common/atomic"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
path string
|
||||||
|
args string
|
||||||
|
noOut bool
|
||||||
|
cmd *exec.Cmd
|
||||||
|
stopped atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProcess(path string, args string, noOut bool) *Process {
|
||||||
|
return &Process{path: path, args: args, noOut: noOut}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Process) Start() error {
|
||||||
|
p.cmd = exec.Command(p.path, strings.Split(p.args, " ")...)
|
||||||
|
|
||||||
|
p.cmd.Stdout = &pipeLogger{prefix: "Extra Core", noOut: p.noOut}
|
||||||
|
p.cmd.Stderr = &pipeLogger{prefix: "Extra Core", noOut: p.noOut}
|
||||||
|
|
||||||
|
err := p.cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.stopped.Store(false)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
fmt.Println(p.path, ":", "process started, waiting for it to end")
|
||||||
|
_ = p.cmd.Wait()
|
||||||
|
if !p.stopped.Load() {
|
||||||
|
fmt.Println("Extra process exited unexpectedly")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Process) Stop() {
|
||||||
|
p.stopped.Store(true)
|
||||||
|
_ = p.cmd.Process.Kill()
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/settings"
|
"github.com/sagernet/sing-box/common/settings"
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||||
@ -13,6 +14,7 @@ import (
|
|||||||
"nekobox_core/gen"
|
"nekobox_core/gen"
|
||||||
"nekobox_core/internal/boxbox"
|
"nekobox_core/internal/boxbox"
|
||||||
"nekobox_core/internal/boxmain"
|
"nekobox_core/internal/boxmain"
|
||||||
|
"nekobox_core/internal/process"
|
||||||
"nekobox_core/internal/sys"
|
"nekobox_core/internal/sys"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -21,6 +23,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var boxInstance *boxbox.Box
|
var boxInstance *boxbox.Box
|
||||||
|
var extraProcess *process.Process
|
||||||
var needUnsetDNS bool
|
var needUnsetDNS bool
|
||||||
var systemProxyController settings.SystemProxy
|
var systemProxyController settings.SystemProxy
|
||||||
var systemProxyAddr metadata.Socksaddr
|
var systemProxyAddr metadata.Socksaddr
|
||||||
@ -59,6 +62,28 @@ func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.Err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if in.NeedExtraProcess {
|
||||||
|
extraConfPath := in.ExtraProcessConfDir + string(os.PathSeparator) + "extra.conf"
|
||||||
|
f, e := os.OpenFile(extraConfPath, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 700)
|
||||||
|
if e != nil {
|
||||||
|
err = E.Cause(e, "Failed to open extra.conf")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, e = f.WriteString(in.ExtraProcessConf)
|
||||||
|
if e != nil {
|
||||||
|
err = E.Cause(e, "Failed to write extra.conf")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = f.Close()
|
||||||
|
args := fmt.Sprintf(in.ExtraProcessArgs, extraConfPath)
|
||||||
|
|
||||||
|
extraProcess = process.NewProcess(in.ExtraProcessPath, args, in.ExtraNoOut)
|
||||||
|
err = extraProcess.Start()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boxInstance, instanceCancel, err = boxmain.Create([]byte(in.CoreConfig))
|
boxInstance, instanceCancel, err = boxmain.Create([]byte(in.CoreConfig))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -99,6 +124,11 @@ func (s *server) Stop(ctx context.Context, in *gen.EmptyReq) (out *gen.ErrorResp
|
|||||||
|
|
||||||
boxInstance = nil
|
boxInstance = nil
|
||||||
|
|
||||||
|
if extraProcess != nil {
|
||||||
|
extraProcess.Stop()
|
||||||
|
extraProcess = nil
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,10 +4,21 @@
|
|||||||
#include "include/sys/Process.hpp"
|
#include "include/sys/Process.hpp"
|
||||||
|
|
||||||
namespace NekoGui {
|
namespace NekoGui {
|
||||||
|
class ExtraCoreData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString path;
|
||||||
|
QString args;
|
||||||
|
QString config;
|
||||||
|
QString configDir;
|
||||||
|
bool noLog;
|
||||||
|
};
|
||||||
|
|
||||||
class BuildConfigResult {
|
class BuildConfigResult {
|
||||||
public:
|
public:
|
||||||
QString error;
|
QString error;
|
||||||
QJsonObject coreConfig;
|
QJsonObject coreConfig;
|
||||||
|
std::shared_ptr<ExtraCoreData> extraCoreData;
|
||||||
|
|
||||||
QList<std::shared_ptr<NekoGui_traffic::TrafficData>> outboundStats; // all, but not including "bypass" "block"
|
QList<std::shared_ptr<NekoGui_traffic::TrafficData>> outboundStats; // all, but not including "bypass" "block"
|
||||||
};
|
};
|
||||||
|
|||||||
34
include/configs/proxy/ExtraCore.h
Normal file
34
include/configs/proxy/ExtraCore.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AbstractBean.hpp"
|
||||||
|
|
||||||
|
namespace NekoGui_fmt {
|
||||||
|
class ExtraCoreBean : public AbstractBean {
|
||||||
|
public:
|
||||||
|
QString socksAddress = "127.0.0.1";
|
||||||
|
int socksPort;
|
||||||
|
QString extraCorePath;
|
||||||
|
QString extraCoreArgs;
|
||||||
|
QString extraCoreConf;
|
||||||
|
bool noLogs;
|
||||||
|
|
||||||
|
ExtraCoreBean() : AbstractBean(0) {
|
||||||
|
_add(new configItem("socks_address", &socksAddress, itemType::string));
|
||||||
|
_add(new configItem("socks_port", &socksPort, itemType::integer));
|
||||||
|
_add(new configItem("extra_core_path", &extraCorePath, itemType::string));
|
||||||
|
_add(new configItem("extra_core_args", &extraCoreArgs, itemType::string));
|
||||||
|
_add(new configItem("extra_core_conf", &extraCoreConf, itemType::string));
|
||||||
|
_add(new configItem("no_logs", &noLogs, itemType::boolean));
|
||||||
|
};
|
||||||
|
|
||||||
|
QString DisplayType() override { return "ExtraCore"; };
|
||||||
|
|
||||||
|
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||||
|
|
||||||
|
bool TryParseLink(const QString &link);
|
||||||
|
|
||||||
|
bool TryParseJson(const QJsonObject &obj);
|
||||||
|
|
||||||
|
QString ToShareLink() override;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -9,3 +9,4 @@
|
|||||||
#include "WireguardBean.h"
|
#include "WireguardBean.h"
|
||||||
#include "SSHBean.h"
|
#include "SSHBean.h"
|
||||||
#include "CustomBean.hpp"
|
#include "CustomBean.hpp"
|
||||||
|
#include "ExtraCore.h"
|
||||||
|
|||||||
@ -56,11 +56,16 @@ namespace NekoGui {
|
|||||||
|
|
||||||
void UpdateRouteChains(const QList<std::shared_ptr<RoutingChain>>& newChain);
|
void UpdateRouteChains(const QList<std::shared_ptr<RoutingChain>>& newChain);
|
||||||
|
|
||||||
|
QStringList GetExtraCorePaths() const;
|
||||||
|
|
||||||
|
bool AddExtraCorePath(const QString &path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// sort by id
|
// sort by id
|
||||||
QList<int> profilesIdOrder;
|
QList<int> profilesIdOrder;
|
||||||
QList<int> groupsIdOrder;
|
QList<int> groupsIdOrder;
|
||||||
QList<int> routesIdOrder;
|
QList<int> routesIdOrder;
|
||||||
|
QSet<QString> extraCorePaths;
|
||||||
|
|
||||||
[[nodiscard]] int NewProfileID() const;
|
[[nodiscard]] int NewProfileID() const;
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include "include/global/NekoGui.hpp"
|
#include "include/global/NekoGui.hpp"
|
||||||
#include "include/stats/traffic/TrafficData.hpp"
|
#include "include/stats/traffic/TrafficData.hpp"
|
||||||
#include "include/configs/proxy/AbstractBean.hpp"
|
#include "include/configs/proxy/AbstractBean.hpp"
|
||||||
|
#include "include/configs/proxy/ExtraCore.h"
|
||||||
|
|
||||||
namespace NekoGui_fmt {
|
namespace NekoGui_fmt {
|
||||||
class SocksHttpBean;
|
class SocksHttpBean;
|
||||||
@ -86,5 +87,9 @@ namespace NekoGui {
|
|||||||
[[nodiscard]] NekoGui_fmt::CustomBean *CustomBean() const {
|
[[nodiscard]] NekoGui_fmt::CustomBean *CustomBean() const {
|
||||||
return (NekoGui_fmt::CustomBean *) bean.get();
|
return (NekoGui_fmt::CustomBean *) bean.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] NekoGui_fmt::ExtraCoreBean *ExtraCoreBean() const {
|
||||||
|
return (NekoGui_fmt::ExtraCoreBean *) bean.get();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
} // namespace NekoGui
|
} // namespace NekoGui
|
||||||
|
|||||||
@ -241,9 +241,9 @@ private:
|
|||||||
|
|
||||||
void url_test_current();
|
void url_test_current();
|
||||||
|
|
||||||
void speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles);
|
void speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles, bool testCurrent = false);
|
||||||
|
|
||||||
void runSpeedTest(const QString& config, bool useDefault, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID = -1);
|
void runSpeedTest(const QString& config, bool useDefault, bool testCurrent, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID = -1);
|
||||||
|
|
||||||
static void stop_core_daemon();
|
static void stop_core_daemon();
|
||||||
|
|
||||||
|
|||||||
28
include/ui/profile/edit_extra_core.h
Normal file
28
include/ui/profile/edit_extra_core.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include "profile_editor.h"
|
||||||
|
#include "ui_edit_extra_core.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui {
|
||||||
|
class EditExtraCore;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class EditExtraCore : public QWidget, public ProfileEditor {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit EditExtraCore(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
~EditExtraCore() override;
|
||||||
|
|
||||||
|
void onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) override;
|
||||||
|
|
||||||
|
bool onEnd() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::EditExtraCore *ui;
|
||||||
|
std::shared_ptr<NekoGui::ProxyEntity> ent;
|
||||||
|
};
|
||||||
155
include/ui/profile/edit_extra_core.ui
Normal file
155
include/ui/profile/edit_extra_core.ui
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>EditExtraCore</class>
|
||||||
|
<widget class="QWidget" name="EditExtraCore">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_2">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Shape::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Shadow::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Socks address</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="socks_address">
|
||||||
|
<property name="text">
|
||||||
|
<string>127.0.0.1</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Socks port</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="socks_port">
|
||||||
|
<property name="text">
|
||||||
|
<string>1080</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Shape::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Shadow::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_3">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Shape::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Shadow::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Core path</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="path_combo">
|
||||||
|
<property name="frame">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="path_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Choose from file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_4">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::Shape::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Shadow::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>args to pass to the executable, with a %s placed in place of the config file path, for example:<br/>./sing-box run -c conf.json<br/>will have its args like:<br/>run -c %s</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Args</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="args">
|
||||||
|
<property name="placeholderText">
|
||||||
|
<string>run -confPath %s</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>contents of the config file that will be passed to the extra core process</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Config</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="config"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="no_logs">
|
||||||
|
<property name="text">
|
||||||
|
<string>No logs</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
@ -46,6 +46,7 @@ namespace NekoGui {
|
|||||||
|
|
||||||
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport, int chainID) {
|
std::shared_ptr<BuildConfigResult> BuildConfig(const std::shared_ptr<ProxyEntity> &ent, bool forTest, bool forExport, int chainID) {
|
||||||
auto result = std::make_shared<BuildConfigResult>();
|
auto result = std::make_shared<BuildConfigResult>();
|
||||||
|
result->extraCoreData = std::make_shared<ExtraCoreData>();
|
||||||
auto status = std::make_shared<BuildConfigStatus>();
|
auto status = std::make_shared<BuildConfigStatus>();
|
||||||
status->ent = ent;
|
status->ent = ent;
|
||||||
status->result = result;
|
status->result = result;
|
||||||
@ -127,6 +128,11 @@ namespace NekoGui {
|
|||||||
|
|
||||||
QJsonArray directDomainArray;
|
QJsonArray directDomainArray;
|
||||||
for (const auto &item: profiles) {
|
for (const auto &item: profiles) {
|
||||||
|
if (item->type == "extracore")
|
||||||
|
{
|
||||||
|
MW_show_log("Skipping ExtraCore conf");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!IsValid(item)) {
|
if (!IsValid(item)) {
|
||||||
MW_show_log("Skipping invalid config: " + item->bean->name);
|
MW_show_log("Skipping invalid config: " + item->bean->name);
|
||||||
item->latency = -1;
|
item->latency = -1;
|
||||||
@ -423,6 +429,15 @@ namespace NekoGui {
|
|||||||
// Outbounds
|
// Outbounds
|
||||||
auto tagProxy = BuildChain(status->chainID, status);
|
auto tagProxy = BuildChain(status->chainID, status);
|
||||||
if (!status->result->error.isEmpty()) return;
|
if (!status->result->error.isEmpty()) return;
|
||||||
|
if (status->ent->type == "extracore")
|
||||||
|
{
|
||||||
|
auto bean = status->ent->ExtraCoreBean();
|
||||||
|
status->result->extraCoreData->path = bean->extraCorePath;
|
||||||
|
status->result->extraCoreData->args = bean->extraCoreArgs;
|
||||||
|
status->result->extraCoreData->config = bean->extraCoreConf;
|
||||||
|
status->result->extraCoreData->configDir = GetBasePath();
|
||||||
|
status->result->extraCoreData->noLog = bean->noLogs;
|
||||||
|
}
|
||||||
|
|
||||||
// Direct domains
|
// Direct domains
|
||||||
bool needDirectDnsRules = false;
|
bool needDirectDnsRules = false;
|
||||||
|
|||||||
@ -334,4 +334,17 @@ namespace NekoGui_fmt {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoreObjOutboundBuildResult ExtraCoreBean::BuildCoreObjSingBox()
|
||||||
|
{
|
||||||
|
CoreObjOutboundBuildResult result;
|
||||||
|
QJsonObject outbound{
|
||||||
|
{"type", "socks"},
|
||||||
|
{"server", socksAddress},
|
||||||
|
{"server_port", socksPort},
|
||||||
|
};
|
||||||
|
result.outbound = outbound;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace NekoGui_fmt
|
} // namespace NekoGui_fmt
|
||||||
|
|||||||
@ -296,4 +296,9 @@ namespace NekoGui_fmt {
|
|||||||
return url.toString(QUrl::FullyEncoded);
|
return url.toString(QUrl::FullyEncoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ExtraCoreBean::ToShareLink()
|
||||||
|
{
|
||||||
|
return "Unsupported for now";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace NekoGui_fmt
|
} // namespace NekoGui_fmt
|
||||||
@ -7,6 +7,7 @@
|
|||||||
#include <include/configs/proxy/VMessBean.hpp>
|
#include <include/configs/proxy/VMessBean.hpp>
|
||||||
#include <include/configs/proxy/WireguardBean.h>
|
#include <include/configs/proxy/WireguardBean.h>
|
||||||
|
|
||||||
|
#include "include/configs/proxy/ExtraCore.h"
|
||||||
#include "include/configs/proxy/SSHBean.h"
|
#include "include/configs/proxy/SSHBean.h"
|
||||||
|
|
||||||
namespace NekoGui_fmt
|
namespace NekoGui_fmt
|
||||||
@ -224,4 +225,9 @@ namespace NekoGui_fmt
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ExtraCoreBean::TryParseJson(const QJsonObject& obj)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -394,4 +394,10 @@ namespace NekoGui_fmt {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ExtraCoreBean::TryParseLink(const QString& link)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace NekoGui_fmt
|
} // namespace NekoGui_fmt
|
||||||
@ -51,6 +51,7 @@ namespace NekoGui {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
profiles[id] = ent;
|
profiles[id] = ent;
|
||||||
|
if (ent->type == "extracore") extraCorePaths.insert(ent->ExtraCoreBean()->extraCorePath);
|
||||||
}
|
}
|
||||||
// Clear Corrupted profile
|
// Clear Corrupted profile
|
||||||
for (auto id: delProfile) {
|
for (auto id: delProfile) {
|
||||||
@ -227,6 +228,8 @@ namespace NekoGui {
|
|||||||
bean = new NekoGui_fmt::SSHBean(NekoGui_fmt::SSHBean());
|
bean = new NekoGui_fmt::SSHBean(NekoGui_fmt::SSHBean());
|
||||||
} else if (type == "custom") {
|
} else if (type == "custom") {
|
||||||
bean = new NekoGui_fmt::CustomBean();
|
bean = new NekoGui_fmt::CustomBean();
|
||||||
|
} else if (type == "extracore") {
|
||||||
|
bean = new NekoGui_fmt::ExtraCoreBean();
|
||||||
} else {
|
} else {
|
||||||
bean = new NekoGui_fmt::AbstractBean(-114514);
|
bean = new NekoGui_fmt::AbstractBean(-114514);
|
||||||
}
|
}
|
||||||
@ -342,6 +345,19 @@ namespace NekoGui {
|
|||||||
return profiles.count(id) ? profiles[id] : nullptr;
|
return profiles.count(id) ? profiles[id] : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList ProfileManager::GetExtraCorePaths() const {
|
||||||
|
return extraCorePaths.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProfileManager::AddExtraCorePath(const QString &path)
|
||||||
|
{
|
||||||
|
if (extraCorePaths.contains(path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
extraCorePaths.insert(path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Group
|
// Group
|
||||||
|
|
||||||
Group::Group() {
|
Group::Group() {
|
||||||
|
|||||||
@ -36,6 +36,11 @@ namespace NekoGui_sys {
|
|||||||
kill();
|
kill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (log.contains("Extra process exited unexpectedly"))
|
||||||
|
{
|
||||||
|
MW_show_log("Extra Core exited, stopping profile...");
|
||||||
|
MW_dialog_message("ExternalProcess", "Crashed");
|
||||||
|
}
|
||||||
if (logCounter.fetchAndAddRelaxed(log.count("\n")) > NekoGui::dataStore->max_log_line) return;
|
if (logCounter.fetchAndAddRelaxed(log.count("\n")) > NekoGui::dataStore->max_log_line) return;
|
||||||
MW_show_log(log);
|
MW_show_log(log);
|
||||||
});
|
});
|
||||||
@ -74,8 +79,8 @@ namespace NekoGui_sys {
|
|||||||
|
|
||||||
// Restart
|
// Restart
|
||||||
start_profile_when_core_is_up = NekoGui::dataStore->started_id;
|
start_profile_when_core_is_up = NekoGui::dataStore->started_id;
|
||||||
MW_show_log("[ERROR] " + QObject::tr("Core exited, restarting."));
|
MW_show_log("[Fatal] " + QObject::tr("Core exited, restarting."));
|
||||||
setTimeout([=] { Restart(); }, this, 1000);
|
setTimeout([=] { Restart(); }, this, 200);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -454,7 +454,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||||||
{
|
{
|
||||||
if (running != nullptr)
|
if (running != nullptr)
|
||||||
{
|
{
|
||||||
speedtest_current_group({running});
|
speedtest_current_group({}, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(ui->actionSpeedtest_Selected, &QAction::triggered, this, [=]()
|
connect(ui->actionSpeedtest_Selected, &QAction::triggered, this, [=]()
|
||||||
|
|||||||
@ -170,9 +170,9 @@ void MainWindow::url_test_current() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles)
|
void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::ProxyEntity>>& profiles, bool testCurrent)
|
||||||
{
|
{
|
||||||
if (profiles.isEmpty()) {
|
if (profiles.isEmpty() && !testCurrent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!speedtestRunning.tryLock()) {
|
if (!speedtestRunning.tryLock()) {
|
||||||
@ -180,22 +180,29 @@ void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::Pr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
runOnNewThread([this, profiles]() {
|
runOnNewThread([this, profiles, testCurrent]() {
|
||||||
auto buildObject = NekoGui::BuildTestConfig(profiles);
|
if (!testCurrent)
|
||||||
if (!buildObject->error.isEmpty()) {
|
{
|
||||||
MW_show_log(tr("Failed to build test config: ") + buildObject->error);
|
auto buildObject = NekoGui::BuildTestConfig(profiles);
|
||||||
speedtestRunning.unlock();
|
if (!buildObject->error.isEmpty()) {
|
||||||
return;
|
MW_show_log(tr("Failed to build test config: ") + buildObject->error);
|
||||||
}
|
speedtestRunning.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
stopSpeedtest.store(false);
|
stopSpeedtest.store(false);
|
||||||
for (const auto &entID: buildObject->fullConfigs.keys()) {
|
for (const auto &entID: buildObject->fullConfigs.keys()) {
|
||||||
auto configStr = buildObject->fullConfigs[entID];
|
auto configStr = buildObject->fullConfigs[entID];
|
||||||
runSpeedTest(configStr, true, {}, {}, entID);
|
runSpeedTest(configStr, true, false, {}, {}, entID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!buildObject->outboundTags.empty()) {
|
if (!buildObject->outboundTags.empty()) {
|
||||||
runSpeedTest(QJsonObject2QString(buildObject->coreConfig, false), false, buildObject->outboundTags, buildObject->tag2entID);
|
runSpeedTest(QJsonObject2QString(buildObject->coreConfig, false), false, false, buildObject->outboundTags, buildObject->tag2entID);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
stopSpeedtest.store(false);
|
||||||
|
runSpeedTest("", true, true, {}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
speedtestRunning.unlock();
|
speedtestRunning.unlock();
|
||||||
@ -206,7 +213,7 @@ void MainWindow::speedtest_current_group(const QList<std::shared_ptr<NekoGui::Pr
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID)
|
void MainWindow::runSpeedTest(const QString& config, bool useDefault, bool testCurrent, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID)
|
||||||
{
|
{
|
||||||
if (stopSpeedtest.load()) {
|
if (stopSpeedtest.load()) {
|
||||||
MW_show_log(tr("Profile speed test aborted"));
|
MW_show_log(tr("Profile speed test aborted"));
|
||||||
@ -222,6 +229,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStr
|
|||||||
req.set_use_default_outbound(useDefault);
|
req.set_use_default_outbound(useDefault);
|
||||||
req.set_test_download(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::DL);
|
req.set_test_download(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::DL);
|
||||||
req.set_test_upload(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::UL);
|
req.set_test_upload(speedtestConf == NekoGui::TestConfig::FULL || speedtestConf == NekoGui::TestConfig::UL);
|
||||||
|
req.set_test_current(testCurrent);
|
||||||
|
|
||||||
// loop query result
|
// loop query result
|
||||||
auto doneMu = new QMutex;
|
auto doneMu = new QMutex;
|
||||||
@ -240,7 +248,7 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStr
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto profile = NekoGui::profileManager->GetProfile(tag2entID[res.result().outbound_tag().c_str()]);
|
auto profile = testCurrent ? running : NekoGui::profileManager->GetProfile(tag2entID[res.result().outbound_tag().c_str()]);
|
||||||
if (profile == nullptr)
|
if (profile == nullptr)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -268,7 +276,8 @@ void MainWindow::runSpeedTest(const QString& config, bool useDefault, const QStr
|
|||||||
if (!rpcOK) return;
|
if (!rpcOK) return;
|
||||||
|
|
||||||
for (const auto &res: result.results()) {
|
for (const auto &res: result.results()) {
|
||||||
if (!tag2entID.empty()) {
|
if (testCurrent) entID = running ? running->id : -1;
|
||||||
|
else {
|
||||||
entID = tag2entID.count(QString(res.outbound_tag().c_str())) == 0 ? -1 : tag2entID[QString(res.outbound_tag().c_str())];
|
entID = tag2entID.count(QString(res.outbound_tag().c_str())) == 0 ? -1 : tag2entID[QString(res.outbound_tag().c_str())];
|
||||||
}
|
}
|
||||||
if (entID == -1) {
|
if (entID == -1) {
|
||||||
@ -361,6 +370,15 @@ void MainWindow::neko_start(int _id) {
|
|||||||
libcore::LoadConfigReq req;
|
libcore::LoadConfigReq req;
|
||||||
req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString());
|
req.set_core_config(QJsonObject2QString(result->coreConfig, true).toStdString());
|
||||||
req.set_disable_stats(NekoGui::dataStore->disable_traffic_stats);
|
req.set_disable_stats(NekoGui::dataStore->disable_traffic_stats);
|
||||||
|
if (ent->type == "extracore")
|
||||||
|
{
|
||||||
|
req.set_need_extra_process(true);
|
||||||
|
req.set_extra_process_path(result->extraCoreData->path.toStdString());
|
||||||
|
req.set_extra_process_args(result->extraCoreData->args.toStdString());
|
||||||
|
req.set_extra_process_conf(result->extraCoreData->config.toStdString());
|
||||||
|
req.set_extra_process_conf_dir(result->extraCoreData->configDir.toStdString());
|
||||||
|
req.set_extra_no_out(result->extraCoreData->noLog);
|
||||||
|
}
|
||||||
//
|
//
|
||||||
bool rpcOK;
|
bool rpcOK;
|
||||||
QString error = defaultClient->Start(&rpcOK, req);
|
QString error = defaultClient->Start(&rpcOK, req);
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "include/ui/profile/edit_wireguard.h"
|
#include "include/ui/profile/edit_wireguard.h"
|
||||||
#include "include/ui/profile/edit_ssh.h"
|
#include "include/ui/profile/edit_ssh.h"
|
||||||
#include "include/ui/profile/edit_custom.h"
|
#include "include/ui/profile/edit_custom.h"
|
||||||
|
#include "include/ui/profile/edit_extra_core.h"
|
||||||
|
|
||||||
#include "include/configs/proxy/includes.h"
|
#include "include/configs/proxy/includes.h"
|
||||||
#include "include/configs/proxy/Preset.hpp"
|
#include "include/configs/proxy/Preset.hpp"
|
||||||
@ -162,6 +163,7 @@ DialogEditProfile::DialogEditProfile(const QString &_type, int profileOrGroupId,
|
|||||||
LOAD_TYPE("ssh")
|
LOAD_TYPE("ssh")
|
||||||
ui->type->addItem(tr("Custom (%1 outbound)").arg(software_core_name), "internal");
|
ui->type->addItem(tr("Custom (%1 outbound)").arg(software_core_name), "internal");
|
||||||
ui->type->addItem(tr("Custom (%1 config)").arg(software_core_name), "internal-full");
|
ui->type->addItem(tr("Custom (%1 config)").arg(software_core_name), "internal-full");
|
||||||
|
ui->type->addItem(tr("Extra Core"), "extracore");
|
||||||
LOAD_TYPE("chain")
|
LOAD_TYPE("chain")
|
||||||
|
|
||||||
// type changed
|
// type changed
|
||||||
@ -229,6 +231,13 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
|||||||
customType = newEnt ? type : ent->CustomBean()->core;
|
customType = newEnt ? type : ent->CustomBean()->core;
|
||||||
if (customType != "custom") _innerWidget->preset_core = customType;
|
if (customType != "custom") _innerWidget->preset_core = customType;
|
||||||
type = "custom";
|
type = "custom";
|
||||||
|
ui->apply_to_group->hide();
|
||||||
|
} else if (type == "extracore")
|
||||||
|
{
|
||||||
|
auto _innerWidget = new EditExtraCore(this);
|
||||||
|
innerWidget = _innerWidget;
|
||||||
|
innerEditor = _innerWidget;
|
||||||
|
ui->apply_to_group->hide();
|
||||||
} else {
|
} else {
|
||||||
validType = false;
|
validType = false;
|
||||||
}
|
}
|
||||||
@ -244,7 +253,7 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// hide some widget
|
// hide some widget
|
||||||
auto showAddressPort = type != "chain" && customType != "internal" && customType != "internal-full";
|
auto showAddressPort = type != "chain" && customType != "internal" && customType != "internal-full" && type != "extracore";
|
||||||
ui->address->setVisible(showAddressPort);
|
ui->address->setVisible(showAddressPort);
|
||||||
ui->address_l->setVisible(showAddressPort);
|
ui->address_l->setVisible(showAddressPort);
|
||||||
ui->port->setVisible(showAddressPort);
|
ui->port->setVisible(showAddressPort);
|
||||||
@ -296,6 +305,10 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
|||||||
show_custom_outbound = false;
|
show_custom_outbound = false;
|
||||||
show_custom_config = false;
|
show_custom_config = false;
|
||||||
}
|
}
|
||||||
|
} else if (type == "extracore")
|
||||||
|
{
|
||||||
|
show_custom_outbound = false;
|
||||||
|
show_custom_config = false;
|
||||||
}
|
}
|
||||||
ui->custom_box->setVisible(show_custom_outbound);
|
ui->custom_box->setVisible(show_custom_outbound);
|
||||||
ui->custom_global_box->setVisible(show_custom_config);
|
ui->custom_global_box->setVisible(show_custom_config);
|
||||||
@ -309,7 +322,7 @@ void DialogEditProfile::typeSelected(const QString &newType) {
|
|||||||
delete old;
|
delete old;
|
||||||
|
|
||||||
// 左边 bean inner editor
|
// 左边 bean inner editor
|
||||||
innerEditor->get_edit_dialog = [&]() { return (QWidget *) this; };
|
innerEditor->get_edit_dialog = [&]() { return static_cast<QWidget*>(this); };
|
||||||
innerEditor->get_edit_text_name = [&]() { return ui->name->text(); };
|
innerEditor->get_edit_text_name = [&]() { return ui->name->text(); };
|
||||||
innerEditor->get_edit_text_serverAddress = [&]() { return ui->address->text(); };
|
innerEditor->get_edit_text_serverAddress = [&]() { return ui->address->text(); };
|
||||||
innerEditor->get_edit_text_serverPort = [&]() { return ui->port->text(); };
|
innerEditor->get_edit_text_serverPort = [&]() { return ui->port->text(); };
|
||||||
|
|||||||
@ -50,7 +50,7 @@ void EditChain::on_select_profile_clicked() {
|
|||||||
|
|
||||||
void EditChain::AddProfileToListIfExist(int profileId) {
|
void EditChain::AddProfileToListIfExist(int profileId) {
|
||||||
auto _ent = NekoGui::profileManager->GetProfile(profileId);
|
auto _ent = NekoGui::profileManager->GetProfile(profileId);
|
||||||
if (_ent != nullptr && _ent->type != "chain") {
|
if (_ent != nullptr && _ent->type != "chain" && _ent->type != "extracore") {
|
||||||
auto wI = new QListWidgetItem();
|
auto wI = new QListWidgetItem();
|
||||||
wI->setData(114514, profileId);
|
wI->setData(114514, profileId);
|
||||||
auto w = new ProxyItem(this, _ent, wI);
|
auto w = new ProxyItem(this, _ent, wI);
|
||||||
@ -69,7 +69,7 @@ void EditChain::AddProfileToListIfExist(int profileId) {
|
|||||||
|
|
||||||
void EditChain::ReplaceProfile(ProxyItem *w, int profileId) {
|
void EditChain::ReplaceProfile(ProxyItem *w, int profileId) {
|
||||||
auto _ent = NekoGui::profileManager->GetProfile(profileId);
|
auto _ent = NekoGui::profileManager->GetProfile(profileId);
|
||||||
if (_ent != nullptr && _ent->type != "chain") {
|
if (_ent != nullptr && _ent->type != "chain" && _ent->type != "extracore") {
|
||||||
w->item->setData(114514, profileId);
|
w->item->setData(114514, profileId);
|
||||||
w->ent = _ent;
|
w->ent = _ent;
|
||||||
w->refresh_data();
|
w->refresh_data();
|
||||||
|
|||||||
61
src/ui/profile/edit_extra_core.cpp
Normal file
61
src/ui/profile/edit_extra_core.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include "include/ui/profile/edit_extra_core.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "include/configs/proxy/ExtraCore.h"
|
||||||
|
#include "include/configs/proxy/Preset.hpp"
|
||||||
|
#include "include/ui/profile/dialog_edit_profile.h"
|
||||||
|
|
||||||
|
EditExtraCore::EditExtraCore(QWidget *parent) : QWidget(parent),
|
||||||
|
ui(new Ui::EditExtraCore) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
EditExtraCore::~EditExtraCore() {
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditExtraCore::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
|
||||||
|
this->ent = _ent;
|
||||||
|
|
||||||
|
auto bean = ent->ExtraCoreBean();
|
||||||
|
ui->socks_address->setText(bean->socksAddress);
|
||||||
|
ui->socks_port->setValidator(new QIntValidator(1, 65534));
|
||||||
|
ui->socks_port->setText(Int2String(bean->socksPort));
|
||||||
|
ui->config->setPlainText(bean->extraCoreConf);
|
||||||
|
ui->args->setText(bean->extraCoreArgs);
|
||||||
|
ui->path_combo->addItems(NekoGui::profileManager->GetExtraCorePaths());
|
||||||
|
ui->path_combo->setCurrentText(bean->extraCorePath);
|
||||||
|
|
||||||
|
connect(ui->path_button, &QPushButton::pressed, this, [=]
|
||||||
|
{
|
||||||
|
auto f = QFileDialog::getOpenFileName();
|
||||||
|
if (f.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (NekoGui::profileManager->AddExtraCorePath(f)) ui->path_combo->addItem(f);
|
||||||
|
ui->path_combo->setCurrentText(f);
|
||||||
|
ui->path_combo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||||
|
adjustSize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditExtraCore::onEnd() {
|
||||||
|
auto bean = ent->ExtraCoreBean();
|
||||||
|
if (!ui->args->text().contains("%s"))
|
||||||
|
{
|
||||||
|
runOnUiThread([=]
|
||||||
|
{
|
||||||
|
MessageBoxWarning("Invalid args", "Args should have a %s as the placeholder for config file path.");
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bean->socksAddress = ui->socks_address->text();
|
||||||
|
bean->socksPort = ui->socks_port->text().toInt();
|
||||||
|
bean->extraCoreConf = ui->config->toPlainText();
|
||||||
|
bean->extraCorePath = ui->path_combo->currentText();
|
||||||
|
bean->extraCoreArgs = ui->args->text();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user