Add connections page Add search in connections, requests Add keyword search in connections, requests, logs Add basic viewing editing capabilities Optimize update profile
434 lines
9.6 KiB
Go
434 lines
9.6 KiB
Go
package main
|
|
|
|
import "C"
|
|
import (
|
|
bridge "core/dart-bridge"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/metacubex/mihomo/adapter"
|
|
"github.com/metacubex/mihomo/adapter/outboundgroup"
|
|
"github.com/metacubex/mihomo/adapter/provider"
|
|
"github.com/metacubex/mihomo/common/structure"
|
|
"github.com/metacubex/mihomo/common/utils"
|
|
"github.com/metacubex/mihomo/component/mmdb"
|
|
"github.com/metacubex/mihomo/config"
|
|
"github.com/metacubex/mihomo/constant"
|
|
cp "github.com/metacubex/mihomo/constant/provider"
|
|
"github.com/metacubex/mihomo/hub/executor"
|
|
"github.com/metacubex/mihomo/log"
|
|
rp "github.com/metacubex/mihomo/rules/provider"
|
|
"github.com/metacubex/mihomo/tunnel"
|
|
"github.com/metacubex/mihomo/tunnel/statistic"
|
|
"golang.org/x/net/context"
|
|
"os"
|
|
"runtime"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
var currentConfig = config.DefaultRawConfig()
|
|
|
|
var isInit = false
|
|
|
|
//export initClash
|
|
func initClash(homeDirStr *C.char) bool {
|
|
if !isInit {
|
|
constant.SetHomeDir(C.GoString(homeDirStr))
|
|
isInit = true
|
|
}
|
|
return isInit
|
|
}
|
|
|
|
//export getIsInit
|
|
func getIsInit() bool {
|
|
return isInit
|
|
}
|
|
|
|
//export restartClash
|
|
func restartClash() bool {
|
|
execPath, _ := os.Executable()
|
|
go restartExecutable(execPath)
|
|
return true
|
|
}
|
|
|
|
//export shutdownClash
|
|
func shutdownClash() bool {
|
|
executor.Shutdown()
|
|
runtime.GC()
|
|
isInit = false
|
|
currentConfig = nil
|
|
return true
|
|
}
|
|
|
|
//export forceGc
|
|
func forceGc() {
|
|
go func() {
|
|
log.Infoln("[APP] request force GC")
|
|
runtime.GC()
|
|
}()
|
|
}
|
|
|
|
//export validateConfig
|
|
func validateConfig(s *C.char, port C.longlong) {
|
|
i := int64(port)
|
|
go func() {
|
|
bytes := []byte(C.GoString(s))
|
|
_, err := config.UnmarshalRawConfig(bytes)
|
|
if err != nil {
|
|
bridge.SendToPort(i, err.Error())
|
|
return
|
|
}
|
|
bridge.SendToPort(i, "")
|
|
}()
|
|
}
|
|
|
|
//export updateConfig
|
|
func updateConfig(s *C.char, port C.longlong) {
|
|
i := int64(port)
|
|
go func() {
|
|
paramsString := C.GoString(s)
|
|
var params = &GenerateConfigParams{}
|
|
err := json.Unmarshal([]byte(paramsString), params)
|
|
if err != nil {
|
|
bridge.SendToPort(i, err.Error())
|
|
return
|
|
}
|
|
prof := decorationConfig(params.ProfilePath, *params.Config, *params.IsCompatible)
|
|
currentConfig = prof
|
|
if *params.IsPatch {
|
|
applyConfig(true)
|
|
} else {
|
|
applyConfig(false)
|
|
}
|
|
bridge.SendToPort(i, "")
|
|
}()
|
|
}
|
|
|
|
//export clearEffect
|
|
func clearEffect(s *C.char) {
|
|
path := C.GoString(s)
|
|
go func() {
|
|
rawCfg := getRawConfigWithPath(&path)
|
|
for _, mapping := range rawCfg.RuleProvider {
|
|
schema := &ruleProviderSchema{}
|
|
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
|
|
if err := decoder.Decode(mapping, schema); err != nil {
|
|
return
|
|
}
|
|
if schema.Type == "http" {
|
|
_ = removeFile(constant.Path.Resolve(schema.Path))
|
|
}
|
|
}
|
|
for _, mapping := range rawCfg.ProxyProvider {
|
|
schema := &proxyProviderSchema{
|
|
HealthCheck: healthCheckSchema{
|
|
Lazy: true,
|
|
},
|
|
}
|
|
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
|
|
if err := decoder.Decode(mapping, schema); err != nil {
|
|
return
|
|
}
|
|
if schema.Type == "http" {
|
|
_ = removeFile(constant.Path.Resolve(schema.Path))
|
|
}
|
|
}
|
|
_ = removeFile(path)
|
|
}()
|
|
}
|
|
|
|
//export getProxies
|
|
func getProxies() *C.char {
|
|
data, err := json.Marshal(tunnel.ProxiesWithProviders())
|
|
if err != nil {
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export changeProxy
|
|
func changeProxy(s *C.char) bool {
|
|
go func() {
|
|
paramsString := C.GoString(s)
|
|
var params = &ChangeProxyParams{}
|
|
err := json.Unmarshal([]byte(paramsString), params)
|
|
if err != nil {
|
|
log.Infoln("Unmarshal ChangeProxyParams %v", err)
|
|
}
|
|
proxies := tunnel.ProxiesWithProviders()
|
|
proxy := proxies[*params.GroupName]
|
|
if proxy == nil {
|
|
return
|
|
}
|
|
log.Infoln("change proxy %s", proxy.Name())
|
|
adapterProxy := proxy.(*adapter.Proxy)
|
|
selector, ok := adapterProxy.ProxyAdapter.(*outboundgroup.Selector)
|
|
if !ok {
|
|
return
|
|
}
|
|
if err := selector.Set(*params.ProxyName); err != nil {
|
|
return
|
|
}
|
|
}()
|
|
return true
|
|
}
|
|
|
|
//export getTraffic
|
|
func getTraffic() *C.char {
|
|
up, down := statistic.DefaultManager.Now()
|
|
traffic := map[string]int64{
|
|
"up": up,
|
|
"down": down,
|
|
}
|
|
data, err := json.Marshal(traffic)
|
|
if err != nil {
|
|
fmt.Println("Error:", err)
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export getTotalTraffic
|
|
func getTotalTraffic() *C.char {
|
|
up, down := statistic.DefaultManager.Total()
|
|
traffic := map[string]int64{
|
|
"up": up,
|
|
"down": down,
|
|
}
|
|
data, err := json.Marshal(traffic)
|
|
if err != nil {
|
|
fmt.Println("Error:", err)
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export resetTraffic
|
|
func resetTraffic() {
|
|
statistic.DefaultManager.ResetStatistic()
|
|
}
|
|
|
|
//export asyncTestDelay
|
|
func asyncTestDelay(s *C.char, port C.longlong) {
|
|
i := int64(port)
|
|
go func() {
|
|
paramsString := C.GoString(s)
|
|
var params = &TestDelayParams{}
|
|
err := json.Unmarshal([]byte(paramsString), params)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
expectedStatus, err := utils.NewUnsignedRanges[uint16]("")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(params.Timeout))
|
|
defer cancel()
|
|
|
|
proxies := tunnel.ProxiesWithProviders()
|
|
proxy := proxies[params.ProxyName]
|
|
|
|
delayData := &Delay{
|
|
Name: params.ProxyName,
|
|
}
|
|
|
|
if proxy == nil {
|
|
delayData.Value = -1
|
|
data, _ := json.Marshal(delayData)
|
|
bridge.SendToPort(i, string(data))
|
|
return
|
|
}
|
|
|
|
delay, err := proxy.URLTest(ctx, constant.DefaultTestURL, expectedStatus)
|
|
if err != nil || delay == 0 {
|
|
delayData.Value = -1
|
|
data, _ := json.Marshal(delayData)
|
|
bridge.SendToPort(i, string(data))
|
|
return
|
|
}
|
|
|
|
delayData.Value = int32(delay)
|
|
data, _ := json.Marshal(delayData)
|
|
bridge.SendToPort(i, string(data))
|
|
return
|
|
}()
|
|
}
|
|
|
|
//export getVersionInfo
|
|
func getVersionInfo() *C.char {
|
|
versionInfo := map[string]string{
|
|
"clashName": constant.Name,
|
|
"version": "1.18.5",
|
|
}
|
|
data, err := json.Marshal(versionInfo)
|
|
if err != nil {
|
|
fmt.Println("Error:", err)
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export getConnections
|
|
func getConnections() *C.char {
|
|
snapshot := statistic.DefaultManager.Snapshot()
|
|
data, err := json.Marshal(snapshot)
|
|
if err != nil {
|
|
fmt.Println("Error:", err)
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export closeConnections
|
|
func closeConnections() bool {
|
|
statistic.DefaultManager.Range(func(c statistic.Tracker) bool {
|
|
err := c.Close()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return true
|
|
}
|
|
|
|
//export closeConnection
|
|
func closeConnection(id *C.char) bool {
|
|
connectionId := C.GoString(id)
|
|
|
|
err := statistic.DefaultManager.Get(connectionId).Close()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
//export getProviders
|
|
func getProviders() *C.char {
|
|
data, err := json.Marshal(tunnel.Providers())
|
|
if err != nil {
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export getProvider
|
|
func getProvider(name *C.char) *C.char {
|
|
providerName := C.GoString(name)
|
|
providers := tunnel.Providers()
|
|
data, err := json.Marshal(providers[providerName])
|
|
if err != nil {
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export getExternalProviders
|
|
func getExternalProviders() *C.char {
|
|
externalProviders := make([]ExternalProvider, 0)
|
|
providers := tunnel.Providers()
|
|
for n, p := range providers {
|
|
if p.VehicleType() != cp.Compatible {
|
|
p := p.(*provider.ProxySetProvider)
|
|
externalProviders = append(externalProviders, ExternalProvider{
|
|
Name: n,
|
|
Type: p.Type().String(),
|
|
VehicleType: p.VehicleType().String(),
|
|
UpdateAt: p.UpdatedAt,
|
|
})
|
|
}
|
|
}
|
|
for n, p := range tunnel.RuleProviders() {
|
|
if p.VehicleType() != cp.Compatible {
|
|
p := p.(*rp.RuleSetProvider)
|
|
externalProviders = append(externalProviders, ExternalProvider{
|
|
Name: n,
|
|
Type: p.Type().String(),
|
|
VehicleType: p.VehicleType().String(),
|
|
UpdateAt: p.UpdatedAt,
|
|
})
|
|
}
|
|
}
|
|
data, err := json.Marshal(externalProviders)
|
|
if err != nil {
|
|
return C.CString("")
|
|
}
|
|
return C.CString(string(data))
|
|
}
|
|
|
|
//export updateExternalProvider
|
|
func updateExternalProvider(providerName *C.char, providerType *C.char, port C.longlong) {
|
|
i := int64(port)
|
|
go func() {
|
|
providerNameString := C.GoString(providerName)
|
|
providerTypeString := C.GoString(providerType)
|
|
|
|
switch providerTypeString {
|
|
case "Proxy":
|
|
providers := tunnel.Providers()
|
|
err := providers[providerNameString].Update()
|
|
if err != nil {
|
|
bridge.SendToPort(i, err.Error())
|
|
return
|
|
}
|
|
case "Rule":
|
|
providers := tunnel.RuleProviders()
|
|
err := providers[providerNameString].Update()
|
|
if err != nil {
|
|
bridge.SendToPort(i, err.Error())
|
|
return
|
|
}
|
|
case "GeoIp":
|
|
err := mmdb.DownloadMMDB(constant.Path.Resolve(providerNameString))
|
|
if err != nil {
|
|
bridge.SendToPort(i, err.Error())
|
|
return
|
|
}
|
|
case "GeoSite":
|
|
err := mmdb.DownloadGeoSite(constant.Path.Resolve(providerNameString))
|
|
if err != nil {
|
|
bridge.SendToPort(i, err.Error())
|
|
return
|
|
}
|
|
case "ASN":
|
|
err := mmdb.DownloadASN(constant.Path.Resolve(providerNameString))
|
|
if err != nil {
|
|
bridge.SendToPort(i, err.Error())
|
|
return
|
|
}
|
|
}
|
|
bridge.SendToPort(i, "")
|
|
}()
|
|
}
|
|
|
|
//export initNativeApiBridge
|
|
func initNativeApiBridge(api unsafe.Pointer, port C.longlong) {
|
|
bridge.InitDartApi(api)
|
|
i := int64(port)
|
|
bridge.Port = &i
|
|
}
|
|
|
|
func init() {
|
|
provider.HealthcheckHook = func(name string, delay uint16) {
|
|
delayData := &Delay{
|
|
Name: name,
|
|
}
|
|
if delay == 0 {
|
|
delayData.Value = -1
|
|
} else {
|
|
delayData.Value = int32(delay)
|
|
}
|
|
bridge.SendMessage(bridge.Message{
|
|
Type: bridge.Delay,
|
|
Data: delayData,
|
|
})
|
|
}
|
|
statistic.DefaultRequestNotify = func(c statistic.Tracker) {
|
|
bridge.SendMessage(bridge.Message{
|
|
Type: bridge.Request,
|
|
Data: c,
|
|
})
|
|
}
|
|
}
|