first commit
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
"browser.local/platform/repo"
|
||||
"browser.local/platform/validator"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHeartbeatIntervalSeconds = 15
|
||||
)
|
||||
|
||||
func (svc *CoreService) RegisterRunHello(hello domain.RunControlHello) (domain.RunControlHelloResult, error) {
|
||||
hello = domain.CopyRunControlHello(hello)
|
||||
if err := validator.ValidateRunControlHello(hello); err != nil {
|
||||
return domain.RunControlHelloResult{}, err
|
||||
}
|
||||
|
||||
stamp := svc.now()
|
||||
endpoint := domain.RunEndpoint{
|
||||
ID: hello.RunEndpointID,
|
||||
DisplayName: hello.DisplayName,
|
||||
Version: hello.Version,
|
||||
Status: domain.RunEndpointStatusOnline,
|
||||
Capabilities: domain.CopyStringSlice(hello.CapabilityReport.Capabilities),
|
||||
Capacity: hello.Capacity,
|
||||
LastHeartbeatAt: stamp,
|
||||
}
|
||||
if err := validator.ValidateRunEndpoint(endpoint); err != nil {
|
||||
return domain.RunControlHelloResult{}, err
|
||||
}
|
||||
|
||||
svc.controlMu.Lock()
|
||||
defer svc.controlMu.Unlock()
|
||||
|
||||
if err := svc.upsertRunEndpoint(endpoint); err != nil {
|
||||
return domain.RunControlHelloResult{}, err
|
||||
}
|
||||
sessionToken := svc.nextSessionToken(hello.RunEndpointID, stamp)
|
||||
svc.runSessions[hello.RunEndpointID] = domain.RunControlSession{
|
||||
RunEndpointID: hello.RunEndpointID,
|
||||
SessionToken: sessionToken,
|
||||
CapabilityFingerprint: hello.CapabilityReport.Fingerprint,
|
||||
HeartbeatIntervalSeconds: defaultHeartbeatIntervalSeconds,
|
||||
CreatedAt: stamp,
|
||||
UpdatedAt: stamp,
|
||||
}
|
||||
|
||||
return domain.CopyRunControlHelloResult(domain.RunControlHelloResult{
|
||||
Accepted: true,
|
||||
RunEndpointID: hello.RunEndpointID,
|
||||
SessionToken: sessionToken,
|
||||
ServerTime: stamp,
|
||||
HeartbeatIntervalSeconds: defaultHeartbeatIntervalSeconds,
|
||||
FeatureFlags: []string{"control.hello", "control.heartbeat"},
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (svc *CoreService) AcceptRunHeartbeat(heartbeat domain.RunControlHeartbeat) (domain.RunControlHeartbeatResult, error) {
|
||||
heartbeat = domain.CopyRunControlHeartbeat(heartbeat)
|
||||
if err := validator.ValidateRunControlHeartbeat(heartbeat); err != nil {
|
||||
return domain.RunControlHeartbeatResult{}, err
|
||||
}
|
||||
|
||||
stamp := svc.now()
|
||||
|
||||
svc.controlMu.Lock()
|
||||
defer svc.controlMu.Unlock()
|
||||
|
||||
session, exists := svc.runSessions[heartbeat.RunEndpointID]
|
||||
if !exists || session.SessionToken != heartbeat.SessionToken {
|
||||
return domain.RunControlHeartbeatResult{}, validationError("sessionToken is invalid")
|
||||
}
|
||||
|
||||
endpoint, err := svc.store.RunEndpoints().Get(heartbeat.RunEndpointID)
|
||||
if err != nil {
|
||||
return domain.RunControlHeartbeatResult{}, err
|
||||
}
|
||||
endpoint.Version = heartbeat.Version
|
||||
endpoint.Status = heartbeat.Status
|
||||
endpoint.Capacity = heartbeat.Capacity
|
||||
endpoint.LastHeartbeatAt = stamp
|
||||
if err := validator.ValidateRunEndpoint(endpoint); err != nil {
|
||||
return domain.RunControlHeartbeatResult{}, err
|
||||
}
|
||||
if err := svc.store.RunEndpoints().Update(endpoint); err != nil {
|
||||
return domain.RunControlHeartbeatResult{}, err
|
||||
}
|
||||
|
||||
refreshCapabilities := session.CapabilityFingerprint != heartbeat.CapabilityFingerprint
|
||||
session.CapabilityFingerprint = heartbeat.CapabilityFingerprint
|
||||
session.UpdatedAt = stamp
|
||||
svc.runSessions[heartbeat.RunEndpointID] = session
|
||||
|
||||
return domain.CopyRunControlHeartbeatResult(domain.RunControlHeartbeatResult{
|
||||
Accepted: true,
|
||||
RunEndpointID: heartbeat.RunEndpointID,
|
||||
NextHeartbeatSeconds: session.HeartbeatIntervalSeconds,
|
||||
RefreshCapabilities: refreshCapabilities,
|
||||
ServerTime: stamp,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (svc *CoreService) upsertRunEndpoint(endpoint domain.RunEndpoint) error {
|
||||
if _, err := svc.store.RunEndpoints().Get(endpoint.ID); err != nil {
|
||||
if errors.Is(err, repo.ErrNotFound) {
|
||||
return svc.store.RunEndpoints().Create(endpoint)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return svc.store.RunEndpoints().Update(endpoint)
|
||||
}
|
||||
|
||||
func (svc *CoreService) nextSessionToken(runEndpointID string, stamp time.Time) string {
|
||||
svc.runSessionSeq++
|
||||
return fmt.Sprintf("session:%s:%d:%d", runEndpointID, stamp.UnixNano(), svc.runSessionSeq)
|
||||
}
|
||||
Reference in New Issue
Block a user