mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-22 01:57:12 -05:00
network observatory service
This commit is contained in:
parent
6f913b31e2
commit
d79b8291c3
@ -25,6 +25,53 @@ const (
|
|||||||
// of the legacy proto package is being used.
|
// of the legacy proto package is being used.
|
||||||
const _ = proto.ProtoPackageIsVersion4
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
|
|
||||||
|
type ObservationResult struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Status []*OutboundStatus `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ObservationResult) Reset() {
|
||||||
|
*x = ObservationResult{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_observatory_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ObservationResult) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ObservationResult) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ObservationResult) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_observatory_config_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ObservationResult.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ObservationResult) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ObservationResult) GetStatus() []*OutboundStatus {
|
||||||
|
if x != nil {
|
||||||
|
return x.Status
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type OutboundStatus struct {
|
type OutboundStatus struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@ -36,19 +83,25 @@ type OutboundStatus struct {
|
|||||||
// @Document The time for probe request to finish.
|
// @Document The time for probe request to finish.
|
||||||
//@Type time.ms
|
//@Type time.ms
|
||||||
//@Restriction ReadOnlyForUser
|
//@Restriction ReadOnlyForUser
|
||||||
Delay uint32 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"`
|
Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"`
|
||||||
// @Document The last error caused this outbound failed to relay probe request
|
// @Document The last error caused this outbound failed to relay probe request
|
||||||
//@Restriction NotMachineReadable
|
//@Restriction NotMachineReadable
|
||||||
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
|
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
|
||||||
// @Document The outbound tag for this Server
|
// @Document The outbound tag for this Server
|
||||||
//@Type id.outboundTag
|
//@Type id.outboundTag
|
||||||
OutboundTag string `protobuf:"bytes,4,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"`
|
OutboundTag string `protobuf:"bytes,4,opt,name=outbound_tag,json=outboundTag,proto3" json:"outbound_tag,omitempty"`
|
||||||
|
// @Document The time this outbound is known to be alive
|
||||||
|
//@Type id.outboundTag
|
||||||
|
LastSeenTime int64 `protobuf:"varint,5,opt,name=last_seen_time,json=lastSeenTime,proto3" json:"last_seen_time,omitempty"`
|
||||||
|
// @Document The time this outbound is tried
|
||||||
|
//@Type id.outboundTag
|
||||||
|
LastTryTime int64 `protobuf:"varint,6,opt,name=last_try_time,json=lastTryTime,proto3" json:"last_try_time,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *OutboundStatus) Reset() {
|
func (x *OutboundStatus) Reset() {
|
||||||
*x = OutboundStatus{}
|
*x = OutboundStatus{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[0]
|
mi := &file_app_observatory_config_proto_msgTypes[1]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -61,7 +114,7 @@ func (x *OutboundStatus) String() string {
|
|||||||
func (*OutboundStatus) ProtoMessage() {}
|
func (*OutboundStatus) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *OutboundStatus) ProtoReflect() protoreflect.Message {
|
func (x *OutboundStatus) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[0]
|
mi := &file_app_observatory_config_proto_msgTypes[1]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -74,7 +127,7 @@ func (x *OutboundStatus) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use OutboundStatus.ProtoReflect.Descriptor instead.
|
// Deprecated: Use OutboundStatus.ProtoReflect.Descriptor instead.
|
||||||
func (*OutboundStatus) Descriptor() ([]byte, []int) {
|
func (*OutboundStatus) Descriptor() ([]byte, []int) {
|
||||||
return file_app_observatory_config_proto_rawDescGZIP(), []int{0}
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *OutboundStatus) GetAlive() bool {
|
func (x *OutboundStatus) GetAlive() bool {
|
||||||
@ -84,7 +137,7 @@ func (x *OutboundStatus) GetAlive() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *OutboundStatus) GetDelay() uint32 {
|
func (x *OutboundStatus) GetDelay() int64 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Delay
|
return x.Delay
|
||||||
}
|
}
|
||||||
@ -105,6 +158,90 @@ func (x *OutboundStatus) GetOutboundTag() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *OutboundStatus) GetLastSeenTime() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.LastSeenTime
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *OutboundStatus) GetLastTryTime() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.LastTryTime
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProbeResult struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
// @Document Whether this outbound is usable
|
||||||
|
//@Restriction ReadOnlyForUser
|
||||||
|
Alive bool `protobuf:"varint,1,opt,name=alive,proto3" json:"alive,omitempty"`
|
||||||
|
// @Document The time for probe request to finish.
|
||||||
|
//@Type time.ms
|
||||||
|
//@Restriction ReadOnlyForUser
|
||||||
|
Delay int64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"`
|
||||||
|
// @Document The error caused this outbound failed to relay probe request
|
||||||
|
//@Restriction NotMachineReadable
|
||||||
|
LastErrorReason string `protobuf:"bytes,3,opt,name=last_error_reason,json=lastErrorReason,proto3" json:"last_error_reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ProbeResult) Reset() {
|
||||||
|
*x = ProbeResult{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_app_observatory_config_proto_msgTypes[2]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ProbeResult) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*ProbeResult) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *ProbeResult) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_observatory_config_proto_msgTypes[2]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use ProbeResult.ProtoReflect.Descriptor instead.
|
||||||
|
func (*ProbeResult) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ProbeResult) GetAlive() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.Alive
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ProbeResult) GetDelay() int64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.Delay
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *ProbeResult) GetLastErrorReason() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.LastErrorReason
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type Intensity struct {
|
type Intensity struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@ -118,7 +255,7 @@ type Intensity struct {
|
|||||||
func (x *Intensity) Reset() {
|
func (x *Intensity) Reset() {
|
||||||
*x = Intensity{}
|
*x = Intensity{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[1]
|
mi := &file_app_observatory_config_proto_msgTypes[3]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -131,7 +268,7 @@ func (x *Intensity) String() string {
|
|||||||
func (*Intensity) ProtoMessage() {}
|
func (*Intensity) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Intensity) ProtoReflect() protoreflect.Message {
|
func (x *Intensity) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[1]
|
mi := &file_app_observatory_config_proto_msgTypes[3]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -144,7 +281,7 @@ func (x *Intensity) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Intensity.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Intensity.ProtoReflect.Descriptor instead.
|
||||||
func (*Intensity) Descriptor() ([]byte, []int) {
|
func (*Intensity) Descriptor() ([]byte, []int) {
|
||||||
return file_app_observatory_config_proto_rawDescGZIP(), []int{1}
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Intensity) GetProbeInterval() uint32 {
|
func (x *Intensity) GetProbeInterval() uint32 {
|
||||||
@ -166,7 +303,7 @@ type Config struct {
|
|||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
*x = Config{}
|
*x = Config{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[2]
|
mi := &file_app_observatory_config_proto_msgTypes[4]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@ -179,7 +316,7 @@ func (x *Config) String() string {
|
|||||||
func (*Config) ProtoMessage() {}
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Config) ProtoReflect() protoreflect.Message {
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_app_observatory_config_proto_msgTypes[2]
|
mi := &file_app_observatory_config_proto_msgTypes[4]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@ -192,7 +329,7 @@ func (x *Config) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
func (*Config) Descriptor() ([]byte, []int) {
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
return file_app_observatory_config_proto_rawDescGZIP(), []int{2}
|
return file_app_observatory_config_proto_rawDescGZIP(), []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetSubjectSelector() []string {
|
func (x *Config) GetSubjectSelector() []string {
|
||||||
@ -208,30 +345,47 @@ var file_app_observatory_config_proto_rawDesc = []byte{
|
|||||||
0x0a, 0x1c, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
|
0x0a, 0x1c, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72,
|
||||||
0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a,
|
0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a,
|
||||||
0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
|
0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f,
|
||||||
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x8b, 0x01, 0x0a, 0x0e, 0x4f,
|
0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x57, 0x0a, 0x11, 0x4f, 0x62,
|
||||||
0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a,
|
0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12,
|
||||||
0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c,
|
0x42, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01,
|
0x2a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70,
|
||||||
0x28, 0x0d, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73,
|
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x4f, 0x75, 0x74,
|
||||||
0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03,
|
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52,
|
0x74, 0x75, 0x73, 0x22, 0xd5, 0x01, 0x0a, 0x0e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64,
|
||||||
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e,
|
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18,
|
||||||
0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74,
|
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05,
|
||||||
0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61, 0x67, 0x22, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65,
|
0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x64, 0x65, 0x6c,
|
||||||
0x6e, 0x73, 0x69, 0x74, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69,
|
0x61, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70,
|
0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c,
|
||||||
0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x33, 0x0a, 0x06,
|
0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x21,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63,
|
0x0a, 0x0c, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x04,
|
||||||
0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x54, 0x61,
|
||||||
0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f,
|
0x67, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x5f, 0x74,
|
||||||
0x72, 0x42, 0x6f, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63,
|
0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x6c, 0x61, 0x73, 0x74, 0x53,
|
||||||
0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74,
|
0x65, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6c, 0x61, 0x73, 0x74, 0x5f,
|
||||||
0x6f, 0x72, 0x79, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
0x74, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b,
|
||||||
0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c, 0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
|
0x6c, 0x61, 0x73, 0x74, 0x54, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x65, 0x0a, 0x0b, 0x50,
|
||||||
0x72, 0x65, 0x2f, 0x76, 0x34, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76,
|
0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c,
|
||||||
0x61, 0x74, 0x6f, 0x72, 0x79, 0xaa, 0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f,
|
0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65,
|
||||||
0x72, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
|
0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||||
0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65,
|
||||||
|
0x72, 0x72, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||||
|
0x09, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x61, 0x73,
|
||||||
|
0x6f, 0x6e, 0x22, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x12,
|
||||||
|
0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
|
||||||
|
0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x22, 0x33, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
|
0x12, 0x29, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x73, 0x65, 0x6c, 0x65,
|
||||||
|
0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a,
|
||||||
|
0x65, 0x63, 0x74, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x6f, 0x0a, 0x1e, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x70,
|
||||||
|
0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x50, 0x01, 0x5a,
|
||||||
|
0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x32, 0x66, 0x6c,
|
||||||
|
0x79, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x34, 0x2f,
|
||||||
|
0x61, 0x70, 0x70, 0x2f, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0xaa,
|
||||||
|
0x02, 0x1a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x70, 0x70,
|
||||||
|
0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -246,18 +400,21 @@ func file_app_observatory_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_app_observatory_config_proto_rawDescData
|
return file_app_observatory_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_app_observatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
var file_app_observatory_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||||
var file_app_observatory_config_proto_goTypes = []interface{}{
|
var file_app_observatory_config_proto_goTypes = []interface{}{
|
||||||
(*OutboundStatus)(nil), // 0: v2ray.core.app.observatory.OutboundStatus
|
(*ObservationResult)(nil), // 0: v2ray.core.app.observatory.ObservationResult
|
||||||
(*Intensity)(nil), // 1: v2ray.core.app.observatory.Intensity
|
(*OutboundStatus)(nil), // 1: v2ray.core.app.observatory.OutboundStatus
|
||||||
(*Config)(nil), // 2: v2ray.core.app.observatory.Config
|
(*ProbeResult)(nil), // 2: v2ray.core.app.observatory.ProbeResult
|
||||||
|
(*Intensity)(nil), // 3: v2ray.core.app.observatory.Intensity
|
||||||
|
(*Config)(nil), // 4: v2ray.core.app.observatory.Config
|
||||||
}
|
}
|
||||||
var file_app_observatory_config_proto_depIdxs = []int32{
|
var file_app_observatory_config_proto_depIdxs = []int32{
|
||||||
0, // [0:0] is the sub-list for method output_type
|
1, // 0: v2ray.core.app.observatory.ObservationResult.status:type_name -> v2ray.core.app.observatory.OutboundStatus
|
||||||
0, // [0:0] is the sub-list for method input_type
|
1, // [1:1] is the sub-list for method output_type
|
||||||
0, // [0:0] is the sub-list for extension type_name
|
1, // [1:1] is the sub-list for method input_type
|
||||||
0, // [0:0] is the sub-list for extension extendee
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
0, // [0:0] is the sub-list for field type_name
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_app_observatory_config_proto_init() }
|
func init() { file_app_observatory_config_proto_init() }
|
||||||
@ -267,7 +424,7 @@ func file_app_observatory_config_proto_init() {
|
|||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
if !protoimpl.UnsafeEnabled {
|
||||||
file_app_observatory_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
file_app_observatory_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*OutboundStatus); i {
|
switch v := v.(*ObservationResult); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@ -279,7 +436,7 @@ func file_app_observatory_config_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_observatory_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
file_app_observatory_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Intensity); i {
|
switch v := v.(*OutboundStatus); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@ -291,6 +448,30 @@ func file_app_observatory_config_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_app_observatory_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
file_app_observatory_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*ProbeResult); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_app_observatory_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*Intensity); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_app_observatory_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*Config); i {
|
switch v := v.(*Config); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@ -309,7 +490,7 @@ func file_app_observatory_config_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_app_observatory_config_proto_rawDesc,
|
RawDescriptor: file_app_observatory_config_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 3,
|
NumMessages: 5,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,7 @@ message OutboundStatus{
|
|||||||
@Type time.ms
|
@Type time.ms
|
||||||
@Restriction ReadOnlyForUser
|
@Restriction ReadOnlyForUser
|
||||||
*/
|
*/
|
||||||
uint32 delay = 2;
|
int64 delay = 2;
|
||||||
/* @Document The last error caused this outbound failed to relay probe request
|
/* @Document The last error caused this outbound failed to relay probe request
|
||||||
@Restriction NotMachineReadable
|
@Restriction NotMachineReadable
|
||||||
*/
|
*/
|
||||||
@ -38,6 +38,22 @@ message OutboundStatus{
|
|||||||
int64 last_try_time = 6;
|
int64 last_try_time = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ProbeResult{
|
||||||
|
/* @Document Whether this outbound is usable
|
||||||
|
@Restriction ReadOnlyForUser
|
||||||
|
*/
|
||||||
|
bool alive = 1;
|
||||||
|
/* @Document The time for probe request to finish.
|
||||||
|
@Type time.ms
|
||||||
|
@Restriction ReadOnlyForUser
|
||||||
|
*/
|
||||||
|
int64 delay = 2;
|
||||||
|
/* @Document The error caused this outbound failed to relay probe request
|
||||||
|
@Restriction NotMachineReadable
|
||||||
|
*/
|
||||||
|
string last_error_reason = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message Intensity{
|
message Intensity{
|
||||||
/* @Document The time interval for a probe request in ms.
|
/* @Document The time interval for a probe request in ms.
|
||||||
@Type time.ms
|
@Type time.ms
|
||||||
|
@ -2,10 +2,20 @@ package observatory
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
core "github.com/v2fly/v2ray-core/v4"
|
||||||
|
"github.com/v2fly/v2ray-core/v4/common"
|
||||||
|
v2net "github.com/v2fly/v2ray-core/v4/common/net"
|
||||||
"github.com/v2fly/v2ray-core/v4/common/signal/done"
|
"github.com/v2fly/v2ray-core/v4/common/signal/done"
|
||||||
|
"github.com/v2fly/v2ray-core/v4/common/task"
|
||||||
"github.com/v2fly/v2ray-core/v4/features/extension"
|
"github.com/v2fly/v2ray-core/v4/features/extension"
|
||||||
"github.com/v2fly/v2ray-core/v4/features/outbound"
|
"github.com/v2fly/v2ray-core/v4/features/outbound"
|
||||||
|
"github.com/v2fly/v2ray-core/v4/transport/internet/tagged"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Observer struct {
|
type Observer struct {
|
||||||
@ -13,13 +23,17 @@ type Observer struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
statusLock sync.Mutex
|
statusLock sync.Mutex
|
||||||
status []OutboundStatus
|
status []*OutboundStatus
|
||||||
|
|
||||||
finished *done.Instance
|
finished *done.Instance
|
||||||
|
|
||||||
ohm outbound.Manager
|
ohm outbound.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
|
||||||
|
return &ObservationResult{Status: o.status}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Observer) Type() interface{} {
|
func (o *Observer) Type() interface{} {
|
||||||
return extension.ObservatoryType()
|
return extension.ObservatoryType()
|
||||||
}
|
}
|
||||||
@ -41,13 +55,136 @@ func (o *Observer) background() {
|
|||||||
newError("outbound.Manager is not a HandlerSelector").WriteToLog()
|
newError("outbound.Manager is not a HandlerSelector").WriteToLog()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
outbounds := hs.Select(o.config.SubjectSelector)
|
outbounds := hs.Select(o.config.SubjectSelector)
|
||||||
|
|
||||||
|
o.updateStatus(outbounds)
|
||||||
|
|
||||||
|
for _, v := range outbounds {
|
||||||
|
result := o.probe(v)
|
||||||
|
o.updateStatusForResult(v, &result)
|
||||||
|
if o.finished.Done() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second * 10)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *Observer) updateStatus(outbounds []string) {
|
func (o *Observer) updateStatus(outbounds []string) {
|
||||||
o.statusLock.Lock()
|
o.statusLock.Lock()
|
||||||
defer o.statusLock.Unlock()
|
defer o.statusLock.Unlock()
|
||||||
|
//TODO should remove old inbound that is removed
|
||||||
o.status
|
}
|
||||||
|
|
||||||
|
func (o *Observer) probe(outbound string) ProbeResult {
|
||||||
|
httpTransport := http.Transport{
|
||||||
|
Proxy: func(*http.Request) (*url.URL, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
|
||||||
|
var connection net.Conn
|
||||||
|
taskErr := task.Run(ctx, func() error {
|
||||||
|
//MUST use V2Fly's built in context system
|
||||||
|
dest, err := v2net.ParseDestination(network + ":" + addr)
|
||||||
|
if err != nil {
|
||||||
|
return newError("cannot understand address").Base(err)
|
||||||
|
}
|
||||||
|
conn, err := tagged.Dialer(o.ctx, dest, outbound)
|
||||||
|
if err != nil {
|
||||||
|
return newError("cannot dial remote address", dest).Base(err)
|
||||||
|
}
|
||||||
|
connection = conn
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if taskErr != nil {
|
||||||
|
return nil, newError("cannot finish connection").Base(taskErr)
|
||||||
|
}
|
||||||
|
return connection, nil
|
||||||
|
},
|
||||||
|
TLSHandshakeTimeout: time.Duration(time.Second * 5),
|
||||||
|
}
|
||||||
|
httpClient := &http.Client{
|
||||||
|
Transport: &httpTransport,
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
},
|
||||||
|
Jar: nil,
|
||||||
|
Timeout: time.Duration(time.Second * 5),
|
||||||
|
}
|
||||||
|
var GETTime time.Duration
|
||||||
|
err := task.Run(o.ctx, func() error {
|
||||||
|
startTime := time.Now()
|
||||||
|
response, err := httpClient.Get("https://api.v2fly.org/checkConnection.svgz")
|
||||||
|
if err != nil {
|
||||||
|
return newError("outbound failed to relay connection").Base(err)
|
||||||
|
}
|
||||||
|
if response.Body != nil {
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
endTime := time.Now()
|
||||||
|
GETTime = endTime.Sub(startTime)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
newError("the outbound ", outbound, "is dead:").Base(err).AtInfo().WriteToLog()
|
||||||
|
return ProbeResult{Alive: false, LastErrorReason: err.Error()}
|
||||||
|
}
|
||||||
|
newError("the outbound ", outbound, "is alive:", GETTime.Seconds()).AtInfo().WriteToLog()
|
||||||
|
return ProbeResult{Alive: true, Delay: GETTime.Milliseconds()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Observer) updateStatusForResult(outbound string, result *ProbeResult) {
|
||||||
|
o.statusLock.Lock()
|
||||||
|
defer o.statusLock.Unlock()
|
||||||
|
var status *OutboundStatus
|
||||||
|
if location := o.findStatusLocationLockHolderOnly(outbound); location != -1 {
|
||||||
|
status = o.status[location]
|
||||||
|
} else {
|
||||||
|
status = &OutboundStatus{}
|
||||||
|
o.status = append(o.status, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
status.LastTryTime = time.Now().Unix()
|
||||||
|
status.OutboundTag = outbound
|
||||||
|
status.Alive = result.Alive
|
||||||
|
if result.Alive {
|
||||||
|
status.Delay = result.Delay
|
||||||
|
status.LastSeenTime = status.LastTryTime
|
||||||
|
status.LastErrorReason = ""
|
||||||
|
} else {
|
||||||
|
status.LastErrorReason = result.LastErrorReason
|
||||||
|
status.Delay = 99999999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Observer) findStatusLocationLockHolderOnly(outbound string) int {
|
||||||
|
for i, v := range o.status {
|
||||||
|
if v.OutboundTag == outbound {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, config *Config) (*Observer, error) {
|
||||||
|
var outboundManager outbound.Manager
|
||||||
|
err := core.RequireFeatures(ctx, func(om outbound.Manager) {
|
||||||
|
outboundManager = om
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, newError("Cannot get depended features").Base(err)
|
||||||
|
}
|
||||||
|
return &Observer{
|
||||||
|
config: config,
|
||||||
|
ctx: ctx,
|
||||||
|
ohm: outboundManager,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return New(ctx, config.(*Config))
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user