feat(session): add session removal endpoint and refactor session management

- Implement new `/sessionMgr/remove` endpoint for superusers to remove sessions
- Refactor session and token management to use sessionTokenMap for better tracking
- Update session cleanup logic to handle both tokens and sessions
- Add documentation for new API endpoint in docs/api.md
- Modify logout handler to use new session removal approach
This commit is contained in:
2026-03-06 19:22:52 +08:00
parent f07b89842a
commit 8b9a757bea
6 changed files with 166 additions and 23 deletions
+34 -20
View File
@@ -21,7 +21,7 @@ type TokenInfo struct {
}
var (
tokenMap = make(map[int]*TokenInfo)
tokenMap = make(map[int]*TokenInfo) // userID -> TokenInfo
tokenMux sync.RWMutex
tokenTTL = time.Hour
)
@@ -34,11 +34,16 @@ type Session struct {
}
var (
sessionMap = make(map[string]*Session)
sessionMap = make(map[string]*Session) // sessionID -> Session
sessionMux sync.RWMutex
sessionTTL = time.Hour
)
var (
sessionTokenMap = make(map[string]string) // sessionID -> token
sessionTokenMux sync.RWMutex
)
func GenerateToken(userID int) (string, error) {
randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes)
@@ -144,7 +149,15 @@ func CleanupExpiredTokens() {
if time.Since(tokenInfo.CreatedAt) > tokenTTL {
delete(tokenMap, userID)
postLog.Debug(fmt.Sprintf("[CleanupExpiredTokens] Removed expired token for userID %d: %s", userID, tokenInfo.Token))
RemoveSession(userID, "")
sessionTokenMux.Lock()
for sessionID, sessionToken := range sessionTokenMap {
if sessionToken == tokenInfo.Token {
delete(sessionTokenMap, sessionID)
delete(sessionMap, sessionID)
postLog.Debug(fmt.Sprintf("[CleanupExpiredTokens] Removed expired session %s for userID %d", sessionID, userID))
}
}
sessionTokenMux.Unlock()
}
}
}
@@ -153,9 +166,13 @@ func CleanupExpiredSessions() {
sessionMux.Lock()
defer sessionMux.Unlock()
sessionTokenMux.Lock()
defer sessionTokenMux.Unlock()
for sessionID, session := range sessionMap {
if time.Now().After(session.ExpireAt) {
delete(sessionMap, sessionID)
delete(sessionTokenMap, sessionID)
postLog.Debug(fmt.Sprintf("[CleanupExpiredSessions] Removed expired session %s for user [%d]%s", sessionID, session.UserID, session.Username))
}
}
@@ -271,35 +288,32 @@ func JoinSession(userID int, userName string, token string) error {
}
sessionMap[sessionID] = session
sessionTokenMap[sessionID] = token
postLog.Debug(fmt.Sprintf("[JoinSession] User [%d]%s joined session %s with token %s", userID, userName, sessionID, token))
return nil
}
func RemoveSession(userID int, token string) error {
func RemoveSession(sessionID string) error {
sessionMux.Lock()
defer sessionMux.Unlock()
var sessionIDToRemove string
for sessionID, session := range sessionMap {
if session.UserID == userID {
sessionIDToRemove = sessionID
break
}
session, exists := sessionMap[sessionID]
if !exists {
return fmt.Errorf("Session not found: %s", sessionID)
}
if sessionIDToRemove != "" {
delete(sessionMap, sessionIDToRemove)
postLog.Info(fmt.Sprintf("[RemoveSession] Removed session %s for user [%d]%s", sessionIDToRemove, userID, GetUsernameByID(userID)))
_, exists = sessionTokenMap[sessionID]
if !exists {
delete(sessionMap, sessionID)
return fmt.Errorf("Token not found for session: %s", sessionID)
}
tokenMux.Lock()
defer tokenMux.Unlock()
delete(sessionMap, sessionID)
delete(sessionTokenMap, sessionID)
if tokenInfo, exists := tokenMap[userID]; exists {
if tokenInfo.Token == token || token == "" {
delete(tokenMap, userID)
postLog.Info(fmt.Sprintf("[RemoveSession] Removed token for user [%d]%s", userID, GetUsernameByID(userID)))
}
if tokenInfo, exists := tokenMap[session.UserID]; exists {
DeleteTokenInfo(session.UserID)
postLog.Info(fmt.Sprintf("[RemoveSession] Removed session '%s': '[%d]%s'", sessionID, tokenInfo.UserID, GetUsernameByID(tokenInfo.UserID)))
}
return nil