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  }