Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/internal/device/session.go
1560 views
1
package device
2
3
import (
4
"time"
5
6
"github.com/alist-org/alist/v3/internal/conf"
7
"github.com/alist-org/alist/v3/internal/db"
8
"github.com/alist-org/alist/v3/internal/errs"
9
"github.com/alist-org/alist/v3/internal/model"
10
"github.com/alist-org/alist/v3/internal/setting"
11
"github.com/alist-org/alist/v3/pkg/utils"
12
"github.com/pkg/errors"
13
"gorm.io/gorm"
14
)
15
16
// Handle verifies device sessions for a user and upserts current session.
17
func Handle(userID uint, deviceKey, ua, ip string) error {
18
ttl := setting.GetInt(conf.DeviceSessionTTL, 86400)
19
if ttl > 0 {
20
_ = db.DeleteSessionsBefore(time.Now().Unix() - int64(ttl))
21
}
22
23
ip = utils.MaskIP(ip)
24
25
now := time.Now().Unix()
26
sess, err := db.GetSession(userID, deviceKey)
27
if err == nil {
28
if sess.Status == model.SessionInactive {
29
return errors.WithStack(errs.SessionInactive)
30
}
31
sess.Status = model.SessionActive
32
sess.LastActive = now
33
sess.UserAgent = ua
34
sess.IP = ip
35
return db.UpsertSession(sess)
36
}
37
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
38
return err
39
}
40
41
max := setting.GetInt(conf.MaxDevices, 0)
42
if max > 0 {
43
count, err := db.CountActiveSessionsByUser(userID)
44
if err != nil {
45
return err
46
}
47
if count >= int64(max) {
48
policy := setting.GetStr(conf.DeviceEvictPolicy, "deny")
49
if policy == "evict_oldest" {
50
if oldest, err := db.GetOldestActiveSession(userID); err == nil {
51
if err := db.MarkInactive(oldest.DeviceKey); err != nil {
52
return err
53
}
54
}
55
} else {
56
return errors.WithStack(errs.TooManyDevices)
57
}
58
}
59
}
60
61
s := &model.Session{UserID: userID, DeviceKey: deviceKey, UserAgent: ua, IP: ip, LastActive: now, Status: model.SessionActive}
62
return db.CreateSession(s)
63
}
64
65
// EnsureActiveOnLogin is used only in login flow:
66
// - If session exists (even Inactive): reactivate and refresh fields.
67
// - If not exists: apply max-devices policy, then create Active session.
68
func EnsureActiveOnLogin(userID uint, deviceKey, ua, ip string) error {
69
ip = utils.MaskIP(ip)
70
now := time.Now().Unix()
71
72
sess, err := db.GetSession(userID, deviceKey)
73
if err == nil {
74
if sess.Status == model.SessionInactive {
75
max := setting.GetInt(conf.MaxDevices, 0)
76
if max > 0 {
77
count, err := db.CountActiveSessionsByUser(userID)
78
if err != nil {
79
return err
80
}
81
if count >= int64(max) {
82
policy := setting.GetStr(conf.DeviceEvictPolicy, "deny")
83
if policy == "evict_oldest" {
84
if oldest, gerr := db.GetOldestActiveSession(userID); gerr == nil {
85
if err := db.MarkInactive(oldest.DeviceKey); err != nil {
86
return err
87
}
88
}
89
} else {
90
return errors.WithStack(errs.TooManyDevices)
91
}
92
}
93
}
94
}
95
sess.Status = model.SessionActive
96
sess.LastActive = now
97
sess.UserAgent = ua
98
sess.IP = ip
99
return db.UpsertSession(sess)
100
}
101
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
102
return err
103
}
104
105
max := setting.GetInt(conf.MaxDevices, 0)
106
if max > 0 {
107
count, err := db.CountActiveSessionsByUser(userID)
108
if err != nil {
109
return err
110
}
111
if count >= int64(max) {
112
policy := setting.GetStr(conf.DeviceEvictPolicy, "deny")
113
if policy == "evict_oldest" {
114
if oldest, gerr := db.GetOldestActiveSession(userID); gerr == nil {
115
if err := db.MarkInactive(oldest.DeviceKey); err != nil {
116
return err
117
}
118
}
119
} else {
120
return errors.WithStack(errs.TooManyDevices)
121
}
122
}
123
}
124
125
return db.CreateSession(&model.Session{
126
UserID: userID,
127
DeviceKey: deviceKey,
128
UserAgent: ua,
129
IP: ip,
130
LastActive: now,
131
Status: model.SessionActive,
132
})
133
}
134
135
// Refresh updates last_active for the session.
136
func Refresh(userID uint, deviceKey string) {
137
_ = db.UpdateSessionLastActive(userID, deviceKey, time.Now().Unix())
138
}
139
140