diff --git a/config/minio.yml b/config/minio.yml index 671824078..1a5377cb7 100644 --- a/config/minio.yml +++ b/config/minio.yml @@ -8,10 +8,10 @@ secretAccessKey: openIM123 sessionToken: # Internal address of the MinIO server #internalAddress: 192.168.1.36:10005 -internalAddress: 192.168.1.91:10005 +internalAddress: 192.168.0.250:10005 # External address of the MinIO server, accessible from outside. Supports both HTTP and HTTPS using a domain name #externalAddress: http://192.168.1.36:10005 -externalAddress: http://192.168.1.91:10005 +externalAddress: http://192.168.0.250:10005 # Flag to enable or disable public read access to the bucket publicRead: false diff --git a/internal/api/friend.go b/internal/api/friend.go index 023ae28c8..7a7538f0a 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -122,3 +122,7 @@ func (o *FriendApi) GetSelfUnhandledApplyCount(c *gin.Context) { func (o *FriendApi) GetPinnedFriendIDs(c *gin.Context) { a2r.Call(c, relation.FriendClient.GetPinnedFriendIDs, o.Client) } + +func (o *FriendApi) AddOnewayFriend(c *gin.Context) { + a2r.Call(c, relation.FriendClient.AddOnewayFriend, o.Client) +} diff --git a/internal/api/router.go b/internal/api/router.go index 442ee67f8..f82dac2f5 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -205,6 +205,7 @@ func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, co friendRouterGroup.POST("/get_full_friend_user_ids", f.GetFullFriendUserIDs) friendRouterGroup.POST("/get_self_unhandled_apply_count", f.GetSelfUnhandledApplyCount) friendRouterGroup.POST("/get_pinned_friend_ids", f.GetPinnedFriendIDs) + friendRouterGroup.POST("/add_oneway_friend", f.AddOnewayFriend) } g := NewGroupApi(group.NewGroupClient(groupConn)) diff --git a/internal/rpc/relation/friend.go b/internal/rpc/relation/friend.go index 38aa23d51..3ed6cb7a5 100644 --- a/internal/rpc/relation/friend.go +++ b/internal/rpc/relation/friend.go @@ -671,6 +671,37 @@ func (s *friendServer) GetPinnedFriendIDs(ctx context.Context, req *relation.Get return &relation.GetPinnedFriendIDsResp{FriendUserIDs: ids}, nil } +// AddOnewayFriend adds B to A's friend list without requiring B's consent. +// Only the A->B side of the friendship is created; B's friend list is unaffected. +func (s *friendServer) AddOnewayFriend(ctx context.Context, req *relation.ApplyToAddFriendReq) (*relation.ApplyToAddFriendResp, error) { + if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config.Share.IMAdminUserID); err != nil { + return nil, err + } + if req.ToUserID == req.FromUserID { + return nil, servererrs.ErrCanNotAddYourself.WrapMsg("req.ToUserID", req.ToUserID) + } + if err := s.userClient.CheckUser(ctx, []string{req.ToUserID, req.FromUserID}); err != nil { + return nil, err + } + in1, _, err := s.db.CheckIn(ctx, req.FromUserID, req.ToUserID) + if err != nil { + return nil, err + } + if in1 { + return nil, servererrs.ErrRelationshipAlready.WrapMsg("already in friend list") + } + if err := s.db.BecomeOnewayFriend(ctx, req.FromUserID, req.ToUserID, constant.BecomeFriendByOneway); err != nil { + return nil, err + } + // Notify only A so that A's incremental friend sync is triggered. + s.notificationSender.FriendApplicationAgreedNotification(ctx, &relation.RespondFriendApplyReq{ + FromUserID: req.FromUserID, + ToUserID: req.ToUserID, + HandleResult: constant.FriendResponseAgree, + }, false) + return &relation.ApplyToAddFriendResp{}, nil +} + func (s *friendServer) getCommonUserMap(ctx context.Context, userIDs []string) (map[string]common_user.CommonUser, error) { users, err := s.userClient.GetUsersInfo(ctx, userIDs) if err != nil { diff --git a/pkg/common/storage/controller/friend.go b/pkg/common/storage/controller/friend.go index db0b34216..65ca90186 100644 --- a/pkg/common/storage/controller/friend.go +++ b/pkg/common/storage/controller/friend.go @@ -92,6 +92,10 @@ type FriendDatabase interface { GetUnhandledCount(ctx context.Context, userID string, ts int64) (int64, error) GetPinnedFriendIDs(ctx context.Context, ownerUserID string) ([]string, error) + + // BecomeOnewayFriend inserts a single-side friendship: ownerUserID -> friendUserID. + // The reverse side (friendUserID -> ownerUserID) is NOT created. + BecomeOnewayFriend(ctx context.Context, ownerUserID, friendUserID string, addSource int32) error } type friendDatabase struct { @@ -408,3 +412,25 @@ func (f *friendDatabase) GetUnhandledCount(ctx context.Context, userID string, t func (f *friendDatabase) GetPinnedFriendIDs(ctx context.Context, ownerUserID string) ([]string, error) { return f.friend.FindPinnedFriendUserIDs(ctx, ownerUserID) } + +// BecomeOnewayFriend creates only the ownerUserID->friendUserID side of the friendship. +// The reverse side is intentionally omitted so that the target user is not aware of being added. +func (f *friendDatabase) BecomeOnewayFriend(ctx context.Context, ownerUserID, friendUserID string, addSource int32) error { + return f.tx.Transaction(ctx, func(ctx context.Context) error { + existing, err := f.friend.FindFriends(ctx, ownerUserID, []string{friendUserID}) + if err != nil { + return err + } + if len(existing) > 0 { + // already in ownerUserID's friend list, nothing to do + return nil + } + opUserID := mcontext.GetOpUserID(ctx) + if err := f.friend.Create(ctx, []*model.Friend{ + {OwnerUserID: ownerUserID, FriendUserID: friendUserID, AddSource: addSource, OperatorUserID: opUserID}, + }); err != nil { + return err + } + return f.cache.DelFriendIDs(ownerUserID).DelMaxFriendVersion(ownerUserID).ChainExecDel(ctx) + }) +} diff --git a/protocol b/protocol index ed16bd0c4..d78ed4f7b 160000 --- a/protocol +++ b/protocol @@ -1 +1 @@ -Subproject commit ed16bd0c4049d722e7b605c3f314ee661b9bc4e1 +Subproject commit d78ed4f7b4563964d1f5250aa80b122ab1ef6b5d