475 lines
17 KiB
Go
475 lines
17 KiB
Go
package repo
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
"sync"
|
|
|
|
"browser.local/platform/domain"
|
|
)
|
|
|
|
var (
|
|
ErrDuplicate = errors.New("resource already exists")
|
|
ErrNotFound = errors.New("resource not found")
|
|
)
|
|
|
|
type UserRepository interface {
|
|
Create(domain.User) error
|
|
Get(id string) (domain.User, error)
|
|
List(domain.UserFilter) ([]domain.User, error)
|
|
Update(domain.User) error
|
|
}
|
|
|
|
type AIProviderRepository interface {
|
|
Create(domain.AIProvider) error
|
|
Get(id string) (domain.AIProvider, error)
|
|
List(domain.AIProviderFilter) ([]domain.AIProvider, error)
|
|
Update(domain.AIProvider) error
|
|
}
|
|
|
|
type GamePluginRepository interface {
|
|
Create(domain.GamePlugin) error
|
|
Get(id string) (domain.GamePlugin, error)
|
|
List(domain.GamePluginFilter) ([]domain.GamePlugin, error)
|
|
Update(domain.GamePlugin) error
|
|
}
|
|
|
|
type ServerInstanceRepository interface {
|
|
Create(domain.ServerInstance) error
|
|
Get(id string) (domain.ServerInstance, error)
|
|
List(domain.ServerInstanceFilter) ([]domain.ServerInstance, error)
|
|
Update(domain.ServerInstance) error
|
|
}
|
|
|
|
type RunEndpointRepository interface {
|
|
Create(domain.RunEndpoint) error
|
|
Get(id string) (domain.RunEndpoint, error)
|
|
List(domain.RunEndpointFilter) ([]domain.RunEndpoint, error)
|
|
Update(domain.RunEndpoint) error
|
|
}
|
|
|
|
type JobRepository interface {
|
|
Create(domain.Job) error
|
|
Get(id string) (domain.Job, error)
|
|
GetByIdempotency(runEndpointID string, idempotencyKey string) (domain.Job, error)
|
|
List(domain.JobFilter) ([]domain.Job, error)
|
|
Update(domain.Job) error
|
|
}
|
|
|
|
type ArtifactRepository interface {
|
|
Create(domain.Artifact) error
|
|
Get(id string) (domain.Artifact, error)
|
|
List(domain.ArtifactFilter) ([]domain.Artifact, error)
|
|
Update(domain.Artifact) error
|
|
}
|
|
|
|
type RuntimeBindingRepository interface {
|
|
Create(domain.RuntimeBinding) error
|
|
Get(id string) (domain.RuntimeBinding, error)
|
|
List(domain.RuntimeBindingFilter) ([]domain.RuntimeBinding, error)
|
|
Update(domain.RuntimeBinding) error
|
|
}
|
|
|
|
type EncryptedComponentKeyRepository interface {
|
|
Create(domain.EncryptedComponentKey) error
|
|
Get(id string) (domain.EncryptedComponentKey, error)
|
|
List(domain.EncryptedComponentKeyFilter) ([]domain.EncryptedComponentKey, error)
|
|
Update(domain.EncryptedComponentKey) error
|
|
}
|
|
|
|
type RunDistributionRepository interface {
|
|
Create(domain.RunDistribution) error
|
|
Get(id string) (domain.RunDistribution, error)
|
|
List(domain.RunDistributionFilter) ([]domain.RunDistribution, error)
|
|
Update(domain.RunDistribution) error
|
|
}
|
|
|
|
type ClientManagerDistributionRepository interface {
|
|
Create(domain.ClientManagerDistribution) error
|
|
Get(id string) (domain.ClientManagerDistribution, error)
|
|
List(domain.ClientManagerDistributionFilter) ([]domain.ClientManagerDistribution, error)
|
|
Update(domain.ClientManagerDistribution) error
|
|
}
|
|
|
|
type DependencyStatusRepository interface {
|
|
Create(domain.DependencyStatus) error
|
|
Get(id string) (domain.DependencyStatus, error)
|
|
List(domain.DependencyStatusFilter) ([]domain.DependencyStatus, error)
|
|
Update(domain.DependencyStatus) error
|
|
}
|
|
|
|
type ClientManagerBuildJobRepository interface {
|
|
Create(domain.ClientManagerBuildJob) error
|
|
Get(id string) (domain.ClientManagerBuildJob, error)
|
|
List(domain.ClientManagerBuildJobFilter) ([]domain.ClientManagerBuildJob, error)
|
|
Update(domain.ClientManagerBuildJob) error
|
|
}
|
|
|
|
type RunUpdateJobRepository interface {
|
|
Create(domain.RunUpdateJob) error
|
|
Get(id string) (domain.RunUpdateJob, error)
|
|
List(domain.RunUpdateJobFilter) ([]domain.RunUpdateJob, error)
|
|
Update(domain.RunUpdateJob) error
|
|
}
|
|
|
|
type LogStreamRepository interface {
|
|
Create(domain.LogStream) error
|
|
Get(id string) (domain.LogStream, error)
|
|
List(domain.LogStreamFilter) ([]domain.LogStream, error)
|
|
Update(domain.LogStream) error
|
|
}
|
|
|
|
type AuditEventRepository interface {
|
|
Create(domain.AuditEvent) error
|
|
Get(id string) (domain.AuditEvent, error)
|
|
List(domain.AuditEventFilter) ([]domain.AuditEvent, error)
|
|
Update(domain.AuditEvent) error
|
|
}
|
|
|
|
type Store interface {
|
|
Users() UserRepository
|
|
AIProviders() AIProviderRepository
|
|
GamePlugins() GamePluginRepository
|
|
ServerInstances() ServerInstanceRepository
|
|
RunEndpoints() RunEndpointRepository
|
|
Jobs() JobRepository
|
|
Artifacts() ArtifactRepository
|
|
RuntimeBindings() RuntimeBindingRepository
|
|
EncryptedComponentKeys() EncryptedComponentKeyRepository
|
|
RunDistributions() RunDistributionRepository
|
|
ClientManagerDistributions() ClientManagerDistributionRepository
|
|
DependencyStatuses() DependencyStatusRepository
|
|
ClientManagerBuildJobs() ClientManagerBuildJobRepository
|
|
RunUpdateJobs() RunUpdateJobRepository
|
|
LogStreams() LogStreamRepository
|
|
AuditEvents() AuditEventRepository
|
|
}
|
|
|
|
type MemoryStore struct {
|
|
users *memoryRepository[domain.User, domain.UserFilter]
|
|
aiProviders *memoryRepository[domain.AIProvider, domain.AIProviderFilter]
|
|
gamePlugins *memoryRepository[domain.GamePlugin, domain.GamePluginFilter]
|
|
serverInstances *memoryRepository[domain.ServerInstance, domain.ServerInstanceFilter]
|
|
runEndpoints *memoryRepository[domain.RunEndpoint, domain.RunEndpointFilter]
|
|
jobs *memoryJobRepository
|
|
artifacts *memoryRepository[domain.Artifact, domain.ArtifactFilter]
|
|
runtimeBindings *memoryRepository[domain.RuntimeBinding, domain.RuntimeBindingFilter]
|
|
componentKeys *memoryRepository[domain.EncryptedComponentKey, domain.EncryptedComponentKeyFilter]
|
|
runDists *memoryRepository[domain.RunDistribution, domain.RunDistributionFilter]
|
|
clientDists *memoryRepository[domain.ClientManagerDistribution, domain.ClientManagerDistributionFilter]
|
|
dependencies *memoryRepository[domain.DependencyStatus, domain.DependencyStatusFilter]
|
|
buildJobs *memoryRepository[domain.ClientManagerBuildJob, domain.ClientManagerBuildJobFilter]
|
|
updateJobs *memoryRepository[domain.RunUpdateJob, domain.RunUpdateJobFilter]
|
|
logStreams *memoryRepository[domain.LogStream, domain.LogStreamFilter]
|
|
auditEvents *memoryRepository[domain.AuditEvent, domain.AuditEventFilter]
|
|
}
|
|
|
|
func NewMemoryStore() *MemoryStore {
|
|
return &MemoryStore{
|
|
users: newMemoryRepository(
|
|
func(user domain.User) string { return user.ID },
|
|
domain.CopyUser,
|
|
matchUser,
|
|
),
|
|
aiProviders: newMemoryRepository(
|
|
func(provider domain.AIProvider) string { return provider.ID },
|
|
domain.CopyAIProvider,
|
|
matchAIProvider,
|
|
),
|
|
gamePlugins: newMemoryRepository(
|
|
func(plugin domain.GamePlugin) string { return plugin.ID },
|
|
domain.CopyGamePlugin,
|
|
matchGamePlugin,
|
|
),
|
|
serverInstances: newMemoryRepository(
|
|
func(instance domain.ServerInstance) string { return instance.ID },
|
|
domain.CopyServerInstance,
|
|
matchServerInstance,
|
|
),
|
|
runEndpoints: newMemoryRepository(
|
|
func(endpoint domain.RunEndpoint) string { return endpoint.ID },
|
|
domain.CopyRunEndpoint,
|
|
matchRunEndpoint,
|
|
),
|
|
jobs: newMemoryJobRepository(),
|
|
artifacts: newMemoryRepository(
|
|
func(artifact domain.Artifact) string { return artifact.ID },
|
|
domain.CopyArtifact,
|
|
matchArtifact,
|
|
),
|
|
runtimeBindings: newMemoryRepository(
|
|
func(binding domain.RuntimeBinding) string { return binding.ID },
|
|
domain.CopyRuntimeBinding,
|
|
matchRuntimeBinding,
|
|
),
|
|
componentKeys: newMemoryRepository(
|
|
func(key domain.EncryptedComponentKey) string { return key.ID },
|
|
domain.CopyEncryptedComponentKey,
|
|
matchEncryptedComponentKey,
|
|
),
|
|
runDists: newMemoryRepository(
|
|
func(distribution domain.RunDistribution) string { return distribution.ID },
|
|
domain.CopyRunDistribution,
|
|
matchRunDistribution,
|
|
),
|
|
clientDists: newMemoryRepository(
|
|
func(distribution domain.ClientManagerDistribution) string { return distribution.ID },
|
|
domain.CopyClientManagerDistribution,
|
|
matchClientManagerDistribution,
|
|
),
|
|
dependencies: newMemoryRepository(
|
|
func(status domain.DependencyStatus) string { return status.ID },
|
|
domain.CopyDependencyStatus,
|
|
matchDependencyStatus,
|
|
),
|
|
buildJobs: newMemoryRepository(
|
|
func(job domain.ClientManagerBuildJob) string { return job.ID },
|
|
domain.CopyClientManagerBuildJob,
|
|
matchClientManagerBuildJob,
|
|
),
|
|
updateJobs: newMemoryRepository(
|
|
func(job domain.RunUpdateJob) string { return job.ID },
|
|
domain.CopyRunUpdateJob,
|
|
matchRunUpdateJob,
|
|
),
|
|
logStreams: newMemoryRepository(
|
|
func(stream domain.LogStream) string { return stream.ID },
|
|
domain.CopyLogStream,
|
|
matchLogStream,
|
|
),
|
|
auditEvents: newMemoryRepository(
|
|
func(event domain.AuditEvent) string { return event.ID },
|
|
domain.CopyAuditEvent,
|
|
matchAuditEvent,
|
|
),
|
|
}
|
|
}
|
|
|
|
func (store *MemoryStore) Users() UserRepository { return store.users }
|
|
func (store *MemoryStore) AIProviders() AIProviderRepository { return store.aiProviders }
|
|
func (store *MemoryStore) GamePlugins() GamePluginRepository { return store.gamePlugins }
|
|
func (store *MemoryStore) ServerInstances() ServerInstanceRepository { return store.serverInstances }
|
|
func (store *MemoryStore) RunEndpoints() RunEndpointRepository { return store.runEndpoints }
|
|
func (store *MemoryStore) Jobs() JobRepository { return store.jobs }
|
|
func (store *MemoryStore) Artifacts() ArtifactRepository { return store.artifacts }
|
|
func (store *MemoryStore) RuntimeBindings() RuntimeBindingRepository { return store.runtimeBindings }
|
|
func (store *MemoryStore) EncryptedComponentKeys() EncryptedComponentKeyRepository {
|
|
return store.componentKeys
|
|
}
|
|
func (store *MemoryStore) RunDistributions() RunDistributionRepository { return store.runDists }
|
|
func (store *MemoryStore) ClientManagerDistributions() ClientManagerDistributionRepository {
|
|
return store.clientDists
|
|
}
|
|
func (store *MemoryStore) DependencyStatuses() DependencyStatusRepository { return store.dependencies }
|
|
func (store *MemoryStore) ClientManagerBuildJobs() ClientManagerBuildJobRepository {
|
|
return store.buildJobs
|
|
}
|
|
func (store *MemoryStore) RunUpdateJobs() RunUpdateJobRepository { return store.updateJobs }
|
|
func (store *MemoryStore) LogStreams() LogStreamRepository { return store.logStreams }
|
|
func (store *MemoryStore) AuditEvents() AuditEventRepository { return store.auditEvents }
|
|
|
|
type memoryRepository[T any, F any] struct {
|
|
mu sync.RWMutex
|
|
byID map[string]T
|
|
idOf func(T) string
|
|
copyOf func(T) T
|
|
match func(T, F) bool
|
|
}
|
|
|
|
func newMemoryRepository[T any, F any](idOf func(T) string, copyOf func(T) T, match func(T, F) bool) *memoryRepository[T, F] {
|
|
return &memoryRepository[T, F]{
|
|
byID: map[string]T{},
|
|
idOf: idOf,
|
|
copyOf: copyOf,
|
|
match: match,
|
|
}
|
|
}
|
|
|
|
func (repository *memoryRepository[T, F]) Create(value T) error {
|
|
repository.mu.Lock()
|
|
defer repository.mu.Unlock()
|
|
|
|
id := repository.idOf(value)
|
|
if _, exists := repository.byID[id]; exists {
|
|
return ErrDuplicate
|
|
}
|
|
repository.byID[id] = repository.copyOf(value)
|
|
return nil
|
|
}
|
|
|
|
func (repository *memoryRepository[T, F]) Get(id string) (T, error) {
|
|
repository.mu.RLock()
|
|
defer repository.mu.RUnlock()
|
|
|
|
value, exists := repository.byID[id]
|
|
if !exists {
|
|
var zero T
|
|
return zero, ErrNotFound
|
|
}
|
|
return repository.copyOf(value), nil
|
|
}
|
|
|
|
func (repository *memoryRepository[T, F]) List(filter F) ([]T, error) {
|
|
repository.mu.RLock()
|
|
defer repository.mu.RUnlock()
|
|
|
|
ids := make([]string, 0, len(repository.byID))
|
|
for id := range repository.byID {
|
|
ids = append(ids, id)
|
|
}
|
|
sort.Strings(ids)
|
|
|
|
values := make([]T, 0, len(ids))
|
|
for _, id := range ids {
|
|
value := repository.byID[id]
|
|
if repository.match(value, filter) {
|
|
values = append(values, repository.copyOf(value))
|
|
}
|
|
}
|
|
return values, nil
|
|
}
|
|
|
|
func (repository *memoryRepository[T, F]) Update(value T) error {
|
|
repository.mu.Lock()
|
|
defer repository.mu.Unlock()
|
|
|
|
id := repository.idOf(value)
|
|
if _, exists := repository.byID[id]; !exists {
|
|
return ErrNotFound
|
|
}
|
|
repository.byID[id] = repository.copyOf(value)
|
|
return nil
|
|
}
|
|
|
|
type memoryJobRepository struct {
|
|
*memoryRepository[domain.Job, domain.JobFilter]
|
|
}
|
|
|
|
func newMemoryJobRepository() *memoryJobRepository {
|
|
return &memoryJobRepository{
|
|
memoryRepository: newMemoryRepository(
|
|
func(job domain.Job) string { return job.ID },
|
|
domain.CopyJob,
|
|
matchJob,
|
|
),
|
|
}
|
|
}
|
|
|
|
func (repository *memoryJobRepository) GetByIdempotency(runEndpointID string, idempotencyKey string) (domain.Job, error) {
|
|
repository.mu.RLock()
|
|
defer repository.mu.RUnlock()
|
|
|
|
for _, job := range repository.byID {
|
|
if job.RunEndpointID == runEndpointID && job.IdempotencyKey == idempotencyKey {
|
|
return domain.CopyJob(job), nil
|
|
}
|
|
}
|
|
return domain.Job{}, ErrNotFound
|
|
}
|
|
|
|
func matchUser(user domain.User, filter domain.UserFilter) bool {
|
|
return filter.Status == "" || user.Status == filter.Status
|
|
}
|
|
|
|
func matchAIProvider(provider domain.AIProvider, filter domain.AIProviderFilter) bool {
|
|
return (filter.Kind == "" || provider.Kind == filter.Kind) &&
|
|
(filter.Status == "" || provider.Status == filter.Status)
|
|
}
|
|
|
|
func matchGamePlugin(plugin domain.GamePlugin, filter domain.GamePluginFilter) bool {
|
|
return (filter.ServerType == "" || plugin.ServerType == filter.ServerType) &&
|
|
(filter.Status == "" || plugin.Status == filter.Status)
|
|
}
|
|
|
|
func matchServerInstance(instance domain.ServerInstance, filter domain.ServerInstanceFilter) bool {
|
|
if instance.State == domain.ServerInstanceStateDeleted && filter.State != domain.ServerInstanceStateDeleted {
|
|
return false
|
|
}
|
|
return (filter.PluginID == "" || instance.PluginID == filter.PluginID) &&
|
|
(filter.RunEndpointID == "" || instance.RunEndpointID == filter.RunEndpointID) &&
|
|
(filter.State == "" || instance.State == filter.State) &&
|
|
(filter.VisibleToUserID == "" || instance.OwnerUserID == filter.VisibleToUserID || containsString(instance.AdminUserIDs, filter.VisibleToUserID))
|
|
}
|
|
|
|
func containsString(values []string, target string) bool {
|
|
for _, value := range values {
|
|
if value == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func matchRunEndpoint(endpoint domain.RunEndpoint, filter domain.RunEndpointFilter) bool {
|
|
return filter.Status == "" || endpoint.Status == filter.Status
|
|
}
|
|
|
|
func matchJob(job domain.Job, filter domain.JobFilter) bool {
|
|
return (filter.ServerInstanceID == "" || job.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.RunEndpointID == "" || job.RunEndpointID == filter.RunEndpointID) &&
|
|
(filter.State == "" || job.State == filter.State)
|
|
}
|
|
|
|
func matchArtifact(artifact domain.Artifact, filter domain.ArtifactFilter) bool {
|
|
return (filter.OwnerKind == "" || artifact.OwnerKind == filter.OwnerKind) &&
|
|
(filter.OwnerID == "" || artifact.OwnerID == filter.OwnerID) &&
|
|
(filter.State == "" || artifact.State == filter.State)
|
|
}
|
|
|
|
func matchRuntimeBinding(binding domain.RuntimeBinding, filter domain.RuntimeBindingFilter) bool {
|
|
return (filter.ServerInstanceID == "" || binding.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.ProfileKey == "" || binding.ProfileKey == filter.ProfileKey) &&
|
|
(filter.Status == "" || binding.Status == filter.Status)
|
|
}
|
|
|
|
func matchEncryptedComponentKey(key domain.EncryptedComponentKey, filter domain.EncryptedComponentKeyFilter) bool {
|
|
return (filter.ServerInstanceID == "" || key.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.ComponentKind == "" || key.ComponentKind == filter.ComponentKind) &&
|
|
(filter.ComponentKey == "" || key.ComponentKey == filter.ComponentKey) &&
|
|
(filter.Status == "" || key.Status == filter.Status)
|
|
}
|
|
|
|
func matchRunDistribution(distribution domain.RunDistribution, filter domain.RunDistributionFilter) bool {
|
|
return (filter.ServerInstanceID == "" || distribution.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.TargetOS == "" || distribution.TargetOS == filter.TargetOS) &&
|
|
(filter.TargetArch == "" || distribution.TargetArch == filter.TargetArch) &&
|
|
(filter.Status == "" || distribution.Status == filter.Status)
|
|
}
|
|
|
|
func matchClientManagerDistribution(distribution domain.ClientManagerDistribution, filter domain.ClientManagerDistributionFilter) bool {
|
|
return (filter.ServerInstanceID == "" || distribution.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.ProfileKey == "" || distribution.ProfileKey == filter.ProfileKey) &&
|
|
(filter.TargetOS == "" || distribution.TargetOS == filter.TargetOS) &&
|
|
(filter.TargetArch == "" || distribution.TargetArch == filter.TargetArch) &&
|
|
(filter.Status == "" || distribution.Status == filter.Status)
|
|
}
|
|
|
|
func matchDependencyStatus(status domain.DependencyStatus, filter domain.DependencyStatusFilter) bool {
|
|
return (filter.ServerInstanceID == "" || status.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.ProbeKey == "" || status.ProbeKey == filter.ProbeKey) &&
|
|
(filter.State == "" || status.State == filter.State)
|
|
}
|
|
|
|
func matchClientManagerBuildJob(job domain.ClientManagerBuildJob, filter domain.ClientManagerBuildJobFilter) bool {
|
|
return (filter.ServerInstanceID == "" || job.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.ProfileKey == "" || job.ProfileKey == filter.ProfileKey) &&
|
|
(filter.Status == "" || job.Status == filter.Status)
|
|
}
|
|
|
|
func matchRunUpdateJob(job domain.RunUpdateJob, filter domain.RunUpdateJobFilter) bool {
|
|
return (filter.ServerInstanceID == "" || job.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.Status == "" || job.Status == filter.Status)
|
|
}
|
|
|
|
func matchLogStream(stream domain.LogStream, filter domain.LogStreamFilter) bool {
|
|
return (filter.ServerInstanceID == "" || stream.ServerInstanceID == filter.ServerInstanceID) &&
|
|
(filter.StreamKey == "" || stream.StreamKey == filter.StreamKey)
|
|
}
|
|
|
|
func matchAuditEvent(event domain.AuditEvent, filter domain.AuditEventFilter) bool {
|
|
return (filter.ActorID == "" || event.ActorID == filter.ActorID) &&
|
|
(filter.ResourceKind == "" || event.ResourceKind == filter.ResourceKind) &&
|
|
(filter.ResourceID == "" || event.ResourceID == filter.ResourceID) &&
|
|
(filter.Result == "" || event.Result == filter.Result)
|
|
}
|