From 4177c00e751b1f5008bc463da489194118ce6dff Mon Sep 17 00:00:00 2001 From: lorsan Date: Sat, 4 Apr 2026 20:19:16 +0300 Subject: [PATCH 1/7] feat: base grpc system and base use --- api/gen/homeops/hub.pb.go | 128 +++++++++++++++++++++++++++++++++ api/gen/homeops/hub_grpc.pb.go | 122 +++++++++++++++++++++++++++++++ api/proto/homeops/hub.proto | 13 ++++ cmd/agent/main.go | 31 ++++++++ cmd/hub/main.go | 24 +++++++ go.mod | 14 +++- go.sum | 41 ++++++++++- internal/agent/grpc/client.go | 1 + internal/hub/grpc/server.go | 19 +++++ 9 files changed, 388 insertions(+), 5 deletions(-) create mode 100644 api/gen/homeops/hub.pb.go create mode 100644 api/gen/homeops/hub_grpc.pb.go create mode 100644 api/proto/homeops/hub.proto create mode 100644 internal/agent/grpc/client.go create mode 100644 internal/hub/grpc/server.go diff --git a/api/gen/homeops/hub.pb.go b/api/gen/homeops/hub.pb.go new file mode 100644 index 0000000..d002a53 --- /dev/null +++ b/api/gen/homeops/hub.pb.go @@ -0,0 +1,128 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v7.34.1 +// source: homeops/hub.proto + +package homeops + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type PongResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Pong string `protobuf:"bytes,1,opt,name=pong,proto3" json:"pong,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PongResponse) Reset() { + *x = PongResponse{} + mi := &file_homeops_hub_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PongResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PongResponse) ProtoMessage() {} + +func (x *PongResponse) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PongResponse.ProtoReflect.Descriptor instead. +func (*PongResponse) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{0} +} + +func (x *PongResponse) GetPong() string { + if x != nil { + return x.Pong + } + return "" +} + +var File_homeops_hub_proto protoreflect.FileDescriptor + +const file_homeops_hub_proto_rawDesc = "" + + "\n" + + "\x11homeops/hub.proto\x1a\x1bgoogle/protobuf/empty.proto\"\"\n" + + "\fPongResponse\x12\x12\n" + + "\x04pong\x18\x01 \x01(\tR\x04pong26\n" + + "\x03Hub\x12/\n" + + "\x04Ping\x12\x16.google.protobuf.Empty\x1a\r.PongResponse\"\x00B google.protobuf.Empty + 0, // 1: Hub.Ping:output_type -> PongResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_homeops_hub_proto_init() } +func file_homeops_hub_proto_init() { + if File_homeops_hub_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_homeops_hub_proto_rawDesc), len(file_homeops_hub_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_homeops_hub_proto_goTypes, + DependencyIndexes: file_homeops_hub_proto_depIdxs, + MessageInfos: file_homeops_hub_proto_msgTypes, + }.Build() + File_homeops_hub_proto = out.File + file_homeops_hub_proto_goTypes = nil + file_homeops_hub_proto_depIdxs = nil +} diff --git a/api/gen/homeops/hub_grpc.pb.go b/api/gen/homeops/hub_grpc.pb.go new file mode 100644 index 0000000..7a39a48 --- /dev/null +++ b/api/gen/homeops/hub_grpc.pb.go @@ -0,0 +1,122 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc v7.34.1 +// source: homeops/hub.proto + +package homeops + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Hub_Ping_FullMethodName = "/Hub/Ping" +) + +// HubClient is the client API for Hub service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HubClient interface { + Ping(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PongResponse, error) +} + +type hubClient struct { + cc grpc.ClientConnInterface +} + +func NewHubClient(cc grpc.ClientConnInterface) HubClient { + return &hubClient{cc} +} + +func (c *hubClient) Ping(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PongResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(PongResponse) + err := c.cc.Invoke(ctx, Hub_Ping_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HubServer is the server API for Hub service. +// All implementations must embed UnimplementedHubServer +// for forward compatibility. +type HubServer interface { + Ping(context.Context, *emptypb.Empty) (*PongResponse, error) + mustEmbedUnimplementedHubServer() +} + +// UnimplementedHubServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedHubServer struct{} + +func (UnimplementedHubServer) Ping(context.Context, *emptypb.Empty) (*PongResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Ping not implemented") +} +func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {} +func (UnimplementedHubServer) testEmbeddedByValue() {} + +// UnsafeHubServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HubServer will +// result in compilation errors. +type UnsafeHubServer interface { + mustEmbedUnimplementedHubServer() +} + +func RegisterHubServer(s grpc.ServiceRegistrar, srv HubServer) { + // If the following call panics, it indicates UnimplementedHubServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Hub_ServiceDesc, srv) +} + +func _Hub_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HubServer).Ping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Hub_Ping_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HubServer).Ping(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// Hub_ServiceDesc is the grpc.ServiceDesc for Hub service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Hub_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "Hub", + HandlerType: (*HubServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Ping", + Handler: _Hub_Ping_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "homeops/hub.proto", +} diff --git a/api/proto/homeops/hub.proto b/api/proto/homeops/hub.proto new file mode 100644 index 0000000..3a45528 --- /dev/null +++ b/api/proto/homeops/hub.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +import "google/protobuf/empty.proto"; + +option go_package = "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops;homeops"; + +service Hub { + rpc Ping (google.protobuf.Empty) returns (PongResponse) {} +} + +message PongResponse { + string pong = 1; +} \ No newline at end of file diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 06ab7d0..9b9e4c9 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -1 +1,32 @@ package main + +import ( + "context" + "log" + "time" + + "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/types/known/emptypb" +) + +func main() { + conn, err := grpc.Dial("127.0.0.1:6756", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("dial: %v", err) + } + defer conn.Close() + + client := homeops.NewHubClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + resp, err := client.Ping(ctx, &emptypb.Empty{}) + if err != nil { + log.Fatalf("dial: %v", err) + } + + defer cancel() + + log.Printf("pong: %+v", resp.Pong) +} diff --git a/cmd/hub/main.go b/cmd/hub/main.go index 06ab7d0..b26c383 100644 --- a/cmd/hub/main.go +++ b/cmd/hub/main.go @@ -1 +1,25 @@ package main + +import ( + "log" + "net" + + "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + grpcserver "github.com/lorsanstand/HomeOps-Hub/internal/hub/grpc" + "google.golang.org/grpc" +) + +func main() { + lis, err := net.Listen("tcp", ":6756") + if err != nil { + return + } + + grpcServer := grpc.NewServer() + + srv := &grpcserver.Server{} + homeops.RegisterHubServer(grpcServer, srv) + + log.Println("Start serve") + grpcServer.Serve(lis) +} diff --git a/go.mod b/go.mod index 9b04953..c165978 100644 --- a/go.mod +++ b/go.mod @@ -2,14 +2,22 @@ module github.com/lorsanstand/HomeOps-Hub go 1.26.1 +require ( + github.com/ilyakaznacheev/cleanenv v1.5.0 + github.com/rs/zerolog v1.35.0 + google.golang.org/grpc v1.80.0 + google.golang.org/protobuf v1.36.11 +) + require ( github.com/BurntSushi/toml v1.2.1 // indirect - github.com/ilyakaznacheev/cleanenv v1.5.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/rs/zerolog v1.35.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect ) diff --git a/go.sum b/go.sum index fc6972b..f0de907 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,17 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2lOrsHHvr4= github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -10,9 +22,34 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI= github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/agent/grpc/client.go b/internal/agent/grpc/client.go new file mode 100644 index 0000000..21e034e --- /dev/null +++ b/internal/agent/grpc/client.go @@ -0,0 +1 @@ +package grpc diff --git a/internal/hub/grpc/server.go b/internal/hub/grpc/server.go new file mode 100644 index 0000000..60b2bf3 --- /dev/null +++ b/internal/hub/grpc/server.go @@ -0,0 +1,19 @@ +package grpc + +import ( + "context" + "log" + + "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + "google.golang.org/protobuf/types/known/emptypb" +) + +type Server struct { + homeops.UnimplementedHubServer +} + +func (s *Server) Ping(ctx context.Context, _ *emptypb.Empty) (*homeops.PongResponse, error) { + log.Println("Answer") + return &homeops.PongResponse{Pong: "Huiiii"}, nil + +} From 02f6bb6813789dc987189eee13a47e53dad03977 Mon Sep 17 00:00:00 2001 From: lorsan Date: Sat, 4 Apr 2026 20:35:41 +0300 Subject: [PATCH 2/7] refactor: init log --- cmd/hub/main.go | 21 --------------------- internal/hub/app/app.go | 11 +++++++++++ internal/hub/grpc/server.go | 2 -- internal/shared/{cfg => config}/config.go | 2 +- internal/shared/log/init.go | 16 +++++++++------- 5 files changed, 21 insertions(+), 31 deletions(-) create mode 100644 internal/hub/app/app.go rename internal/shared/{cfg => config}/config.go (98%) diff --git a/cmd/hub/main.go b/cmd/hub/main.go index b26c383..da29a2c 100644 --- a/cmd/hub/main.go +++ b/cmd/hub/main.go @@ -1,25 +1,4 @@ package main -import ( - "log" - "net" - - "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" - grpcserver "github.com/lorsanstand/HomeOps-Hub/internal/hub/grpc" - "google.golang.org/grpc" -) - func main() { - lis, err := net.Listen("tcp", ":6756") - if err != nil { - return - } - - grpcServer := grpc.NewServer() - - srv := &grpcserver.Server{} - homeops.RegisterHubServer(grpcServer, srv) - - log.Println("Start serve") - grpcServer.Serve(lis) } diff --git a/internal/hub/app/app.go b/internal/hub/app/app.go new file mode 100644 index 0000000..48bacef --- /dev/null +++ b/internal/hub/app/app.go @@ -0,0 +1,11 @@ +package app + +import ( + "github.com/lorsanstand/HomeOps-Hub/internal/shared/config" + "github.com/rs/zerolog" +) + +type App struct { + cfg *config.Config + log *zerolog.Logger +} diff --git a/internal/hub/grpc/server.go b/internal/hub/grpc/server.go index 60b2bf3..0083b07 100644 --- a/internal/hub/grpc/server.go +++ b/internal/hub/grpc/server.go @@ -2,7 +2,6 @@ package grpc import ( "context" - "log" "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" "google.golang.org/protobuf/types/known/emptypb" @@ -13,7 +12,6 @@ type Server struct { } func (s *Server) Ping(ctx context.Context, _ *emptypb.Empty) (*homeops.PongResponse, error) { - log.Println("Answer") return &homeops.PongResponse{Pong: "Huiiii"}, nil } diff --git a/internal/shared/cfg/config.go b/internal/shared/config/config.go similarity index 98% rename from internal/shared/cfg/config.go rename to internal/shared/config/config.go index dfd9754..1981df0 100644 --- a/internal/shared/cfg/config.go +++ b/internal/shared/config/config.go @@ -1,4 +1,4 @@ -package cfg +package config import ( "fmt" diff --git a/internal/shared/log/init.go b/internal/shared/log/init.go index f44fc6b..8e4661a 100644 --- a/internal/shared/log/init.go +++ b/internal/shared/log/init.go @@ -1,11 +1,11 @@ package log import ( + "io" "os" "time" "github.com/rs/zerolog" - "github.com/rs/zerolog/log" ) type cfgLogStore interface { @@ -13,15 +13,17 @@ type cfgLogStore interface { GetMode() string } -func Init(cfg cfgLogStore) { - zerolog.TimeFieldFormat = zerolog.TimeFormatUnix +func NewLogger(cfg cfgLogStore) zerolog.Logger { + var output io.Writer = os.Stdout if cfg.GetMode() != "PROD" { - log.Logger = log.Output(zerolog.ConsoleWriter{ + output = zerolog.ConsoleWriter{ Out: os.Stdout, TimeFormat: time.Kitchen, - }) - } else { - zerolog.SetGlobalLevel(zerolog.InfoLevel) + } } + + level := cfg.GetLogLevel() + + return zerolog.New(output).Level(level).With().Timestamp().Logger() } From 602175481fb1d5a0fccd6ee355f38c54298c7cc0 Mon Sep 17 00:00:00 2001 From: lorsan Date: Sat, 4 Apr 2026 21:16:39 +0300 Subject: [PATCH 3/7] refractor: hub conn --- .gitignore | 1 + cmd/hub/main.go | 5 ++++ internal/hub/app/app.go | 42 +++++++++++++++++++++++++++++++- internal/hub/grpc/server.go | 12 ++++++--- internal/shared/config/config.go | 11 +++++---- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index e69de29..2eea525 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/cmd/hub/main.go b/cmd/hub/main.go index da29a2c..afc06ec 100644 --- a/cmd/hub/main.go +++ b/cmd/hub/main.go @@ -1,4 +1,9 @@ package main +import "github.com/lorsanstand/HomeOps-Hub/internal/hub/app" + func main() { + start := app.NewApp() + + start.Run() } diff --git a/internal/hub/app/app.go b/internal/hub/app/app.go index 48bacef..55e6989 100644 --- a/internal/hub/app/app.go +++ b/internal/hub/app/app.go @@ -1,11 +1,51 @@ package app import ( + "fmt" + "net" + + pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + grpcserv "github.com/lorsanstand/HomeOps-Hub/internal/hub/grpc" "github.com/lorsanstand/HomeOps-Hub/internal/shared/config" + "github.com/lorsanstand/HomeOps-Hub/internal/shared/log" "github.com/rs/zerolog" + "google.golang.org/grpc" ) type App struct { cfg *config.Config - log *zerolog.Logger + log zerolog.Logger +} + +func NewApp() *App { + cfg, err := config.NewConfig() + if err != nil { + fmt.Errorf("failed get config: %v", err) + } + + logger := log.NewLogger(cfg) + + return &App{cfg: cfg, log: logger} +} + +func (a *App) Run() { + address := fmt.Sprintf("http://0.0.0.0:%v", a.cfg.Port) + + lis, err := net.Listen("tcp", fmt.Sprintf(":%v", a.cfg.Port)) + if err != nil { + a.log.Error().Err(err).Msg("failed started listen") + return + } + + grpcServer := grpc.NewServer() + pb.RegisterHubServer(grpcServer, grpcserv.NewServer()) + + a.log.Info().Str("address", address).Msg("server started") + + err = grpcServer.Serve(lis) + if err != nil { + a.log.Error().Err(err).Msg("failed started grpc server") + return + } + } diff --git a/internal/hub/grpc/server.go b/internal/hub/grpc/server.go index 0083b07..9fccb5a 100644 --- a/internal/hub/grpc/server.go +++ b/internal/hub/grpc/server.go @@ -3,15 +3,19 @@ package grpc import ( "context" - "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" "google.golang.org/protobuf/types/known/emptypb" ) type Server struct { - homeops.UnimplementedHubServer + pb.UnimplementedHubServer } -func (s *Server) Ping(ctx context.Context, _ *emptypb.Empty) (*homeops.PongResponse, error) { - return &homeops.PongResponse{Pong: "Huiiii"}, nil +func NewServer() *Server { + return &Server{} +} + +func (s *Server) Ping(ctx context.Context, _ *emptypb.Empty) (*pb.PongResponse, error) { + return &pb.PongResponse{Pong: "Pong"}, nil } diff --git a/internal/shared/config/config.go b/internal/shared/config/config.go index 1981df0..5ce002d 100644 --- a/internal/shared/config/config.go +++ b/internal/shared/config/config.go @@ -9,13 +9,14 @@ import ( ) type Config struct { - DBHost string `env:"DB_HOST" env-required:"true"` - DBPort int `env:"DB_PORT" env-required:"true"` - DBPassword string `env:"DB_PASS" env-required:"true"` - DBUser string `env:"DB_USER" env-required:"true"` - DBName string `env:"DB_NAME" env-required:"true"` + DBHost string `env:"DB_HOST"` + DBPort int `env:"DB_PORT"` + DBPassword string `env:"DB_PASS"` + DBUser string `env:"DB_USER"` + DBName string `env:"DB_NAME"` LogLevel string `env:"LOG_LEVEL" env-default:"INFO"` Mode string `env:"MODE" env-default:"DEV"` + Port int `env:"PORT" env-default:"9000"` } func NewConfig() (*Config, error) { From ff01d2bedb6ca3293ad7578ad3197c7fe51d8142 Mon Sep 17 00:00:00 2001 From: lorsan Date: Sun, 5 Apr 2026 10:17:43 +0300 Subject: [PATCH 4/7] feat: GRPC method register agent --- api/gen/homeops/hub.pb.go | 154 ++++++++++++++++++++++++++++++--- api/gen/homeops/hub_grpc.pb.go | 40 ++++++++- api/proto/homeops/hub.proto | 13 +++ 3 files changed, 196 insertions(+), 11 deletions(-) diff --git a/api/gen/homeops/hub.pb.go b/api/gen/homeops/hub.pb.go index d002a53..0e317ab 100644 --- a/api/gen/homeops/hub.pb.go +++ b/api/gen/homeops/hub.pb.go @@ -66,15 +66,145 @@ func (x *PongResponse) GetPong() string { return "" } +type RegisterAgentRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + AgentId string `protobuf:"bytes,1,opt,name=agent_id,json=agentId,proto3" json:"agent_id,omitempty"` + AgentName string `protobuf:"bytes,2,opt,name=agent_name,json=agentName,proto3" json:"agent_name,omitempty"` + Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + Arch string `protobuf:"bytes,5,opt,name=arch,proto3" json:"arch,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterAgentRequest) Reset() { + *x = RegisterAgentRequest{} + mi := &file_homeops_hub_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterAgentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterAgentRequest) ProtoMessage() {} + +func (x *RegisterAgentRequest) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterAgentRequest.ProtoReflect.Descriptor instead. +func (*RegisterAgentRequest) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{1} +} + +func (x *RegisterAgentRequest) GetAgentId() string { + if x != nil { + return x.AgentId + } + return "" +} + +func (x *RegisterAgentRequest) GetAgentName() string { + if x != nil { + return x.AgentName + } + return "" +} + +func (x *RegisterAgentRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *RegisterAgentRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *RegisterAgentRequest) GetArch() string { + if x != nil { + return x.Arch + } + return "" +} + +type RegisterAgentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + HeartbeatIntervalSecond int64 `protobuf:"varint,1,opt,name=heartbeat_interval_second,json=heartbeatIntervalSecond,proto3" json:"heartbeat_interval_second,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RegisterAgentResponse) Reset() { + *x = RegisterAgentResponse{} + mi := &file_homeops_hub_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterAgentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterAgentResponse) ProtoMessage() {} + +func (x *RegisterAgentResponse) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterAgentResponse.ProtoReflect.Descriptor instead. +func (*RegisterAgentResponse) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterAgentResponse) GetHeartbeatIntervalSecond() int64 { + if x != nil { + return x.HeartbeatIntervalSecond + } + return 0 +} + var File_homeops_hub_proto protoreflect.FileDescriptor const file_homeops_hub_proto_rawDesc = "" + "\n" + "\x11homeops/hub.proto\x1a\x1bgoogle/protobuf/empty.proto\"\"\n" + "\fPongResponse\x12\x12\n" + - "\x04pong\x18\x01 \x01(\tR\x04pong26\n" + + "\x04pong\x18\x01 \x01(\tR\x04pong\"\x9a\x01\n" + + "\x14RegisterAgentRequest\x12\x19\n" + + "\bagent_id\x18\x01 \x01(\tR\aagentId\x12\x1d\n" + + "\n" + + "agent_name\x18\x02 \x01(\tR\tagentName\x12\x1a\n" + + "\bhostname\x18\x03 \x01(\tR\bhostname\x12\x18\n" + + "\aversion\x18\x04 \x01(\tR\aversion\x12\x12\n" + + "\x04arch\x18\x05 \x01(\tR\x04arch\"S\n" + + "\x15RegisterAgentResponse\x12:\n" + + "\x19heartbeat_interval_second\x18\x01 \x01(\x03R\x17heartbeatIntervalSecond2x\n" + "\x03Hub\x12/\n" + - "\x04Ping\x12\x16.google.protobuf.Empty\x1a\r.PongResponse\"\x00B google.protobuf.Empty - 0, // 1: Hub.Ping:output_type -> PongResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type + 3, // 0: Hub.Ping:input_type -> google.protobuf.Empty + 1, // 1: Hub.RegisterAgent:input_type -> RegisterAgentRequest + 0, // 2: Hub.Ping:output_type -> PongResponse + 2, // 3: Hub.RegisterAgent:output_type -> RegisterAgentResponse + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -114,7 +248,7 @@ func file_homeops_hub_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_homeops_hub_proto_rawDesc), len(file_homeops_hub_proto_rawDesc)), NumEnums: 0, - NumMessages: 1, + NumMessages: 3, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/homeops/hub_grpc.pb.go b/api/gen/homeops/hub_grpc.pb.go index 7a39a48..e032d6c 100644 --- a/api/gen/homeops/hub_grpc.pb.go +++ b/api/gen/homeops/hub_grpc.pb.go @@ -20,7 +20,8 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - Hub_Ping_FullMethodName = "/Hub/Ping" + Hub_Ping_FullMethodName = "/Hub/Ping" + Hub_RegisterAgent_FullMethodName = "/Hub/RegisterAgent" ) // HubClient is the client API for Hub service. @@ -28,6 +29,7 @@ const ( // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type HubClient interface { Ping(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PongResponse, error) + RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) } type hubClient struct { @@ -48,11 +50,22 @@ func (c *hubClient) Ping(ctx context.Context, in *emptypb.Empty, opts ...grpc.Ca return out, nil } +func (c *hubClient) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RegisterAgentResponse) + err := c.cc.Invoke(ctx, Hub_RegisterAgent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // HubServer is the server API for Hub service. // All implementations must embed UnimplementedHubServer // for forward compatibility. type HubServer interface { Ping(context.Context, *emptypb.Empty) (*PongResponse, error) + RegisterAgent(context.Context, *RegisterAgentRequest) (*RegisterAgentResponse, error) mustEmbedUnimplementedHubServer() } @@ -66,6 +79,9 @@ type UnimplementedHubServer struct{} func (UnimplementedHubServer) Ping(context.Context, *emptypb.Empty) (*PongResponse, error) { return nil, status.Error(codes.Unimplemented, "method Ping not implemented") } +func (UnimplementedHubServer) RegisterAgent(context.Context, *RegisterAgentRequest) (*RegisterAgentResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RegisterAgent not implemented") +} func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {} func (UnimplementedHubServer) testEmbeddedByValue() {} @@ -105,6 +121,24 @@ func _Hub_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{ return interceptor(ctx, in, info, handler) } +func _Hub_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterAgentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HubServer).RegisterAgent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Hub_RegisterAgent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HubServer).RegisterAgent(ctx, req.(*RegisterAgentRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Hub_ServiceDesc is the grpc.ServiceDesc for Hub service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -116,6 +150,10 @@ var Hub_ServiceDesc = grpc.ServiceDesc{ MethodName: "Ping", Handler: _Hub_Ping_Handler, }, + { + MethodName: "RegisterAgent", + Handler: _Hub_RegisterAgent_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "homeops/hub.proto", diff --git a/api/proto/homeops/hub.proto b/api/proto/homeops/hub.proto index 3a45528..4c987f2 100644 --- a/api/proto/homeops/hub.proto +++ b/api/proto/homeops/hub.proto @@ -6,8 +6,21 @@ option go_package = "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops;homeops" service Hub { rpc Ping (google.protobuf.Empty) returns (PongResponse) {} + rpc RegisterAgent (RegisterAgentRequest) returns (RegisterAgentResponse) {} } message PongResponse { string pong = 1; +} + +message RegisterAgentRequest { + string agent_id = 1; + string agent_name = 2; + string hostname = 3; + string version = 4; + string arch = 5; +} + +message RegisterAgentResponse { + int64 heartbeat_interval_second = 1; } \ No newline at end of file From acbc100928c6082c28fe331bd1166b6c6eacd7a3 Mon Sep 17 00:00:00 2001 From: lorsan Date: Sun, 5 Apr 2026 11:21:54 +0300 Subject: [PATCH 5/7] refactor: connection agent -> hub --- internal/agent/grpc/client.go | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/internal/agent/grpc/client.go b/internal/agent/grpc/client.go index 21e034e..d333f5b 100644 --- a/internal/agent/grpc/client.go +++ b/internal/agent/grpc/client.go @@ -1 +1,38 @@ package grpc + +import ( + "context" + "fmt" + "time" + + pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + "google.golang.org/grpc" + "google.golang.org/protobuf/types/known/emptypb" +) + +type HomeOpsAgent struct { + conn pb.HubClient +} + +func NewConnectAgent(address string) (*HomeOpsAgent, error) { + conn, err := grpc.NewClient(address) + if err != nil { + return nil, fmt.Errorf("failed connection hub: %v", err) + } + + client := pb.NewHubClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + resp, err := client.Ping(ctx, &emptypb.Empty{}) + if err != nil { + return nil, fmt.Errorf("failed connection hub: %v", err) + } + + if resp.Pong != "Pong" { + return nil, fmt.Errorf("failed connection hub: %v", err) + } + + return &HomeOpsAgent{conn: client}, nil +} From 306dcff31f5fb3f250aa15674e1fcae47e1c0eae Mon Sep 17 00:00:00 2001 From: lorsan Date: Sun, 5 Apr 2026 15:48:23 +0300 Subject: [PATCH 6/7] feat: update conn in hub --- api/gen/homeops/hub_grpc.pb.go | 8 ++--- cmd/agent/main.go | 30 ++--------------- internal/agent/app/app.go | 38 ++++++++++++++++++++++ internal/agent/grpc/client.go | 38 ---------------------- internal/agent/rpc/client.go | 32 ++++++++++++++++++ internal/agent/utils/config_yaml/config.go | 18 ++++++++++ internal/hub/app/app.go | 7 ++-- internal/hub/{grpc => rpc}/server.go | 2 +- 8 files changed, 100 insertions(+), 73 deletions(-) create mode 100644 internal/agent/app/app.go delete mode 100644 internal/agent/grpc/client.go create mode 100644 internal/agent/rpc/client.go rename internal/hub/{grpc => rpc}/server.go (96%) diff --git a/api/gen/homeops/hub_grpc.pb.go b/api/gen/homeops/hub_grpc.pb.go index e032d6c..f18e257 100644 --- a/api/gen/homeops/hub_grpc.pb.go +++ b/api/gen/homeops/hub_grpc.pb.go @@ -1,6 +1,6 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// Code generated by protoc-gen-go-rpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.6.1 +// - protoc-gen-go-rpc v1.6.1 // - protoc v7.34.1 // source: homeops/hub.proto @@ -15,7 +15,7 @@ import ( ) // This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. +// is compatible with the rpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 @@ -140,7 +140,7 @@ func _Hub_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec func(i } // Hub_ServiceDesc is the grpc.ServiceDesc for Hub service. -// It's only intended for direct use with grpc.RegisterService, +// It's only intended for direct use with rpc.RegisterService, // and not to be introspected or modified (even as a copy) var Hub_ServiceDesc = grpc.ServiceDesc{ ServiceName: "Hub", diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 9b9e4c9..3a3986a 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -1,32 +1,8 @@ package main -import ( - "context" - "log" - "time" - - "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/protobuf/types/known/emptypb" -) +import "github.com/lorsanstand/HomeOps-Hub/internal/hub/app" func main() { - conn, err := grpc.Dial("127.0.0.1:6756", grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - log.Fatalf("dial: %v", err) - } - defer conn.Close() - - client := homeops.NewHubClient(conn) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - resp, err := client.Ping(ctx, &emptypb.Empty{}) - if err != nil { - log.Fatalf("dial: %v", err) - } - - defer cancel() - - log.Printf("pong: %+v", resp.Pong) + start := app.NewApp() + start.Run() } diff --git a/internal/agent/app/app.go b/internal/agent/app/app.go new file mode 100644 index 0000000..5d68171 --- /dev/null +++ b/internal/agent/app/app.go @@ -0,0 +1,38 @@ +package app + +import ( + standartlog "log" + + "github.com/lorsanstand/HomeOps-Hub/internal/agent/rpc" + "github.com/lorsanstand/HomeOps-Hub/internal/agent/utils/config_yaml" + log2 "github.com/lorsanstand/HomeOps-Hub/internal/shared/log" + "github.com/rs/zerolog" +) + +type App struct { + log zerolog.Logger + cfg *config_yaml.AgentConfig + hubConn *rpc.Connection +} + +func NewApp() *App { + cfg, err := config_yaml.NewConfig() + if err != nil { + standartlog.Fatalf("failed get config: %v", err) + } + + log := log2.NewLogger(cfg) + + return &App{cfg: cfg, log: log} +} + +func (a *App) Run() { + conn, err := rpc.NewConnectAgent(a.cfg.GetGRPCAddress()) + if err != nil { + a.log.Error().Err(err) + return + } + + a.hubConn = conn + r +} diff --git a/internal/agent/grpc/client.go b/internal/agent/grpc/client.go deleted file mode 100644 index d333f5b..0000000 --- a/internal/agent/grpc/client.go +++ /dev/null @@ -1,38 +0,0 @@ -package grpc - -import ( - "context" - "fmt" - "time" - - pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" - "google.golang.org/grpc" - "google.golang.org/protobuf/types/known/emptypb" -) - -type HomeOpsAgent struct { - conn pb.HubClient -} - -func NewConnectAgent(address string) (*HomeOpsAgent, error) { - conn, err := grpc.NewClient(address) - if err != nil { - return nil, fmt.Errorf("failed connection hub: %v", err) - } - - client := pb.NewHubClient(conn) - - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - resp, err := client.Ping(ctx, &emptypb.Empty{}) - if err != nil { - return nil, fmt.Errorf("failed connection hub: %v", err) - } - - if resp.Pong != "Pong" { - return nil, fmt.Errorf("failed connection hub: %v", err) - } - - return &HomeOpsAgent{conn: client}, nil -} diff --git a/internal/agent/rpc/client.go b/internal/agent/rpc/client.go new file mode 100644 index 0000000..174d0c4 --- /dev/null +++ b/internal/agent/rpc/client.go @@ -0,0 +1,32 @@ +package rpc + +import ( + "fmt" + + pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + "google.golang.org/grpc" +) + +type Connection struct { + hub pb.HubClient + conn *grpc.ClientConn +} + +func NewConnectAgent(address string) (*Connection, error) { + conn, err := grpc.NewClient(address) + if err != nil { + return nil, fmt.Errorf("failed connection hub: %v", err) + } + + client := pb.NewHubClient(conn) + + return &Connection{hub: client, conn: conn}, nil +} + +func (c *Connection) Close() error { + return c.conn.Close() +} + +func (c *Connection) Hub() pb.HubClient { + return c.hub +} diff --git a/internal/agent/utils/config_yaml/config.go b/internal/agent/utils/config_yaml/config.go index f9c2f72..5472b34 100644 --- a/internal/agent/utils/config_yaml/config.go +++ b/internal/agent/utils/config_yaml/config.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/rs/zerolog" "gopkg.in/yaml.v3" ) @@ -13,6 +14,7 @@ type AgentConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` } `yaml:"hub"` + LogLevel string `yaml:"log_level"` } func NewConfig() (*AgentConfig, error) { @@ -29,3 +31,19 @@ func NewConfig() (*AgentConfig, error) { return &cfg, nil } + +func (c *AgentConfig) GetLogLevel() zerolog.Level { + level, err := zerolog.ParseLevel(c.LogLevel) + if err != nil { + return zerolog.InfoLevel + } + return level +} + +func (c *AgentConfig) GetMode() string { + return "PROD" +} + +func (c *AgentConfig) GetGRPCAddress() string { + return fmt.Sprintf("%v:%v", c.HubConnect.Host, c.HubConnect.Port) +} diff --git a/internal/hub/app/app.go b/internal/hub/app/app.go index 55e6989..002eacc 100644 --- a/internal/hub/app/app.go +++ b/internal/hub/app/app.go @@ -2,10 +2,11 @@ package app import ( "fmt" + standartlog "log" "net" pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" - grpcserv "github.com/lorsanstand/HomeOps-Hub/internal/hub/grpc" + grpcserv "github.com/lorsanstand/HomeOps-Hub/internal/hub/rpc" "github.com/lorsanstand/HomeOps-Hub/internal/shared/config" "github.com/lorsanstand/HomeOps-Hub/internal/shared/log" "github.com/rs/zerolog" @@ -20,7 +21,7 @@ type App struct { func NewApp() *App { cfg, err := config.NewConfig() if err != nil { - fmt.Errorf("failed get config: %v", err) + standartlog.Fatalf("failed get config: %v", err) } logger := log.NewLogger(cfg) @@ -44,7 +45,7 @@ func (a *App) Run() { err = grpcServer.Serve(lis) if err != nil { - a.log.Error().Err(err).Msg("failed started grpc server") + a.log.Error().Err(err).Msg("failed started rpc server") return } diff --git a/internal/hub/grpc/server.go b/internal/hub/rpc/server.go similarity index 96% rename from internal/hub/grpc/server.go rename to internal/hub/rpc/server.go index 9fccb5a..9a4b83f 100644 --- a/internal/hub/grpc/server.go +++ b/internal/hub/rpc/server.go @@ -1,4 +1,4 @@ -package grpc +package rpc import ( "context" From 0bd52aebeaa5066c45ee469f67a1ed04b1a36404 Mon Sep 17 00:00:00 2001 From: lorsan Date: Mon, 6 Apr 2026 11:39:19 +0300 Subject: [PATCH 7/7] refactor: hub grpc system --- .idea/.gitignore | 10 +++ .idea/HomeOps-Hub.iml | 4 + .idea/go.imports.xml | 11 +++ .idea/vcs.xml | 6 ++ api/gen/homeops/hub.pb.go | 102 ++++++++++++++++++---- api/gen/homeops/hub_grpc.pb.go | 8 +- api/proto/homeops/hub.proto | 6 ++ internal/agent/app/app.go | 1 - internal/agent/service/hub_service/hub.go | 1 + internal/hub/app/app.go | 47 +++++----- internal/hub/rpc/server.go | 21 +++-- 11 files changed, 167 insertions(+), 50 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/HomeOps-Hub.iml create mode 100644 .idea/go.imports.xml create mode 100644 .idea/vcs.xml create mode 100644 internal/agent/service/hub_service/hub.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/HomeOps-Hub.iml b/.idea/HomeOps-Hub.iml new file mode 100644 index 0000000..7ee078d --- /dev/null +++ b/.idea/HomeOps-Hub.iml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml new file mode 100644 index 0000000..d7202f0 --- /dev/null +++ b/.idea/go.imports.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/api/gen/homeops/hub.pb.go b/api/gen/homeops/hub.pb.go index 0e317ab..5f6836c 100644 --- a/api/gen/homeops/hub.pb.go +++ b/api/gen/homeops/hub.pb.go @@ -73,6 +73,7 @@ type RegisterAgentRequest struct { Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` Arch string `protobuf:"bytes,5,opt,name=arch,proto3" json:"arch,omitempty"` + Config *AgentConfig `protobuf:"bytes,6,opt,name=config,proto3" json:"config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -142,6 +143,65 @@ func (x *RegisterAgentRequest) GetArch() string { return "" } +func (x *RegisterAgentRequest) GetConfig() *AgentConfig { + if x != nil { + return x.Config + } + return nil +} + +type AgentConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + System string `protobuf:"bytes,1,opt,name=system,proto3" json:"system,omitempty"` + Docker bool `protobuf:"varint,2,opt,name=docker,proto3" json:"docker,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AgentConfig) Reset() { + *x = AgentConfig{} + mi := &file_homeops_hub_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AgentConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AgentConfig) ProtoMessage() {} + +func (x *AgentConfig) ProtoReflect() protoreflect.Message { + mi := &file_homeops_hub_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AgentConfig.ProtoReflect.Descriptor instead. +func (*AgentConfig) Descriptor() ([]byte, []int) { + return file_homeops_hub_proto_rawDescGZIP(), []int{2} +} + +func (x *AgentConfig) GetSystem() string { + if x != nil { + return x.System + } + return "" +} + +func (x *AgentConfig) GetDocker() bool { + if x != nil { + return x.Docker + } + return false +} + type RegisterAgentResponse struct { state protoimpl.MessageState `protogen:"open.v1"` HeartbeatIntervalSecond int64 `protobuf:"varint,1,opt,name=heartbeat_interval_second,json=heartbeatIntervalSecond,proto3" json:"heartbeat_interval_second,omitempty"` @@ -151,7 +211,7 @@ type RegisterAgentResponse struct { func (x *RegisterAgentResponse) Reset() { *x = RegisterAgentResponse{} - mi := &file_homeops_hub_proto_msgTypes[2] + mi := &file_homeops_hub_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -163,7 +223,7 @@ func (x *RegisterAgentResponse) String() string { func (*RegisterAgentResponse) ProtoMessage() {} func (x *RegisterAgentResponse) ProtoReflect() protoreflect.Message { - mi := &file_homeops_hub_proto_msgTypes[2] + mi := &file_homeops_hub_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -176,7 +236,7 @@ func (x *RegisterAgentResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterAgentResponse.ProtoReflect.Descriptor instead. func (*RegisterAgentResponse) Descriptor() ([]byte, []int) { - return file_homeops_hub_proto_rawDescGZIP(), []int{2} + return file_homeops_hub_proto_rawDescGZIP(), []int{3} } func (x *RegisterAgentResponse) GetHeartbeatIntervalSecond() int64 { @@ -192,14 +252,18 @@ const file_homeops_hub_proto_rawDesc = "" + "\n" + "\x11homeops/hub.proto\x1a\x1bgoogle/protobuf/empty.proto\"\"\n" + "\fPongResponse\x12\x12\n" + - "\x04pong\x18\x01 \x01(\tR\x04pong\"\x9a\x01\n" + + "\x04pong\x18\x01 \x01(\tR\x04pong\"\xc0\x01\n" + "\x14RegisterAgentRequest\x12\x19\n" + "\bagent_id\x18\x01 \x01(\tR\aagentId\x12\x1d\n" + "\n" + "agent_name\x18\x02 \x01(\tR\tagentName\x12\x1a\n" + "\bhostname\x18\x03 \x01(\tR\bhostname\x12\x18\n" + "\aversion\x18\x04 \x01(\tR\aversion\x12\x12\n" + - "\x04arch\x18\x05 \x01(\tR\x04arch\"S\n" + + "\x04arch\x18\x05 \x01(\tR\x04arch\x12$\n" + + "\x06config\x18\x06 \x01(\v2\f.AgentConfigR\x06config\"=\n" + + "\vAgentConfig\x12\x16\n" + + "\x06system\x18\x01 \x01(\tR\x06system\x12\x16\n" + + "\x06docker\x18\x02 \x01(\bR\x06docker\"S\n" + "\x15RegisterAgentResponse\x12:\n" + "\x19heartbeat_interval_second\x18\x01 \x01(\x03R\x17heartbeatIntervalSecond2x\n" + "\x03Hub\x12/\n" + @@ -218,23 +282,25 @@ func file_homeops_hub_proto_rawDescGZIP() []byte { return file_homeops_hub_proto_rawDescData } -var file_homeops_hub_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_homeops_hub_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_homeops_hub_proto_goTypes = []any{ (*PongResponse)(nil), // 0: PongResponse (*RegisterAgentRequest)(nil), // 1: RegisterAgentRequest - (*RegisterAgentResponse)(nil), // 2: RegisterAgentResponse - (*emptypb.Empty)(nil), // 3: google.protobuf.Empty + (*AgentConfig)(nil), // 2: AgentConfig + (*RegisterAgentResponse)(nil), // 3: RegisterAgentResponse + (*emptypb.Empty)(nil), // 4: google.protobuf.Empty } var file_homeops_hub_proto_depIdxs = []int32{ - 3, // 0: Hub.Ping:input_type -> google.protobuf.Empty - 1, // 1: Hub.RegisterAgent:input_type -> RegisterAgentRequest - 0, // 2: Hub.Ping:output_type -> PongResponse - 2, // 3: Hub.RegisterAgent:output_type -> RegisterAgentResponse - 2, // [2:4] is the sub-list for method output_type - 0, // [0:2] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 2, // 0: RegisterAgentRequest.config:type_name -> AgentConfig + 4, // 1: Hub.Ping:input_type -> google.protobuf.Empty + 1, // 2: Hub.RegisterAgent:input_type -> RegisterAgentRequest + 0, // 3: Hub.Ping:output_type -> PongResponse + 3, // 4: Hub.RegisterAgent:output_type -> RegisterAgentResponse + 3, // [3:5] is the sub-list for method output_type + 1, // [1:3] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension 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_homeops_hub_proto_init() } @@ -248,7 +314,7 @@ func file_homeops_hub_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_homeops_hub_proto_rawDesc), len(file_homeops_hub_proto_rawDesc)), NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/homeops/hub_grpc.pb.go b/api/gen/homeops/hub_grpc.pb.go index f18e257..e032d6c 100644 --- a/api/gen/homeops/hub_grpc.pb.go +++ b/api/gen/homeops/hub_grpc.pb.go @@ -1,6 +1,6 @@ -// Code generated by protoc-gen-go-rpc. DO NOT EDIT. +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-rpc v1.6.1 +// - protoc-gen-go-grpc v1.6.1 // - protoc v7.34.1 // source: homeops/hub.proto @@ -15,7 +15,7 @@ import ( ) // This is a compile-time assertion to ensure that this generated file -// is compatible with the rpc package it is being compiled against. +// is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 @@ -140,7 +140,7 @@ func _Hub_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec func(i } // Hub_ServiceDesc is the grpc.ServiceDesc for Hub service. -// It's only intended for direct use with rpc.RegisterService, +// It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var Hub_ServiceDesc = grpc.ServiceDesc{ ServiceName: "Hub", diff --git a/api/proto/homeops/hub.proto b/api/proto/homeops/hub.proto index 4c987f2..01c9698 100644 --- a/api/proto/homeops/hub.proto +++ b/api/proto/homeops/hub.proto @@ -19,6 +19,12 @@ message RegisterAgentRequest { string hostname = 3; string version = 4; string arch = 5; + AgentConfig config = 6; +} + +message AgentConfig { + string system = 1; + bool docker = 2; } message RegisterAgentResponse { diff --git a/internal/agent/app/app.go b/internal/agent/app/app.go index 5d68171..18f4f60 100644 --- a/internal/agent/app/app.go +++ b/internal/agent/app/app.go @@ -34,5 +34,4 @@ func (a *App) Run() { } a.hubConn = conn - r } diff --git a/internal/agent/service/hub_service/hub.go b/internal/agent/service/hub_service/hub.go new file mode 100644 index 0000000..e80229d --- /dev/null +++ b/internal/agent/service/hub_service/hub.go @@ -0,0 +1 @@ +package hub_service diff --git a/internal/hub/app/app.go b/internal/hub/app/app.go index 002eacc..0db6be1 100644 --- a/internal/hub/app/app.go +++ b/internal/hub/app/app.go @@ -5,17 +5,16 @@ import ( standartlog "log" "net" - pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" grpcserv "github.com/lorsanstand/HomeOps-Hub/internal/hub/rpc" "github.com/lorsanstand/HomeOps-Hub/internal/shared/config" "github.com/lorsanstand/HomeOps-Hub/internal/shared/log" "github.com/rs/zerolog" - "google.golang.org/grpc" ) type App struct { - cfg *config.Config - log zerolog.Logger + cfg *config.Config + log zerolog.Logger + server *grpcserv.HubHandler } func NewApp() *App { @@ -26,27 +25,31 @@ func NewApp() *App { logger := log.NewLogger(cfg) - return &App{cfg: cfg, log: logger} + server := grpcserv.NewHubHandler(logger) + + return &App{cfg: cfg, log: logger, server: server} } func (a *App) Run() { - address := fmt.Sprintf("http://0.0.0.0:%v", a.cfg.Port) - - lis, err := net.Listen("tcp", fmt.Sprintf(":%v", a.cfg.Port)) + err := a.hubServe() if err != nil { - a.log.Error().Err(err).Msg("failed started listen") - return + a.log.Error().Err(err).Msg("failed start server") } - - grpcServer := grpc.NewServer() - pb.RegisterHubServer(grpcServer, grpcserv.NewServer()) - - a.log.Info().Str("address", address).Msg("server started") - - err = grpcServer.Serve(lis) - if err != nil { - a.log.Error().Err(err).Msg("failed started rpc server") - return - } - +} + +func (a *App) hubServe() error { + address := fmt.Sprintf("0.0.0.0:%v", a.cfg.Port) + a.log.Info().Str("address", "http://"+address).Msg("start GRPC server") + + lis, err := net.Listen("tcp", address) + if err != nil { + return err + } + + err = a.server.GrpcServer.Serve(lis) + if err != nil { + return err + } + + return nil } diff --git a/internal/hub/rpc/server.go b/internal/hub/rpc/server.go index 9a4b83f..0692d17 100644 --- a/internal/hub/rpc/server.go +++ b/internal/hub/rpc/server.go @@ -4,18 +4,29 @@ import ( "context" pb "github.com/lorsanstand/HomeOps-Hub/api/gen/homeops" + "github.com/rs/zerolog" + "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" ) -type Server struct { +type HubHandler struct { pb.UnimplementedHubServer + log zerolog.Logger + GrpcServer *grpc.Server } -func NewServer() *Server { - return &Server{} +func NewHubHandler(logger zerolog.Logger) *HubHandler { + hub := &HubHandler{log: logger} + + grpcServer := grpc.NewServer() + pb.RegisterHubServer(grpcServer, hub) + + hub.GrpcServer = grpcServer + + return hub } -func (s *Server) Ping(ctx context.Context, _ *emptypb.Empty) (*pb.PongResponse, error) { +func (h *HubHandler) Ping(ctx context.Context, _ *emptypb.Empty) (*pb.PongResponse, error) { + h.log.Info().Msg("pong request") return &pb.PongResponse{Pong: "Pong"}, nil - }