diff --git a/Ragon.Server/Sources/Project/ProjectRegistry.cs b/Ragon.Server/Sources/Project/ProjectRegistry.cs new file mode 100644 index 0000000..6b1fb6e --- /dev/null +++ b/Ragon.Server/Sources/Project/ProjectRegistry.cs @@ -0,0 +1,89 @@ +/* + * Copyright 2023-2024 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Ragon.Server.Project; + +public class ProjectRegistry +{ + private readonly Dictionary _projectsByKey; + private readonly Dictionary _projectsById; + private readonly int _connectionLimitPerProject; + + public ProjectRegistry(int connectionLimitPerProject) + { + _projectsByKey = new Dictionary(); + _projectsById = new Dictionary(); + _connectionLimitPerProject = connectionLimitPerProject; + } + + public bool ValidateKey(string key) + { + if (string.IsNullOrWhiteSpace(key)) + return false; + + if (key.Length < 4) + return false; + + return true; + } + + public RagonProject? GetOrCreateProject(string projectKey) + { + if (!_projectsByKey.TryGetValue(projectKey, out var project)) + { + project = new RagonProject(projectKey); + _projectsByKey[projectKey] = project; + _projectsById[project.Id] = project; + } + + return project; + } + + public bool CanConnect(string projectKey) + { + if (!_projectsByKey.TryGetValue(projectKey, out var project)) + return true; + + return project.ActiveConnections < _connectionLimitPerProject; + } + + public void RegisterConnection(int projectId) + { + if (_projectsById.TryGetValue(projectId, out var project)) + { + project.IncrementConnections(); + } + } + + public void UnregisterConnection(int projectId) + { + if (_projectsById.TryGetValue(projectId, out var project)) + { + project.DecrementConnections(); + + if (project.ActiveConnections <= 0) + { + _projectsByKey.Remove(project.Key); + _projectsById.Remove(projectId); + } + } + } + + public RagonProject? GetProjectById(int projectId) + { + return _projectsById.TryGetValue(projectId, out var project) ? project : null; + } +} diff --git a/Ragon.Server/Sources/Project/RagonProject.cs b/Ragon.Server/Sources/Project/RagonProject.cs new file mode 100644 index 0000000..a8cc6ca --- /dev/null +++ b/Ragon.Server/Sources/Project/RagonProject.cs @@ -0,0 +1,44 @@ +/* + * Copyright 2023-2024 Eduard Kargin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Ragon.Server.Project; + +public class RagonProject +{ + private static int _idGenerator = 0; + + public int Id { get; } + public string Key { get; } + public int ActiveConnections { get; private set; } + + public RagonProject(string key) + { + Id = Interlocked.Increment(ref _idGenerator); + Key = key; + ActiveConnections = 0; + } + + public void IncrementConnections() + { + ActiveConnections++; + } + + public void DecrementConnections() + { + if (ActiveConnections > 0) + ActiveConnections--; + } +}