github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/store/sqlstore/compliance_store.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/masterhung0112/hk_server/v5/model"
    13  	"github.com/masterhung0112/hk_server/v5/store"
    14  )
    15  
    16  type SqlComplianceStore struct {
    17  	*SqlStore
    18  }
    19  
    20  func newSqlComplianceStore(sqlStore *SqlStore) store.ComplianceStore {
    21  	s := &SqlComplianceStore{sqlStore}
    22  
    23  	for _, db := range sqlStore.GetAllConns() {
    24  		table := db.AddTableWithName(model.Compliance{}, "Compliances").SetKeys(false, "Id")
    25  		table.ColMap("Id").SetMaxSize(26)
    26  		table.ColMap("UserId").SetMaxSize(26)
    27  		table.ColMap("Status").SetMaxSize(64)
    28  		table.ColMap("Desc").SetMaxSize(512)
    29  		table.ColMap("Type").SetMaxSize(64)
    30  		table.ColMap("Keywords").SetMaxSize(512)
    31  		table.ColMap("Emails").SetMaxSize(1024)
    32  	}
    33  
    34  	return s
    35  }
    36  
    37  func (s SqlComplianceStore) createIndexesIfNotExists() {
    38  }
    39  
    40  func (s SqlComplianceStore) Save(compliance *model.Compliance) (*model.Compliance, error) {
    41  	compliance.PreSave()
    42  	if err := compliance.IsValid(); err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	if err := s.GetMaster().Insert(compliance); err != nil {
    47  		return nil, errors.Wrap(err, "failed to save Compliance")
    48  	}
    49  	return compliance, nil
    50  }
    51  
    52  func (s SqlComplianceStore) Update(compliance *model.Compliance) (*model.Compliance, error) {
    53  	if err := compliance.IsValid(); err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	if _, err := s.GetMaster().Update(compliance); err != nil {
    58  		return nil, errors.Wrap(err, "failed to update Compliance")
    59  	}
    60  	return compliance, nil
    61  }
    62  
    63  func (s SqlComplianceStore) GetAll(offset, limit int) (model.Compliances, error) {
    64  	query := "SELECT * FROM Compliances ORDER BY CreateAt DESC LIMIT :Limit OFFSET :Offset"
    65  
    66  	var compliances model.Compliances
    67  	if _, err := s.GetReplica().Select(&compliances, query, map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil {
    68  		return nil, errors.Wrap(err, "failed to find all Compliances")
    69  	}
    70  	return compliances, nil
    71  }
    72  
    73  func (s SqlComplianceStore) Get(id string) (*model.Compliance, error) {
    74  	obj, err := s.GetReplica().Get(model.Compliance{}, id)
    75  	if err != nil {
    76  		return nil, errors.Wrapf(err, "failed to get Compliance with id=%s", id)
    77  	}
    78  	if obj == nil {
    79  		return nil, store.NewErrNotFound("Compliance", id)
    80  	}
    81  	return obj.(*model.Compliance), nil
    82  }
    83  
    84  func (s SqlComplianceStore) ComplianceExport(job *model.Compliance, cursor model.ComplianceExportCursor, limit int) ([]*model.CompliancePost, model.ComplianceExportCursor, error) {
    85  	props := map[string]interface{}{"EndTime": job.EndAt, "Limit": limit}
    86  
    87  	keywordQuery := ""
    88  	keywords := strings.Fields(strings.TrimSpace(strings.ToLower(strings.Replace(job.Keywords, ",", " ", -1))))
    89  	if len(keywords) > 0 {
    90  		clauses := make([]string, len(keywords))
    91  
    92  		for i, keyword := range keywords {
    93  			keyword = sanitizeSearchTerm(keyword, "\\")
    94  			clauses[i] = "LOWER(Posts.Message) LIKE :Keyword" + strconv.Itoa(i)
    95  			props["Keyword"+strconv.Itoa(i)] = "%" + keyword + "%"
    96  		}
    97  
    98  		keywordQuery = "AND (" + strings.Join(clauses, " OR ") + ")"
    99  	}
   100  
   101  	emailQuery := ""
   102  	emails := strings.Fields(strings.TrimSpace(strings.ToLower(strings.Replace(job.Emails, ",", " ", -1))))
   103  	if len(emails) > 0 {
   104  		clauses := make([]string, len(emails))
   105  
   106  		for i, email := range emails {
   107  			clauses[i] = "Users.Email = :Email" + strconv.Itoa(i)
   108  			props["Email"+strconv.Itoa(i)] = email
   109  		}
   110  
   111  		emailQuery = "AND (" + strings.Join(clauses, " OR ") + ")"
   112  	}
   113  
   114  	// The idea is to first iterate over the channel posts, and then when we run out of those,
   115  	// start iterating over the direct message posts.
   116  
   117  	var channelPosts []*model.CompliancePost
   118  	channelsQuery := ""
   119  	if !cursor.ChannelsQueryCompleted {
   120  		if cursor.LastChannelsQueryPostCreateAt == 0 {
   121  			cursor.LastChannelsQueryPostCreateAt = job.StartAt
   122  		}
   123  		props["LastPostCreateAt"] = cursor.LastChannelsQueryPostCreateAt
   124  		props["LastPostId"] = cursor.LastChannelsQueryPostID
   125  		channelsQuery = `
   126  		SELECT
   127  			Teams.Name AS TeamName,
   128  			Teams.DisplayName AS TeamDisplayName,
   129  			Channels.Name AS ChannelName,
   130  			Channels.DisplayName AS ChannelDisplayName,
   131  			Channels.Type AS ChannelType,
   132  			Users.Username AS UserUsername,
   133  			Users.Email AS UserEmail,
   134  			Users.Nickname AS UserNickname,
   135  			Posts.Id AS PostId,
   136  			Posts.CreateAt AS PostCreateAt,
   137  			Posts.UpdateAt AS PostUpdateAt,
   138  			Posts.DeleteAt AS PostDeleteAt,
   139  			Posts.RootId AS PostRootId,
   140  			Posts.ParentId AS PostParentId,
   141  			Posts.OriginalId AS PostOriginalId,
   142  			Posts.Message AS PostMessage,
   143  			Posts.Type AS PostType,
   144  			Posts.Props AS PostProps,
   145  			Posts.Hashtags AS PostHashtags,
   146  			Posts.FileIds AS PostFileIds,
   147  			Bots.UserId IS NOT NULL AS IsBot
   148  		FROM
   149  			Teams,
   150  			Channels,
   151  			Users,
   152  			Posts
   153  		LEFT JOIN
   154  			Bots ON Bots.UserId = Posts.UserId
   155  		WHERE
   156  			Teams.Id = Channels.TeamId
   157  				AND Posts.ChannelId = Channels.Id
   158  				AND Posts.UserId = Users.Id
   159  				AND (
   160  					Posts.CreateAt > :LastPostCreateAt
   161  					OR (Posts.CreateAt = :LastPostCreateAt AND Posts.Id > :LastPostId)
   162  				)
   163  				AND Posts.CreateAt < :EndTime
   164  				` + emailQuery + `
   165  				` + keywordQuery + `
   166  		ORDER BY Posts.CreateAt, Posts.Id
   167  		LIMIT :Limit`
   168  		if _, err := s.GetReplica().Select(&channelPosts, channelsQuery, props); err != nil {
   169  			return nil, cursor, errors.Wrap(err, "unable to export compliance")
   170  		}
   171  		if len(channelPosts) < limit {
   172  			cursor.ChannelsQueryCompleted = true
   173  		} else {
   174  			cursor.LastChannelsQueryPostCreateAt = channelPosts[len(channelPosts)-1].PostCreateAt
   175  			cursor.LastChannelsQueryPostID = channelPosts[len(channelPosts)-1].PostId
   176  		}
   177  	}
   178  
   179  	var directMessagePosts []*model.CompliancePost
   180  	directMessagesQuery := ""
   181  	if !cursor.DirectMessagesQueryCompleted && len(channelPosts) < limit {
   182  		if cursor.LastDirectMessagesQueryPostCreateAt == 0 {
   183  			cursor.LastDirectMessagesQueryPostCreateAt = job.StartAt
   184  		}
   185  		props["LastPostCreateAt"] = cursor.LastDirectMessagesQueryPostCreateAt
   186  		props["LastPostId"] = cursor.LastDirectMessagesQueryPostID
   187  		props["Limit"] = limit - len(channelPosts)
   188  		directMessagesQuery = `
   189  		SELECT
   190  			'direct-messages' AS TeamName,
   191  			'Direct Messages' AS TeamDisplayName,
   192  			Channels.Name AS ChannelName,
   193  			Channels.DisplayName AS ChannelDisplayName,
   194  			Channels.Type AS ChannelType,
   195  			Users.Username AS UserUsername,
   196  			Users.Email AS UserEmail,
   197  			Users.Nickname AS UserNickname,
   198  			Posts.Id AS PostId,
   199  			Posts.CreateAt AS PostCreateAt,
   200  			Posts.UpdateAt AS PostUpdateAt,
   201  			Posts.DeleteAt AS PostDeleteAt,
   202  			Posts.RootId AS PostRootId,
   203  			Posts.ParentId AS PostParentId,
   204  			Posts.OriginalId AS PostOriginalId,
   205  			Posts.Message AS PostMessage,
   206  			Posts.Type AS PostType,
   207  			Posts.Props AS PostProps,
   208  			Posts.Hashtags AS PostHashtags,
   209  			Posts.FileIds AS PostFileIds,
   210  			Bots.UserId IS NOT NULL AS IsBot
   211  		FROM
   212  			Channels,
   213  			Users,
   214  			Posts
   215  		LEFT JOIN
   216  			Bots ON Bots.UserId = Posts.UserId
   217  		WHERE
   218  			Channels.TeamId = ''
   219  				AND Posts.ChannelId = Channels.Id
   220  				AND Posts.UserId = Users.Id
   221  				AND (
   222  					Posts.CreateAt > :LastPostCreateAt
   223  					OR (Posts.CreateAt = :LastPostCreateAt AND Posts.Id > :LastPostId)
   224  				)
   225  				AND Posts.CreateAt < :EndTime
   226  				` + emailQuery + `
   227  				` + keywordQuery + `
   228  		ORDER BY Posts.CreateAt, Posts.Id
   229  		LIMIT :Limit`
   230  
   231  		if _, err := s.GetReplica().Select(&directMessagePosts, directMessagesQuery, props); err != nil {
   232  			return nil, cursor, errors.Wrap(err, "unable to export compliance")
   233  		}
   234  		if len(directMessagePosts) < limit {
   235  			cursor.DirectMessagesQueryCompleted = true
   236  		} else {
   237  			cursor.LastDirectMessagesQueryPostCreateAt = directMessagePosts[len(directMessagePosts)-1].PostCreateAt
   238  			cursor.LastDirectMessagesQueryPostID = directMessagePosts[len(directMessagePosts)-1].PostId
   239  		}
   240  	}
   241  
   242  	return append(channelPosts, directMessagePosts...), cursor, nil
   243  }
   244  
   245  func (s SqlComplianceStore) MessageExport(cursor model.MessageExportCursor, limit int) ([]*model.MessageExport, model.MessageExportCursor, error) {
   246  	props := map[string]interface{}{
   247  		"LastPostUpdateAt": cursor.LastPostUpdateAt,
   248  		"LastPostId":       cursor.LastPostId,
   249  		"Limit":            limit,
   250  	}
   251  	query :=
   252  		`SELECT
   253  			Posts.Id AS PostId,
   254  			Posts.CreateAt AS PostCreateAt,
   255  			Posts.UpdateAt AS PostUpdateAt,
   256  			Posts.DeleteAt AS PostDeleteAt,
   257  			Posts.Message AS PostMessage,
   258  			Posts.Type AS PostType,
   259  			Posts.Props AS PostProps,
   260  			Posts.OriginalId AS PostOriginalId,
   261  			Posts.RootId AS PostRootId,
   262  			Posts.FileIds AS PostFileIds,
   263  			Teams.Id AS TeamId,
   264  			Teams.Name AS TeamName,
   265  			Teams.DisplayName AS TeamDisplayName,
   266  			Channels.Id AS ChannelId,
   267  			CASE
   268  				WHEN Channels.Type = 'D' THEN 'Direct Message'
   269  				WHEN Channels.Type = 'G' THEN 'Group Message'
   270  				ELSE Channels.DisplayName
   271  			END AS ChannelDisplayName,
   272  			Channels.Name AS ChannelName,
   273  			Channels.Type AS ChannelType,
   274  			Users.Id AS UserId,
   275  			Users.Email AS UserEmail,
   276  			Users.Username,
   277  			Bots.UserId IS NOT NULL AS IsBot
   278  		FROM
   279  			Posts
   280  		LEFT OUTER JOIN Channels ON Posts.ChannelId = Channels.Id
   281  		LEFT OUTER JOIN Teams ON Channels.TeamId = Teams.Id
   282  		LEFT OUTER JOIN Users ON Posts.UserId = Users.Id
   283  		LEFT JOIN Bots ON Bots.UserId = Posts.UserId
   284  		WHERE (
   285  			Posts.UpdateAt > :LastPostUpdateAt
   286  			OR (
   287  				Posts.UpdateAt = :LastPostUpdateAt
   288  				AND Posts.Id > :LastPostId
   289  			)
   290  		) AND Posts.Type NOT LIKE 'system_%'
   291  		ORDER BY PostUpdateAt, PostId
   292  		LIMIT :Limit`
   293  
   294  	var cposts []*model.MessageExport
   295  	if _, err := s.GetReplica().Select(&cposts, query, props); err != nil {
   296  		return nil, cursor, errors.Wrap(err, "unable to export messages")
   297  	}
   298  	if len(cposts) > 0 {
   299  		cursor.LastPostUpdateAt = *cposts[len(cposts)-1].PostUpdateAt
   300  		cursor.LastPostId = *cposts[len(cposts)-1].PostId
   301  	}
   302  	return cposts, cursor, nil
   303  }