mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 13:42:51 +08:00
Add Connection list &&
Improve QTable performace
This commit is contained in:
parent
76bddde9a4
commit
ccee10b972
@ -206,6 +206,8 @@ set(PROJECT_SOURCES
|
||||
src/sys/AutoRun.cpp
|
||||
src/sys/Process.cpp
|
||||
include/ui/mainwindow_interface.h
|
||||
include/stats/connections/connectionLister.hpp
|
||||
src/stats/connectionLister/connectionLister.cpp
|
||||
)
|
||||
|
||||
# Qt exe
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/sagernet/sing-box/common/settings"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi"
|
||||
"github.com/sagernet/sing/common/metadata"
|
||||
"nekobox_core/internal/boxbox"
|
||||
grpc_server "nekobox_core/server"
|
||||
@ -148,7 +149,6 @@ func (s *server) StopTest(ctx context.Context, in *gen.EmptyReq) (*gen.EmptyResp
|
||||
|
||||
func (s *server) QueryStats(ctx context.Context, in *gen.QueryStatsReq) (out *gen.QueryStatsResp, _ error) {
|
||||
out = &gen.QueryStatsResp{}
|
||||
|
||||
if instance != nil && instance.Router().V2RayServer() != nil {
|
||||
if ss, ok := instance.Router().V2RayServer().(*boxapi.SbV2rayServer); ok {
|
||||
out.Traffic = ss.QueryStats(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", in.Tag, in.Direct))
|
||||
@ -159,10 +159,13 @@ func (s *server) QueryStats(ctx context.Context, in *gen.QueryStatsReq) (out *ge
|
||||
}
|
||||
|
||||
func (s *server) ListConnections(ctx context.Context, in *gen.EmptyReq) (*gen.ListConnectionsResp, error) {
|
||||
if instance == nil {
|
||||
return &gen.ListConnectionsResp{}, nil
|
||||
}
|
||||
if instance.Router().ClashServer() == nil {
|
||||
return nil, errors.New("no clash server found")
|
||||
}
|
||||
clash, ok := instance.Router().ClashServer().(*boxapi.SbClashServer)
|
||||
clash, ok := instance.Router().ClashServer().(*clashapi.Server)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid state, should not be here")
|
||||
}
|
||||
@ -170,6 +173,11 @@ func (s *server) ListConnections(ctx context.Context, in *gen.EmptyReq) (*gen.Li
|
||||
|
||||
res := make([]*gen.ConnectionMetaData, 0)
|
||||
for _, c := range connections {
|
||||
process := ""
|
||||
if c.Metadata.ProcessInfo != nil {
|
||||
spl := strings.Split(c.Metadata.ProcessInfo.ProcessPath, string(os.PathSeparator))
|
||||
process = spl[len(spl)-1]
|
||||
}
|
||||
r := &gen.ConnectionMetaData{
|
||||
Id: c.ID.String(),
|
||||
CreatedAt: c.CreatedAt.UnixMilli(),
|
||||
@ -180,6 +188,7 @@ func (s *server) ListConnections(ctx context.Context, in *gen.EmptyReq) (*gen.Li
|
||||
Dest: c.Metadata.Destination.String(),
|
||||
Protocol: c.Metadata.Protocol,
|
||||
Domain: c.Metadata.Domain,
|
||||
Process: process,
|
||||
}
|
||||
res = append(res, r)
|
||||
}
|
||||
|
||||
@ -1,359 +0,0 @@
|
||||
package boxapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/urltest"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/experimental"
|
||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/json"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/filemanager"
|
||||
"github.com/sagernet/ws"
|
||||
"github.com/sagernet/ws/wsutil"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
func init() {
|
||||
experimental.RegisterClashServerConstructor(NewServer)
|
||||
}
|
||||
|
||||
var _ adapter.ClashServer = (*SbClashServer)(nil)
|
||||
|
||||
type SbClashServer struct {
|
||||
ctx context.Context
|
||||
router adapter.Router
|
||||
logger log.Logger
|
||||
httpServer *http.Server
|
||||
trafficManager *trafficontrol.Manager
|
||||
urlTestHistory *urltest.HistoryStorage
|
||||
mode string
|
||||
modeList []string
|
||||
modeUpdateHook chan<- struct{}
|
||||
|
||||
externalController bool
|
||||
externalUI string
|
||||
externalUIDownloadURL string
|
||||
externalUIDownloadDetour string
|
||||
}
|
||||
|
||||
func NewServer(ctx context.Context, router adapter.Router, logFactory log.ObservableFactory, options option.ClashAPIOptions) (adapter.ClashServer, error) {
|
||||
trafficManager := trafficontrol.NewManager()
|
||||
chiRouter := chi.NewRouter()
|
||||
server := &SbClashServer{
|
||||
ctx: ctx,
|
||||
router: router,
|
||||
logger: logFactory.NewLogger("clash-api"),
|
||||
httpServer: &http.Server{
|
||||
Addr: options.ExternalController,
|
||||
Handler: chiRouter,
|
||||
},
|
||||
trafficManager: trafficManager,
|
||||
modeList: options.ModeList,
|
||||
externalController: options.ExternalController != "",
|
||||
externalUIDownloadURL: options.ExternalUIDownloadURL,
|
||||
externalUIDownloadDetour: options.ExternalUIDownloadDetour,
|
||||
}
|
||||
server.urlTestHistory = service.PtrFromContext[urltest.HistoryStorage](ctx)
|
||||
if server.urlTestHistory == nil {
|
||||
server.urlTestHistory = urltest.NewHistoryStorage()
|
||||
}
|
||||
defaultMode := "Rule"
|
||||
if options.DefaultMode != "" {
|
||||
defaultMode = options.DefaultMode
|
||||
}
|
||||
if !common.Contains(server.modeList, defaultMode) {
|
||||
server.modeList = append([]string{defaultMode}, server.modeList...)
|
||||
}
|
||||
server.mode = defaultMode
|
||||
//goland:noinspection GoDeprecation
|
||||
//nolint:staticcheck
|
||||
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.CacheFile != "" || options.CacheID != "" {
|
||||
return nil, E.New("cache_file and related fields in Clash API is deprecated in sing-box 1.8.0, use experimental.cache_file instead.")
|
||||
}
|
||||
allowedOrigins := options.AccessControlAllowOrigin
|
||||
if len(allowedOrigins) == 0 {
|
||||
allowedOrigins = []string{"*"}
|
||||
}
|
||||
if options.ExternalUI != "" {
|
||||
server.externalUI = filemanager.BasePath(ctx, os.ExpandEnv(options.ExternalUI))
|
||||
chiRouter.Group(func(r chi.Router) {
|
||||
r.Get("/ui", http.RedirectHandler("/ui/", http.StatusMovedPermanently).ServeHTTP)
|
||||
r.Handle("/ui/*", http.StripPrefix("/ui/", http.FileServer(http.Dir(server.externalUI))))
|
||||
})
|
||||
}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func (s *SbClashServer) PreStart() error {
|
||||
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||
if cacheFile != nil {
|
||||
mode := cacheFile.LoadMode()
|
||||
if common.Any(s.modeList, func(it string) bool {
|
||||
return strings.EqualFold(it, mode)
|
||||
}) {
|
||||
s.mode = mode
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SbClashServer) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SbClashServer) Close() error {
|
||||
return common.Close(
|
||||
common.PtrOrNil(s.httpServer),
|
||||
s.trafficManager,
|
||||
s.urlTestHistory,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *SbClashServer) Mode() string {
|
||||
return s.mode
|
||||
}
|
||||
|
||||
func (s *SbClashServer) ModeList() []string {
|
||||
return s.modeList
|
||||
}
|
||||
|
||||
func (s *SbClashServer) SetModeUpdateHook(hook chan<- struct{}) {
|
||||
s.modeUpdateHook = hook
|
||||
}
|
||||
|
||||
func (s *SbClashServer) SetMode(newMode string) {
|
||||
if !common.Contains(s.modeList, newMode) {
|
||||
newMode = common.Find(s.modeList, func(it string) bool {
|
||||
return strings.EqualFold(it, newMode)
|
||||
})
|
||||
}
|
||||
if !common.Contains(s.modeList, newMode) {
|
||||
return
|
||||
}
|
||||
if newMode == s.mode {
|
||||
return
|
||||
}
|
||||
s.mode = newMode
|
||||
if s.modeUpdateHook != nil {
|
||||
select {
|
||||
case s.modeUpdateHook <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
s.router.ClearDNSCache()
|
||||
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||
if cacheFile != nil {
|
||||
err := cacheFile.StoreMode(newMode)
|
||||
if err != nil {
|
||||
s.logger.Error(E.Cause(err, "save mode"))
|
||||
}
|
||||
}
|
||||
s.logger.Info("updated mode: ", newMode)
|
||||
}
|
||||
|
||||
func (s *SbClashServer) HistoryStorage() *urltest.HistoryStorage {
|
||||
return s.urlTestHistory
|
||||
}
|
||||
|
||||
func (s *SbClashServer) TrafficManager() *trafficontrol.Manager {
|
||||
return s.trafficManager
|
||||
}
|
||||
|
||||
func (s *SbClashServer) RoutedConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, matchedRule adapter.Rule) (net.Conn, adapter.Tracker) {
|
||||
tracker := trafficontrol.NewTCPTracker(conn, s.trafficManager, metadata, s.router, matchedRule)
|
||||
return tracker, tracker
|
||||
}
|
||||
|
||||
func (s *SbClashServer) RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, matchedRule adapter.Rule) (N.PacketConn, adapter.Tracker) {
|
||||
tracker := trafficontrol.NewUDPTracker(conn, s.trafficManager, metadata, s.router, matchedRule)
|
||||
return tracker, tracker
|
||||
}
|
||||
|
||||
func authentication(serverSecret string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
if serverSecret == "" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Browser websocket not support custom header
|
||||
if r.Header.Get("Upgrade") == "websocket" && r.URL.Query().Get("token") != "" {
|
||||
token := r.URL.Query().Get("token")
|
||||
if token != serverSecret {
|
||||
render.Status(r, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
header := r.Header.Get("Authorization")
|
||||
bearer, token, found := strings.Cut(header, " ")
|
||||
|
||||
hasInvalidHeader := bearer != "Bearer"
|
||||
hasInvalidSecret := !found || token != serverSecret
|
||||
if hasInvalidHeader || hasInvalidSecret {
|
||||
render.Status(r, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
}
|
||||
|
||||
func hello(redirect bool) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if !redirect || contentType == "application/json" {
|
||||
render.JSON(w, r, render.M{"hello": "clash"})
|
||||
} else {
|
||||
http.Redirect(w, r, "/ui/", http.StatusTemporaryRedirect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Traffic struct {
|
||||
Up int64 `json:"up"`
|
||||
Down int64 `json:"down"`
|
||||
}
|
||||
|
||||
func traffic(trafficManager *trafficontrol.Manager) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var conn net.Conn
|
||||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
var err error
|
||||
conn, _, _, err = ws.UpgradeHTTP(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
}
|
||||
|
||||
if conn == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
render.Status(r, http.StatusOK)
|
||||
}
|
||||
|
||||
tick := time.NewTicker(time.Second)
|
||||
defer tick.Stop()
|
||||
buf := &bytes.Buffer{}
|
||||
uploadTotal, downloadTotal := trafficManager.Total()
|
||||
for range tick.C {
|
||||
buf.Reset()
|
||||
uploadTotalNew, downloadTotalNew := trafficManager.Total()
|
||||
err := json.NewEncoder(buf).Encode(Traffic{
|
||||
Up: uploadTotalNew - uploadTotal,
|
||||
Down: downloadTotalNew - downloadTotal,
|
||||
})
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if conn == nil {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsutil.WriteServerText(conn, buf.Bytes())
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
uploadTotal = uploadTotalNew
|
||||
downloadTotal = downloadTotalNew
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
Type string `json:"type"`
|
||||
Payload string `json:"payload"`
|
||||
}
|
||||
|
||||
func getLogs(logFactory log.ObservableFactory) func(w http.ResponseWriter, r *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
levelText := r.URL.Query().Get("level")
|
||||
if levelText == "" {
|
||||
levelText = "info"
|
||||
}
|
||||
|
||||
level, ok := log.ParseLevel(levelText)
|
||||
if ok != nil {
|
||||
render.Status(r, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
subscription, done, err := logFactory.Subscribe()
|
||||
if err != nil {
|
||||
render.Status(r, http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
defer logFactory.UnSubscribe(subscription)
|
||||
|
||||
var conn net.Conn
|
||||
if r.Header.Get("Upgrade") == "websocket" {
|
||||
conn, _, _, err = ws.UpgradeHTTP(r, w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
}
|
||||
|
||||
if conn == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
render.Status(r, http.StatusOK)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
var logEntry log.Entry
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case logEntry = <-subscription:
|
||||
}
|
||||
if logEntry.Level > level {
|
||||
continue
|
||||
}
|
||||
buf.Reset()
|
||||
err = json.NewEncoder(buf).Encode(Log{
|
||||
Type: log.FormatLevel(logEntry.Level),
|
||||
Payload: logEntry.Message,
|
||||
})
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if conn == nil {
|
||||
_, err = w.Write(buf.Bytes())
|
||||
w.(http.Flusher).Flush()
|
||||
} else {
|
||||
err = wsutil.WriteServerText(conn, buf.Bytes())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func version(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, render.M{"version": "sing-box " + C.Version, "premium": true, "meta": true})
|
||||
}
|
||||
@ -762,6 +762,7 @@ type ConnectionMetaData struct {
|
||||
Dest string `protobuf:"bytes,7,opt,name=dest,proto3" json:"dest,omitempty"`
|
||||
Protocol string `protobuf:"bytes,8,opt,name=protocol,proto3" json:"protocol,omitempty"`
|
||||
Domain string `protobuf:"bytes,9,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||
Process string `protobuf:"bytes,10,opt,name=process,proto3" json:"process,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ConnectionMetaData) Reset() {
|
||||
@ -859,6 +860,13 @@ func (x *ConnectionMetaData) GetDomain() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ConnectionMetaData) GetProcess() string {
|
||||
if x != nil {
|
||||
return x.Process
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetGeoIPListResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -1358,7 +1366,7 @@ var file_libcore_proto_rawDesc = []byte{
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e,
|
||||
0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xf5, 0x01, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x8f, 0x02, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x6e,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0e,
|
||||
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d,
|
||||
0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01,
|
||||
@ -1373,104 +1381,106 @@ var file_libcore_proto_rawDesc = []byte{
|
||||
0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69,
|
||||
0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22,
|
||||
0x2c, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73,
|
||||
0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x2e, 0x0a,
|
||||
0x16, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x24, 0x0a,
|
||||
0x0e, 0x47, 0x65, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70,
|
||||
0x61, 0x74, 0x68, 0x22, 0x42, 0x0a, 0x18, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65,
|
||||
0x6f, 0x49, 0x50, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x07, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x22, 0x2c, 0x0a, 0x14, 0x47, 0x65, 0x74,
|
||||
0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x2e, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x47, 0x65,
|
||||
0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x24, 0x0a, 0x0e, 0x47, 0x65, 0x6f, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x42, 0x0a,
|
||||
0x18, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x54, 0x6f, 0x53,
|
||||
0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65,
|
||||
0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x22, 0x44, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x53,
|
||||
0x69, 0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69,
|
||||
0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x44, 0x0a, 0x1a, 0x43, 0x6f, 0x6d, 0x70, 0x69,
|
||||
0x6c, 0x65, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x49, 0x0a,
|
||||
0x15, 0x53, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18,
|
||||
0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x60, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x53,
|
||||
0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05,
|
||||
0x63, 0x6c, 0x65, 0x61, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73,
|
||||
0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12,
|
||||
0x19, 0x0a, 0x08, 0x73, 0x65, 0x74, 0x5f, 0x64, 0x68, 0x63, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x07, 0x73, 0x65, 0x74, 0x44, 0x68, 0x63, 0x70, 0x22, 0x49, 0x0a, 0x14, 0x47, 0x65,
|
||||
0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x17, 0x0a, 0x07,
|
||||
0x69, 0x73, 0x5f, 0x64, 0x68, 0x63, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69,
|
||||
0x73, 0x44, 0x68, 0x63, 0x70, 0x2a, 0x27, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x10, 0x00,
|
||||
0x12, 0x0c, 0x0a, 0x08, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x10, 0x01, 0x32, 0xc1,
|
||||
0x07, 0x0a, 0x0e, 0x4c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x12, 0x2f, 0x0a, 0x04, 0x45, 0x78, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c,
|
||||
0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70,
|
||||
0x22, 0x00, 0x12, 0x33, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x6c,
|
||||
0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71,
|
||||
0x1a, 0x13, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74,
|
||||
0x65, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74,
|
||||
0x12, 0x16, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x43,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x2f,
|
||||
0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12,
|
||||
0x2d, 0x0a, 0x04, 0x54, 0x65, 0x73, 0x74, 0x12, 0x10, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x31,
|
||||
0x0a, 0x08, 0x53, 0x74, 0x6f, 0x70, 0x54, 0x65, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e,
|
||||
0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73,
|
||||
0x70, 0x12, 0x3f, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12,
|
||||
0x16, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53,
|
||||
0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||
0x22, 0x00, 0x12, 0x44, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x47,
|
||||
0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x47, 0x65, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x47,
|
||||
0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x4a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69,
|
||||
0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x49, 0x0a, 0x15, 0x53, 0x65, 0x74, 0x53, 0x79,
|
||||
0x73, 0x74, 0x65, 0x6d, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72,
|
||||
0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
|
||||
0x73, 0x73, 0x22, 0x60, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44,
|
||||
0x4e, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x65,
|
||||
0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
||||
0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x65, 0x74,
|
||||
0x5f, 0x64, 0x68, 0x63, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x65, 0x74,
|
||||
0x44, 0x68, 0x63, 0x70, 0x22, 0x49, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65,
|
||||
0x6d, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x64, 0x68, 0x63,
|
||||
0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x44, 0x68, 0x63, 0x70, 0x2a,
|
||||
0x27, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||
0x09, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x6f,
|
||||
0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x10, 0x01, 0x32, 0xc1, 0x07, 0x0a, 0x0e, 0x4c, 0x69, 0x62,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x45,
|
||||
0x78, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x06,
|
||||
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65,
|
||||
0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x6c, 0x69, 0x62,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x22,
|
||||
0x00, 0x12, 0x35, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x16, 0x2e, 0x6c, 0x69, 0x62,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4c, 0x6f, 0x61, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||
0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x72, 0x72,
|
||||
0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x04, 0x53, 0x74, 0x6f, 0x70,
|
||||
0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||
0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x04, 0x54, 0x65, 0x73,
|
||||
0x74, 0x12, 0x10, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x65, 0x73, 0x74,
|
||||
0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x65,
|
||||
0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x08, 0x53, 0x74, 0x6f, 0x70,
|
||||
0x54, 0x65, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72,
|
||||
0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x12, 0x3f, 0x0a, 0x0a, 0x51,
|
||||
0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x6c, 0x69, 0x62, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65,
|
||||
0x71, 0x1a, 0x17, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72,
|
||||
0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0f,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
|
||||
0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52,
|
||||
0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
|
||||
0x22, 0x00, 0x12, 0x46, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x12, 0x17, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x6f,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69,
|
||||
0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x11,
|
||||
0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x54, 0x6f, 0x53, 0x72,
|
||||
0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
|
||||
0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x12, 0x4e, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x70,
|
||||
0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x12,
|
||||
0x23, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c,
|
||||
0x65, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x12, 0x44, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x53,
|
||||
0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x62,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x72,
|
||||
0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40,
|
||||
0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53, 0x12, 0x11,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x69,
|
||||
0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0e, 0x47, 0x65,
|
||||
0x74, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x6c,
|
||||
0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e,
|
||||
0x47, 0x65, 0x74, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c,
|
||||
0x65, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69,
|
||||
0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f,
|
||||
0x49, 0x50, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
|
||||
0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65,
|
||||
0x71, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53,
|
||||
0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x40, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53,
|
||||
0x12, 0x1c, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x79,
|
||||
0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
|
||||
0x73, 0x70, 0x12, 0x4e, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f,
|
||||
0x53, 0x69, 0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x12, 0x23, 0x2e, 0x6c, 0x69, 0x62, 0x63,
|
||||
0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x47, 0x65, 0x6f, 0x53, 0x69,
|
||||
0x74, 0x65, 0x54, 0x6f, 0x53, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12,
|
||||
0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65,
|
||||
0x73, 0x70, 0x42, 0x11, 0x5a, 0x0f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x2f, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x73, 0x70, 0x12, 0x44, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50,
|
||||
0x72, 0x6f, 0x78, 0x79, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x53,
|
||||
0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x12, 0x40, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53,
|
||||
0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53, 0x12, 0x11, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1d, 0x2e, 0x6c, 0x69,
|
||||
0x62, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44,
|
||||
0x4e, 0x53, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x53, 0x65,
|
||||
0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e, 0x53, 0x12, 0x1c, 0x2e, 0x6c, 0x69, 0x62,
|
||||
0x63, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x44, 0x4e,
|
||||
0x53, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x6c, 0x69, 0x62, 0x63, 0x6f,
|
||||
0x72, 0x65, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x42, 0x11, 0x5a, 0x0f,
|
||||
0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x67, 0x65, 0x6e, 0x62,
|
||||
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@ -101,6 +101,7 @@ message ConnectionMetaData {
|
||||
string dest = 7;
|
||||
string protocol = 8;
|
||||
string domain = 9;
|
||||
string process = 10;
|
||||
}
|
||||
|
||||
message GetGeoIPListResponse {
|
||||
|
||||
@ -42,6 +42,8 @@ namespace NekoGui_rpc {
|
||||
|
||||
QString SetSystemDNS(bool *rpcOK, const QStringList& servers, bool dhcp, bool clear) const;
|
||||
|
||||
libcore::ListConnectionsResp ListConnections(bool *rpcOK) const;
|
||||
|
||||
private:
|
||||
std::function<std::unique_ptr<QtGrpc::Http2GrpcChannelPrivate>()> make_grpc_channel;
|
||||
std::unique_ptr<QtGrpc::Http2GrpcChannelPrivate> default_grpc_channel;
|
||||
|
||||
@ -92,6 +92,7 @@ namespace NekoGui {
|
||||
bool start_minimal = false;
|
||||
int max_log_line = 200;
|
||||
QString splitter_state = "";
|
||||
bool enable_stats = true;
|
||||
|
||||
// Subscription
|
||||
QString user_agent = ""; // set at main.cpp
|
||||
|
||||
62
include/stats/connections/connectionLister.hpp
Normal file
62
include/stats/connections/connectionLister.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
|
||||
namespace NekoGui_traffic
|
||||
{
|
||||
constexpr int IDKEY = 242315;
|
||||
|
||||
enum ConnectionSort
|
||||
{
|
||||
Default,
|
||||
ByDownload,
|
||||
ByUpload,
|
||||
ByDomain
|
||||
};
|
||||
|
||||
class ConnectionMetadata
|
||||
{
|
||||
public:
|
||||
QString id;
|
||||
long long createdAtMs;
|
||||
long long upload;
|
||||
long long download;
|
||||
QString outbound;
|
||||
QString network;
|
||||
QString dest;
|
||||
QString protocol;
|
||||
QString domain;
|
||||
QString process;
|
||||
};
|
||||
|
||||
class ConnectionLister
|
||||
{
|
||||
public:
|
||||
ConnectionLister();
|
||||
|
||||
bool suspend = true;
|
||||
|
||||
void Loop();
|
||||
|
||||
void ForceUpdate();
|
||||
|
||||
void stopLoop();
|
||||
|
||||
void setSort(ConnectionSort newSort);
|
||||
|
||||
private:
|
||||
void update();
|
||||
|
||||
QMutex mu;
|
||||
|
||||
bool stop = false;
|
||||
|
||||
std::shared_ptr<QSet<QString>> state;
|
||||
|
||||
ConnectionSort sort = Default;
|
||||
|
||||
bool asc = false;
|
||||
};
|
||||
|
||||
extern ConnectionLister* connection_lister;
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "include/global/NekoGui.hpp"
|
||||
#include "include/stats/connections/connectionLister.hpp"
|
||||
|
||||
#ifndef MW_INTERFACE
|
||||
|
||||
@ -73,6 +74,10 @@ public:
|
||||
|
||||
void DownloadAssets(const QString &geoipUrl, const QString &geositeUrl);
|
||||
|
||||
void UpdateConnectionList(const QMap<QString, NekoGui_traffic::ConnectionMetadata>& toUpdate, const QMap<QString, NekoGui_traffic::ConnectionMetadata>& toAdd);
|
||||
|
||||
void UpdateConnectionListWithRecreate(const QList<NekoGui_traffic::ConnectionMetadata>& connections);
|
||||
|
||||
signals:
|
||||
|
||||
void profile_selected(int id);
|
||||
@ -215,6 +220,8 @@ private:
|
||||
|
||||
void CheckUpdate();
|
||||
|
||||
void setupConnectionList();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
|
||||
@ -173,6 +173,9 @@
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -182,13 +185,13 @@
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
@ -279,13 +282,149 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="masterLogBrowser">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
<widget class="QTabWidget" name="stats_widget">
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openLinks">
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::TabPosition::North</enum>
|
||||
</property>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::TabShape::Rounded</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::TextElideMode::ElideNone</enum>
|
||||
</property>
|
||||
<property name="usesScrollButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tabsClosable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabBarAutoHide">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="connections_tab">
|
||||
<attribute name="title">
|
||||
<string>Connections</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="connections">
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Destination</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click To Disable Sorting</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Domain</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click To Sort By Domain</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Network</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Protocol</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Download</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click To Sort By Download</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Upload</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click To Sort By Upload</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="Logs">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Logs</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextBrowser" name="masterLogBrowser">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="openLinks">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@ -418,4 +418,20 @@ namespace NekoGui_rpc {
|
||||
}
|
||||
}
|
||||
|
||||
libcore::ListConnectionsResp Client::ListConnections(bool* rpcOK) const
|
||||
{
|
||||
libcore::EmptyReq req;
|
||||
libcore::ListConnectionsResp resp;
|
||||
auto status = default_grpc_channel->Call("ListConnections", req, &resp);
|
||||
if (status == QNetworkReply::NoError) {
|
||||
*rpcOK = true;
|
||||
return resp;
|
||||
} else {
|
||||
NOT_OK
|
||||
MW_show_log(QString("Failed to list connections: " + qt_error_string(status)));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace NekoGui_rpc
|
||||
|
||||
@ -710,13 +710,19 @@ namespace NekoGui {
|
||||
|
||||
// experimental
|
||||
QJsonObject experimentalObj;
|
||||
QJsonObject clash_api = {
|
||||
{"default_mode", "Rule"} // dummy to make sure it is created
|
||||
};
|
||||
|
||||
if (!status->forTest && dataStore->core_box_clash_api > 0) {
|
||||
QJsonObject clash_api = {
|
||||
if (!status->forTest)
|
||||
{
|
||||
if (dataStore->core_box_clash_api > 0){
|
||||
clash_api = {
|
||||
{"external_controller", NekoGui::dataStore->core_box_clash_listen_addr + ":" + Int2String(dataStore->core_box_clash_api)},
|
||||
{"secret", dataStore->core_box_clash_api_secret},
|
||||
{"external_ui", "dashboard"},
|
||||
};
|
||||
};
|
||||
}
|
||||
experimentalObj["clash_api"] = clash_api;
|
||||
}
|
||||
|
||||
|
||||
@ -309,6 +309,7 @@ namespace NekoGui {
|
||||
_add(new configItem("is_dhcp", &is_dhcp, itemType::boolean));
|
||||
_add(new configItem("system_dns_servers", &system_dns_servers, itemType::stringList));
|
||||
_add(new configItem("windows_set_admin", &windows_set_admin, itemType::boolean));
|
||||
_add(new configItem("enable_stats", &enable_stats, itemType::boolean));
|
||||
}
|
||||
|
||||
void DataStore::UpdateStartedId(int id) {
|
||||
|
||||
140
src/stats/connectionLister/connectionLister.cpp
Normal file
140
src/stats/connectionLister/connectionLister.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#include <QThread>
|
||||
#include <core/cmd/nekobox_core/server/gen/libcore.pb.h>
|
||||
#include <include/api/gRPC.h>
|
||||
#include "include/ui/mainwindow_interface.h"
|
||||
#include <include/stats/connections/connectionLister.hpp>
|
||||
|
||||
namespace NekoGui_traffic
|
||||
{
|
||||
ConnectionLister* connection_lister = new ConnectionLister();
|
||||
|
||||
ConnectionLister::ConnectionLister()
|
||||
{
|
||||
state = std::make_shared<QSet<QString>>();
|
||||
}
|
||||
|
||||
void ConnectionLister::ForceUpdate()
|
||||
{
|
||||
mu.lock();
|
||||
update();
|
||||
mu.unlock();
|
||||
}
|
||||
|
||||
|
||||
void ConnectionLister::Loop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (stop) return;
|
||||
auto sleep_ms = NekoGui::dataStore->traffic_loop_interval;
|
||||
QThread::msleep(sleep_ms);
|
||||
|
||||
if (suspend || !NekoGui::dataStore->enable_stats) continue;
|
||||
|
||||
mu.lock();
|
||||
update();
|
||||
mu.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionLister::update()
|
||||
{
|
||||
bool ok;
|
||||
libcore::ListConnectionsResp resp = NekoGui_rpc::defaultClient->ListConnections(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMap<QString, ConnectionMetadata> toUpdate;
|
||||
QMap<QString, ConnectionMetadata> toAdd;
|
||||
QSet<QString> newState;
|
||||
QList<ConnectionMetadata> sorted;
|
||||
auto conns = resp.connections();
|
||||
for (auto conn : conns)
|
||||
{
|
||||
auto c = ConnectionMetadata();
|
||||
c.id = QString(conn.mutable_id()->c_str());
|
||||
c.createdAtMs = conn.created_at();
|
||||
c.dest = QString(conn.mutable_dest()->c_str());
|
||||
c.upload = conn.upload();
|
||||
c.download = conn.download();
|
||||
c.domain = QString(conn.mutable_domain()->c_str());
|
||||
c.network = QString(conn.mutable_network()->c_str());
|
||||
c.outbound = QString(conn.mutable_outbound()->c_str());
|
||||
c.process = QString(conn.mutable_process()->c_str());
|
||||
c.protocol = QString(conn.mutable_protocol()->c_str());
|
||||
if (sort == Default)
|
||||
{
|
||||
if (state->contains(c.id))
|
||||
{
|
||||
toUpdate[c.id] = c;
|
||||
} else
|
||||
{
|
||||
toAdd[c.id] = c;
|
||||
}
|
||||
} else
|
||||
{
|
||||
sorted.append(c);
|
||||
}
|
||||
newState.insert(c.id);
|
||||
}
|
||||
|
||||
state->clear();
|
||||
for (const auto& id : newState) state->insert(id);
|
||||
|
||||
if (sort == Default)
|
||||
{
|
||||
runOnUiThread([=] {
|
||||
auto m = GetMainWindow();
|
||||
m->UpdateConnectionList(toUpdate, toAdd);
|
||||
});
|
||||
} else
|
||||
{
|
||||
if (sort == ByDownload)
|
||||
{
|
||||
std::sort(sorted.begin(), sorted.end(), [=](const ConnectionMetadata& a, const ConnectionMetadata& b)
|
||||
{
|
||||
if (a.download == b.download) return asc ? a.id > b.id : a.id < b.id;
|
||||
return asc ? a.download < b.download : a.download > b.download;
|
||||
});
|
||||
}
|
||||
if (sort == ByUpload)
|
||||
{
|
||||
std::sort(sorted.begin(), sorted.end(), [=](const ConnectionMetadata& a, const ConnectionMetadata& b)
|
||||
{
|
||||
if (a.upload == b.upload) return asc ? a.id > b.id : a.id < b.id;
|
||||
return asc ? a.upload < b.upload : a.upload > b.upload;
|
||||
});
|
||||
}
|
||||
if (sort == ByDomain)
|
||||
{
|
||||
std::sort(sorted.begin(), sorted.end(), [=](const ConnectionMetadata& a, const ConnectionMetadata& b)
|
||||
{
|
||||
if (a.domain == b.domain) return asc ? a.id > b.id : a.id < b.id;
|
||||
return asc ? a.domain > b.domain : a.domain < b.domain;
|
||||
});
|
||||
}
|
||||
runOnUiThread([=] {
|
||||
auto m = GetMainWindow();
|
||||
m->UpdateConnectionListWithRecreate(sorted);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionLister::stopLoop()
|
||||
{
|
||||
stop = true;
|
||||
}
|
||||
|
||||
void ConnectionLister::setSort(const ConnectionSort newSort)
|
||||
{
|
||||
if (sort == newSort) asc = !asc;
|
||||
else
|
||||
{
|
||||
sort = newSort;
|
||||
asc = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -154,6 +154,38 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
runOnUiThread([=] { show_log_impl(log); });
|
||||
};
|
||||
|
||||
// setup connection UI
|
||||
setupConnectionList();
|
||||
connect(ui->connections->horizontalHeader(), &QHeaderView::sectionClicked, this, [=](int index)
|
||||
{
|
||||
// TODO this is a very bad idea to hardcode it like this, need to refactor it later
|
||||
if (index == 0)
|
||||
{
|
||||
NekoGui_traffic::connection_lister->setSort(NekoGui_traffic::Default);
|
||||
NekoGui_traffic::connection_lister->ForceUpdate();
|
||||
}
|
||||
if (index == 2 || index == 3)
|
||||
{
|
||||
// ignore
|
||||
return;
|
||||
}
|
||||
if (index == 1)
|
||||
{
|
||||
NekoGui_traffic::connection_lister->setSort(NekoGui_traffic::ByDomain);
|
||||
NekoGui_traffic::connection_lister->ForceUpdate();
|
||||
}
|
||||
if (index == 4)
|
||||
{
|
||||
NekoGui_traffic::connection_lister->setSort(NekoGui_traffic::ByDownload);
|
||||
NekoGui_traffic::connection_lister->ForceUpdate();
|
||||
}
|
||||
if (index == 5)
|
||||
{
|
||||
NekoGui_traffic::connection_lister->setSort(NekoGui_traffic::ByUpload);
|
||||
NekoGui_traffic::connection_lister->ForceUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
// table UI
|
||||
ui->proxyListTable->callback_save_order = [=] {
|
||||
auto group = NekoGui::profileManager->CurrentGroup();
|
||||
@ -814,6 +846,139 @@ void MainWindow::neko_set_spmode_vpn(bool enable, bool save) {
|
||||
if (NekoGui::dataStore->started_id >= 0) neko_start(NekoGui::dataStore->started_id);
|
||||
}
|
||||
|
||||
void MainWindow::setupConnectionList()
|
||||
{
|
||||
ui->connections->horizontalHeader()->setHighlightSections(false);
|
||||
ui->connections->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
ui->connections->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
ui->connections->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
ui->connections->horizontalHeader()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
|
||||
ui->connections->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents);
|
||||
ui->connections->horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents);
|
||||
ui->connections->horizontalHeader()->setSectionResizeMode(5, QHeaderView::ResizeToContents);
|
||||
ui->connections->verticalHeader()->hide();
|
||||
}
|
||||
|
||||
void MainWindow::UpdateConnectionList(const QMap<QString, NekoGui_traffic::ConnectionMetadata>& toUpdate, const QMap<QString, NekoGui_traffic::ConnectionMetadata>& toAdd)
|
||||
{
|
||||
ui->connections->setUpdatesEnabled(false);
|
||||
for (int row=0;row<ui->connections->rowCount();row++)
|
||||
{
|
||||
auto key = ui->connections->item(row, 0)->data(NekoGui_traffic::IDKEY).toString();
|
||||
if (!toUpdate.contains(key))
|
||||
{
|
||||
ui->connections->removeRow(row);
|
||||
row--;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto conn = toUpdate[key];
|
||||
// C0: Dest
|
||||
ui->connections->item(row, 0)->setText(conn.dest);
|
||||
|
||||
// C1: Domain
|
||||
ui->connections->item(row, 1)->setText(conn.domain);
|
||||
|
||||
// C2: Network
|
||||
ui->connections->item(row, 2)->setText(conn.network);
|
||||
|
||||
// C3: Protocol
|
||||
ui->connections->item(row, 3)->setText(conn.protocol);
|
||||
|
||||
// C4: Download
|
||||
ui->connections->item(row, 4)->setText(ReadableSize(conn.download));
|
||||
|
||||
// C5: Upload
|
||||
ui->connections->item(row, 5)->setText(ReadableSize(conn.upload));
|
||||
}
|
||||
int row = ui->connections->rowCount();
|
||||
for (const auto& conn : toAdd)
|
||||
{
|
||||
ui->connections->insertRow(row);
|
||||
auto f0 = std::make_unique<QTableWidgetItem>();
|
||||
f0->setData(NekoGui_traffic::IDKEY, conn.id);
|
||||
|
||||
// C0: Dest
|
||||
auto f = f0->clone();
|
||||
f->setText(conn.dest);
|
||||
ui->connections->setItem(row, 0, f);
|
||||
|
||||
// C1: Domain
|
||||
f = f0->clone();
|
||||
f->setText(conn.domain);
|
||||
ui->connections->setItem(row, 1, f);
|
||||
|
||||
// C2: Network
|
||||
f = f0->clone();
|
||||
f->setText(conn.network);
|
||||
ui->connections->setItem(row, 2, f);
|
||||
|
||||
// C3: Protocol
|
||||
f = f0->clone();
|
||||
f->setText(conn.protocol);
|
||||
ui->connections->setItem(row, 3, f);
|
||||
|
||||
// C4: Download
|
||||
f = f0->clone();
|
||||
f->setText(ReadableSize(conn.download));
|
||||
ui->connections->setItem(row, 4, f);
|
||||
|
||||
// C5: Upload
|
||||
f = f0->clone();
|
||||
f->setText(ReadableSize(conn.upload));
|
||||
ui->connections->setItem(row, 5, f);
|
||||
|
||||
row++;
|
||||
}
|
||||
ui->connections->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void MainWindow::UpdateConnectionListWithRecreate(const QList<NekoGui_traffic::ConnectionMetadata>& connections)
|
||||
{
|
||||
ui->connections->setUpdatesEnabled(false);
|
||||
ui->connections->setRowCount(0);
|
||||
int row=0;
|
||||
for (const auto& conn : connections)
|
||||
{
|
||||
ui->connections->insertRow(row);
|
||||
auto f0 = std::make_unique<QTableWidgetItem>();
|
||||
f0->setData(NekoGui_traffic::IDKEY, conn.id);
|
||||
|
||||
// C0: Dest
|
||||
auto f = f0->clone();
|
||||
f->setText(conn.dest);
|
||||
ui->connections->setItem(row, 0, f);
|
||||
|
||||
// C1: Domain
|
||||
f = f0->clone();
|
||||
f->setText(conn.domain);
|
||||
ui->connections->setItem(row, 1, f);
|
||||
|
||||
// C2: Network
|
||||
f = f0->clone();
|
||||
f->setText(conn.network);
|
||||
ui->connections->setItem(row, 2, f);
|
||||
|
||||
// C3: Protocol
|
||||
f = f0->clone();
|
||||
f->setText(conn.protocol);
|
||||
ui->connections->setItem(row, 3, f);
|
||||
|
||||
// C4: Download
|
||||
f = f0->clone();
|
||||
f->setText(ReadableSize(conn.download));
|
||||
ui->connections->setItem(row, 4, f);
|
||||
|
||||
// C5: Upload
|
||||
f = f0->clone();
|
||||
f->setText(ReadableSize(conn.upload));
|
||||
ui->connections->setItem(row, 5, f);
|
||||
|
||||
row++;
|
||||
}
|
||||
ui->connections->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void MainWindow::refresh_status(const QString &traffic_update) {
|
||||
auto refresh_speed_label = [=] {
|
||||
if (NekoGui::dataStore->disable_traffic_stats) {
|
||||
@ -955,6 +1120,7 @@ void MainWindow::refresh_proxy_list(const int &id) {
|
||||
}
|
||||
|
||||
void MainWindow::refresh_proxy_list_impl(const int &id, GroupSortAction groupSortAction) {
|
||||
ui->proxyListTable->setUpdatesEnabled(false);
|
||||
// id < 0 重绘
|
||||
if (id < 0) {
|
||||
// 清空数据
|
||||
@ -975,7 +1141,11 @@ void MainWindow::refresh_proxy_list_impl(const int &id, GroupSortAction groupSor
|
||||
switch (groupSortAction.method) {
|
||||
case GroupSortMethod::Raw: {
|
||||
auto group = NekoGui::profileManager->CurrentGroup();
|
||||
if (group == nullptr) return;
|
||||
if (group == nullptr)
|
||||
{
|
||||
ui->proxyListTable->setUpdatesEnabled(true);
|
||||
return;
|
||||
}
|
||||
ui->proxyListTable->order = group->order;
|
||||
break;
|
||||
}
|
||||
@ -1043,11 +1213,13 @@ 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);
|
||||
if (id >= 0)
|
||||
{
|
||||
if (ui->proxyListTable->id2Row.count(id) == 0)
|
||||
{
|
||||
qDebug("Invalid proxy list id, data might be corrupted");
|
||||
ui->proxyListTable->setUpdatesEnabled(true);
|
||||
return;
|
||||
}
|
||||
auto rowID = ui->proxyListTable->id2Row[id];
|
||||
@ -1061,6 +1233,7 @@ void MainWindow::refresh_proxy_list_impl_refresh_data(const int &id, bool stoppi
|
||||
refresh_table_item(row, profile, stopping);
|
||||
}
|
||||
}
|
||||
ui->proxyListTable->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void MainWindow::refresh_table_item(const int row, const std::shared_ptr<NekoGui::ProxyEntity>& profile, bool stopping)
|
||||
|
||||
@ -26,6 +26,7 @@ void MainWindow::setup_grpc() {
|
||||
|
||||
// Looper
|
||||
runOnNewThread([=] { NekoGui_traffic::trafficLooper->Loop(); });
|
||||
runOnNewThread([=] {NekoGui_traffic::connection_lister->Loop(); });
|
||||
}
|
||||
|
||||
void MainWindow::RunSpeedTest(const QString& config, bool useDefault, const QStringList& outboundTags, const QMap<QString, int>& tag2entID, int entID) {
|
||||
@ -279,6 +280,7 @@ void MainWindow::neko_start(int _id) {
|
||||
NekoGui_traffic::trafficLooper->items = result->outboundStats;
|
||||
NekoGui::dataStore->ignoreConnTag = result->ignoreConnTag;
|
||||
NekoGui_traffic::trafficLooper->loop_enabled = true;
|
||||
NekoGui_traffic::connection_lister->suspend = false;
|
||||
|
||||
NekoGui::dataStore->UpdateStartedId(ent->id);
|
||||
running = ent;
|
||||
@ -403,6 +405,7 @@ void MainWindow::neko_stop(bool crash, bool sem, bool manual) {
|
||||
auto restartMsgboxTimer = new MessageBoxTimer(this, restartMsgbox, 5000);
|
||||
|
||||
NekoGui_traffic::trafficLooper->loop_enabled = false;
|
||||
NekoGui_traffic::connection_lister->suspend = true;
|
||||
NekoGui_traffic::trafficLooper->loop_mutex.lock();
|
||||
if (NekoGui::dataStore->traffic_loop_interval != 0) {
|
||||
NekoGui_traffic::trafficLooper->UpdateAll();
|
||||
|
||||
@ -38,7 +38,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
|
||||
});
|
||||
|
||||
// Style
|
||||
ui->connection_statistics_box->setDisabled(true);
|
||||
ui->connection_statistics->setChecked(NekoGui::dataStore->enable_stats);
|
||||
//
|
||||
D_LOAD_BOOL(start_minimal)
|
||||
D_LOAD_INT(max_log_line)
|
||||
@ -175,6 +175,7 @@ void DialogBasicSettings::accept() {
|
||||
|
||||
// Style
|
||||
|
||||
NekoGui::dataStore->enable_stats = ui->connection_statistics->isChecked();
|
||||
NekoGui::dataStore->language = ui->language->currentIndex();
|
||||
D_SAVE_BOOL(start_minimal)
|
||||
D_SAVE_INT(max_log_line)
|
||||
@ -271,7 +272,6 @@ void DialogBasicSettings::on_core_settings_clicked() {
|
||||
w->setLayout(layout);
|
||||
//
|
||||
auto line = -1;
|
||||
QCheckBox *core_box_enable_clash_api;
|
||||
MyLineEdit *core_box_clash_api;
|
||||
MyLineEdit *core_box_clash_api_secret;
|
||||
MyLineEdit *core_box_underlying_dns;
|
||||
@ -288,12 +288,6 @@ void DialogBasicSettings::on_core_settings_clicked() {
|
||||
layout->addWidget(core_box_underlying_dns_l, ++line, 0);
|
||||
layout->addWidget(core_box_underlying_dns, line, 1);
|
||||
//
|
||||
auto core_box_enable_clash_api_l = new QLabel("Enable Clash API");
|
||||
core_box_enable_clash_api = new QCheckBox;
|
||||
core_box_enable_clash_api->setChecked(NekoGui::dataStore->core_box_clash_api > 0);
|
||||
layout->addWidget(core_box_enable_clash_api_l, ++line, 0);
|
||||
layout->addWidget(core_box_enable_clash_api, line, 1);
|
||||
//
|
||||
auto core_box_clash_listen_addr_l = new QLabel("Clash Api Listen Address");
|
||||
core_box_clash_listen_addr = new MyLineEdit;
|
||||
core_box_clash_listen_addr->setText(NekoGui::dataStore->core_box_clash_listen_addr);
|
||||
@ -302,7 +296,7 @@ void DialogBasicSettings::on_core_settings_clicked() {
|
||||
//
|
||||
auto core_box_clash_api_l = new QLabel("Clash API Listen Port");
|
||||
core_box_clash_api = new MyLineEdit;
|
||||
core_box_clash_api->setText(Int2String(std::abs(NekoGui::dataStore->core_box_clash_api)));
|
||||
core_box_clash_api->setText(NekoGui::dataStore->core_box_clash_api > 0 ? Int2String(NekoGui::dataStore->core_box_clash_api) : "");
|
||||
layout->addWidget(core_box_clash_api_l, ++line, 0);
|
||||
layout->addWidget(core_box_clash_api, line, 1);
|
||||
//
|
||||
@ -317,7 +311,7 @@ void DialogBasicSettings::on_core_settings_clicked() {
|
||||
box->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
connect(box, &QDialogButtonBox::accepted, w, [=] {
|
||||
NekoGui::dataStore->core_box_underlying_dns = core_box_underlying_dns->text();
|
||||
NekoGui::dataStore->core_box_clash_api = core_box_clash_api->text().toInt() * (core_box_enable_clash_api->isChecked() ? 1 : -1);
|
||||
NekoGui::dataStore->core_box_clash_api = core_box_clash_api->text().toInt();
|
||||
NekoGui::dataStore->core_box_clash_listen_addr = core_box_clash_listen_addr->text();
|
||||
NekoGui::dataStore->core_box_clash_api_secret = core_box_clash_api_secret->text();
|
||||
MW_dialog_message(Dialog_DialogBasicSettings, "UpdateDataStore");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user