package server import ( "crypto/rand" "encoding/hex" "sync" "time" ) // Session represents an authenticated user session. type Session struct { ID string UID int64 CompanyID int64 Login string CreatedAt time.Time LastActivity time.Time } // SessionStore is a thread-safe in-memory session store. type SessionStore struct { mu sync.RWMutex sessions map[string]*Session ttl time.Duration } // NewSessionStore creates a new session store with the given TTL. func NewSessionStore(ttl time.Duration) *SessionStore { return &SessionStore{ sessions: make(map[string]*Session), ttl: ttl, } } // New creates a new session and returns it. func (s *SessionStore) New(uid, companyID int64, login string) *Session { s.mu.Lock() defer s.mu.Unlock() token := generateToken() sess := &Session{ ID: token, UID: uid, CompanyID: companyID, Login: login, CreatedAt: time.Now(), LastActivity: time.Now(), } s.sessions[token] = sess return sess } // Get retrieves a session by ID. Returns nil if not found or expired. func (s *SessionStore) Get(id string) *Session { s.mu.RLock() sess, ok := s.sessions[id] s.mu.RUnlock() if !ok { return nil } if time.Since(sess.LastActivity) > s.ttl { s.Delete(id) return nil } // Update last activity s.mu.Lock() sess.LastActivity = time.Now() s.mu.Unlock() return sess } // Delete removes a session. func (s *SessionStore) Delete(id string) { s.mu.Lock() defer s.mu.Unlock() delete(s.sessions, id) } func generateToken() string { b := make([]byte, 32) rand.Read(b) return hex.EncodeToString(b) }