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
This commit is contained in:
71
modules/lighthouse/users.go
Normal file
71
modules/lighthouse/users.go
Normal file
@@ -0,0 +1,71 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user