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  }