github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/store/sqlstore/session_store.go (about) 1 package sqlstore 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 sq "github.com/Masterminds/squirrel" 9 "github.com/pkg/errors" 10 11 "github.com/masterhung0112/hk_server/v5/model" 12 "github.com/masterhung0112/hk_server/v5/shared/mlog" 13 "github.com/masterhung0112/hk_server/v5/store" 14 ) 15 16 const ( 17 SessionsCleanupDelayMilliseconds = 100 18 ) 19 20 type SqlSessionStore struct { 21 *SqlStore 22 } 23 24 func newSqlSessionStore(sqlStore *SqlStore) store.SessionStore { 25 us := &SqlSessionStore{sqlStore} 26 27 for _, db := range sqlStore.GetAllConns() { 28 table := db.AddTableWithName(model.Session{}, "Sessions").SetKeys(false, "Id") 29 table.ColMap("Id").SetMaxSize(26) 30 table.ColMap("Token").SetMaxSize(26) 31 table.ColMap("UserId").SetMaxSize(26) 32 table.ColMap("DeviceId").SetMaxSize(512) 33 table.ColMap("Roles").SetMaxSize(64) 34 table.ColMap("Props").SetMaxSize(1000) 35 } 36 37 return us 38 } 39 40 func (me SqlSessionStore) createIndexesIfNotExists() { 41 me.CreateIndexIfNotExists("idx_sessions_user_id", "Sessions", "UserId") 42 me.CreateIndexIfNotExists("idx_sessions_token", "Sessions", "Token") 43 me.CreateIndexIfNotExists("idx_sessions_expires_at", "Sessions", "ExpiresAt") 44 me.CreateIndexIfNotExists("idx_sessions_create_at", "Sessions", "CreateAt") 45 me.CreateIndexIfNotExists("idx_sessions_last_activity_at", "Sessions", "LastActivityAt") 46 } 47 48 func (me SqlSessionStore) Save(session *model.Session) (*model.Session, error) { 49 if session.Id != "" { 50 return nil, store.NewErrInvalidInput("Session", "id", session.Id) 51 } 52 session.PreSave() 53 54 if err := me.GetMaster().Insert(session); err != nil { 55 return nil, errors.Wrapf(err, "failed to save Session with id=%s", session.Id) 56 } 57 58 teamMembers, err := me.Team().GetTeamsForUser(context.Background(), session.UserId) 59 if err != nil { 60 return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", session.UserId) 61 } 62 63 session.TeamMembers = make([]*model.TeamMember, 0, len(teamMembers)) 64 for _, tm := range teamMembers { 65 if tm.DeleteAt == 0 { 66 session.TeamMembers = append(session.TeamMembers, tm) 67 } 68 } 69 70 return session, nil 71 } 72 73 func (me SqlSessionStore) Get(ctx context.Context, sessionIdOrToken string) (*model.Session, error) { 74 var sessions []*model.Session 75 76 if _, err := me.DBFromContext(ctx).Select(&sessions, "SELECT * FROM Sessions WHERE Token = :Token OR Id = :Id LIMIT 1", map[string]interface{}{"Token": sessionIdOrToken, "Id": sessionIdOrToken}); err != nil { 77 return nil, errors.Wrapf(err, "failed to find Sessions with sessionIdOrToken=%s", sessionIdOrToken) 78 } else if len(sessions) == 0 { 79 return nil, store.NewErrNotFound("Session", fmt.Sprintf("sessionIdOrToken=%s", sessionIdOrToken)) 80 } 81 session := sessions[0] 82 83 tempMembers, err := me.Team().GetTeamsForUser( 84 WithMaster(context.Background()), 85 session.UserId) 86 if err != nil { 87 return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", session.UserId) 88 } 89 sessions[0].TeamMembers = make([]*model.TeamMember, 0, len(tempMembers)) 90 for _, tm := range tempMembers { 91 if tm.DeleteAt == 0 { 92 sessions[0].TeamMembers = append(sessions[0].TeamMembers, tm) 93 } 94 } 95 return session, nil 96 } 97 98 func (me SqlSessionStore) GetSessions(userId string) ([]*model.Session, error) { 99 var sessions []*model.Session 100 101 if _, err := me.GetReplica().Select(&sessions, "SELECT * FROM Sessions WHERE UserId = :UserId ORDER BY LastActivityAt DESC", map[string]interface{}{"UserId": userId}); err != nil { 102 return nil, errors.Wrapf(err, "failed to find Sessions with userId=%s", userId) 103 } 104 105 teamMembers, err := me.Team().GetTeamsForUser(context.Background(), userId) 106 if err != nil { 107 return nil, errors.Wrapf(err, "failed to find TeamMembers for Session with userId=%s", userId) 108 } 109 110 for _, session := range sessions { 111 session.TeamMembers = make([]*model.TeamMember, 0, len(teamMembers)) 112 for _, tm := range teamMembers { 113 if tm.DeleteAt == 0 { 114 session.TeamMembers = append(session.TeamMembers, tm) 115 } 116 } 117 } 118 return sessions, nil 119 } 120 121 func (me SqlSessionStore) GetSessionsWithActiveDeviceIds(userId string) ([]*model.Session, error) { 122 query := 123 `SELECT * 124 FROM 125 Sessions 126 WHERE 127 UserId = :UserId AND 128 ExpiresAt != 0 AND 129 :ExpiresAt <= ExpiresAt AND 130 DeviceId != ''` 131 132 var sessions []*model.Session 133 134 _, err := me.GetReplica().Select(&sessions, query, map[string]interface{}{"UserId": userId, "ExpiresAt": model.GetMillis()}) 135 if err != nil { 136 return nil, errors.Wrapf(err, "failed to find Sessions with userId=%s", userId) 137 } 138 return sessions, nil 139 } 140 141 func (me SqlSessionStore) GetSessionsExpired(thresholdMillis int64, mobileOnly bool, unnotifiedOnly bool) ([]*model.Session, error) { 142 now := model.GetMillis() 143 builder := me.getQueryBuilder(). 144 Select("*"). 145 From("Sessions"). 146 Where(sq.NotEq{"ExpiresAt": 0}). 147 Where(sq.Lt{"ExpiresAt": now}). 148 Where(sq.Gt{"ExpiresAt": now - thresholdMillis}) 149 if mobileOnly { 150 builder = builder.Where(sq.NotEq{"DeviceId": ""}) 151 } 152 if unnotifiedOnly { 153 builder = builder.Where(sq.NotEq{"ExpiredNotify": true}) 154 } 155 156 query, args, err := builder.ToSql() 157 if err != nil { 158 return nil, errors.Wrap(err, "sessions_tosql") 159 } 160 161 var sessions []*model.Session 162 163 _, err = me.GetReplica().Select(&sessions, query, args...) 164 if err != nil { 165 return nil, errors.Wrap(err, "failed to find Sessions") 166 } 167 return sessions, nil 168 } 169 170 func (me SqlSessionStore) UpdateExpiredNotify(sessionId string, notified bool) error { 171 query, args, err := me.getQueryBuilder(). 172 Update("Sessions"). 173 Set("ExpiredNotify", notified). 174 Where(sq.Eq{"Id": sessionId}). 175 ToSql() 176 if err != nil { 177 return errors.Wrap(err, "sessions_tosql") 178 } 179 180 _, err = me.GetMaster().Exec(query, args...) 181 if err != nil { 182 return errors.Wrapf(err, "failed to update Session with id=%s", sessionId) 183 } 184 return nil 185 } 186 187 func (me SqlSessionStore) Remove(sessionIdOrToken string) error { 188 _, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE Id = :Id Or Token = :Token", map[string]interface{}{"Id": sessionIdOrToken, "Token": sessionIdOrToken}) 189 if err != nil { 190 return errors.Wrapf(err, "failed to delete Session with sessionIdOrToken=%s", sessionIdOrToken) 191 } 192 return nil 193 } 194 195 func (me SqlSessionStore) RemoveAllSessions() error { 196 _, err := me.GetMaster().Exec("DELETE FROM Sessions") 197 if err != nil { 198 return errors.Wrap(err, "failed to delete all Sessions") 199 } 200 return nil 201 } 202 203 func (me SqlSessionStore) PermanentDeleteSessionsByUser(userId string) error { 204 _, err := me.GetMaster().Exec("DELETE FROM Sessions WHERE UserId = :UserId", map[string]interface{}{"UserId": userId}) 205 if err != nil { 206 return errors.Wrapf(err, "failed to delete Session with userId=%s", userId) 207 } 208 209 return nil 210 } 211 212 func (me SqlSessionStore) UpdateExpiresAt(sessionId string, time int64) error { 213 _, err := me.GetMaster().Exec("UPDATE Sessions SET ExpiresAt = :ExpiresAt, ExpiredNotify = false WHERE Id = :Id", map[string]interface{}{"ExpiresAt": time, "Id": sessionId}) 214 if err != nil { 215 return errors.Wrapf(err, "failed to update Session with sessionId=%s", sessionId) 216 } 217 return nil 218 } 219 220 func (me SqlSessionStore) UpdateLastActivityAt(sessionId string, time int64) error { 221 _, err := me.GetMaster().Exec("UPDATE Sessions SET LastActivityAt = :LastActivityAt WHERE Id = :Id", map[string]interface{}{"LastActivityAt": time, "Id": sessionId}) 222 if err != nil { 223 return errors.Wrapf(err, "failed to update Session with id=%s", sessionId) 224 } 225 return nil 226 } 227 228 func (me SqlSessionStore) UpdateRoles(userId, roles string) (string, error) { 229 query := "UPDATE Sessions SET Roles = :Roles WHERE UserId = :UserId" 230 231 _, err := me.GetMaster().Exec(query, map[string]interface{}{"Roles": roles, "UserId": userId}) 232 if err != nil { 233 return "", errors.Wrapf(err, "failed to update Session with userId=%s and roles=%s", userId, roles) 234 } 235 return userId, nil 236 } 237 238 func (me SqlSessionStore) UpdateDeviceId(id string, deviceId string, expiresAt int64) (string, error) { 239 query := "UPDATE Sessions SET DeviceId = :DeviceId, ExpiresAt = :ExpiresAt, ExpiredNotify = false WHERE Id = :Id" 240 241 _, err := me.GetMaster().Exec(query, map[string]interface{}{"DeviceId": deviceId, "Id": id, "ExpiresAt": expiresAt}) 242 if err != nil { 243 return "", errors.Wrapf(err, "failed to update Session with id=%s", id) 244 } 245 return deviceId, nil 246 } 247 248 func (me SqlSessionStore) UpdateProps(session *model.Session) error { 249 oldSession, err := me.Get(context.Background(), session.Id) 250 if err != nil { 251 return err 252 } 253 oldSession.Props = session.Props 254 255 count, err := me.GetMaster().Update(oldSession) 256 if err != nil { 257 return errors.Wrap(err, "failed to update Session") 258 } 259 if count != 1 { 260 return fmt.Errorf("updated Sessions were %d, expected 1", count) 261 } 262 return nil 263 } 264 265 func (me SqlSessionStore) AnalyticsSessionCount() (int64, error) { 266 query := 267 `SELECT 268 COUNT(*) 269 FROM 270 Sessions 271 WHERE ExpiresAt > :Time` 272 count, err := me.GetReplica().SelectInt(query, map[string]interface{}{"Time": model.GetMillis()}) 273 if err != nil { 274 return int64(0), errors.Wrap(err, "failed to count Sessions") 275 } 276 return count, nil 277 } 278 279 func (me SqlSessionStore) Cleanup(expiryTime int64, batchSize int64) { 280 mlog.Debug("Cleaning up session store.") 281 282 var query string 283 if me.DriverName() == model.DATABASE_DRIVER_POSTGRES { 284 query = "DELETE FROM Sessions WHERE Id = any (array (SELECT Id FROM Sessions WHERE ExpiresAt != 0 AND :ExpiresAt > ExpiresAt LIMIT :Limit))" 285 } else { 286 query = "DELETE FROM Sessions WHERE ExpiresAt != 0 AND :ExpiresAt > ExpiresAt LIMIT :Limit" 287 } 288 289 var rowsAffected int64 = 1 290 291 for rowsAffected > 0 { 292 sqlResult, err := me.GetMaster().Exec(query, map[string]interface{}{"ExpiresAt": expiryTime, "Limit": batchSize}) 293 if err != nil { 294 mlog.Error("Unable to cleanup session store.", mlog.Err(err)) 295 return 296 } 297 var rowErr error 298 rowsAffected, rowErr = sqlResult.RowsAffected() 299 if rowErr != nil { 300 mlog.Error("Unable to cleanup session store.", mlog.Err(err)) 301 return 302 } 303 304 time.Sleep(SessionsCleanupDelayMilliseconds * time.Millisecond) 305 } 306 }