sessions 1
This commit is contained in:
parent
2a2ffc9cc5
commit
21559bd6b7
74
core/sessions/Session.go
Normal file
74
core/sessions/Session.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package sessions
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.sharkk.net/Sky/Moonshark/core/logger"
|
||||
)
|
||||
|
||||
// Session stores data for a single user session
|
||||
type Session struct {
|
||||
ID string
|
||||
Data map[string]any
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
mu sync.RWMutex // Protect concurrent access to Data
|
||||
}
|
||||
|
||||
// NewSession creates a new session with the given ID
|
||||
func NewSession(id string) *Session {
|
||||
now := time.Now()
|
||||
return &Session{
|
||||
ID: id,
|
||||
Data: make(map[string]any),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a value from the session
|
||||
func (s *Session) Get(key string) any {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.Data[key]
|
||||
}
|
||||
|
||||
// Set stores a value in the session
|
||||
func (s *Session) Set(key string, value any) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.Data[key] = value
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// Delete removes a value from the session
|
||||
func (s *Session) Delete(key string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
delete(s.Data, key)
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// Clear removes all data from the session
|
||||
func (s *Session) Clear() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.Data = make(map[string]any)
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// GetAll returns a copy of all session data
|
||||
func (s *Session) GetAll() map[string]any {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
// Create a copy to avoid concurrent map access issues
|
||||
copy := make(map[string]any, len(s.Data))
|
||||
for k, v := range s.Data {
|
||||
logger.Debug("Session Key: %s, Has Data: %s", k, v)
|
||||
copy[k] = v
|
||||
}
|
||||
|
||||
return copy
|
||||
}
|
153
core/sessions/SessionManager.go
Normal file
153
core/sessions/SessionManager.go
Normal file
|
@ -0,0 +1,153 @@
|
|||
package sessions
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SessionManager handles multiple sessions
|
||||
type SessionManager struct {
|
||||
sessions map[string]*Session
|
||||
mu sync.RWMutex
|
||||
cookieName string
|
||||
cookiePath string
|
||||
cookieDomain string
|
||||
cookieSecure bool
|
||||
cookieHTTPOnly bool
|
||||
cookieMaxAge int
|
||||
gcInterval time.Duration
|
||||
}
|
||||
|
||||
// NewSessionManager creates a new session manager
|
||||
func NewSessionManager() *SessionManager {
|
||||
sm := &SessionManager{
|
||||
sessions: make(map[string]*Session),
|
||||
cookieName: "MSESSID",
|
||||
cookiePath: "/",
|
||||
cookieHTTPOnly: true,
|
||||
cookieMaxAge: 86400, // 1 day
|
||||
gcInterval: time.Hour,
|
||||
}
|
||||
|
||||
// Start the garbage collector
|
||||
go sm.startGC()
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
// generateSessionID creates a cryptographically secure random session ID
|
||||
func (sm *SessionManager) generateSessionID() string {
|
||||
b := make([]byte, 32)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return time.Now().String() // Fallback
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// GetSession retrieves a session by ID, or creates a new one if it doesn't exist
|
||||
func (sm *SessionManager) GetSession(id string) *Session {
|
||||
sm.mu.RLock()
|
||||
session, exists := sm.sessions[id]
|
||||
sm.mu.RUnlock()
|
||||
|
||||
if exists {
|
||||
return session
|
||||
}
|
||||
|
||||
// Create new session if it doesn't exist
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
// Double check to avoid race conditions
|
||||
if session, exists = sm.sessions[id]; exists {
|
||||
return session
|
||||
}
|
||||
|
||||
session = NewSession(id)
|
||||
sm.sessions[id] = session
|
||||
return session
|
||||
}
|
||||
|
||||
// CreateSession generates a new session with a unique ID
|
||||
func (sm *SessionManager) CreateSession() *Session {
|
||||
id := sm.generateSessionID()
|
||||
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
session := NewSession(id)
|
||||
sm.sessions[id] = session
|
||||
return session
|
||||
}
|
||||
|
||||
// DestroySession removes a session
|
||||
func (sm *SessionManager) DestroySession(id string) {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
delete(sm.sessions, id)
|
||||
}
|
||||
|
||||
// startGC starts the garbage collector to clean up expired sessions
|
||||
func (sm *SessionManager) startGC() {
|
||||
ticker := time.NewTicker(sm.gcInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
sm.gc()
|
||||
}
|
||||
}
|
||||
|
||||
// gc removes expired sessions (inactive for 24 hours)
|
||||
func (sm *SessionManager) gc() {
|
||||
expiry := time.Now().Add(-24 * time.Hour)
|
||||
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
for id, session := range sm.sessions {
|
||||
session.mu.RLock()
|
||||
lastUpdated := session.UpdatedAt
|
||||
session.mu.RUnlock()
|
||||
|
||||
if lastUpdated.Before(expiry) {
|
||||
delete(sm.sessions, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSessionCount returns the number of active sessions
|
||||
func (sm *SessionManager) GetSessionCount() int {
|
||||
sm.mu.RLock()
|
||||
defer sm.mu.RUnlock()
|
||||
return len(sm.sessions)
|
||||
}
|
||||
|
||||
// CookieOptions returns the cookie options for this session manager
|
||||
func (sm *SessionManager) CookieOptions() map[string]any {
|
||||
return map[string]any{
|
||||
"name": sm.cookieName,
|
||||
"path": sm.cookiePath,
|
||||
"domain": sm.cookieDomain,
|
||||
"secure": sm.cookieSecure,
|
||||
"http_only": sm.cookieHTTPOnly,
|
||||
"max_age": sm.cookieMaxAge,
|
||||
}
|
||||
}
|
||||
|
||||
// SetCookieOptions configures cookie parameters
|
||||
func (sm *SessionManager) SetCookieOptions(name, path, domain string, secure, httpOnly bool, maxAge int) {
|
||||
sm.mu.Lock()
|
||||
defer sm.mu.Unlock()
|
||||
|
||||
sm.cookieName = name
|
||||
sm.cookiePath = path
|
||||
sm.cookieDomain = domain
|
||||
sm.cookieSecure = secure
|
||||
sm.cookieHTTPOnly = httpOnly
|
||||
sm.cookieMaxAge = maxAge
|
||||
}
|
||||
|
||||
// GlobalSessionManager is the default session manager instance
|
||||
var GlobalSessionManager = NewSessionManager()
|
Loading…
Reference in New Issue
Block a user