mirror of https://github.com/Xhofe/alist
139 lines
3.7 KiB
Go
139 lines
3.7 KiB
Go
package device
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/alist-org/alist/v3/internal/conf"
|
|
"github.com/alist-org/alist/v3/internal/db"
|
|
"github.com/alist-org/alist/v3/internal/errs"
|
|
"github.com/alist-org/alist/v3/internal/model"
|
|
"github.com/alist-org/alist/v3/internal/setting"
|
|
"github.com/alist-org/alist/v3/pkg/utils"
|
|
"github.com/pkg/errors"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Handle verifies device sessions for a user and upserts current session.
|
|
func Handle(userID uint, deviceKey, ua, ip string) error {
|
|
ttl := setting.GetInt(conf.DeviceSessionTTL, 86400)
|
|
if ttl > 0 {
|
|
_ = db.DeleteSessionsBefore(time.Now().Unix() - int64(ttl))
|
|
}
|
|
|
|
ip = utils.MaskIP(ip)
|
|
|
|
now := time.Now().Unix()
|
|
sess, err := db.GetSession(userID, deviceKey)
|
|
if err == nil {
|
|
if sess.Status == model.SessionInactive {
|
|
return errors.WithStack(errs.SessionInactive)
|
|
}
|
|
sess.Status = model.SessionActive
|
|
sess.LastActive = now
|
|
sess.UserAgent = ua
|
|
sess.IP = ip
|
|
return db.UpsertSession(sess)
|
|
}
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
|
|
max := setting.GetInt(conf.MaxDevices, 0)
|
|
if max > 0 {
|
|
count, err := db.CountActiveSessionsByUser(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count >= int64(max) {
|
|
policy := setting.GetStr(conf.DeviceEvictPolicy, "deny")
|
|
if policy == "evict_oldest" {
|
|
if oldest, err := db.GetOldestActiveSession(userID); err == nil {
|
|
if err := db.MarkInactive(oldest.DeviceKey); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
return errors.WithStack(errs.TooManyDevices)
|
|
}
|
|
}
|
|
}
|
|
|
|
s := &model.Session{UserID: userID, DeviceKey: deviceKey, UserAgent: ua, IP: ip, LastActive: now, Status: model.SessionActive}
|
|
return db.CreateSession(s)
|
|
}
|
|
|
|
// EnsureActiveOnLogin is used only in login flow:
|
|
// - If session exists (even Inactive): reactivate and refresh fields.
|
|
// - If not exists: apply max-devices policy, then create Active session.
|
|
func EnsureActiveOnLogin(userID uint, deviceKey, ua, ip string) error {
|
|
ip = utils.MaskIP(ip)
|
|
now := time.Now().Unix()
|
|
|
|
sess, err := db.GetSession(userID, deviceKey)
|
|
if err == nil {
|
|
if sess.Status == model.SessionInactive {
|
|
max := setting.GetInt(conf.MaxDevices, 0)
|
|
if max > 0 {
|
|
count, err := db.CountActiveSessionsByUser(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count >= int64(max) {
|
|
policy := setting.GetStr(conf.DeviceEvictPolicy, "deny")
|
|
if policy == "evict_oldest" {
|
|
if oldest, gerr := db.GetOldestActiveSession(userID); gerr == nil {
|
|
if err := db.MarkInactive(oldest.DeviceKey); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
return errors.WithStack(errs.TooManyDevices)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sess.Status = model.SessionActive
|
|
sess.LastActive = now
|
|
sess.UserAgent = ua
|
|
sess.IP = ip
|
|
return db.UpsertSession(sess)
|
|
}
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return err
|
|
}
|
|
|
|
max := setting.GetInt(conf.MaxDevices, 0)
|
|
if max > 0 {
|
|
count, err := db.CountActiveSessionsByUser(userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if count >= int64(max) {
|
|
policy := setting.GetStr(conf.DeviceEvictPolicy, "deny")
|
|
if policy == "evict_oldest" {
|
|
if oldest, gerr := db.GetOldestActiveSession(userID); gerr == nil {
|
|
if err := db.MarkInactive(oldest.DeviceKey); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
return errors.WithStack(errs.TooManyDevices)
|
|
}
|
|
}
|
|
}
|
|
|
|
return db.CreateSession(&model.Session{
|
|
UserID: userID,
|
|
DeviceKey: deviceKey,
|
|
UserAgent: ua,
|
|
IP: ip,
|
|
LastActive: now,
|
|
Status: model.SessionActive,
|
|
})
|
|
}
|
|
|
|
// Refresh updates last_active for the session.
|
|
func Refresh(userID uint, deviceKey string) {
|
|
_ = db.UpdateSessionLastActive(userID, deviceKey, time.Now().Unix())
|
|
}
|