feat(plugin): gate pages on companion features

This commit is contained in:
npc0-hue
2026-07-29 09:53:39 +08:00
parent 271e1e684f
commit c9494abc48
16 changed files with 300 additions and 61 deletions
+51 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"sort"
"strings"
"time"
"browser.local/platform/domain"
@@ -55,7 +56,7 @@ func (svc *CoreService) GetGameClientBridgeStatusForSession(sessionID, serverIns
if err != nil {
return domain.GameClientBridgeStatus{}, err
}
status := domain.GameClientBridgeStatus{ServerInstanceID: instance.ID, PluginID: plugin.ID, Reason: "plugin does not declare a game client bridge profile", Profiles: []domain.GameClientBridgeProfileDeclaration{}}
status := domain.GameClientBridgeStatus{ServerInstanceID: instance.ID, PluginID: plugin.ID, Reason: "plugin does not declare a game client bridge profile", Profiles: []domain.GameClientBridgeProfileDeclaration{}, Features: []domain.GameClientBridgeFeatureAvailability{}}
installations, err := svc.store.ClientManagerInstallations().List(domain.ClientManagerInstallationFilter{ServerInstanceID: instance.ID})
if err != nil {
return domain.GameClientBridgeStatus{}, err
@@ -77,6 +78,8 @@ func (svc *CoreService) GetGameClientBridgeStatusForSession(sessionID, serverIns
if svc.now().Before(session.ExpiresAt) && containsString(session.Capabilities, gameClientBridgeCapability) && session.KeyGeneration == installation.KeyGeneration && session.DeploymentGeneration == installation.DeploymentGeneration && session.ArtifactID == installation.ActiveArtifactID {
declaration.Available = true
declaration.Reason = ""
declaration.HandlerTypes = append(declaration.HandlerTypes, gameClientBridgeSessionCapabilityValues(session.Capabilities, "handler.")...)
declaration.EventProducerTypes = append(declaration.EventProducerTypes, gameClientBridgeSessionCapabilityValues(session.Capabilities, "event-producer.")...)
break
}
}
@@ -90,9 +93,56 @@ func (svc *CoreService) GetGameClientBridgeStatusForSession(sessionID, serverIns
if status.Available {
status.Reason = ""
}
status.Features = gameClientBridgeFeatureAvailability(plugin.GameClientBridge.Features, status.Profiles)
return domain.CopyGameClientBridgeStatus(status), nil
}
func gameClientBridgeSessionCapabilityValues(capabilities []string, prefix string) []string {
values := make([]string, 0, len(capabilities))
for _, capability := range capabilities {
if value, found := strings.CutPrefix(capability, prefix); found && value != "" {
values = append(values, value)
}
}
sort.Strings(values)
return values
}
func gameClientBridgeFeatureAvailability(features []domain.GameClientBridgeFeatureDeclaration, profiles []domain.GameClientBridgeProfileDeclaration) []domain.GameClientBridgeFeatureAvailability {
result := make([]domain.GameClientBridgeFeatureAvailability, 0, len(features))
for _, feature := range features {
handlers, producers := map[string]bool{}, map[string]bool{}
for _, profile := range profiles {
if !profile.Available {
continue
}
for _, handler := range profile.HandlerTypes {
handlers[handler] = true
}
for _, producer := range profile.EventProducerTypes {
producers[producer] = true
}
}
missing := make([]string, 0)
for _, handler := range feature.RequiredHandlers {
if !handlers[handler] {
missing = append(missing, "handler."+handler)
}
}
for _, producer := range feature.RequiredEventProducers {
if !producers[producer] {
missing = append(missing, "event-producer."+producer)
}
}
availability := domain.GameClientBridgeFeatureAvailability{Key: feature.Key, Available: len(missing) == 0}
if len(missing) > 0 {
availability.Reason = "compatible Companion is missing " + strings.Join(missing, ", ")
}
result = append(result, availability)
}
return result
}
func (svc *CoreService) ListGameClientBridgeCommandsForSession(sessionID string, filter domain.GameClientBridgeCommandFilter) ([]domain.GameClientBridgeCommand, error) {
if err := svc.authorizeServerLifecycle(sessionID, filter.ServerInstanceID); err != nil {
return nil, err