Files
summercms/modules/lighthouse/users.go
Jakub Zych cada7a4442 feat(11-03): add the lighthouse realtime package and its Centrifugo driver
- lighthouse: Service/From with realtime.driver selection, RegisterDriver
  registry, null/log/memory drivers, Route/Surface/Mount, users and actors
- centrifugo: HTTP API client (apikey header, 2xx success, no request
  without a key), five-generator HS256 TokenIssuer, TokenHandler with the
  WinterCMS 401/503 bodies
- module README and root modules table row
2026-09-30 12:18:11 +02:00

72 lines
1.8 KiB
Go

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
}