104 lines
2.4 KiB
Go
104 lines
2.4 KiB
Go
package session
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"reflect"
|
|
"time"
|
|
|
|
"git.pcux.cn/xuans/Smail/services/dbmigration"
|
|
"github.com/alexedwards/scs/v2"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var errMigrationSessionWrite = errors.New("session changes are paused during database migration")
|
|
|
|
// migrationStore 只跳过冻结期间未改变内容的会话续期。
|
|
// 不能新增会话、修改权限、延长有效期或伪称已保存安全状态变更。
|
|
type migrationStore struct {
|
|
scs.Store
|
|
codec scs.Codec
|
|
paused func() bool
|
|
}
|
|
|
|
func (s *migrationStore) Commit(token string, data []byte, expiry time.Time) error {
|
|
if !s.paused() {
|
|
return s.Store.Commit(token, data, expiry)
|
|
}
|
|
previous, found, err := s.Store.Find(token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !found {
|
|
return errMigrationSessionWrite
|
|
}
|
|
beforeDeadline, before, err := s.codec.Decode(previous)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
afterDeadline, after, err := s.codec.Decode(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !beforeDeadline.Equal(afterDeadline) || !reflect.DeepEqual(before, after) {
|
|
return errMigrationSessionWrite
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *migrationStore) Delete(token string) error {
|
|
if s.paused() {
|
|
return errMigrationSessionWrite
|
|
}
|
|
return s.Store.Delete(token)
|
|
}
|
|
|
|
func (s *migrationStore) All() (map[string][]byte, error) {
|
|
store, ok := s.Store.(scs.IterableStore)
|
|
if !ok {
|
|
return nil, errors.New("session store does not support iteration")
|
|
}
|
|
return store.All()
|
|
}
|
|
|
|
var stopCleanup context.CancelFunc
|
|
|
|
// Stop 在内部重启时停止旧会话清理器,防止它继续访问旧库。
|
|
func Stop() {
|
|
if stopCleanup != nil {
|
|
stopCleanup()
|
|
stopCleanup = nil
|
|
}
|
|
}
|
|
|
|
func startCleanup(database *sql.DB, sqlite bool) {
|
|
Stop()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
stopCleanup = cancel
|
|
go func() {
|
|
ticker := time.NewTicker(5 * time.Minute)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
err := dbmigration.WhileWritable(func() error {
|
|
operation, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
defer cancel()
|
|
query := "DELETE FROM sessions WHERE expiry < CURRENT_TIMESTAMP"
|
|
if sqlite {
|
|
query = "DELETE FROM sessions WHERE expiry < julianday('now')"
|
|
}
|
|
_, err := database.ExecContext(operation, query)
|
|
return err
|
|
})
|
|
if err != nil && ctx.Err() == nil {
|
|
log.Warn("清理过期会话失败")
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|