feat: 设置页新增登录管理 TAB
- Session 结构体新增 IP、UserAgent、Remark 字段,登录/注册时记录设备信息 - Store 接口新增 ListByUID、DeleteByUIDExclude 方法,MemoryStore 完整实现 - SessionManager 新增 ListByUID、DestroyOtherByUID、UpdateRemark 方法 - 新增 UA 轻量解析器(session/ua.go),提取浏览器/OS/设备类型 - 新增 settings_api_sessions.go,提供列表/踢出/一键踢出/备注 4 个 API - 控制器层声明 sessionManager 最小接口(ISP),SettingsController 注入依赖 - Auth 中间件注入 sid 到 gin context,支持识别当前会话 - 设置页左侧导航增加登录管理入口,tab 白名单新增 sessions - 前端实现设备列表展示(浏览器/OS/设备图标/IP/时间)、当前设备标识、备注输入、踢出按钮、一键踢出 - settings.css 新增会话管理全套样式及响应式适配
This commit is contained in:
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"metazone.cc/metalab/internal/common"
|
||||
"metazone.cc/metalab/internal/model"
|
||||
"metazone.cc/metalab/internal/session"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -37,11 +38,12 @@ type SettingsController struct {
|
||||
authService profileProvider
|
||||
avatarService avatarProvider
|
||||
auditService auditSubmittable
|
||||
sessionMgr sessionManager
|
||||
}
|
||||
|
||||
// NewSettingsController 构造函数
|
||||
func NewSettingsController(authService profileProvider, avatarService avatarProvider, auditService auditSubmittable) *SettingsController {
|
||||
return &SettingsController{authService: authService, avatarService: avatarService, auditService: auditService}
|
||||
func NewSettingsController(authService profileProvider, avatarService avatarProvider, auditService auditSubmittable, sessionMgr sessionManager) *SettingsController {
|
||||
return &SettingsController{authService: authService, avatarService: avatarService, auditService: auditService, sessionMgr: sessionMgr}
|
||||
}
|
||||
|
||||
// SettingsPage 个人设置页面(需登录)
|
||||
@ -55,7 +57,7 @@ func (sc *SettingsController) SettingsPage(c *gin.Context) {
|
||||
uid := uidVal.(uint)
|
||||
|
||||
tab := c.Param("tab")
|
||||
if tab != "profile" && tab != "account" {
|
||||
if tab != "profile" && tab != "account" && tab != "sessions" {
|
||||
c.String(http.StatusNotFound, "页面不存在")
|
||||
return
|
||||
}
|
||||
@ -69,7 +71,7 @@ func (sc *SettingsController) SettingsPage(c *gin.Context) {
|
||||
// 查询待审核类型(用于前端显示审核提示)
|
||||
pendingTypes, _ := sc.auditService.GetPendingTypes(uid)
|
||||
|
||||
c.HTML(http.StatusOK, "settings/index.html", common.BuildPageData(c, gin.H{
|
||||
data := gin.H{
|
||||
"Title": "个人设置",
|
||||
"ExtraCSS": "/static/css/settings.css",
|
||||
"ActiveTab": tab,
|
||||
@ -78,7 +80,34 @@ func (sc *SettingsController) SettingsPage(c *gin.Context) {
|
||||
"StatusName": model.StatusDisplayNames[user.Status],
|
||||
"PendingTypes": pendingTypes,
|
||||
"AuditTypes": model.AuditTypeNames,
|
||||
}))
|
||||
}
|
||||
|
||||
// 登录管理 tab 需要额外数据
|
||||
if tab == "sessions" {
|
||||
sessions, _ := sc.sessionMgr.ListByUID(uid)
|
||||
if sessions == nil {
|
||||
sessions = []*session.Session{}
|
||||
}
|
||||
currentSID, _ := c.Cookie(common.SessionCookieName)
|
||||
// 解析每个 session 的设备信息
|
||||
type sessionView struct {
|
||||
*session.Session
|
||||
DeviceInfo session.DeviceInfo
|
||||
IsCurrent bool
|
||||
}
|
||||
var views []sessionView
|
||||
for _, s := range sessions {
|
||||
views = append(views, sessionView{
|
||||
Session: s,
|
||||
DeviceInfo: session.ParseUA(s.UserAgent),
|
||||
IsCurrent: s.ID == currentSID,
|
||||
})
|
||||
}
|
||||
data["Sessions"] = views
|
||||
data["CurrentSID"] = currentSID
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "settings/index.html", common.BuildPageData(c, data))
|
||||
}
|
||||
|
||||
// AuditStatus 查询当前用户的待审核类型(需登录)
|
||||
|
||||
Reference in New Issue
Block a user