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 }