From ef7b3f952c666758079184adb3ddc7f06ab67cf8 Mon Sep 17 00:00:00 2001 From: "ming.hsu" Date: Sun, 27 Mar 2022 21:32:30 +0800 Subject: [PATCH] add user service --- README.md | 5 + cmd/user.go | 23 ++ configs/config.example.yaml | 9 + deployments/docker-compose.yaml | 27 ++ deployments/prometheus/prometheus.yaml | 3 + internal/proto_gen/chat/user.pb.go | 304 ++------------------ internal/proto_gen/user/user.pb.go | 378 +++++++++++++++++++++++++ internal/wire/wire.go | 30 +- internal/wire/wire_gen.go | 41 ++- pkg/chat/grpc_rpc.go | 20 -- pkg/chat/http.go | 13 +- pkg/chat/http_api.go | 41 --- pkg/chat/repo.go | 31 -- pkg/chat/service.go | 30 +- pkg/chat/util.go | 17 -- pkg/common/util.go | 17 ++ pkg/config/config.go | 21 ++ pkg/match/closer.go | 3 + pkg/match/grpc.go | 16 ++ pkg/match/repo.go | 21 +- pkg/user/closer.go | 16 ++ pkg/user/domain.go | 6 + pkg/user/grpc.go | 53 ++++ pkg/user/grpc_rpc.go | 29 ++ pkg/user/http.go | 82 ++++++ pkg/user/http_api.go | 50 ++++ pkg/user/presenter.go | 6 + pkg/user/repo.go | 61 ++++ pkg/user/router.go | 28 ++ pkg/user/service.go | 34 +++ proto/chat/user.proto | 15 - proto/user/user.proto | 23 ++ web/assets/js/chat.js | 4 +- 33 files changed, 1009 insertions(+), 448 deletions(-) create mode 100644 cmd/user.go create mode 100644 internal/proto_gen/user/user.pb.go create mode 100644 pkg/user/closer.go create mode 100644 pkg/user/domain.go create mode 100644 pkg/user/grpc.go create mode 100644 pkg/user/grpc_rpc.go create mode 100644 pkg/user/http.go create mode 100644 pkg/user/http_api.go create mode 100644 pkg/user/presenter.go create mode 100644 pkg/user/repo.go create mode 100644 pkg/user/router.go create mode 100644 pkg/user/service.go create mode 100644 proto/user/user.proto diff --git a/README.md b/README.md index 73ce21a..060956e 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ Features: - Real-time communication and efficient websocket handling using [Melody](https://github.com/olahol/melody). - Microservices architecture. All services can be horizontally scaled on demand. - `web`: frontend server + - `user`: user account server - `match`: user matching server - `chat`: messaging server - `uploader`: file uploader @@ -30,6 +31,10 @@ System architecture: image ## Usage +Prerequisite: +- Docker-Compose v2 +- root permission + To run locally, execute the following command: ```bash cd deployments diff --git a/cmd/user.go b/cmd/user.go new file mode 100644 index 0000000..e3ec525 --- /dev/null +++ b/cmd/user.go @@ -0,0 +1,23 @@ +package cmd + +import ( + "github.com/minghsu0107/go-random-chat/internal/wire" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var userCmd = &cobra.Command{ + Use: "user", + Short: "user server", + Run: func(cmd *cobra.Command, args []string) { + server, err := wire.InitializeUserServer("user") + if err != nil { + log.Fatal(err) + } + server.Serve() + }, +} + +func init() { + rootCmd.AddCommand(userCmd) +} diff --git a/configs/config.example.yaml b/configs/config.example.yaml index 412e094..4ef506f 100644 --- a/configs/config.example.yaml +++ b/configs/config.example.yaml @@ -25,6 +25,8 @@ match: client: chat: endpoint: "localhost:4000" + user: + endpoint: "localhost:4001" jwt: secret: mysecret expirationSecond: 86400 @@ -44,6 +46,13 @@ uploader: secretKey: testsecret jwt: secret: mysecret +user: + http: + server: + port: "80" + grpc: + server: + port: "4001" redis: password: pass.123 addrs: localhost:6379 diff --git a/deployments/docker-compose.yaml b/deployments/docker-compose.yaml index 9e5a1e1..9b575b4 100644 --- a/deployments/docker-compose.yaml +++ b/deployments/docker-compose.yaml @@ -76,6 +76,7 @@ services: MATCH_HTTP_SERVER_PORT: "80" MATCH_HTTP_SERVER_MAXCONN: "200" MATCH_GRPC_CLIENT_CHAT_ENDPOINT: "reverse-proxy:80" + MATCH_GRPC_CLIENT_USER_ENDPOINT: "reverse-proxy:80" MATCH_JWT_SECRET: ${JWT_SECRET} MATCH_JWT_EXPIRATIONSECOND: "86400" REDIS_PASSWORD: ${REDIS_PASSWORD} @@ -117,6 +118,32 @@ services: - "traefik.http.routers.uploader.entrypoints=web" - "traefik.http.routers.uploader.service=uploader" - "traefik.http.services.uploader.loadbalancer.server.port=80" + user: + image: minghsu0107/random-chat-api:main + restart: always + expose: + - "80" + command: + - user + environment: + USER_HTTP_SERVER_PORT: "80" + USER_GRPC_SERVER_PORT: "4000" + REDIS_PASSWORD: ${REDIS_PASSWORD} + REDIS_ADDRS: redis-node1:7000,redis-node2:7001,redis-node3:7002,redis-node4:7003,redis-node5:7004,redis-node6:7005 + REDIS_EXPIRATIONHOUR: "24" + OBSERVABILITY_PROMETHEUS_PORT: "8080" + OBSERVABILITY_TRACING_JAEGERURL: http://jaeger:14268/api/traces + labels: + - "traefik.enable=true" + - "traefik.http.routers.user.rule=PathPrefix(`/api/user`)" + - "traefik.http.routers.user.entrypoints=web" + - "traefik.http.routers.user.service=user" + - "traefik.http.services.user.loadbalancer.server.port=80" + - "traefik.http.routers.user-grpc.rule=Headers(`content-type`,`application/grpc`) && Headers(`service-id`, `user`)" + - "traefik.http.routers.user-grpc.entrypoints=web" + - "traefik.http.routers.user-grpc.service=user-grpc" + - "traefik.http.services.user-grpc.loadbalancer.server.port=4000" + - "traefik.http.services.user-grpc.loadbalancer.server.scheme=h2c" minio: image: minio/minio:RELEASE.2021-03-17T02-33-02Z@sha256:d33b2e9559ee59acf7591cd83cb7238837158a316956e6140e6692a8e4e12fe9 volumes: diff --git a/deployments/prometheus/prometheus.yaml b/deployments/prometheus/prometheus.yaml index eaaa6fc..5f1a536 100644 --- a/deployments/prometheus/prometheus.yaml +++ b/deployments/prometheus/prometheus.yaml @@ -19,3 +19,6 @@ scrape_configs: - job_name: 'uploader_monitor' static_configs: - targets: ['deployments-uploader-1:8080'] + - job_name: 'user_monitor' + static_configs: + - targets: ['deployments-user-1:8080'] diff --git a/internal/proto_gen/chat/user.pb.go b/internal/proto_gen/chat/user.pb.go index a4c975c..0c1b250 100644 --- a/internal/proto_gen/chat/user.pb.go +++ b/internal/proto_gen/chat/user.pb.go @@ -24,163 +24,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type User struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *User) Reset() { - *x = User{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_chat_user_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *User) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*User) ProtoMessage() {} - -func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_proto_chat_user_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 User.ProtoReflect.Descriptor instead. -func (*User) Descriptor() ([]byte, []int) { - return file_proto_chat_user_proto_rawDescGZIP(), []int{0} -} - -func (x *User) GetId() uint64 { - if x != nil { - return x.Id - } - return 0 -} - -func (x *User) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type GetUserRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - UserId uint64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` -} - -func (x *GetUserRequest) Reset() { - *x = GetUserRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_chat_user_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetUserRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetUserRequest) ProtoMessage() {} - -func (x *GetUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_chat_user_proto_msgTypes[1] - 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 GetUserRequest.ProtoReflect.Descriptor instead. -func (*GetUserRequest) Descriptor() ([]byte, []int) { - return file_proto_chat_user_proto_rawDescGZIP(), []int{1} -} - -func (x *GetUserRequest) GetUserId() uint64 { - if x != nil { - return x.UserId - } - return 0 -} - -type GetUserResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Exist bool `protobuf:"varint,1,opt,name=exist,proto3" json:"exist,omitempty"` - User *User `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` -} - -func (x *GetUserResponse) Reset() { - *x = GetUserResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_chat_user_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetUserResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetUserResponse) ProtoMessage() {} - -func (x *GetUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_chat_user_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 GetUserResponse.ProtoReflect.Descriptor instead. -func (*GetUserResponse) Descriptor() ([]byte, []int) { - return file_proto_chat_user_proto_rawDescGZIP(), []int{2} -} - -func (x *GetUserResponse) GetExist() bool { - if x != nil { - return x.Exist - } - return false -} - -func (x *GetUserResponse) GetUser() *User { - if x != nil { - return x.User - } - return nil -} - type AddUserRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -193,7 +36,7 @@ type AddUserRequest struct { func (x *AddUserRequest) Reset() { *x = AddUserRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_chat_user_proto_msgTypes[3] + mi := &file_proto_chat_user_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -206,7 +49,7 @@ func (x *AddUserRequest) String() string { func (*AddUserRequest) ProtoMessage() {} func (x *AddUserRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_chat_user_proto_msgTypes[3] + mi := &file_proto_chat_user_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -219,7 +62,7 @@ func (x *AddUserRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddUserRequest.ProtoReflect.Descriptor instead. func (*AddUserRequest) Descriptor() ([]byte, []int) { - return file_proto_chat_user_proto_rawDescGZIP(), []int{3} + return file_proto_chat_user_proto_rawDescGZIP(), []int{0} } func (x *AddUserRequest) GetChannelId() uint64 { @@ -245,7 +88,7 @@ type AddUserResponse struct { func (x *AddUserResponse) Reset() { *x = AddUserResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_chat_user_proto_msgTypes[4] + mi := &file_proto_chat_user_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -258,7 +101,7 @@ func (x *AddUserResponse) String() string { func (*AddUserResponse) ProtoMessage() {} func (x *AddUserResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_chat_user_proto_msgTypes[4] + mi := &file_proto_chat_user_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -271,41 +114,28 @@ func (x *AddUserResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AddUserResponse.ProtoReflect.Descriptor instead. func (*AddUserResponse) Descriptor() ([]byte, []int) { - return file_proto_chat_user_proto_rawDescGZIP(), []int{4} + return file_proto_chat_user_proto_rawDescGZIP(), []int{1} } var File_proto_chat_user_proto protoreflect.FileDescriptor var file_proto_chat_user_proto_rawDesc = []byte{ 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x2f, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x63, 0x68, 0x61, 0x74, 0x22, 0x2a, 0x0a, - 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x64, 0x22, 0x47, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x69, 0x73, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x78, 0x69, 0x73, 0x74, 0x12, 0x1e, 0x0a, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x63, 0x68, - 0x61, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x48, 0x0a, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x63, 0x68, 0x61, 0x74, 0x22, 0x48, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x11, 0x0a, 0x0f, 0x41, 0x64, 0x64, 0x55, 0x73, - 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x8a, 0x01, 0x0a, 0x0b, 0x55, - 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x47, 0x65, - 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x47, 0x65, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x63, 0x68, - 0x61, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x10, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x54, - 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x14, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x2e, - 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, - 0x2e, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1e, 0x5a, 0x1c, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x68, - 0x61, 0x74, 0x3b, 0x63, 0x68, 0x61, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x50, 0x0a, 0x0b, 0x55, 0x73, + 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x10, 0x41, 0x64, 0x64, + 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x14, 0x2e, + 0x63, 0x68, 0x61, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1e, 0x5a, 0x1c, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x67, + 0x65, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x74, 0x3b, 0x63, 0x68, 0x61, 0x74, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -320,25 +150,19 @@ func file_proto_chat_user_proto_rawDescGZIP() []byte { return file_proto_chat_user_proto_rawDescData } -var file_proto_chat_user_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_proto_chat_user_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_proto_chat_user_proto_goTypes = []interface{}{ - (*User)(nil), // 0: chat.User - (*GetUserRequest)(nil), // 1: chat.GetUserRequest - (*GetUserResponse)(nil), // 2: chat.GetUserResponse - (*AddUserRequest)(nil), // 3: chat.AddUserRequest - (*AddUserResponse)(nil), // 4: chat.AddUserResponse + (*AddUserRequest)(nil), // 0: chat.AddUserRequest + (*AddUserResponse)(nil), // 1: chat.AddUserResponse } var file_proto_chat_user_proto_depIdxs = []int32{ - 0, // 0: chat.GetUserResponse.user:type_name -> chat.User - 1, // 1: chat.UserService.GetUser:input_type -> chat.GetUserRequest - 3, // 2: chat.UserService.AddUserToChannel:input_type -> chat.AddUserRequest - 2, // 3: chat.UserService.GetUser:output_type -> chat.GetUserResponse - 4, // 4: chat.UserService.AddUserToChannel:output_type -> chat.AddUserResponse - 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 + 0, // 0: chat.UserService.AddUserToChannel:input_type -> chat.AddUserRequest + 1, // 1: chat.UserService.AddUserToChannel:output_type -> chat.AddUserResponse + 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_proto_chat_user_proto_init() } @@ -348,42 +172,6 @@ func file_proto_chat_user_proto_init() { } if !protoimpl.UnsafeEnabled { file_proto_chat_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*User); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_chat_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_chat_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_chat_user_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddUserRequest); i { case 0: return &v.state @@ -395,7 +183,7 @@ func file_proto_chat_user_proto_init() { return nil } } - file_proto_chat_user_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_proto_chat_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddUserResponse); i { case 0: return &v.state @@ -414,7 +202,7 @@ func file_proto_chat_user_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_chat_user_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 2, NumExtensions: 0, NumServices: 1, }, @@ -440,7 +228,6 @@ const _ = grpc.SupportPackageIsVersion6 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type UserServiceClient interface { - GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) AddUserToChannel(ctx context.Context, in *AddUserRequest, opts ...grpc.CallOption) (*AddUserResponse, error) } @@ -452,15 +239,6 @@ func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { return &userServiceClient{cc} } -func (c *userServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) { - out := new(GetUserResponse) - err := c.cc.Invoke(ctx, "/chat.UserService/GetUser", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *userServiceClient) AddUserToChannel(ctx context.Context, in *AddUserRequest, opts ...grpc.CallOption) (*AddUserResponse, error) { out := new(AddUserResponse) err := c.cc.Invoke(ctx, "/chat.UserService/AddUserToChannel", in, out, opts...) @@ -472,7 +250,6 @@ func (c *userServiceClient) AddUserToChannel(ctx context.Context, in *AddUserReq // UserServiceServer is the server API for UserService service. type UserServiceServer interface { - GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) AddUserToChannel(context.Context, *AddUserRequest) (*AddUserResponse, error) } @@ -480,9 +257,6 @@ type UserServiceServer interface { type UnimplementedUserServiceServer struct { } -func (*UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") -} func (*UnimplementedUserServiceServer) AddUserToChannel(context.Context, *AddUserRequest) (*AddUserResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method AddUserToChannel not implemented") } @@ -491,24 +265,6 @@ func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) { s.RegisterService(&_UserService_serviceDesc, srv) } -func _UserService_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetUserRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(UserServiceServer).GetUser(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/chat.UserService/GetUser", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(UserServiceServer).GetUser(ctx, req.(*GetUserRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _UserService_AddUserToChannel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(AddUserRequest) if err := dec(in); err != nil { @@ -531,10 +287,6 @@ var _UserService_serviceDesc = grpc.ServiceDesc{ ServiceName: "chat.UserService", HandlerType: (*UserServiceServer)(nil), Methods: []grpc.MethodDesc{ - { - MethodName: "GetUser", - Handler: _UserService_GetUser_Handler, - }, { MethodName: "AddUserToChannel", Handler: _UserService_AddUserToChannel_Handler, diff --git a/internal/proto_gen/user/user.pb.go b/internal/proto_gen/user/user.pb.go new file mode 100644 index 0000000..138951f --- /dev/null +++ b/internal/proto_gen/user/user.pb.go @@ -0,0 +1,378 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.4 +// source: proto/user/user.proto + +package user + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +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 User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_user_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 User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_proto_user_user_proto_rawDescGZIP(), []int{0} +} + +func (x *User) GetId() uint64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *User) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GetUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId uint64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (x *GetUserRequest) Reset() { + *x = GetUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_user_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserRequest) ProtoMessage() {} + +func (x *GetUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_user_proto_msgTypes[1] + 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 GetUserRequest.ProtoReflect.Descriptor instead. +func (*GetUserRequest) Descriptor() ([]byte, []int) { + return file_proto_user_user_proto_rawDescGZIP(), []int{1} +} + +func (x *GetUserRequest) GetUserId() uint64 { + if x != nil { + return x.UserId + } + return 0 +} + +type GetUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Exist bool `protobuf:"varint,1,opt,name=exist,proto3" json:"exist,omitempty"` + User *User `protobuf:"bytes,2,opt,name=user,proto3" json:"user,omitempty"` +} + +func (x *GetUserResponse) Reset() { + *x = GetUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_user_user_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserResponse) ProtoMessage() {} + +func (x *GetUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_user_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 GetUserResponse.ProtoReflect.Descriptor instead. +func (*GetUserResponse) Descriptor() ([]byte, []int) { + return file_proto_user_user_proto_rawDescGZIP(), []int{2} +} + +func (x *GetUserResponse) GetExist() bool { + if x != nil { + return x.Exist + } + return false +} + +func (x *GetUserResponse) GetUser() *User { + if x != nil { + return x.User + } + return nil +} + +var File_proto_user_user_proto protoreflect.FileDescriptor + +var file_proto_user_user_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x75, 0x73, 0x65, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x2a, 0x0a, + 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, + 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x22, 0x47, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x69, 0x73, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x78, 0x69, 0x73, 0x74, 0x12, 0x1e, 0x0a, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x32, 0x47, 0x0a, + 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x38, 0x0a, 0x07, + 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x14, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1e, 0x5a, 0x1c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5f, 0x67, 0x65, 0x6e, 0x2f, 0x75, 0x73, 0x65, + 0x72, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_user_user_proto_rawDescOnce sync.Once + file_proto_user_user_proto_rawDescData = file_proto_user_user_proto_rawDesc +) + +func file_proto_user_user_proto_rawDescGZIP() []byte { + file_proto_user_user_proto_rawDescOnce.Do(func() { + file_proto_user_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_user_user_proto_rawDescData) + }) + return file_proto_user_user_proto_rawDescData +} + +var file_proto_user_user_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_proto_user_user_proto_goTypes = []interface{}{ + (*User)(nil), // 0: user.User + (*GetUserRequest)(nil), // 1: user.GetUserRequest + (*GetUserResponse)(nil), // 2: user.GetUserResponse +} +var file_proto_user_user_proto_depIdxs = []int32{ + 0, // 0: user.GetUserResponse.user:type_name -> user.User + 1, // 1: user.UserService.GetUser:input_type -> user.GetUserRequest + 2, // 2: user.UserService.GetUser:output_type -> user.GetUserResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] 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_proto_user_user_proto_init() } +func file_proto_user_user_proto_init() { + if File_proto_user_user_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_user_user_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_user_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_user_user_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_user_user_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_user_user_proto_goTypes, + DependencyIndexes: file_proto_user_user_proto_depIdxs, + MessageInfos: file_proto_user_user_proto_msgTypes, + }.Build() + File_proto_user_user_proto = out.File + file_proto_user_user_proto_rawDesc = nil + file_proto_user_user_proto_goTypes = nil + file_proto_user_user_proto_depIdxs = nil +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConnInterface + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion6 + +// UserServiceClient is the client API for UserService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type UserServiceClient interface { + GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) +} + +type userServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { + return &userServiceClient{cc} +} + +func (c *userServiceClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) { + out := new(GetUserResponse) + err := c.cc.Invoke(ctx, "/user.UserService/GetUser", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UserServiceServer is the server API for UserService service. +type UserServiceServer interface { + GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) +} + +// UnimplementedUserServiceServer can be embedded to have forward compatible implementations. +type UnimplementedUserServiceServer struct { +} + +func (*UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") +} + +func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) { + s.RegisterService(&_UserService_serviceDesc, srv) +} + +func _UserService_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).GetUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/user.UserService/GetUser", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).GetUser(ctx, req.(*GetUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _UserService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "user.UserService", + HandlerType: (*UserServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetUser", + Handler: _UserService_GetUser_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/user/user.proto", +} diff --git a/internal/wire/wire.go b/internal/wire/wire.go index c8f6dea..c52f900 100644 --- a/internal/wire/wire.go +++ b/internal/wire/wire.go @@ -11,6 +11,7 @@ import ( "github.com/minghsu0107/go-random-chat/pkg/infra" "github.com/minghsu0107/go-random-chat/pkg/match" "github.com/minghsu0107/go-random-chat/pkg/uploader" + "github.com/minghsu0107/go-random-chat/pkg/user" "github.com/minghsu0107/go-random-chat/pkg/web" ) @@ -38,6 +39,7 @@ func InitializeMatchServer(name string) (*common.Server, error) { infra.NewRedisCache, match.NewChatClientConn, + match.NewUserClientConn, match.NewChannelRepo, match.NewUserRepo, @@ -75,7 +77,7 @@ func InitializeChatServer(name string) (*common.Server, error) { chat.NewMessageSubscriber, - chat.NewSonyFlake, + common.NewSonyFlake, chat.NewUserService, chat.NewMessageService, @@ -106,3 +108,29 @@ func InitializeUploaderServer(name string) (*common.Server, error) { ) return &common.Server{}, nil } + +func InitializeUserServer(name string) (*common.Server, error) { + wire.Build( + config.NewConfig, + common.NewObservibilityInjector, + common.NewHttpLogrus, + common.NewGrpcLogrus, + + infra.NewRedisClient, + infra.NewRedisCache, + + user.NewUserRepo, + + common.NewSonyFlake, + + user.NewUserService, + + user.NewGinServer, + user.NewHttpServer, + user.NewGrpcServer, + user.NewRouter, + user.NewInfraCloser, + common.NewServer, + ) + return &common.Server{}, nil +} diff --git a/internal/wire/wire_gen.go b/internal/wire/wire_gen.go index 250848e..f7b1383 100644 --- a/internal/wire/wire_gen.go +++ b/internal/wire/wire_gen.go @@ -13,6 +13,7 @@ import ( "github.com/minghsu0107/go-random-chat/pkg/infra" "github.com/minghsu0107/go-random-chat/pkg/match" "github.com/minghsu0107/go-random-chat/pkg/uploader" + "github.com/minghsu0107/go-random-chat/pkg/user" "github.com/minghsu0107/go-random-chat/pkg/web" ) @@ -45,11 +46,15 @@ func InitializeMatchServer(name string) (*common.Server, error) { if err != nil { return nil, err } + userClientConn, err := match.NewUserClientConn(configConfig) + if err != nil { + return nil, err + } chatClientConn, err := match.NewChatClientConn(configConfig) if err != nil { return nil, err } - userRepo := match.NewUserRepo(chatClientConn) + userRepo := match.NewUserRepo(userClientConn, chatClientConn) userService := match.NewUserService(userRepo) matchSubscriber := match.NewMatchSubscriber(configConfig, universalClient, melodyMatchConn, userService) redisCache := infra.NewRedisCache(universalClient) @@ -79,12 +84,12 @@ func InitializeChatServer(name string) (*common.Server, error) { messageSubscriber := chat.NewMessageSubscriber(configConfig, universalClient, melodyChatConn) redisCache := infra.NewRedisCache(universalClient) userRepo := chat.NewUserRepo(redisCache) - idGenerator, err := chat.NewSonyFlake() + userService := chat.NewUserService(userRepo) + messageRepo := chat.NewMessageRepo(configConfig, redisCache) + idGenerator, err := common.NewSonyFlake() if err != nil { return nil, err } - userService := chat.NewUserService(userRepo, idGenerator) - messageRepo := chat.NewMessageRepo(configConfig, redisCache) messageService := chat.NewMessageService(messageRepo, userRepo, idGenerator) channelRepo := chat.NewChannelRepo(redisCache) channelService := chat.NewChannelService(channelRepo, userRepo, idGenerator) @@ -112,3 +117,31 @@ func InitializeUploaderServer(name string) (*common.Server, error) { server := common.NewServer(name, router, infraCloser, observibilityInjector) return server, nil } + +func InitializeUserServer(name string) (*common.Server, error) { + httpLogrus := common.NewHttpLogrus() + configConfig, err := config.NewConfig() + if err != nil { + return nil, err + } + engine := user.NewGinServer(name, httpLogrus, configConfig) + universalClient, err := infra.NewRedisClient(configConfig) + if err != nil { + return nil, err + } + redisCache := infra.NewRedisCache(universalClient) + userRepo := user.NewUserRepo(redisCache) + idGenerator, err := common.NewSonyFlake() + if err != nil { + return nil, err + } + userService := user.NewUserService(userRepo, idGenerator) + httpServer := user.NewHttpServer(name, httpLogrus, configConfig, engine, userService) + grpcLogrus := common.NewGrpcLogrus() + grpcServer := user.NewGrpcServer(grpcLogrus, configConfig, userService) + router := user.NewRouter(httpServer, grpcServer) + infraCloser := user.NewInfraCloser() + observibilityInjector := common.NewObservibilityInjector(configConfig) + server := common.NewServer(name, router, infraCloser, observibilityInjector) + return server, nil +} diff --git a/pkg/chat/grpc_rpc.go b/pkg/chat/grpc_rpc.go index 300cee4..8f03479 100644 --- a/pkg/chat/grpc_rpc.go +++ b/pkg/chat/grpc_rpc.go @@ -19,26 +19,6 @@ func (srv *GrpcServer) CreateChannel(ctx context.Context, req *chatpb.CreateChan }, nil } -func (srv *GrpcServer) GetUser(ctx context.Context, req *chatpb.GetUserRequest) (*chatpb.GetUserResponse, error) { - user, err := srv.userSvc.GetUser(ctx, req.UserId) - if err != nil { - if err == ErrUserNotFound { - return &chatpb.GetUserResponse{ - Exist: false, - }, nil - } - srv.logger.Error(err) - return nil, status.Error(codes.Internal, err.Error()) - } - return &chatpb.GetUserResponse{ - Exist: true, - User: &chatpb.User{ - Id: user.ID, - Name: user.Name, - }, - }, nil -} - func (srv *GrpcServer) AddUserToChannel(ctx context.Context, req *chatpb.AddUserRequest) (*chatpb.AddUserResponse, error) { if err := srv.userSvc.AddUserToChannel(ctx, req.ChannelId, req.UserId); err != nil { srv.logger.Error(err) diff --git a/pkg/chat/http.go b/pkg/chat/http.go index 1159b09..3f0be1d 100644 --- a/pkg/chat/http.go +++ b/pkg/chat/http.go @@ -86,16 +86,11 @@ func initJWT(config *config.Config) { func (r *HttpServer) RegisterRoutes() { r.svr.GET("/api/chat", r.StartChat) - userGroup := r.svr.Group("/api/user") + chanUsersGroup := r.svr.Group("/api/chanusers") + chanUsersGroup.Use(common.JWTAuth()) { - userGroup.POST("", r.CreateUser) - userGroup.GET("/:uid/name", r.GetUserName) - } - usersGroup := r.svr.Group("/api/users") - usersGroup.Use(common.JWTAuth()) - { - usersGroup.GET("", r.GetChannelUsers) - usersGroup.GET("/online", r.GetOnlineUsers) + chanUsersGroup.GET("", r.GetChannelUsers) + chanUsersGroup.GET("/online", r.GetOnlineUsers) } channelGroup := r.svr.Group("/api/channel") channelGroup.Use(common.JWTAuth()) diff --git a/pkg/chat/http_api.go b/pkg/chat/http_api.go index af4b1b2..bb6e800 100644 --- a/pkg/chat/http_api.go +++ b/pkg/chat/http_api.go @@ -45,24 +45,6 @@ func (r *HttpServer) StartChat(c *gin.Context) { r.mc.HandleRequest(c.Writer, c.Request) } -func (r *HttpServer) CreateUser(c *gin.Context) { - var userPresenter UserPresenter - if err := c.ShouldBindJSON(&userPresenter); err != nil { - response(c, http.StatusBadRequest, common.ErrInvalidParam) - return - } - user, err := r.userSvc.CreateUser(c.Request.Context(), userPresenter.Name) - if err != nil { - r.logger.Error(err) - response(c, http.StatusInternalServerError, common.ErrServer) - return - } - c.JSON(http.StatusCreated, &UserPresenter{ - ID: strconv.FormatUint(user.ID, 10), - Name: user.Name, - }) -} - func (r *HttpServer) GetChannelUsers(c *gin.Context) { channelID, ok := c.Request.Context().Value(common.ChannelKey).(uint64) if !ok { @@ -113,29 +95,6 @@ func (r *HttpServer) GetOnlineUsers(c *gin.Context) { }) } -func (r *HttpServer) GetUserName(c *gin.Context) { - id := c.Param("uid") - userID, err := strconv.ParseUint(id, 10, 64) - if err != nil { - response(c, http.StatusBadRequest, common.ErrInvalidParam) - return - } - user, err := r.userSvc.GetUser(c.Request.Context(), userID) - if err != nil { - if err == ErrUserNotFound { - response(c, http.StatusNotFound, ErrUserNotFound) - return - } - r.logger.Error(err) - response(c, http.StatusInternalServerError, common.ErrServer) - return - } - c.JSON(http.StatusOK, &UserPresenter{ - ID: id, - Name: user.Name, - }) -} - func (r *HttpServer) ListMessages(c *gin.Context) { channelID, ok := c.Request.Context().Value(common.ChannelKey).(uint64) if !ok { diff --git a/pkg/chat/repo.go b/pkg/chat/repo.go index f90776a..008c699 100644 --- a/pkg/chat/repo.go +++ b/pkg/chat/repo.go @@ -2,7 +2,6 @@ package chat import ( "context" - "encoding/json" "errors" "strconv" @@ -14,21 +13,17 @@ var ( messagePubSubTopic = "rc:msg:pubsub" messagesPrefix = "rc:msgs" channelPrefix = "rc:chan" - userPrefix = "rc:user" channelUsersPrefix = "rc:chanusers" onlineUsersPrefix = "rc:onlineusers" seenMessagesPrefix = "rc:seenmsgs" ) var ( - ErrUserNotFound = errors.New("error user not found") ErrChannelNotFound = errors.New("error channel not found") ErrChannelOrUserNotFound = errors.New("error channel or user not found") ) type UserRepo interface { - CreateUser(ctx context.Context, user *User) (*User, error) - GetUserByID(ctx context.Context, userID uint64) (*User, error) AddUserToChannel(ctx context.Context, channelID uint64, userID uint64) error IsChannelUserExist(ctx context.Context, channelID, userID uint64) (bool, error) GetChannelUserIDs(ctx context.Context, channelID uint64) ([]uint64, error) @@ -57,32 +52,6 @@ type UserRepoImpl struct { func NewUserRepo(r infra.RedisCache) UserRepo { return &UserRepoImpl{r} } -func (repo *UserRepoImpl) CreateUser(ctx context.Context, user *User) (*User, error) { - data, err := json.Marshal(user) - if err != nil { - return nil, err - } - err = repo.r.Set(ctx, constructKey(userPrefix, user.ID), data) - if err != nil { - return nil, err - } - return &User{ - ID: user.ID, - Name: user.Name, - }, nil -} -func (repo *UserRepoImpl) GetUserByID(ctx context.Context, userID uint64) (*User, error) { - key := constructKey(userPrefix, userID) - var user User - exist, err := repo.r.Get(ctx, key, &user) - if err != nil { - return nil, err - } - if !exist { - return nil, ErrUserNotFound - } - return &user, nil -} func (repo *UserRepoImpl) AddUserToChannel(ctx context.Context, channelID uint64, userID uint64) error { key := constructKey(channelUsersPrefix, channelID) return repo.r.HSet(ctx, key, strconv.FormatUint(userID, 10), 0) diff --git a/pkg/chat/service.go b/pkg/chat/service.go index 7757c03..f6433dd 100644 --- a/pkg/chat/service.go +++ b/pkg/chat/service.go @@ -4,6 +4,8 @@ import ( "context" "strconv" "time" + + "github.com/minghsu0107/go-random-chat/pkg/common" ) type MessageService interface { @@ -16,8 +18,6 @@ type MessageService interface { } type UserService interface { - CreateUser(ctx context.Context, userName string) (*User, error) - GetUser(ctx context.Context, uid uint64) (*User, error) AddUserToChannel(ctx context.Context, channelID, userID uint64) error IsChannelUserExist(ctx context.Context, channelID, userID uint64) (bool, error) GetChannelUserIDs(ctx context.Context, channelID uint64) ([]uint64, error) @@ -34,10 +34,10 @@ type ChannelService interface { type MessageServiceImpl struct { msgRepo MessageRepo userRepo UserRepo - sf IDGenerator + sf common.IDGenerator } -func NewMessageService(msgRepo MessageRepo, userRepo UserRepo, sf IDGenerator) MessageService { +func NewMessageService(msgRepo MessageRepo, userRepo UserRepo, sf common.IDGenerator) MessageService { return &MessageServiceImpl{msgRepo, userRepo, sf} } func (svc *MessageServiceImpl) BroadcastTextMessage(ctx context.Context, channelID, userID uint64, payload string) error { @@ -126,24 +126,10 @@ func (svc *MessageServiceImpl) ListMessages(ctx context.Context, channelID uint6 type UserServiceImpl struct { userRepo UserRepo - sf IDGenerator } -func NewUserService(userRepo UserRepo, sf IDGenerator) UserService { - return &UserServiceImpl{userRepo, sf} -} -func (svc *UserServiceImpl) CreateUser(ctx context.Context, userName string) (*User, error) { - userID, err := svc.sf.NextID() - if err != nil { - return nil, err - } - return svc.userRepo.CreateUser(ctx, &User{ - ID: userID, - Name: userName, - }) -} -func (svc *UserServiceImpl) GetUser(ctx context.Context, uid uint64) (*User, error) { - return svc.userRepo.GetUserByID(ctx, uid) +func NewUserService(userRepo UserRepo) UserService { + return &UserServiceImpl{userRepo} } func (svc *UserServiceImpl) AddUserToChannel(ctx context.Context, channelID, userID uint64) error { return svc.userRepo.AddUserToChannel(ctx, channelID, userID) @@ -167,10 +153,10 @@ func (svc *UserServiceImpl) GetOnlineUserIDs(ctx context.Context, channelID uint type ChannelServiceImpl struct { chanRepo ChannelRepo userRepo UserRepo - sf IDGenerator + sf common.IDGenerator } -func NewChannelService(chanRepo ChannelRepo, userRepo UserRepo, sf IDGenerator) ChannelService { +func NewChannelService(chanRepo ChannelRepo, userRepo UserRepo, sf common.IDGenerator) ChannelService { return &ChannelServiceImpl{chanRepo, userRepo, sf} } func (svc *ChannelServiceImpl) CreateChannel(ctx context.Context) (*Channel, error) { diff --git a/pkg/chat/util.go b/pkg/chat/util.go index 8a942e6..663b3ee 100644 --- a/pkg/chat/util.go +++ b/pkg/chat/util.go @@ -2,25 +2,8 @@ package chat import ( "encoding/json" - "errors" - - "github.com/sony/sonyflake" ) -// IDGenerator is the inteface for generatring unique ID -type IDGenerator interface { - NextID() (uint64, error) -} - -func NewSonyFlake() (IDGenerator, error) { - var st sonyflake.Settings - sf := sonyflake.NewSonyflake(st) - if sf == nil { - return nil, errors.New("sonyflake not created") - } - return sf, nil -} - func DecodeToMessagePresenter(data []byte) (*MessagePresenter, error) { var msg MessagePresenter if err := json.Unmarshal(data, &msg); err != nil { diff --git a/pkg/common/util.go b/pkg/common/util.go index f4662bd..7bdae72 100644 --- a/pkg/common/util.go +++ b/pkg/common/util.go @@ -1,9 +1,26 @@ package common import ( + "errors" "strings" + + "github.com/sony/sonyflake" ) +// IDGenerator is the inteface for generatring unique ID +type IDGenerator interface { + NextID() (uint64, error) +} + +func NewSonyFlake() (IDGenerator, error) { + var st sonyflake.Settings + sf := sonyflake.NewSonyflake(st) + if sf == nil { + return nil, errors.New("sonyflake not created") + } + return sf, nil +} + func GetServerAddrs(addrs string) []string { return strings.Split(addrs, ",") } diff --git a/pkg/config/config.go b/pkg/config/config.go index 507e96e..109f989 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -9,6 +9,7 @@ type Config struct { Chat *ChatConfig `mapstructure:"chat"` Match *MatchConfig `mapstructure:"match"` Uploader *UploaderConfig `mapstructure:"uploader"` + User *UserConfig `mapstructure:"user"` Redis *RedisConfig `mapstructure:"redis"` Observability *ObservabilityConfig `mapstructure:"observability"` } @@ -55,6 +56,9 @@ type MatchConfig struct { Chat struct { Endpoint string } + User struct { + Endpoint string + } } } JWT struct { @@ -85,6 +89,19 @@ type UploaderConfig struct { } } +type UserConfig struct { + Http struct { + Server struct { + Port string + } + } + Grpc struct { + Server struct { + Port string + } + } +} + type RedisConfig struct { Password string Addrs string @@ -118,6 +135,7 @@ func setDefault() { viper.SetDefault("match.http.server.port", "5002") viper.SetDefault("match.http.server.maxConn", 200) viper.SetDefault("match.grpc.client.chat.endpoint", "localhost:4000") + viper.SetDefault("match.grpc.client.user.endpoint", "localhost:4001") viper.SetDefault("match.jwt.secret", "replaceme") viper.SetDefault("match.jwt.expirationSecond", 86400) viper.SetDefault("match.worker", 4) @@ -133,6 +151,9 @@ func setDefault() { viper.SetDefault("uploader.s3.secretKey", "") viper.SetDefault("uploader.jwt.secret", "replaceme") + viper.SetDefault("user.http.server.port", "5004") + viper.SetDefault("user.grpc.server.port", "4001") + viper.SetDefault("redis.password", "") viper.SetDefault("redis.addrs", "localhost:6379") viper.SetDefault("redis.expirationHour", 24) diff --git a/pkg/match/closer.go b/pkg/match/closer.go index 3203d05..87a36fc 100644 --- a/pkg/match/closer.go +++ b/pkg/match/closer.go @@ -15,5 +15,8 @@ func (closer *InfraCloser) Close() error { if err := ChatConn.Conn.Close(); err != nil { return err } + if err := UserConn.Conn.Close(); err != nil { + return err + } return infra.RedisClient.Close() } diff --git a/pkg/match/grpc.go b/pkg/match/grpc.go index cf93ebd..44b328f 100644 --- a/pkg/match/grpc.go +++ b/pkg/match/grpc.go @@ -8,12 +8,17 @@ import ( var ( ChatConn *ChatClientConn + UserConn *UserClientConn ) type ChatClientConn struct { Conn *grpc.ClientConn } +type UserClientConn struct { + Conn *grpc.ClientConn +} + func NewChatClientConn(config *config.Config) (*ChatClientConn, error) { conn, err := transport.InitializeGrpcClient(config.Match.Grpc.Client.Chat.Endpoint) if err != nil { @@ -24,3 +29,14 @@ func NewChatClientConn(config *config.Config) (*ChatClientConn, error) { } return ChatConn, nil } + +func NewUserClientConn(config *config.Config) (*UserClientConn, error) { + conn, err := transport.InitializeGrpcClient(config.Match.Grpc.Client.User.Endpoint) + if err != nil { + return nil, err + } + UserConn = &UserClientConn{ + Conn: conn, + } + return UserConn, nil +} diff --git a/pkg/match/repo.go b/pkg/match/repo.go index 8287483..6b6860c 100644 --- a/pkg/match/repo.go +++ b/pkg/match/repo.go @@ -8,6 +8,7 @@ import ( "github.com/go-kit/kit/endpoint" chatpb "github.com/minghsu0107/go-random-chat/internal/proto_gen/chat" + userpb "github.com/minghsu0107/go-random-chat/internal/proto_gen/user" "github.com/minghsu0107/go-random-chat/pkg/infra" "github.com/minghsu0107/go-random-chat/pkg/transport" ) @@ -40,10 +41,10 @@ type ChannelRepoImpl struct { createChannel endpoint.Endpoint } -func NewChannelRepo(conn *ChatClientConn) ChannelRepo { +func NewChannelRepo(chatConn *ChatClientConn) ChannelRepo { return &ChannelRepoImpl{ createChannel: transport.NewGrpcEndpoint( - conn.Conn, + chatConn.Conn, "chat", "chat.ChannelService", "CreateChannel", @@ -65,17 +66,17 @@ type UserRepoImpl struct { addUserToChannel endpoint.Endpoint } -func NewUserRepo(conn *ChatClientConn) UserRepo { +func NewUserRepo(userConn *UserClientConn, chatConn *ChatClientConn) UserRepo { return &UserRepoImpl{ getUser: transport.NewGrpcEndpoint( - conn.Conn, - "chat", - "chat.UserService", + userConn.Conn, + "user", + "user.UserService", "GetUser", - &chatpb.GetUserResponse{}, + &userpb.GetUserResponse{}, ), addUserToChannel: transport.NewGrpcEndpoint( - conn.Conn, + chatConn.Conn, "chat", "chat.UserService", "AddUserToChannel", @@ -85,13 +86,13 @@ func NewUserRepo(conn *ChatClientConn) UserRepo { } func (repo *UserRepoImpl) GetUserByID(ctx context.Context, userID uint64) (*User, error) { - res, err := repo.getUser(ctx, &chatpb.GetUserRequest{ + res, err := repo.getUser(ctx, &userpb.GetUserRequest{ UserId: userID, }) if err != nil { return nil, err } - pbUser := res.(*chatpb.GetUserResponse) + pbUser := res.(*userpb.GetUserResponse) return &User{ ID: pbUser.User.Id, Name: pbUser.User.Name, diff --git a/pkg/user/closer.go b/pkg/user/closer.go new file mode 100644 index 0000000..fabcf1d --- /dev/null +++ b/pkg/user/closer.go @@ -0,0 +1,16 @@ +package user + +import ( + "github.com/minghsu0107/go-random-chat/pkg/common" + "github.com/minghsu0107/go-random-chat/pkg/infra" +) + +type InfraCloser struct{} + +func NewInfraCloser() common.InfraCloser { + return &InfraCloser{} +} + +func (closer *InfraCloser) Close() error { + return infra.RedisClient.Close() +} diff --git a/pkg/user/domain.go b/pkg/user/domain.go new file mode 100644 index 0000000..a794de2 --- /dev/null +++ b/pkg/user/domain.go @@ -0,0 +1,6 @@ +package user + +type User struct { + ID uint64 + Name string +} diff --git a/pkg/user/grpc.go b/pkg/user/grpc.go new file mode 100644 index 0000000..be4d9ac --- /dev/null +++ b/pkg/user/grpc.go @@ -0,0 +1,53 @@ +package user + +import ( + "net" + + "google.golang.org/grpc" + + grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + userpb "github.com/minghsu0107/go-random-chat/internal/proto_gen/user" + "github.com/minghsu0107/go-random-chat/pkg/common" + "github.com/minghsu0107/go-random-chat/pkg/config" + "github.com/minghsu0107/go-random-chat/pkg/transport" +) + +type GrpcServer struct { + grpcPort string + logger common.GrpcLogrus + s *grpc.Server + userSvc UserService +} + +func NewGrpcServer(logger common.GrpcLogrus, config *config.Config, userSvc UserService) common.GrpcServer { + srv := &GrpcServer{ + grpcPort: config.User.Grpc.Server.Port, + logger: logger, + userSvc: userSvc, + } + srv.s = transport.InitializeGrpcServer(srv.logger) + return srv +} + +func (srv *GrpcServer) Register() { + userpb.RegisterUserServiceServer(srv.s, srv) + grpc_prometheus.Register(srv.s) +} + +func (srv *GrpcServer) Run() { + go func() { + addr := "0.0.0.0:" + srv.grpcPort + srv.logger.Infoln("grpc server listening on ", addr) + lis, err := net.Listen("tcp", addr) + if err != nil { + srv.logger.Fatal(err) + } + if err := srv.s.Serve(lis); err != nil { + srv.logger.Fatal(err) + } + }() +} + +func (srv *GrpcServer) GracefulStop() { + srv.s.GracefulStop() +} diff --git a/pkg/user/grpc_rpc.go b/pkg/user/grpc_rpc.go new file mode 100644 index 0000000..53828df --- /dev/null +++ b/pkg/user/grpc_rpc.go @@ -0,0 +1,29 @@ +package user + +import ( + "context" + + userpb "github.com/minghsu0107/go-random-chat/internal/proto_gen/user" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (srv *GrpcServer) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*userpb.GetUserResponse, error) { + user, err := srv.userSvc.GetUser(ctx, req.UserId) + if err != nil { + if err == ErrUserNotFound { + return &userpb.GetUserResponse{ + Exist: false, + }, nil + } + srv.logger.Error(err) + return nil, status.Error(codes.Internal, err.Error()) + } + return &userpb.GetUserResponse{ + Exist: true, + User: &userpb.User{ + Id: user.ID, + Name: user.Name, + }, + }, nil +} diff --git a/pkg/user/http.go b/pkg/user/http.go new file mode 100644 index 0000000..d94ad70 --- /dev/null +++ b/pkg/user/http.go @@ -0,0 +1,82 @@ +package user + +import ( + "context" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/minghsu0107/go-random-chat/pkg/common" + "github.com/minghsu0107/go-random-chat/pkg/config" + metrics "github.com/slok/go-http-metrics/metrics/prometheus" + prommiddleware "github.com/slok/go-http-metrics/middleware" + ginmiddleware "github.com/slok/go-http-metrics/middleware/gin" +) + +type HttpServer struct { + name string + logger common.HttpLogrus + svr *gin.Engine + httpPort string + httpServer *http.Server + userSvc UserService +} + +func NewGinServer(name string, logger common.HttpLogrus, config *config.Config) *gin.Engine { + common.InitLogging() + + svr := gin.New() + svr.Use(gin.Recovery()) + svr.Use(common.LoggingMiddleware(logger)) + svr.Use(common.CORSMiddleware()) + + mdlw := prommiddleware.New(prommiddleware.Config{ + Recorder: metrics.NewRecorder(metrics.Config{ + Prefix: name, + }), + }) + svr.Use(ginmiddleware.Handler("", mdlw)) + return svr +} + +func NewHttpServer(name string, logger common.HttpLogrus, config *config.Config, svr *gin.Engine, userSvc UserService) common.HttpServer { + return &HttpServer{ + name: name, + logger: logger, + svr: svr, + httpPort: config.User.Http.Server.Port, + userSvc: userSvc, + } +} + +func (r *HttpServer) RegisterRoutes() { + userGroup := r.svr.Group("/api/user") + { + userGroup.POST("", r.CreateUser) + userGroup.GET("/:uid/name", r.GetUserName) + } +} + +func (r *HttpServer) Run() { + go func() { + addr := ":" + r.httpPort + r.httpServer = &http.Server{ + Addr: addr, + Handler: common.NewOtelHttpHandler(r.svr, r.name+"_http"), + } + r.logger.Infoln("http server listening on ", addr) + err := r.httpServer.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + r.logger.Fatal(err) + } + }() +} +func (r *HttpServer) GracefulStop(ctx context.Context) error { + return r.httpServer.Shutdown(ctx) +} + +func response(c *gin.Context, httpCode int, err error) { + message := err.Error() + c.JSON(httpCode, common.ErrResponse{ + Message: message, + }) +} diff --git a/pkg/user/http_api.go b/pkg/user/http_api.go new file mode 100644 index 0000000..442eec1 --- /dev/null +++ b/pkg/user/http_api.go @@ -0,0 +1,50 @@ +package user + +import ( + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/minghsu0107/go-random-chat/pkg/common" +) + +func (r *HttpServer) CreateUser(c *gin.Context) { + var userPresenter UserPresenter + if err := c.ShouldBindJSON(&userPresenter); err != nil { + response(c, http.StatusBadRequest, common.ErrInvalidParam) + return + } + user, err := r.userSvc.CreateUser(c.Request.Context(), userPresenter.Name) + if err != nil { + r.logger.Error(err) + response(c, http.StatusInternalServerError, common.ErrServer) + return + } + c.JSON(http.StatusCreated, &UserPresenter{ + ID: strconv.FormatUint(user.ID, 10), + Name: user.Name, + }) +} + +func (r *HttpServer) GetUserName(c *gin.Context) { + id := c.Param("uid") + userID, err := strconv.ParseUint(id, 10, 64) + if err != nil { + response(c, http.StatusBadRequest, common.ErrInvalidParam) + return + } + user, err := r.userSvc.GetUser(c.Request.Context(), userID) + if err != nil { + if err == ErrUserNotFound { + response(c, http.StatusNotFound, ErrUserNotFound) + return + } + r.logger.Error(err) + response(c, http.StatusInternalServerError, common.ErrServer) + return + } + c.JSON(http.StatusOK, &UserPresenter{ + ID: id, + Name: user.Name, + }) +} diff --git a/pkg/user/presenter.go b/pkg/user/presenter.go new file mode 100644 index 0000000..e7df332 --- /dev/null +++ b/pkg/user/presenter.go @@ -0,0 +1,6 @@ +package user + +type UserPresenter struct { + ID string `json:"id"` + Name string `json:"name" binding:"required"` +} diff --git a/pkg/user/repo.go b/pkg/user/repo.go new file mode 100644 index 0000000..0d15da9 --- /dev/null +++ b/pkg/user/repo.go @@ -0,0 +1,61 @@ +package user + +import ( + "context" + "encoding/json" + "errors" + "strconv" + + "github.com/minghsu0107/go-random-chat/pkg/infra" +) + +var ( + userPrefix = "rc:user" +) + +var ( + ErrUserNotFound = errors.New("error user not found") +) + +type UserRepo interface { + CreateUser(ctx context.Context, user *User) (*User, error) + GetUserByID(ctx context.Context, userID uint64) (*User, error) +} + +type UserRepoImpl struct { + r infra.RedisCache +} + +func NewUserRepo(r infra.RedisCache) UserRepo { + return &UserRepoImpl{r} +} +func (repo *UserRepoImpl) CreateUser(ctx context.Context, user *User) (*User, error) { + data, err := json.Marshal(user) + if err != nil { + return nil, err + } + err = repo.r.Set(ctx, constructKey(userPrefix, user.ID), data) + if err != nil { + return nil, err + } + return &User{ + ID: user.ID, + Name: user.Name, + }, nil +} +func (repo *UserRepoImpl) GetUserByID(ctx context.Context, userID uint64) (*User, error) { + key := constructKey(userPrefix, userID) + var user User + exist, err := repo.r.Get(ctx, key, &user) + if err != nil { + return nil, err + } + if !exist { + return nil, ErrUserNotFound + } + return &user, nil +} + +func constructKey(prefix string, id uint64) string { + return prefix + ":" + strconv.FormatUint(id, 10) +} diff --git a/pkg/user/router.go b/pkg/user/router.go new file mode 100644 index 0000000..4d3366d --- /dev/null +++ b/pkg/user/router.go @@ -0,0 +1,28 @@ +package user + +import ( + "context" + + "github.com/minghsu0107/go-random-chat/pkg/common" +) + +type Router struct { + httpServer common.HttpServer + grpcServer common.GrpcServer +} + +func NewRouter(httpServer common.HttpServer, grpcServer common.GrpcServer) common.Router { + return &Router{httpServer, grpcServer} +} + +func (r *Router) Run() { + r.httpServer.RegisterRoutes() + r.httpServer.Run() + + r.grpcServer.Register() + r.grpcServer.Run() +} +func (r *Router) GracefulStop(ctx context.Context) error { + r.grpcServer.GracefulStop() + return r.httpServer.GracefulStop(ctx) +} diff --git a/pkg/user/service.go b/pkg/user/service.go new file mode 100644 index 0000000..9786ef7 --- /dev/null +++ b/pkg/user/service.go @@ -0,0 +1,34 @@ +package user + +import ( + "context" + + "github.com/minghsu0107/go-random-chat/pkg/common" +) + +type UserService interface { + CreateUser(ctx context.Context, userName string) (*User, error) + GetUser(ctx context.Context, uid uint64) (*User, error) +} + +type UserServiceImpl struct { + userRepo UserRepo + sf common.IDGenerator +} + +func NewUserService(userRepo UserRepo, sf common.IDGenerator) UserService { + return &UserServiceImpl{userRepo, sf} +} +func (svc *UserServiceImpl) CreateUser(ctx context.Context, userName string) (*User, error) { + userID, err := svc.sf.NextID() + if err != nil { + return nil, err + } + return svc.userRepo.CreateUser(ctx, &User{ + ID: userID, + Name: userName, + }) +} +func (svc *UserServiceImpl) GetUser(ctx context.Context, uid uint64) (*User, error) { + return svc.userRepo.GetUserByID(ctx, uid) +} diff --git a/proto/chat/user.proto b/proto/chat/user.proto index d3ffc25..037ae30 100644 --- a/proto/chat/user.proto +++ b/proto/chat/user.proto @@ -4,20 +4,6 @@ package chat; option go_package = "internal/proto_gen/chat;chat"; -message User { - uint64 id = 1; - string name = 2; -} - -message GetUserRequest { - uint64 user_id = 1; -} - -message GetUserResponse { - bool exist = 1; - User user = 2; -} - message AddUserRequest { uint64 channel_id = 1; uint64 user_id = 2; @@ -27,6 +13,5 @@ message AddUserResponse { } service UserService { - rpc GetUser (GetUserRequest) returns (GetUserResponse) {}; rpc AddUserToChannel(AddUserRequest) returns (AddUserResponse) {}; } \ No newline at end of file diff --git a/proto/user/user.proto b/proto/user/user.proto new file mode 100644 index 0000000..1d8934c --- /dev/null +++ b/proto/user/user.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package user; + +option go_package = "internal/proto_gen/user;user"; + +message User { + uint64 id = 1; + string name = 2; +} + +message GetUserRequest { + uint64 user_id = 1; +} + +message GetUserResponse { + bool exist = 1; + User user = 2; +} + +service UserService { + rpc GetUser (GetUserRequest) returns (GetUserResponse) {}; +} \ No newline at end of file diff --git a/web/assets/js/chat.js b/web/assets/js/chat.js index afc401f..c2a0693 100644 --- a/web/assets/js/chat.js +++ b/web/assets/js/chat.js @@ -317,7 +317,7 @@ function sendBrowserNotification(msg) { } async function getAllChannelUserNames() { - return fetch(`/api/users`, { + return fetch(`/api/chanusers`, { method: 'GET', headers: new Headers({ 'Authorization': 'Bearer ' + ACCESS_TOKEN @@ -488,7 +488,7 @@ async function setPeerName(peerID) { } async function updateOnlineUsers() { - return fetch(`/api/users/online`, { + return fetch(`/api/chanusers/online`, { method: 'GET', headers: new Headers({ 'Authorization': 'Bearer ' + ACCESS_TOKEN