Files
MeBox/internal/service/session_tracker_activity.go
T
truewhile b0fe40142a Rebrand MMTL to MeBox (name, logo, Docker image) (#17)
* Rebrand MMTL to MeBox across codebase and assets

Rename the project display name, Go module path, environment variable
prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl
to MeBox/mebox. Replace logo assets with the new MeBox icon and keep
legacy SQLite migration support for existing mmtl.db deployments.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

* Fix logo icons: use cube-only crop without truncated text

Previous icon generation cropped too much of the source image, including
partial MeBox wordmark text that was cut off in square icon containers.
Regenerate logo-64/192/512, favicon, and SVG from cube-only region.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
2026-09-02 16:26:28 +08:00

85 lines
2.3 KiB
Go

package service
import (
"context"
"strings"
"time"
"github.com/truewhile/MeBox/internal/model"
)
func (s *SessionTrackerService) ApplyToUsers(ctx context.Context, users []model.User) {
if s == nil || len(users) == 0 {
return
}
activity := s.activityByUser(ctx)
for i := range users {
a, ok := activity[users[i].ID]
if !ok {
continue
}
if a.LastActivityAt != nil && (users[i].LastLoginAt == nil || a.LastActivityAt.After(*users[i].LastLoginAt)) {
t := *a.LastActivityAt
users[i].LastLoginAt = &t
}
users[i].RealtimeOnline = a.Online
users[i].RealtimeDeviceCount = a.ActiveDeviceCount
}
}
func (s *SessionTrackerService) UserRecentlyActive(ctx context.Context, userID string, within time.Duration) bool {
if s == nil || within <= 0 {
return false
}
activity := s.activityByUser(ctx)[strings.TrimSpace(userID)]
return activity.LastActivityAt != nil && activity.LastActivityAt.After(s.now().Add(-within))
}
func (s *SessionTrackerService) activityByUser(ctx context.Context) map[string]userRealtimeActivity {
sessions := s.List(ctx)
now := s.now()
out := make(map[string]userRealtimeActivity)
for userID, lastActivity := range s.userActivitySnapshot() {
if strings.TrimSpace(userID) == "" {
continue
}
a := out[userID]
if a.LastActivityAt == nil || lastActivity.After(*a.LastActivityAt) {
t := lastActivity
a.LastActivityAt = &t
}
out[userID] = a
}
seenDevices := make(map[string]map[string]struct{})
for _, sess := range sessions {
if strings.TrimSpace(sess.UserID) == "" {
continue
}
a := out[sess.UserID]
if a.LastActivityAt == nil || sess.LastActivityAt.After(*a.LastActivityAt) {
t := sess.LastActivityAt
a.LastActivityAt = &t
}
if sess.LastActivityAt.After(now.Add(-realtimeSessionOnlineTTL)) {
a.Online = true
}
if seenDevices[sess.UserID] == nil {
seenDevices[sess.UserID] = map[string]struct{}{}
}
seenDevices[sess.UserID][sessionDeviceKey(sess)] = struct{}{}
a.ActiveDeviceCount = len(seenDevices[sess.UserID])
out[sess.UserID] = a
}
return out
}
func (s *SessionTrackerService) userActivitySnapshot() map[string]time.Time {
s.mu.RLock()
defer s.mu.RUnlock()
out := make(map[string]time.Time, len(s.activity))
for userID, lastActivity := range s.activity {
out[userID] = lastActivity
}
return out
}