diff --git a/api/v2/resource_name.go b/api/v2/resource_name.go index a12231f5..a9f28522 100644 --- a/api/v2/resource_name.go +++ b/api/v2/resource_name.go @@ -10,6 +10,7 @@ import ( ) const ( + UserNamePrefix = "users/" InboxNamePrefix = "inboxes/" ) @@ -33,6 +34,15 @@ func GetNameParentTokens(name string, tokenPrefixes ...string) ([]string, error) return tokens, nil } +// GetUsername returns the username from a resource name. +func GetUsername(name string) (string, error) { + tokens, err := GetNameParentTokens(name, UserNamePrefix) + if err != nil { + return "", err + } + return tokens[0], nil +} + // GetInboxID returns the inbox ID from a resource name. func GetInboxID(name string) (int32, error) { tokens, err := GetNameParentTokens(name, InboxNamePrefix) diff --git a/api/v2/user_service.go b/api/v2/user_service.go index 7ec9ff26..a393aab8 100644 --- a/api/v2/user_service.go +++ b/api/v2/user_service.go @@ -2,6 +2,7 @@ package v2 import ( "context" + "fmt" "net/http" "regexp" "strings" @@ -27,8 +28,12 @@ var ( ) func (s *APIV2Service) GetUser(ctx context.Context, request *apiv2pb.GetUserRequest) (*apiv2pb.GetUserResponse, error) { + username, err := GetUsername(request.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "name is required") + } user, err := s.Store.GetUser(ctx, &store.FindUser{ - Username: &request.Username, + Username: &username, }) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get user: %v", err) @@ -53,8 +58,12 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs return nil, status.Errorf(codes.PermissionDenied, "permission denied") } - if !usernameMatcher.MatchString(strings.ToLower(request.User.Username)) { - return nil, status.Errorf(codes.InvalidArgument, "invalid username: %s", request.User.Username) + username, err := GetUsername(request.User.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "name is required") + } + if !usernameMatcher.MatchString(strings.ToLower(username)) { + return nil, status.Errorf(codes.InvalidArgument, "invalid username: %s", username) } passwordHash, err := bcrypt.GenerateFromPassword([]byte(request.User.Password), bcrypt.DefaultCost) if err != nil { @@ -62,7 +71,7 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs } user, err := s.Store.CreateUser(ctx, &store.User{ - Username: request.User.Username, + Username: username, Role: convertUserRoleToStore(request.User.Role), Email: request.User.Email, Nickname: request.User.Nickname, @@ -79,11 +88,15 @@ func (s *APIV2Service) CreateUser(ctx context.Context, request *apiv2pb.CreateUs } func (s *APIV2Service) UpdateUser(ctx context.Context, request *apiv2pb.UpdateUserRequest) (*apiv2pb.UpdateUserResponse, error) { + username, err := GetUsername(request.User.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "name is required") + } currentUser, err := getCurrentUser(ctx, s.Store) if err != nil { return nil, status.Errorf(codes.Internal, "failed to get user: %v", err) } - if currentUser.Username != request.User.Username && currentUser.Role != store.RoleAdmin { + if currentUser.Username != username && currentUser.Role != store.RoleAdmin { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } if request.UpdateMask == nil || len(request.UpdateMask.Paths) == 0 { @@ -97,10 +110,10 @@ func (s *APIV2Service) UpdateUser(ctx context.Context, request *apiv2pb.UpdateUs } for _, field := range request.UpdateMask.Paths { if field == "username" { - if !usernameMatcher.MatchString(strings.ToLower(request.User.Username)) { - return nil, status.Errorf(codes.InvalidArgument, "invalid username: %s", request.User.Username) + if !usernameMatcher.MatchString(strings.ToLower(username)) { + return nil, status.Errorf(codes.InvalidArgument, "invalid username: %s", username) } - update.Username = &request.User.Username + update.Username = &username } else if field == "nickname" { update.Nickname = &request.User.Nickname } else if field == "email" { @@ -146,17 +159,21 @@ func (s *APIV2Service) ListUserAccessTokens(ctx context.Context, request *apiv2p } userID := user.ID + username, err := GetUsername(request.Name) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "name is required") + } // List access token for other users need to be verified. - if user.Username != request.Username { + if user.Username != username { // Normal users can only list their access tokens. if user.Role == store.RoleUser { return nil, status.Errorf(codes.PermissionDenied, "permission denied") } // The request user must be exist. - requestUser, err := s.Store.GetUser(ctx, &store.FindUser{Username: &request.Username}) + requestUser, err := s.Store.GetUser(ctx, &store.FindUser{Username: &username}) if requestUser == nil || err != nil { - return nil, status.Errorf(codes.NotFound, "fail to find user %s", request.Username) + return nil, status.Errorf(codes.NotFound, "fail to find user %s", username) } userID = requestUser.ID } @@ -217,21 +234,7 @@ func (s *APIV2Service) CreateUserAccessToken(ctx context.Context, request *apiv2 expiresAt = request.ExpiresAt.AsTime() } - // Create access token for other users need to be verified. - if user.Username != request.Username { - // Normal users can only create access tokens for others. - if user.Role == store.RoleUser { - return nil, status.Errorf(codes.PermissionDenied, "permission denied") - } - - // The request user must be exist. - requestUser, err := s.Store.GetUser(ctx, &store.FindUser{Username: &request.Username}) - if requestUser == nil || err != nil { - return nil, status.Errorf(codes.NotFound, "fail to find user %s", request.Username) - } - } - - accessToken, err := auth.GenerateAccessToken(request.Username, user.ID, expiresAt, []byte(s.Secret)) + accessToken, err := auth.GenerateAccessToken(user.Username, user.ID, expiresAt, []byte(s.Secret)) if err != nil { return nil, status.Errorf(codes.Internal, "failed to generate access token: %v", err) } @@ -329,11 +332,11 @@ func (s *APIV2Service) UpsertAccessTokenToStore(ctx context.Context, user *store func convertUserFromStore(user *store.User) *apiv2pb.User { return &apiv2pb.User{ - Id: int32(user.ID), + Name: fmt.Sprintf("%s%s", UserNamePrefix, user.Username), + Id: user.ID, RowStatus: convertRowStatusFromStore(user.RowStatus), CreateTime: timestamppb.New(time.Unix(user.CreatedTs, 0)), UpdateTime: timestamppb.New(time.Unix(user.UpdatedTs, 0)), - Username: user.Username, Role: convertUserRoleFromStore(user.Role), Email: user.Email, Nickname: user.Nickname, diff --git a/proto/api/v2/user_service.proto b/proto/api/v2/user_service.proto index fbce9267..a54937c2 100644 --- a/proto/api/v2/user_service.proto +++ b/proto/api/v2/user_service.proto @@ -13,8 +13,8 @@ option go_package = "gen/api/v2"; service UserService { rpc GetUser(GetUserRequest) returns (GetUserResponse) { - option (google.api.http) = {get: "/api/v2/users/{username}"}; - option (google.api.method_signature) = "username"; + option (google.api.http) = {get: "/api/v2/{name=users/*}"}; + option (google.api.method_signature) = "name"; } rpc CreateUser(CreateUserRequest) returns (CreateUserResponse) { option (google.api.http) = { @@ -25,35 +25,37 @@ service UserService { } rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse) { option (google.api.http) = { - patch: "/api/v2/users/{user.username}" + patch: "/api/v2/{user.name=users/*}" body: "user" }; option (google.api.method_signature) = "user,update_mask"; } // ListUserAccessTokens returns a list of access tokens for a user. rpc ListUserAccessTokens(ListUserAccessTokensRequest) returns (ListUserAccessTokensResponse) { - option (google.api.http) = {get: "/api/v2/users/{username}/access_tokens"}; - option (google.api.method_signature) = "username"; + option (google.api.http) = {get: "/api/v2/{name=users/*}/access_tokens"}; + option (google.api.method_signature) = "name"; } // CreateUserAccessToken creates a new access token for a user. rpc CreateUserAccessToken(CreateUserAccessTokenRequest) returns (CreateUserAccessTokenResponse) { option (google.api.http) = { - post: "/api/v2/users/{username}/access_tokens" + post: "/api/v2/{name=users/*}/access_tokens" body: "*" }; - option (google.api.method_signature) = "username"; + option (google.api.method_signature) = "name"; } // DeleteUserAccessToken deletes an access token for a user. rpc DeleteUserAccessToken(DeleteUserAccessTokenRequest) returns (DeleteUserAccessTokenResponse) { - option (google.api.http) = {delete: "/api/v2/users/{username}/access_tokens/{access_token}"}; - option (google.api.method_signature) = "username,access_token"; + option (google.api.http) = {delete: "/api/v2/{name=users/*}/access_tokens/{access_token}"}; + option (google.api.method_signature) = "name,access_token"; } } message User { - int32 id = 1; + // The name of the user. + // Format: users/{username} + string name = 1; - string username = 2; + int32 id = 2; enum Role { ROLE_UNSPECIFIED = 0; @@ -79,7 +81,9 @@ message User { } message GetUserRequest { - string username = 1; + // The name of the user. + // Format: users/{username} + string name = 1; } message GetUserResponse { @@ -105,7 +109,9 @@ message UpdateUserResponse { } message ListUserAccessTokensRequest { - string username = 1; + // The name of the user. + // Format: users/{username} + string name = 1; } message ListUserAccessTokensResponse { @@ -113,7 +119,9 @@ message ListUserAccessTokensResponse { } message CreateUserAccessTokenRequest { - string username = 1; + // The name of the user. + // Format: users/{username} + string name = 1; string description = 2; @@ -125,7 +133,9 @@ message CreateUserAccessTokenResponse { } message DeleteUserAccessTokenRequest { - string username = 1; + // The name of the user. + // Format: users/{username} + string name = 1; // access_token is the access token to delete. string access_token = 2; } diff --git a/proto/gen/api/v2/README.md b/proto/gen/api/v2/README.md index e2d55fc6..04231822 100644 --- a/proto/gen/api/v2/README.md +++ b/proto/gen/api/v2/README.md @@ -1032,7 +1032,7 @@ | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| username | [string](#string) | | | +| name | [string](#string) | | The name of the user. Format: users/{username} | | description | [string](#string) | | | | expires_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | optional | | @@ -1094,7 +1094,7 @@ | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| username | [string](#string) | | | +| name | [string](#string) | | The name of the user. Format: users/{username} | | access_token | [string](#string) | | access_token is the access token to delete. | @@ -1120,7 +1120,7 @@ | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| username | [string](#string) | | | +| name | [string](#string) | | The name of the user. Format: users/{username} | @@ -1150,7 +1150,7 @@ | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| username | [string](#string) | | | +| name | [string](#string) | | The name of the user. Format: users/{username} | @@ -1211,8 +1211,8 @@ | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | +| name | [string](#string) | | The name of the user. Format: users/{username} | | id | [int32](#int32) | | | -| username | [string](#string) | | | | role | [User.Role](#memos-api-v2-User-Role) | | | | email | [string](#string) | | | | nickname | [string](#string) | | | diff --git a/proto/gen/api/v2/user_service.pb.go b/proto/gen/api/v2/user_service.pb.go index d53fa1d7..c35a6886 100644 --- a/proto/gen/api/v2/user_service.pb.go +++ b/proto/gen/api/v2/user_service.pb.go @@ -80,8 +80,10 @@ type User struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` - Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + // The name of the user. + // Format: users/{username} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Id int32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` Role User_Role `protobuf:"varint,3,opt,name=role,proto3,enum=memos.api.v2.User_Role" json:"role,omitempty"` Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email,omitempty"` Nickname string `protobuf:"bytes,5,opt,name=nickname,proto3" json:"nickname,omitempty"` @@ -124,6 +126,13 @@ func (*User) Descriptor() ([]byte, []int) { return file_api_v2_user_service_proto_rawDescGZIP(), []int{0} } +func (x *User) GetName() string { + if x != nil { + return x.Name + } + return "" +} + func (x *User) GetId() int32 { if x != nil { return x.Id @@ -131,13 +140,6 @@ func (x *User) GetId() int32 { return 0 } -func (x *User) GetUsername() string { - if x != nil { - return x.Username - } - return "" -} - func (x *User) GetRole() User_Role { if x != nil { return x.Role @@ -199,7 +201,9 @@ type GetUserRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + // The name of the user. + // Format: users/{username} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *GetUserRequest) Reset() { @@ -234,9 +238,9 @@ func (*GetUserRequest) Descriptor() ([]byte, []int) { return file_api_v2_user_service_proto_rawDescGZIP(), []int{1} } -func (x *GetUserRequest) GetUsername() string { +func (x *GetUserRequest) GetName() string { if x != nil { - return x.Username + return x.Name } return "" } @@ -489,7 +493,9 @@ type ListUserAccessTokensRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + // The name of the user. + // Format: users/{username} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` } func (x *ListUserAccessTokensRequest) Reset() { @@ -524,9 +530,9 @@ func (*ListUserAccessTokensRequest) Descriptor() ([]byte, []int) { return file_api_v2_user_service_proto_rawDescGZIP(), []int{7} } -func (x *ListUserAccessTokensRequest) GetUsername() string { +func (x *ListUserAccessTokensRequest) GetName() string { if x != nil { - return x.Username + return x.Name } return "" } @@ -583,7 +589,9 @@ type CreateUserAccessTokenRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + // The name of the user. + // Format: users/{username} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=expires_at,json=expiresAt,proto3,oneof" json:"expires_at,omitempty"` } @@ -620,9 +628,9 @@ func (*CreateUserAccessTokenRequest) Descriptor() ([]byte, []int) { return file_api_v2_user_service_proto_rawDescGZIP(), []int{9} } -func (x *CreateUserAccessTokenRequest) GetUsername() string { +func (x *CreateUserAccessTokenRequest) GetName() string { if x != nil { - return x.Username + return x.Name } return "" } @@ -693,7 +701,9 @@ type DeleteUserAccessTokenRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + // The name of the user. + // Format: users/{username} + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // access_token is the access token to delete. AccessToken string `protobuf:"bytes,2,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` } @@ -730,9 +740,9 @@ func (*DeleteUserAccessTokenRequest) Descriptor() ([]byte, []int) { return file_api_v2_user_service_proto_rawDescGZIP(), []int{11} } -func (x *DeleteUserAccessTokenRequest) GetUsername() string { +func (x *DeleteUserAccessTokenRequest) GetName() string { if x != nil { - return x.Username + return x.Name } return "" } @@ -869,38 +879,37 @@ var file_api_v2_user_service_proto_rawDesc = []byte{ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0, 0x03, 0x0a, 0x04, 0x55, 0x73, - 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, - 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, - 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, - 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x1f, 0x0a, 0x08, - 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, - 0xe0, 0x41, 0x04, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x36, 0x0a, - 0x0a, 0x72, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, - 0x2e, 0x52, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x09, 0x72, 0x6f, 0x77, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, - 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, - 0x3b, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x4f, 0x4c, 0x45, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, - 0x04, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, - 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x03, 0x22, 0x2c, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, + 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb8, 0x03, 0x0a, 0x04, 0x55, 0x73, + 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x2e, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x72, + 0x6f, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, + 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, + 0x6b, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, + 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x76, 0x61, 0x74, 0x61, + 0x72, 0x55, 0x72, 0x6c, 0x12, 0x1f, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0, 0x41, 0x04, 0x52, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x36, 0x0a, 0x0a, 0x72, 0x6f, 0x77, 0x5f, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, + 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x52, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x09, 0x72, 0x6f, 0x77, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3b, 0x0a, + 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x3b, 0x0a, 0x04, 0x52, 0x6f, 0x6c, 0x65, 0x12, + 0x14, 0x0a, 0x10, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x01, 0x12, + 0x09, 0x0a, 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, + 0x45, 0x52, 0x10, 0x03, 0x22, 0x24, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, @@ -923,21 +932,20 @@ var file_api_v2_user_service_proto_rawDesc = []byte{ 0x3c, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x39, 0x0a, + 0x76, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x31, 0x0a, 0x1b, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x62, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, - 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0c, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0xab, 0x01, 0x0a, - 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x62, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x42, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x73, 0x22, 0xa3, 0x01, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, + 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, @@ -950,98 +958,96 @@ var file_api_v2_user_service_proto_rawDesc = []byte{ 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x5d, 0x0a, + 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x55, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x1f, 0x0a, 0x1d, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xca, 0x01, - 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x09, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x32, 0xab, 0x07, 0x0a, 0x0b, 0x55, - 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x07, 0x47, 0x65, - 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x2b, 0xda, 0x41, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x12, 0x18, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x75, - 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x12, - 0x6f, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, - 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, - 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x1e, 0xda, 0x41, 0x04, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x3a, - 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, - 0x12, 0x8f, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, - 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x1f, 0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, + 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xca, 0x01, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x20, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, + 0x0a, 0x09, 0x69, 0x73, 0x73, 0x75, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x69, + 0x73, 0x73, 0x75, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, + 0x41, 0x74, 0x32, 0x91, 0x07, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x6d, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x2e, + 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6d, 0x65, + 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0xda, 0x41, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x61, 0x70, 0x69, 0x2f, + 0x76, 0x32, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, + 0x7d, 0x12, 0x6f, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, + 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x3e, 0xda, 0x41, 0x10, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x04, 0x75, - 0x73, 0x65, 0x72, 0x32, 0x1d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x75, 0x73, 0x65, - 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x7d, 0x12, 0xa8, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1e, 0xda, 0x41, 0x04, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x11, 0x3a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x22, 0x09, 0x2f, 0x76, 0x31, 0x2f, 0x75, 0x73, 0x65, + 0x72, 0x73, 0x12, 0x8d, 0x01, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x12, 0x1f, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3c, 0xda, 0x41, 0x10, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, + 0x04, 0x75, 0x73, 0x65, 0x72, 0x32, 0x1b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x7b, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, + 0x2a, 0x7d, 0x12, 0xa2, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x29, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x39, 0xda, 0x41, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x75, - 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0xae, 0x01, - 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, - 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x3c, 0xda, 0x41, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2b, 0x3a, 0x01, 0x2a, 0x22, 0x26, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, - 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x7d, - 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0xc7, - 0x01, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, + 0x73, 0x65, 0x22, 0x33, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x26, 0x12, 0x24, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, + 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0xa8, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x2a, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, + 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0xda, 0x41, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, 0x22, 0x24, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x75, 0x73, 0x65, 0x72, + 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x73, 0x12, 0xc1, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, + 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2a, 0x2e, 0x6d, + 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, - 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x55, 0xda, 0x41, 0x15, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x61, + 0x65, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4f, 0xda, 0x41, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x37, 0x2a, 0x35, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, - 0x2f, 0x7b, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x2f, 0x7b, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x7d, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, - 0x2e, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x42, 0x10, 0x55, - 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, - 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, - 0x65, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, - 0x69, 0x76, 0x32, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, - 0x73, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, - 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, - 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, - 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x35, 0x2a, 0x33, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, + 0x3d, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x2a, 0x7d, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x2f, 0x7b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x7d, 0x42, 0xa8, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x6d, + 0x65, 0x6d, 0x6f, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x42, 0x10, 0x55, 0x73, 0x65, + 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, + 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x75, 0x73, 0x65, 0x6d, + 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x65, 0x6d, 0x6f, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, 0x69, 0x76, + 0x32, 0xa2, 0x02, 0x03, 0x4d, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x2e, + 0x41, 0x70, 0x69, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x0c, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, + 0x70, 0x69, 0x5c, 0x56, 0x32, 0xe2, 0x02, 0x18, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x5c, 0x41, 0x70, + 0x69, 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x0e, 0x4d, 0x65, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, + 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/gen/api/v2/user_service.pb.gw.go b/proto/gen/api/v2/user_service.pb.gw.go index 8076647b..8b2fd304 100644 --- a/proto/gen/api/v2/user_service.pb.gw.go +++ b/proto/gen/api/v2/user_service.pb.gw.go @@ -42,14 +42,14 @@ func request_UserService_GetUser_0(ctx context.Context, marshaler runtime.Marsha _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := client.GetUser(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -68,14 +68,14 @@ func local_request_UserService_GetUser_0(ctx context.Context, marshaler runtime. _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := server.GetUser(ctx, &protoReq) @@ -118,7 +118,7 @@ func local_request_UserService_CreateUser_0(ctx context.Context, marshaler runti } var ( - filter_UserService_UpdateUser_0 = &utilities.DoubleArray{Encoding: map[string]int{"user": 0, "username": 1}, Base: []int{1, 4, 5, 2, 0, 0, 0, 0}, Check: []int{0, 1, 1, 2, 4, 2, 2, 3}} + filter_UserService_UpdateUser_0 = &utilities.DoubleArray{Encoding: map[string]int{"user": 0, "name": 1}, Base: []int{1, 4, 5, 2, 0, 0, 0, 0}, Check: []int{0, 1, 1, 2, 4, 2, 2, 3}} ) func request_UserService_UpdateUser_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { @@ -147,14 +147,14 @@ func request_UserService_UpdateUser_0(ctx context.Context, marshaler runtime.Mar _ = err ) - val, ok = pathParams["user.username"] + val, ok = pathParams["user.name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "user.username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "user.name") } - err = runtime.PopulateFieldFromPath(&protoReq, "user.username", val) + err = runtime.PopulateFieldFromPath(&protoReq, "user.name", val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user.username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user.name", err) } if err := req.ParseForm(); err != nil { @@ -195,14 +195,14 @@ func local_request_UserService_UpdateUser_0(ctx context.Context, marshaler runti _ = err ) - val, ok = pathParams["user.username"] + val, ok = pathParams["user.name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "user.username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "user.name") } - err = runtime.PopulateFieldFromPath(&protoReq, "user.username", val) + err = runtime.PopulateFieldFromPath(&protoReq, "user.name", val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user.username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "user.name", err) } if err := req.ParseForm(); err != nil { @@ -228,14 +228,14 @@ func request_UserService_ListUserAccessTokens_0(ctx context.Context, marshaler r _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := client.ListUserAccessTokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -254,14 +254,14 @@ func local_request_UserService_ListUserAccessTokens_0(ctx context.Context, marsh _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := server.ListUserAccessTokens(ctx, &protoReq) @@ -288,14 +288,14 @@ func request_UserService_CreateUserAccessToken_0(ctx context.Context, marshaler _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := client.CreateUserAccessToken(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) @@ -322,14 +322,14 @@ func local_request_UserService_CreateUserAccessToken_0(ctx context.Context, mars _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } msg, err := server.CreateUserAccessToken(ctx, &protoReq) @@ -348,14 +348,14 @@ func request_UserService_DeleteUserAccessToken_0(ctx context.Context, marshaler _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } val, ok = pathParams["access_token"] @@ -384,14 +384,14 @@ func local_request_UserService_DeleteUserAccessToken_0(ctx context.Context, mars _ = err ) - val, ok = pathParams["username"] + val, ok = pathParams["name"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "username") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } - protoReq.Username, err = runtime.String(val) + protoReq.Name, err = runtime.String(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "username", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) } val, ok = pathParams["access_token"] @@ -423,7 +423,7 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/GetUser", runtime.WithHTTPPathPattern("/api/v2/users/{username}")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/GetUser", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -473,7 +473,7 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/UpdateUser", runtime.WithHTTPPathPattern("/api/v2/users/{user.username}")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/UpdateUser", runtime.WithHTTPPathPattern("/api/v2/{user.name=users/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -498,7 +498,7 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/ListUserAccessTokens", runtime.WithHTTPPathPattern("/api/v2/users/{username}/access_tokens")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/ListUserAccessTokens", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}/access_tokens")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -523,7 +523,7 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/CreateUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/users/{username}/access_tokens")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/CreateUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}/access_tokens")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -548,7 +548,7 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/DeleteUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/users/{username}/access_tokens/{access_token}")) + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v2.UserService/DeleteUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}/access_tokens/{access_token}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -612,7 +612,7 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/GetUser", runtime.WithHTTPPathPattern("/api/v2/users/{username}")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/GetUser", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -656,7 +656,7 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/UpdateUser", runtime.WithHTTPPathPattern("/api/v2/users/{user.username}")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/UpdateUser", runtime.WithHTTPPathPattern("/api/v2/{user.name=users/*}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -678,7 +678,7 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/ListUserAccessTokens", runtime.WithHTTPPathPattern("/api/v2/users/{username}/access_tokens")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/ListUserAccessTokens", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}/access_tokens")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -700,7 +700,7 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/CreateUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/users/{username}/access_tokens")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/CreateUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}/access_tokens")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -722,7 +722,7 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) var err error var annotatedContext context.Context - annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/DeleteUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/users/{username}/access_tokens/{access_token}")) + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/memos.api.v2.UserService/DeleteUserAccessToken", runtime.WithHTTPPathPattern("/api/v2/{name=users/*}/access_tokens/{access_token}")) if err != nil { runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) return @@ -742,17 +742,17 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux } var ( - pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v2", "users", "username"}, "")) + pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v2", "users", "name"}, "")) pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "users"}, "")) - pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v2", "users", "user.username"}, "")) + pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v2", "users", "user.name"}, "")) - pattern_UserService_ListUserAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v2", "users", "username", "access_tokens"}, "")) + pattern_UserService_ListUserAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v2", "users", "name", "access_tokens"}, "")) - pattern_UserService_CreateUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v2", "users", "username", "access_tokens"}, "")) + pattern_UserService_CreateUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v2", "users", "name", "access_tokens"}, "")) - pattern_UserService_DeleteUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v2", "users", "username", "access_tokens", "access_token"}, "")) + pattern_UserService_DeleteUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v2", "users", "name", "access_tokens", "access_token"}, "")) ) var ( diff --git a/web/src/components/ChangePasswordDialog.tsx b/web/src/components/ChangePasswordDialog.tsx index fe894c3a..4bb4a8f3 100644 --- a/web/src/components/ChangePasswordDialog.tsx +++ b/web/src/components/ChangePasswordDialog.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { useGlobalStore, useUserStore } from "@/store/module"; import { useUserV1Store } from "@/store/v1"; +import { UserNamePrefix } from "@/store/v1/resourceName"; import { useTranslate } from "@/utils/i18n"; import { generateDialog } from "./Dialog"; import Icon from "./Icon"; @@ -54,7 +55,7 @@ const ChangePasswordDialog: React.FC = ({ destroy }: Props) => { const user = userStore.getState().user as User; await userV1Store.updateUser( { - username: user.username, + name: `${UserNamePrefix}${user.username}`, password: newPassword, }, ["password"] diff --git a/web/src/components/CreateAccessTokenDialog.tsx b/web/src/components/CreateAccessTokenDialog.tsx index 463ac1f5..cae2749f 100644 --- a/web/src/components/CreateAccessTokenDialog.tsx +++ b/web/src/components/CreateAccessTokenDialog.tsx @@ -69,7 +69,7 @@ const CreateAccessTokenDialog: React.FC = (props: Props) => { try { await userServiceClient.createUserAccessToken({ - username: currentUser.username, + name: currentUser.name, description: state.description, expiresAt: state.expiration ? new Date(Date.now() + state.expiration * 1000) : undefined, }); diff --git a/web/src/components/Inbox/MemoCommentMessage.tsx b/web/src/components/Inbox/MemoCommentMessage.tsx index a43009b6..ccbca9e2 100644 --- a/web/src/components/Inbox/MemoCommentMessage.tsx +++ b/web/src/components/Inbox/MemoCommentMessage.tsx @@ -5,7 +5,7 @@ import toast from "react-hot-toast"; import { activityServiceClient } from "@/grpcweb"; import useNavigateTo from "@/hooks/useNavigateTo"; import useInboxStore from "@/store/v1/inbox"; -import { extractUsernameFromName } from "@/store/v1/user"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { Activity } from "@/types/proto/api/v2/activity_service"; import { Inbox, Inbox_Status } from "@/types/proto/api/v2/inbox_service"; import { useTranslate } from "@/utils/i18n"; diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index 7e90ac56..aa54b9a4 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -9,6 +9,7 @@ import useCurrentUser from "@/hooks/useCurrentUser"; import useNavigateTo from "@/hooks/useNavigateTo"; import { useFilterStore, useMemoStore, useUserStore } from "@/store/module"; import { useUserV1Store } from "@/store/v1"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { useTranslate } from "@/utils/i18n"; import showChangeMemoCreatedTsDialog from "./ChangeMemoCreatedTsDialog"; import { showCommonDialog } from "./Dialog/CommonDialog"; @@ -42,7 +43,7 @@ const Memo: React.FC = (props: Props) => { const [shouldRender, setShouldRender] = useState(lazyRendering ? false : true); const [displayTime, setDisplayTime] = useState(getRelativeTimeString(memo.displayTs)); const memoContainerRef = useRef(null); - const readonly = memo.creatorUsername !== user?.username; + const readonly = memo.creatorUsername !== extractUsernameFromName(user?.name); const [creator, setCreator] = useState(userV1Store.getUserByUsername(memo.creatorUsername)); const referenceRelations = memo.relationList.filter((relation) => relation.type === "REFERENCE"); const commentRelations = memo.relationList.filter((relation) => relation.relatedMemoId === memo.id && relation.type === "COMMENT"); @@ -300,7 +301,7 @@ const Memo: React.FC = (props: Props) => { - {creator.nickname || creator.username} + {creator.nickname || extractUsernameFromName(creator.name)} diff --git a/web/src/components/MemoList.tsx b/web/src/components/MemoList.tsx index c99fc0b4..6af94e3d 100644 --- a/web/src/components/MemoList.tsx +++ b/web/src/components/MemoList.tsx @@ -7,6 +7,7 @@ import { getTimeStampByDate } from "@/helpers/datetime"; import useCurrentUser from "@/hooks/useCurrentUser"; import { TAG_REG } from "@/labs/marked/parser"; import { useFilterStore, useMemoStore } from "@/store/module"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { useTranslate } from "@/utils/i18n"; import Empty from "./Empty"; import Memo from "./Memo"; @@ -21,7 +22,7 @@ const MemoList: React.FC = () => { const user = useCurrentUser(); const { tag: tagQuery, duration, text: textQuery, visibility } = filter; const showMemoFilter = Boolean(tagQuery || (duration && duration.from < duration.to) || textQuery || visibility); - const username = params.username || user?.username || ""; + const username = params.username || extractUsernameFromName(user.name) || ""; const fetchMoreRef = useRef(null); diff --git a/web/src/components/Settings/AccessTokenSection.tsx b/web/src/components/Settings/AccessTokenSection.tsx index 9b87ba6c..90ddb40f 100644 --- a/web/src/components/Settings/AccessTokenSection.tsx +++ b/web/src/components/Settings/AccessTokenSection.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { userServiceClient } from "@/grpcweb"; import useCurrentUser from "@/hooks/useCurrentUser"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { UserAccessToken } from "@/types/proto/api/v2/user_service"; import { useTranslate } from "@/utils/i18n"; import showCreateAccessTokenDialog from "../CreateAccessTokenDialog"; @@ -12,7 +13,7 @@ import Icon from "../Icon"; import LearnMore from "../LearnMore"; const listAccessTokens = async (username: string) => { - const { accessTokens } = await userServiceClient.listUserAccessTokens({ username: username }); + const { accessTokens } = await userServiceClient.listUserAccessTokens({ name: `${UserAccessToken}${username}` }); return accessTokens; }; @@ -22,13 +23,13 @@ const AccessTokenSection = () => { const [userAccessTokens, setUserAccessTokens] = useState([]); useEffect(() => { - listAccessTokens(currentUser.username).then((accessTokens) => { + listAccessTokens(extractUsernameFromName(currentUser.name)).then((accessTokens) => { setUserAccessTokens(accessTokens); }); }, []); const handleCreateAccessTokenDialogConfirm = async () => { - const accessTokens = await listAccessTokens(currentUser.username); + const accessTokens = await listAccessTokens(extractUsernameFromName(currentUser.name)); setUserAccessTokens(accessTokens); }; @@ -44,7 +45,7 @@ const AccessTokenSection = () => { style: "danger", dialogName: "delete-access-token-dialog", onConfirm: async () => { - await userServiceClient.deleteUserAccessToken({ username: currentUser.username, accessToken: accessToken }); + await userServiceClient.deleteUserAccessToken({ name: currentUser.name, accessToken: accessToken }); setUserAccessTokens(userAccessTokens.filter((token) => token.accessToken !== accessToken)); }, }); diff --git a/web/src/components/Settings/MyAccountSection.tsx b/web/src/components/Settings/MyAccountSection.tsx index 31b9ae91..2f42670e 100644 --- a/web/src/components/Settings/MyAccountSection.tsx +++ b/web/src/components/Settings/MyAccountSection.tsx @@ -1,5 +1,6 @@ import { Button } from "@mui/joy"; import useCurrentUser from "@/hooks/useCurrentUser"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { useTranslate } from "@/utils/i18n"; import showChangePasswordDialog from "../ChangePasswordDialog"; import showUpdateAccountDialog from "../UpdateAccountDialog"; @@ -18,7 +19,7 @@ const MyAccountSection = () => {
{user.nickname} - ({user.username}) + ({extractUsernameFromName(user.name)})
diff --git a/web/src/components/ShareMemoDialog.tsx b/web/src/components/ShareMemoDialog.tsx index c01b2939..851dd466 100644 --- a/web/src/components/ShareMemoDialog.tsx +++ b/web/src/components/ShareMemoDialog.tsx @@ -6,6 +6,7 @@ import { getDateTimeString } from "@/helpers/datetime"; import useLoading from "@/hooks/useLoading"; import toImage from "@/labs/html2image"; import { useUserV1Store } from "@/store/v1"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { useTranslate } from "@/utils/i18n"; import { generateDialog } from "./Dialog"; import showEmbedMemoDialog from "./EmbedMemoDialog"; @@ -120,7 +121,7 @@ const ShareMemoDialog: React.FC = (props: Props) => {
- {user.nickname || user.username} + {user.nickname || extractUsernameFromName(user.name)}
diff --git a/web/src/components/UsageHeatMap.tsx b/web/src/components/UsageHeatMap.tsx index 7e6b9691..6854f8e4 100644 --- a/web/src/components/UsageHeatMap.tsx +++ b/web/src/components/UsageHeatMap.tsx @@ -6,6 +6,7 @@ import * as utils from "@/helpers/utils"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useGlobalStore } from "@/store/module"; import { useUserV1Store } from "@/store/v1"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { useTranslate, Translations } from "@/utils/i18n"; import { useFilterStore, useMemoStore } from "../store/module"; import "@/less/usage-heat-map.less"; @@ -53,20 +54,20 @@ const UsageHeatMap = () => { const containerElRef = useRef(null); useEffect(() => { - userV1Store.getOrFetchUserByUsername(user.username).then((user) => { + userV1Store.getOrFetchUserByUsername(extractUsernameFromName(user.name)).then((user) => { if (!user) { return; } setCreatedDays(Math.ceil((Date.now() - getTimeStampByDate(user.createTime)) / 1000 / 3600 / 24)); }); - }, [user.username]); + }, [user.name]); useEffect(() => { if (memos.length === 0) { return; } - getMemoStats(user.username) + getMemoStats(extractUsernameFromName(user.name)) .then(({ data }) => { setMemoAmount(data.length); const newStat: DailyUsageStat[] = getInitialUsageStat(usedDaysAmount, beginDayTimestamp); @@ -85,7 +86,7 @@ const UsageHeatMap = () => { .catch((error) => { console.error(error); }); - }, [memos.length, user.username]); + }, [memos.length, user.name]); const handleUsageStatItemMouseEnter = useCallback((event: React.MouseEvent, item: DailyUsageStat) => { const tempDiv = document.createElement("div"); diff --git a/web/src/components/UserBanner.tsx b/web/src/components/UserBanner.tsx index 223f51c3..5f4df67a 100644 --- a/web/src/components/UserBanner.tsx +++ b/web/src/components/UserBanner.tsx @@ -1,6 +1,7 @@ import useCurrentUser from "@/hooks/useCurrentUser"; import useNavigateTo from "@/hooks/useNavigateTo"; import { useGlobalStore, useUserStore } from "@/store/module"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { User_Role } from "@/types/proto/api/v2/user_service"; import { useTranslate } from "@/utils/i18n"; import showAboutSiteDialog from "./AboutSiteDialog"; @@ -18,7 +19,7 @@ const UserBanner = () => { const title = user ? user.nickname : systemStatus.customizedProfile.name || "memos"; const handleMyAccountClick = () => { - navigateTo(`/u/${encodeURIComponent(user.username)}`); + navigateTo(`/u/${encodeURIComponent(extractUsernameFromName(user.name))}`); }; const handleAboutBtnClick = () => { diff --git a/web/src/components/kit/DatePicker.tsx b/web/src/components/kit/DatePicker.tsx index 69ef1b28..d46df5ee 100644 --- a/web/src/components/kit/DatePicker.tsx +++ b/web/src/components/kit/DatePicker.tsx @@ -5,6 +5,7 @@ import { getMemoStats } from "@/helpers/api"; import { DAILY_TIMESTAMP } from "@/helpers/consts"; import { getDateStampByDate, isFutureDate } from "@/helpers/datetime"; import useCurrentUser from "@/hooks/useCurrentUser"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { useTranslate } from "@/utils/i18n"; import Icon from "../Icon"; import "@/less/common/date-picker.less"; @@ -28,7 +29,7 @@ const DatePicker: React.FC = (props: DatePickerProps) => { }, [datestamp]); useEffect(() => { - getMemoStats(user.username).then(({ data }) => { + getMemoStats(extractUsernameFromName(user.name)).then(({ data }) => { const m = new Map(); for (const record of data) { const date = getDateStampByDate(record * 1000); @@ -36,7 +37,7 @@ const DatePicker: React.FC = (props: DatePickerProps) => { } setCountByDate(m); }); - }, [user.username]); + }, [user.name]); const firstDate = new Date(currentDateStamp); const dayList = []; diff --git a/web/src/pages/DailyReview.tsx b/web/src/pages/DailyReview.tsx index 879c301c..c5893c23 100644 --- a/web/src/pages/DailyReview.tsx +++ b/web/src/pages/DailyReview.tsx @@ -15,6 +15,7 @@ import { DAILY_TIMESTAMP, DEFAULT_MEMO_LIMIT } from "@/helpers/consts"; import { getDateStampByDate, getNormalizedDateString, getTimeStampByDate, getTimeString } from "@/helpers/datetime"; import useCurrentUser from "@/hooks/useCurrentUser"; import { useMemoStore, useUserStore } from "@/store/module"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { useTranslate } from "@/utils/i18n"; const DailyReview = () => { @@ -32,7 +33,7 @@ const DailyReview = () => { const selectedDateStampWithOffset = selectedDateStamp + localSetting.dailyReviewTimeOffset * 60 * 60 * 1000; return ( m.rowStatus === "NORMAL" && - m.creatorUsername === user.username && + m.creatorUsername === extractUsernameFromName(user.name) && displayTimestamp >= selectedDateStampWithOffset && displayTimestamp < selectedDateStampWithOffset + DAILY_TIMESTAMP ); diff --git a/web/src/pages/MemoDetail.tsx b/web/src/pages/MemoDetail.tsx index 204e0ecb..7be780c6 100644 --- a/web/src/pages/MemoDetail.tsx +++ b/web/src/pages/MemoDetail.tsx @@ -20,6 +20,7 @@ import useCurrentUser from "@/hooks/useCurrentUser"; import useNavigateTo from "@/hooks/useNavigateTo"; import { useGlobalStore, useMemoStore } from "@/store/module"; import { useUserV1Store } from "@/store/v1"; +import { extractUsernameFromName } from "@/store/v1/resourceName"; import { User, User_Role } from "@/types/proto/api/v2/user_service"; import { useTranslate } from "@/utils/i18n"; @@ -35,7 +36,7 @@ const MemoDetail = () => { const { systemStatus } = globalStore.state; const memoId = Number(params.memoId); const memo = memoStore.state.memos.find((memo) => memo.id === memoId); - const allowEdit = memo?.creatorUsername === currentUser?.username; + const allowEdit = memo?.creatorUsername === extractUsernameFromName(currentUser.name); const referenceRelations = memo?.relationList.filter((relation) => relation.type === "REFERENCE") || []; const commentRelations = memo?.relationList.filter((relation) => relation.relatedMemoId === memo.id && relation.type === "COMMENT") || []; const comments = commentRelations diff --git a/web/src/store/v1/resourceName.ts b/web/src/store/v1/resourceName.ts new file mode 100644 index 00000000..7fc71ce2 --- /dev/null +++ b/web/src/store/v1/resourceName.ts @@ -0,0 +1,5 @@ +export const UserNamePrefix = "users/"; + +export const extractUsernameFromName = (name: string) => { + return name.split("/")[1]; +}; diff --git a/web/src/store/v1/user.ts b/web/src/store/v1/user.ts index 6e04a555..f25d67b6 100644 --- a/web/src/store/v1/user.ts +++ b/web/src/store/v1/user.ts @@ -1,6 +1,7 @@ import { create } from "zustand"; import { userServiceClient } from "@/grpcweb"; import { User } from "@/types/proto/api/v2/user_service"; +import { UserNamePrefix, extractUsernameFromName } from "./resourceName"; interface UserV1Store { userMapByUsername: Record; @@ -25,7 +26,7 @@ const useUserV1Store = create()((set, get) => ({ const promisedUser = userServiceClient .getUser({ - username: username, + name: `${UserNamePrefix}${username}`, }) .then(({ user }) => user); requestCache.set(username, promisedUser); @@ -50,15 +51,12 @@ const useUserV1Store = create()((set, get) => ({ if (!updatedUser) { throw new Error("User not found"); } + const username = extractUsernameFromName(updatedUser.name); const userMap = get().userMapByUsername; - userMap[updatedUser.username] = updatedUser; + userMap[username] = updatedUser; set(userMap); return updatedUser; }, })); -export const extractUsernameFromName = (name: string) => { - return name.split("/")[1]; -}; - export default useUserV1Store;