This repository has been archived on 2026-06-21. You can view files and clone it, but cannot push or open issues or pull requests.
Files
MetaLab/internal/session/manager.go
Victor_Jay 9fe87a7003 refactor: 重写导航栏布局与交互
- 导航项重新排序:首页、投稿、信封图标、头像下拉菜单
- 铃铛图标替换为信封图标
- 用户名/设置/退出移入头像 hover 下拉菜单
- 设置改名为个人中心,退出改名为退出登录
- 头像点击跳转 /space,hover 展开下拉菜单
- 无头像时显示用户名首字彩色圆形占位
- Avatar 数据链路打通:Session→Middleware→BuildPageData→模板
- FindByIDForAuth 查询增加 avatar 列
- 新增 substr 模板函数
2026-05-31 16:17:35 +08:00

136 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package session
import (
"log"
"time"
"metazone.cc/metalab/internal/config"
"metazone.cc/metalab/internal/model"
)
// userStore SessionManager 对 UserRepo 的最小接口ISP1 个方法)
type userStore interface {
FindByIDForAuth(userID uint) (*model.User, error)
}
// Manager 会话管理器:创建、验证、销毁、续期
type Manager struct {
store Store
userRepo userStore
cfg *config.Config
}
// NewManager 构造函数
func NewManager(store Store, userRepo userStore, cfg *config.Config) *Manager {
return &Manager{store: store, userRepo: userRepo, cfg: cfg}
}
// Create 创建会话并写入存储
func (m *Manager) Create(user *model.User, rememberMe bool, ip, userAgent string) (string, error) {
sid, err := NewID()
if err != nil {
return "", err
}
now := time.Now()
s := &Session{
ID: sid,
UserID: user.ID,
Email: user.Email,
Username: user.Username,
Avatar: user.Avatar,
Role: user.Role,
RememberMe: rememberMe,
IP: ip,
UserAgent: userAgent,
CreatedAt: now,
LastAccess: now,
}
if err := m.store.Set(s); err != nil {
return "", err
}
log.Printf("[SessionManager] Created sid=%s uid=%d rememberMe=%v ip=%s", sid[:16]+"...", user.ID, rememberMe, ip)
return sid, nil
}
// Validate 验证会话:读取 → 检查过期 → 检查用户状态 → 续期 → 返回
// 返回 nil 表示会话无效(不存在/过期/用户被封禁)
func (m *Manager) Validate(sid string) (*Session, error) {
if sid == "" {
return nil, nil
}
s, err := m.store.Get(sid)
if err != nil || s == nil {
return nil, nil
}
// 检查用户是否仍然有效(封禁/注销后拒绝已有会话)
user, err := m.userRepo.FindByIDForAuth(s.UserID)
if err != nil {
log.Printf("[SessionManager] Validate: user not found uid=%d err=%v", s.UserID, err)
m.store.Delete(sid)
return nil, nil
}
// 封禁用户直接拒绝
if user.Status == model.StatusBanned {
log.Printf("[SessionManager] Validate: user banned uid=%d", s.UserID)
m.store.Delete(sid)
return nil, nil
}
// 滑动窗口续期
s.Touch()
if err := m.store.Set(s); err != nil {
log.Printf("[SessionManager] Validate: touch failed sid=%s err=%v", sid[:16]+"...", err)
}
return s, nil
}
// Destroy 销毁单个会话(用户主动退出登录)
func (m *Manager) Destroy(sid string) error {
return m.store.Delete(sid)
}
// DestroyByUID 销毁某用户的所有会话(改密/注销/强制下线)
func (m *Manager) DestroyByUID(uid uint) error {
return m.store.DeleteByUID(uid)
}
// IdleTimeout 根据 rememberMe 返回对应的空闲超时时间
func (m *Manager) IdleTimeout(rememberMe bool) time.Duration {
if rememberMe {
return time.Duration(m.cfg.Session.RememberTimeout) * time.Minute
}
return time.Duration(m.cfg.Session.IdleTimeout) * time.Minute
}
// ListByUID 列出某用户的所有活跃会话
func (m *Manager) ListByUID(uid uint) ([]*Session, error) {
return m.store.ListByUID(uid)
}
// DestroyOtherByUID 销毁某用户除当前会话外的所有会话
func (m *Manager) DestroyOtherByUID(uid uint, currentSID string) error {
return m.store.DeleteByUIDExclude(uid, currentSID)
}
// UpdateRemark 更新指定会话的备注(需验证归属)
func (m *Manager) UpdateRemark(sid string, uid uint, remark string) error {
s, err := m.store.Get(sid)
if err != nil || s == nil {
return err
}
if s.UserID != uid {
return nil
}
s.Remark = remark
return m.store.Set(s)
}
// GetStoreMetrics 返回当前存储的状态信息
func (m *Manager) GetStoreMetrics() StoreMetrics {
return m.store.Metrics()
}