github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/services/sharedchannel/attachment.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sharedchannel 5 6 import ( 7 "context" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "sync" 12 13 "github.com/masterhung0112/hk_server/v5/model" 14 "github.com/masterhung0112/hk_server/v5/services/remotecluster" 15 "github.com/masterhung0112/hk_server/v5/shared/mlog" 16 ) 17 18 // postsToAttachments returns the file attachments for a slice of posts that need to be synchronized. 19 func (scs *Service) shouldSyncAttachment(fi *model.FileInfo, rc *model.RemoteCluster) bool { 20 sca, err := scs.server.GetStore().SharedChannel().GetAttachment(fi.Id, rc.RemoteId) 21 if err != nil { 22 if _, ok := err.(errNotFound); !ok { 23 scs.server.GetLogger().Log(mlog.LvlSharedChannelServiceError, "error fetching shared channel attachment", 24 mlog.String("file_id", fi.Id), 25 mlog.String("remote_id", rc.RemoteId), 26 mlog.Err(err), 27 ) 28 } 29 // no record so sync is needed 30 return true 31 } 32 33 return sca.LastSyncAt < fi.UpdateAt 34 } 35 36 // sendAttachmentForRemote asynchronously sends a file attachment to a remote cluster. 37 func (scs *Service) sendAttachmentForRemote(fi *model.FileInfo, post *model.Post, rc *model.RemoteCluster) error { 38 rcs := scs.server.GetRemoteClusterService() 39 if rcs == nil { 40 return fmt.Errorf("cannot update remote cluster for remote id %s; Remote Cluster Service not enabled", rc.RemoteId) 41 } 42 43 us := &model.UploadSession{ 44 Id: model.NewId(), 45 Type: model.UploadTypeAttachment, 46 UserId: post.UserId, 47 ChannelId: post.ChannelId, 48 Filename: fi.Name, 49 FileSize: fi.Size, 50 RemoteId: rc.RemoteId, 51 ReqFileId: fi.Id, 52 } 53 54 payload, err := json.Marshal(us) 55 if err != nil { 56 return err 57 } 58 59 msg := model.NewRemoteClusterMsg(TopicUploadCreate, payload) 60 61 ctx, cancel := context.WithTimeout(context.Background(), remotecluster.SendTimeout) 62 defer cancel() 63 64 var usResp model.UploadSession 65 var respErr error 66 var wg sync.WaitGroup 67 wg.Add(1) 68 69 // creating the upload session on the remote server needs to be done synchronously. 70 err = rcs.SendMsg(ctx, msg, rc, func(msg model.RemoteClusterMsg, rc *model.RemoteCluster, resp *remotecluster.Response, err error) { 71 defer wg.Done() 72 if err != nil { 73 respErr = err 74 return 75 } 76 if !resp.IsSuccess() { 77 respErr = errors.New(resp.Err) 78 return 79 } 80 respErr = json.Unmarshal(resp.Payload, &usResp) 81 }) 82 83 if err != nil { 84 return fmt.Errorf("error sending create upload session to remote %s for post %s: %w", rc.RemoteId, post.Id, err) 85 } 86 87 wg.Wait() 88 89 if respErr != nil { 90 return fmt.Errorf("invalid create upload session response for remote %s and post %s: %w", rc.RemoteId, post.Id, respErr) 91 } 92 93 ctx2, cancel2 := context.WithTimeout(context.Background(), remotecluster.SendFileTimeout) 94 defer cancel2() 95 96 return rcs.SendFile(ctx2, &usResp, fi, rc, scs.app, func(us *model.UploadSession, rc *model.RemoteCluster, resp *remotecluster.Response, err error) { 97 if err != nil { 98 return // this means the response could not be parsed; already logged 99 } 100 101 if !resp.IsSuccess() { 102 scs.server.GetLogger().Log(mlog.LvlSharedChannelServiceError, "send file failed", 103 mlog.String("remote", rc.DisplayName), 104 mlog.String("uploadId", usResp.Id), 105 mlog.String("err", resp.Err), 106 ) 107 return 108 } 109 110 // response payload should be a model.FileInfo. 111 var fi model.FileInfo 112 if err2 := json.Unmarshal(resp.Payload, &fi); err2 != nil { 113 scs.server.GetLogger().Log(mlog.LvlSharedChannelServiceError, "invalid file info response after send file", 114 mlog.String("remote", rc.DisplayName), 115 mlog.String("uploadId", usResp.Id), 116 mlog.Err(err2), 117 ) 118 return 119 } 120 121 // save file attachment record in SharedChannelAttachments table 122 sca := &model.SharedChannelAttachment{ 123 FileId: fi.Id, 124 RemoteId: rc.RemoteId, 125 } 126 if _, err2 := scs.server.GetStore().SharedChannel().UpsertAttachment(sca); err2 != nil { 127 scs.server.GetLogger().Log(mlog.LvlSharedChannelServiceError, "error saving SharedChannelAttachment", 128 mlog.String("remote", rc.DisplayName), 129 mlog.String("uploadId", usResp.Id), 130 mlog.Err(err2), 131 ) 132 return 133 } 134 135 scs.server.GetLogger().Log(mlog.LvlSharedChannelServiceDebug, "send file successful", 136 mlog.String("remote", rc.DisplayName), 137 mlog.String("uploadId", usResp.Id), 138 ) 139 }) 140 } 141 142 // onReceiveUploadCreate is called when a message requesting to create an upload session is received. An upload session is 143 // created and the id returned in the response. 144 func (scs *Service) onReceiveUploadCreate(msg model.RemoteClusterMsg, rc *model.RemoteCluster, response *remotecluster.Response) error { 145 var us model.UploadSession 146 147 if err := json.Unmarshal(msg.Payload, &us); err != nil { 148 return fmt.Errorf("invalid upload session request: %w", err) 149 } 150 151 // make sure channel is shared for the remote sender 152 if _, err := scs.server.GetStore().SharedChannel().GetRemoteByIds(us.ChannelId, rc.RemoteId); err != nil { 153 return fmt.Errorf("could not validate upload session for remote: %w", err) 154 } 155 156 us.RemoteId = rc.RemoteId // don't let remotes try to impersonate each other 157 158 // create upload session. 159 usSaved, appErr := scs.app.CreateUploadSession(&us) 160 if appErr != nil { 161 return appErr 162 } 163 164 response.SetPayload(usSaved) 165 return nil 166 }