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) }