Refactor endpoint in started interface

This commit is contained in:
世界
2026-07-18 00:28:16 +08:00
parent 414fec5371
commit 60f3012444
20 changed files with 326 additions and 110 deletions
+2 -2
View File
@@ -61,8 +61,8 @@ func NewRemoteClient(options RemoteClientOptions) (*grpc.ClientConn, error) {
}
return grpc.NewClient(target,
grpc.WithTransportCredentials(transportCredentials),
grpc.WithUnaryInterceptor(NewClientAuthUnaryInterceptor(options.Secret)),
grpc.WithStreamInterceptor(NewClientAuthStreamInterceptor(options.Secret)),
grpc.WithChainUnaryInterceptor(UnaryClientLocaleInterceptor, NewClientAuthUnaryInterceptor(options.Secret)),
grpc.WithChainStreamInterceptor(StreamClientLocaleInterceptor, NewClientAuthStreamInterceptor(options.Secret)),
)
}
+54
View File
@@ -0,0 +1,54 @@
package daemon
import (
"context"
"github.com/sagernet/sing-box/experimental/locale"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func contextWithLocale(ctx context.Context) context.Context {
requestMetadata, loaded := metadata.FromIncomingContext(ctx)
if !loaded {
return ctx
}
for _, localeID := range requestMetadata.Get("accept-language") {
localizedContext, matched := locale.ContextWithLocale(ctx, localeID)
if matched {
return localizedContext
}
}
return ctx
}
func UnaryLocaleInterceptor(ctx context.Context, request any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
return handler(contextWithLocale(ctx), request)
}
func StreamLocaleInterceptor(server any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
return handler(server, &localeServerStream{
ServerStream: stream,
ctx: contextWithLocale(stream.Context()),
})
}
func UnaryClientLocaleInterceptor(ctx context.Context, method string, request, reply any, connection *grpc.ClientConn, invoker grpc.UnaryInvoker, options ...grpc.CallOption) error {
ctx = metadata.AppendToOutgoingContext(ctx, "accept-language", locale.FromContext(ctx).Locale)
return invoker(ctx, method, request, reply, connection, options...)
}
func StreamClientLocaleInterceptor(ctx context.Context, description *grpc.StreamDesc, connection *grpc.ClientConn, method string, streamer grpc.Streamer, options ...grpc.CallOption) (grpc.ClientStream, error) {
ctx = metadata.AppendToOutgoingContext(ctx, "accept-language", locale.FromContext(ctx).Locale)
return streamer(ctx, description, connection, method, options...)
}
type localeServerStream struct {
grpc.ServerStream
ctx context.Context
}
func (s *localeServerStream) Context() context.Context {
return s.ctx
}
+2 -2
View File
@@ -15,8 +15,8 @@ import (
func NewServer(startedService *StartedService, secret string) *grpc.Server {
server := grpc.NewServer(
grpc.ChainUnaryInterceptor(newUnaryAuthInterceptor(secret)),
grpc.ChainStreamInterceptor(newStreamAuthInterceptor(secret)),
grpc.ChainUnaryInterceptor(newUnaryAuthInterceptor(secret), UnaryLocaleInterceptor),
grpc.ChainStreamInterceptor(newStreamAuthInterceptor(secret), StreamLocaleInterceptor),
)
healthServer := health.NewServer()
RegisterStartedServiceServer(server, startedService)
+13 -6
View File
@@ -17,6 +17,7 @@ import (
"github.com/sagernet/sing-box/common/urltest"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental/deprecated"
"github.com/sagernet/sing-box/experimental/locale"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/protocol/group"
"github.com/sagernet/sing/common"
@@ -1410,6 +1411,7 @@ func (s *StartedService) SubscribeTailscaleStatus(
var tags []string
statuses := make(map[string]*adapter.TailscaleEndpointStatus, len(endpoints))
selectedLocale := locale.FromContext(server.Context())
for update := range updates {
if _, exists := statuses[update.tag]; !exists {
tags = append(tags, update.tag)
@@ -1417,7 +1419,7 @@ func (s *StartedService) SubscribeTailscaleStatus(
statuses[update.tag] = update.status
protoEndpoints := make([]*TailscaleEndpointStatus, 0, len(statuses))
for _, tag := range tags {
protoEndpoints = append(protoEndpoints, tailscaleEndpointStatusToProto(tag, statuses[tag]))
protoEndpoints = append(protoEndpoints, tailscaleEndpointStatusToProto(tag, statuses[tag], selectedLocale))
}
sendErr := server.Send(&TailscaleStatusUpdate{
Endpoints: protoEndpoints,
@@ -1429,7 +1431,7 @@ func (s *StartedService) SubscribeTailscaleStatus(
return nil
}
func tailscaleEndpointStatusToProto(tag string, s *adapter.TailscaleEndpointStatus) *TailscaleEndpointStatus {
func tailscaleEndpointStatusToProto(tag string, s *adapter.TailscaleEndpointStatus, selectedLocale *locale.Locale) *TailscaleEndpointStatus {
userGroups := make([]*TailscaleUserGroup, len(s.UserGroups))
for i, group := range s.UserGroups {
peers := make([]*TailscalePeer, len(group.Peers))
@@ -1447,6 +1449,7 @@ func tailscaleEndpointStatusToProto(tag string, s *adapter.TailscaleEndpointStat
result := &TailscaleEndpointStatus{
EndpointTag: tag,
BackendState: s.BackendState,
StateText: selectedLocale.TailscaleStateText(s.BackendState),
AuthURL: s.AuthURL,
NetworkName: s.NetworkName,
MagicDNSSuffix: s.MagicDNSSuffix,
@@ -1599,19 +1602,21 @@ func (s *StartedService) SubscribeOpenConnectStatus(
s.serviceAccess.RUnlock()
endpointManager := service.FromContext[adapter.EndpointManager](boxService.ctx)
selectedLocale := locale.FromContext(server.Context())
return subscribeEndpointStatus(server.Context(), endpointManager, C.TypeOpenConnect, "OpenConnect client", func(endpoints []adapter.OpenConnectEndpoint) error {
return server.Send(&OpenConnectStatusUpdate{
Endpoints: common.Map(endpoints, func(endpoint adapter.OpenConnectEndpoint) *OpenConnectEndpointStatus {
return openConnectEndpointStatusToProto(endpoint.Tag(), endpoint.OpenConnectStatus())
return openConnectEndpointStatusToProto(endpoint.Tag(), endpoint.OpenConnectStatus(), selectedLocale)
}),
})
})
}
func openConnectEndpointStatusToProto(tag string, endpointStatus adapter.OpenConnectStatus) *OpenConnectEndpointStatus {
func openConnectEndpointStatusToProto(tag string, endpointStatus adapter.OpenConnectStatus, selectedLocale *locale.Locale) *OpenConnectEndpointStatus {
result := &OpenConnectEndpointStatus{
EndpointTag: tag,
State: endpointStatus.State,
StateText: selectedLocale.VPNStateText(endpointStatus.State),
Error: endpointStatus.Error,
TunnelInfo: openConnectTunnelInfoToProto(endpointStatus.TunnelInfo),
}
@@ -1696,19 +1701,21 @@ func (s *StartedService) SubscribeOpenVPNStatus(
s.serviceAccess.RUnlock()
endpointManager := service.FromContext[adapter.EndpointManager](boxService.ctx)
selectedLocale := locale.FromContext(server.Context())
return subscribeEndpointStatus(server.Context(), endpointManager, C.TypeOpenVPNClient, "OpenVPN client", func(endpoints []adapter.OpenVPNEndpoint) error {
return server.Send(&OpenVPNStatusUpdate{
Endpoints: common.Map(endpoints, func(endpoint adapter.OpenVPNEndpoint) *OpenVPNEndpointStatus {
return openVPNEndpointStatusToProto(endpoint.Tag(), endpoint.OpenVPNStatus())
return openVPNEndpointStatusToProto(endpoint.Tag(), endpoint.OpenVPNStatus(), selectedLocale)
}),
})
})
}
func openVPNEndpointStatusToProto(tag string, endpointStatus adapter.OpenVPNStatus) *OpenVPNEndpointStatus {
func openVPNEndpointStatusToProto(tag string, endpointStatus adapter.OpenVPNStatus, selectedLocale *locale.Locale) *OpenVPNEndpointStatus {
result := &OpenVPNEndpointStatus{
EndpointTag: tag,
State: endpointStatus.State,
StateText: selectedLocale.VPNStateText(endpointStatus.State),
Error: endpointStatus.Error,
TunnelInfo: openVPNTunnelInfoToProto(endpointStatus.TunnelInfo),
}
+61 -32
View File
@@ -2222,13 +2222,14 @@ type TailscaleEndpointStatus struct {
state protoimpl.MessageState `protogen:"open.v1"`
EndpointTag string `protobuf:"bytes,1,opt,name=endpointTag,proto3" json:"endpointTag,omitempty"`
BackendState string `protobuf:"bytes,2,opt,name=backendState,proto3" json:"backendState,omitempty"`
AuthURL string `protobuf:"bytes,3,opt,name=authURL,proto3" json:"authURL,omitempty"`
NetworkName string `protobuf:"bytes,4,opt,name=networkName,proto3" json:"networkName,omitempty"`
MagicDNSSuffix string `protobuf:"bytes,5,opt,name=magicDNSSuffix,proto3" json:"magicDNSSuffix,omitempty"`
Self *TailscalePeer `protobuf:"bytes,6,opt,name=self,proto3" json:"self,omitempty"`
UserGroups []*TailscaleUserGroup `protobuf:"bytes,7,rep,name=userGroups,proto3" json:"userGroups,omitempty"`
ExitNode *TailscalePeer `protobuf:"bytes,8,opt,name=exitNode,proto3" json:"exitNode,omitempty"`
KeyAuth bool `protobuf:"varint,9,opt,name=keyAuth,proto3" json:"keyAuth,omitempty"`
StateText string `protobuf:"bytes,3,opt,name=stateText,proto3" json:"stateText,omitempty"`
AuthURL string `protobuf:"bytes,4,opt,name=authURL,proto3" json:"authURL,omitempty"`
NetworkName string `protobuf:"bytes,5,opt,name=networkName,proto3" json:"networkName,omitempty"`
MagicDNSSuffix string `protobuf:"bytes,6,opt,name=magicDNSSuffix,proto3" json:"magicDNSSuffix,omitempty"`
Self *TailscalePeer `protobuf:"bytes,7,opt,name=self,proto3" json:"self,omitempty"`
UserGroups []*TailscaleUserGroup `protobuf:"bytes,8,rep,name=userGroups,proto3" json:"userGroups,omitempty"`
ExitNode *TailscalePeer `protobuf:"bytes,9,opt,name=exitNode,proto3" json:"exitNode,omitempty"`
KeyAuth bool `protobuf:"varint,10,opt,name=keyAuth,proto3" json:"keyAuth,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -2277,6 +2278,13 @@ func (x *TailscaleEndpointStatus) GetBackendState() string {
return ""
}
func (x *TailscaleEndpointStatus) GetStateText() string {
if x != nil {
return x.StateText
}
return ""
}
func (x *TailscaleEndpointStatus) GetAuthURL() string {
if x != nil {
return x.AuthURL
@@ -4666,9 +4674,10 @@ type OpenConnectEndpointStatus struct {
state protoimpl.MessageState `protogen:"open.v1"`
EndpointTag string `protobuf:"bytes,1,opt,name=endpointTag,proto3" json:"endpointTag,omitempty"`
State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"`
AuthForm *OpenConnectAuthForm `protobuf:"bytes,3,opt,name=authForm,proto3" json:"authForm,omitempty"`
Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
TunnelInfo *OpenConnectTunnelInfo `protobuf:"bytes,5,opt,name=tunnelInfo,proto3" json:"tunnelInfo,omitempty"`
StateText string `protobuf:"bytes,3,opt,name=stateText,proto3" json:"stateText,omitempty"`
AuthForm *OpenConnectAuthForm `protobuf:"bytes,4,opt,name=authForm,proto3" json:"authForm,omitempty"`
Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"`
TunnelInfo *OpenConnectTunnelInfo `protobuf:"bytes,6,opt,name=tunnelInfo,proto3" json:"tunnelInfo,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -4717,6 +4726,13 @@ func (x *OpenConnectEndpointStatus) GetState() string {
return ""
}
func (x *OpenConnectEndpointStatus) GetStateText() string {
if x != nil {
return x.StateText
}
return ""
}
func (x *OpenConnectEndpointStatus) GetAuthForm() *OpenConnectAuthForm {
if x != nil {
return x.AuthForm
@@ -5218,9 +5234,10 @@ type OpenVPNEndpointStatus struct {
state protoimpl.MessageState `protogen:"open.v1"`
EndpointTag string `protobuf:"bytes,1,opt,name=endpointTag,proto3" json:"endpointTag,omitempty"`
State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"`
Challenge *OpenVPNChallenge `protobuf:"bytes,3,opt,name=challenge,proto3" json:"challenge,omitempty"`
Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
TunnelInfo *OpenVPNTunnelInfo `protobuf:"bytes,5,opt,name=tunnelInfo,proto3" json:"tunnelInfo,omitempty"`
StateText string `protobuf:"bytes,3,opt,name=stateText,proto3" json:"stateText,omitempty"`
Challenge *OpenVPNChallenge `protobuf:"bytes,4,opt,name=challenge,proto3" json:"challenge,omitempty"`
Error string `protobuf:"bytes,5,opt,name=error,proto3" json:"error,omitempty"`
TunnelInfo *OpenVPNTunnelInfo `protobuf:"bytes,6,opt,name=tunnelInfo,proto3" json:"tunnelInfo,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -5269,6 +5286,13 @@ func (x *OpenVPNEndpointStatus) GetState() string {
return ""
}
func (x *OpenVPNEndpointStatus) GetStateText() string {
if x != nil {
return x.StateText
}
return ""
}
func (x *OpenVPNEndpointStatus) GetChallenge() *OpenVPNChallenge {
if x != nil {
return x.Challenge
@@ -5844,19 +5868,21 @@ const file_daemon_started_service_proto_rawDesc = "" +
"\x05error\x18\a \x01(\tR\x05error\x12*\n" +
"\x10natTypeSupported\x18\b \x01(\bR\x10natTypeSupported\"V\n" +
"\x15TailscaleStatusUpdate\x12=\n" +
"\tendpoints\x18\x01 \x03(\v2\x1f.daemon.TailscaleEndpointStatusR\tendpoints\"\xf7\x02\n" +
"\tendpoints\x18\x01 \x03(\v2\x1f.daemon.TailscaleEndpointStatusR\tendpoints\"\x95\x03\n" +
"\x17TailscaleEndpointStatus\x12 \n" +
"\vendpointTag\x18\x01 \x01(\tR\vendpointTag\x12\"\n" +
"\fbackendState\x18\x02 \x01(\tR\fbackendState\x12\x18\n" +
"\aauthURL\x18\x03 \x01(\tR\aauthURL\x12 \n" +
"\vnetworkName\x18\x04 \x01(\tR\vnetworkName\x12&\n" +
"\x0emagicDNSSuffix\x18\x05 \x01(\tR\x0emagicDNSSuffix\x12)\n" +
"\x04self\x18\x06 \x01(\v2\x15.daemon.TailscalePeerR\x04self\x12:\n" +
"\fbackendState\x18\x02 \x01(\tR\fbackendState\x12\x1c\n" +
"\tstateText\x18\x03 \x01(\tR\tstateText\x12\x18\n" +
"\aauthURL\x18\x04 \x01(\tR\aauthURL\x12 \n" +
"\vnetworkName\x18\x05 \x01(\tR\vnetworkName\x12&\n" +
"\x0emagicDNSSuffix\x18\x06 \x01(\tR\x0emagicDNSSuffix\x12)\n" +
"\x04self\x18\a \x01(\v2\x15.daemon.TailscalePeerR\x04self\x12:\n" +
"\n" +
"userGroups\x18\a \x03(\v2\x1a.daemon.TailscaleUserGroupR\n" +
"userGroups\x18\b \x03(\v2\x1a.daemon.TailscaleUserGroupR\n" +
"userGroups\x121\n" +
"\bexitNode\x18\b \x01(\v2\x15.daemon.TailscalePeerR\bexitNode\x12\x18\n" +
"\akeyAuth\x18\t \x01(\bR\akeyAuth\"\xbf\x01\n" +
"\bexitNode\x18\t \x01(\v2\x15.daemon.TailscalePeerR\bexitNode\x12\x18\n" +
"\akeyAuth\x18\n" +
" \x01(\bR\akeyAuth\"\xbf\x01\n" +
"\x12TailscaleUserGroup\x12\x16\n" +
"\x06userID\x18\x01 \x01(\x03R\x06userID\x12\x1c\n" +
"\tloginName\x18\x02 \x01(\tR\tloginName\x12 \n" +
@@ -6040,14 +6066,15 @@ const file_daemon_started_service_proto_rawDesc = "" +
"\abackend\x18\x04 \x01(\x0e2\x12.daemon.USBBackendR\abackend\x12,\n" +
"\x05state\x18\x05 \x01(\x0e2\x16.daemon.USBDeviceStateR\x05state\"Z\n" +
"\x17OpenConnectStatusUpdate\x12?\n" +
"\tendpoints\x18\x01 \x03(\v2!.daemon.OpenConnectEndpointStatusR\tendpoints\"\xe1\x01\n" +
"\tendpoints\x18\x01 \x03(\v2!.daemon.OpenConnectEndpointStatusR\tendpoints\"\xff\x01\n" +
"\x19OpenConnectEndpointStatus\x12 \n" +
"\vendpointTag\x18\x01 \x01(\tR\vendpointTag\x12\x14\n" +
"\x05state\x18\x02 \x01(\tR\x05state\x127\n" +
"\bauthForm\x18\x03 \x01(\v2\x1b.daemon.OpenConnectAuthFormR\bauthForm\x12\x14\n" +
"\x05error\x18\x04 \x01(\tR\x05error\x12=\n" +
"\x05state\x18\x02 \x01(\tR\x05state\x12\x1c\n" +
"\tstateText\x18\x03 \x01(\tR\tstateText\x127\n" +
"\bauthForm\x18\x04 \x01(\v2\x1b.daemon.OpenConnectAuthFormR\bauthForm\x12\x14\n" +
"\x05error\x18\x05 \x01(\tR\x05error\x12=\n" +
"\n" +
"tunnelInfo\x18\x05 \x01(\v2\x1d.daemon.OpenConnectTunnelInfoR\n" +
"tunnelInfo\x18\x06 \x01(\v2\x1d.daemon.OpenConnectTunnelInfoR\n" +
"tunnelInfo\"\xd9\x01\n" +
"\x15OpenConnectTunnelInfo\x12\x16\n" +
"\x06server\x18\x01 \x01(\tR\x06server\x12\x16\n" +
@@ -6086,14 +6113,15 @@ const file_daemon_started_service_proto_rawDesc = "" +
"\vendpointTag\x18\x01 \x01(\tR\vendpointTag\x12\x16\n" +
"\x06formID\x18\x02 \x01(\tR\x06formID\"R\n" +
"\x13OpenVPNStatusUpdate\x12;\n" +
"\tendpoints\x18\x01 \x03(\v2\x1d.daemon.OpenVPNEndpointStatusR\tendpoints\"\xd8\x01\n" +
"\tendpoints\x18\x01 \x03(\v2\x1d.daemon.OpenVPNEndpointStatusR\tendpoints\"\xf6\x01\n" +
"\x15OpenVPNEndpointStatus\x12 \n" +
"\vendpointTag\x18\x01 \x01(\tR\vendpointTag\x12\x14\n" +
"\x05state\x18\x02 \x01(\tR\x05state\x126\n" +
"\tchallenge\x18\x03 \x01(\v2\x18.daemon.OpenVPNChallengeR\tchallenge\x12\x14\n" +
"\x05error\x18\x04 \x01(\tR\x05error\x129\n" +
"\x05state\x18\x02 \x01(\tR\x05state\x12\x1c\n" +
"\tstateText\x18\x03 \x01(\tR\tstateText\x126\n" +
"\tchallenge\x18\x04 \x01(\v2\x18.daemon.OpenVPNChallengeR\tchallenge\x12\x14\n" +
"\x05error\x18\x05 \x01(\tR\x05error\x129\n" +
"\n" +
"tunnelInfo\x18\x05 \x01(\v2\x19.daemon.OpenVPNTunnelInfoR\n" +
"tunnelInfo\x18\x06 \x01(\v2\x19.daemon.OpenVPNTunnelInfoR\n" +
"tunnelInfo\"\xd7\x01\n" +
"\x11OpenVPNTunnelInfo\x12\x16\n" +
"\x06server\x18\x01 \x01(\tR\x06server\x12\x18\n" +
@@ -6284,6 +6312,7 @@ var (
(*emptypb.Empty)(nil), // 82: google.protobuf.Empty
}
)
var file_daemon_started_service_proto_depIdxs = []int32{
4, // 0: daemon.ServiceStatus.status:type_name -> daemon.ServiceStatus.Type
80, // 1: daemon.Log.messages:type_name -> daemon.Log.Message
+16 -13
View File
@@ -274,13 +274,14 @@ message TailscaleStatusUpdate {
message TailscaleEndpointStatus {
string endpointTag = 1;
string backendState = 2;
string authURL = 3;
string networkName = 4;
string magicDNSSuffix = 5;
TailscalePeer self = 6;
repeated TailscaleUserGroup userGroups = 7;
TailscalePeer exitNode = 8;
bool keyAuth = 9;
string stateText = 3;
string authURL = 4;
string networkName = 5;
string magicDNSSuffix = 6;
TailscalePeer self = 7;
repeated TailscaleUserGroup userGroups = 8;
TailscalePeer exitNode = 9;
bool keyAuth = 10;
}
message TailscaleUserGroup {
@@ -530,9 +531,10 @@ message OpenConnectStatusUpdate {
message OpenConnectEndpointStatus {
string endpointTag = 1;
string state = 2;
OpenConnectAuthForm authForm = 3;
string error = 4;
OpenConnectTunnelInfo tunnelInfo = 5;
string stateText = 3;
OpenConnectAuthForm authForm = 4;
string error = 5;
OpenConnectTunnelInfo tunnelInfo = 6;
}
message OpenConnectTunnelInfo {
@@ -587,9 +589,10 @@ message OpenVPNStatusUpdate {
message OpenVPNEndpointStatus {
string endpointTag = 1;
string state = 2;
OpenVPNChallenge challenge = 3;
string error = 4;
OpenVPNTunnelInfo tunnelInfo = 5;
string stateText = 3;
OpenVPNChallenge challenge = 4;
string error = 5;
OpenVPNTunnelInfo tunnelInfo = 6;
}
message OpenVPNTunnelInfo {