From 8623da5361e487e3310374a88339d5d98ff8ac77 Mon Sep 17 00:00:00 2001 From: okatu-loli Date: Fri, 29 Aug 2025 11:53:55 +0800 Subject: [PATCH] feat(session): Added user session limit and device eviction logic - Renamed `CountSessionsByUser` to `CountActiveSessionsByUser` and added session status filtering - Added user and device session limit, with policy handling when exceeding the limit - Introduced device eviction policy: If the maximum number of devices is exceeded, the oldest session will be evicted using the "evict_oldest" policy - Modified `LastActive` update logic to ensure accurate session activity time --- internal/db/session.go | 6 ++++-- internal/device/session.go | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/internal/db/session.go b/internal/db/session.go index 8db9fa69..e8dce441 100644 --- a/internal/db/session.go +++ b/internal/db/session.go @@ -26,9 +26,11 @@ func DeleteSession(userID uint, deviceKey string) error { return errors.WithStack(db.Where("user_id = ? AND device_key = ?", userID, deviceKey).Delete(&model.Session{}).Error) } -func CountSessionsByUser(userID uint) (int64, error) { +func CountActiveSessionsByUser(userID uint) (int64, error) { var count int64 - err := db.Model(&model.Session{}).Where("user_id = ?", userID).Count(&count).Error + err := db.Model(&model.Session{}). + Where("user_id = ? AND status = ?", userID, model.SessionActive). + Count(&count).Error return count, errors.WithStack(err) } diff --git a/internal/device/session.go b/internal/device/session.go index 49bf74b6..5d5a3996 100644 --- a/internal/device/session.go +++ b/internal/device/session.go @@ -25,7 +25,25 @@ func Handle(userID uint, deviceKey, ua, ip string) error { now := time.Now().Unix() sess, err := db.GetSession(userID, deviceKey) if err == nil { - // reactivate existing session if it was inactive + if sess.Status == model.SessionInactive { + max := setting.GetInt(conf.MaxDevices, 0) + if max > 0 { + count, cerr := db.CountActiveSessionsByUser(userID) + if cerr != nil { + return cerr + } + if count >= int64(max) { + policy := setting.GetStr(conf.DeviceEvictPolicy, "deny") + if policy == "evict_oldest" { + if oldest, gerr := db.GetOldestSession(userID); gerr == nil { + _ = db.DeleteSession(userID, oldest.DeviceKey) + } + } else { + return errors.WithStack(errs.TooManyDevices) + } + } + } + } sess.Status = model.SessionActive sess.LastActive = now sess.UserAgent = ua @@ -38,7 +56,7 @@ func Handle(userID uint, deviceKey, ua, ip string) error { max := setting.GetInt(conf.MaxDevices, 0) if max > 0 { - count, err := db.CountSessionsByUser(userID) + count, err := db.CountActiveSessionsByUser(userID) if err != nil { return err }