sessions 1

This commit is contained in:
Sky Johnson 2025-03-29 14:01:10 -05:00
parent 2a2ffc9cc5
commit 21559bd6b7
2 changed files with 227 additions and 0 deletions

74
core/sessions/Session.go Normal file
View 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
}

View 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()