package lighthouse import ( "context" "git.golem15.com/golem15/summercms/modules/bouncer" ) // User is the identity a realtime driver needs: the id and display name. type User struct { ID uint Name *string } // UserLookup loads a user by id. found is false when no such user exists. type UserLookup func(ctx context.Context, id uint) (user User, found bool, err error) // SetUserLookup installs the application's user lookup. func (s *Service) SetUserLookup(fn UserLookup) { if s == nil { return } s.mu.Lock() s.lookup = fn s.mu.Unlock() } // User loads user id through the installed lookup. Without a lookup it // returns User{ID: id} with a nil Name and found true. func (s *Service) User(ctx context.Context, id uint) (User, bool, error) { if s == nil { return User{ID: id}, true, nil } s.mu.RLock() fn := s.lookup s.mu.RUnlock() if fn == nil { return User{ID: id}, true, nil } return fn(ctx, id) } // Actor is who caused a broadcast. It marshals as {"user_id":…,"name":…}. type Actor struct { UserID *uint `json:"user_id"` Name *string `json:"name"` } // SystemActor is the actor of writes made without a signed-in user: // {"user_id":null,"name":"System"}. func SystemActor() Actor { name := "System" return Actor{Name: &name} } // Actor returns the actor of the request in ctx. No principal, or a // backend-admin principal, gives SystemActor: the WinterCMS frontend user is // empty in backend requests. Otherwise it is the principal's id and the // looked-up name (nil when the lookup fails or finds nothing). func (s *Service) Actor(ctx context.Context) Actor { p, ok := bouncer.User(ctx) if !ok || p == nil || p.Backend { return SystemActor() } id := p.ID a := Actor{UserID: &id} if u, found, err := s.User(ctx, id); err == nil && found { a.Name = u.Name } return a }