From a9cdb6589593ea23afb6a590f0c5cb5fca70c35b Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 20 Oct 2024 19:32:30 +0000 Subject: [PATCH] Added channel saving. Fixed channel loading. Added policy store to channels. --- Models/Channel.cs | 1 + Models/Policy.cs | 12 +++ Services/ChannelManager.cs | 51 +++++++++++-- Services/DatabaseService.cs | 16 ++-- Socket/Handlers/HermesLoginHandler.cs | 9 ++- Store/PolicyStore.cs | 105 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 16 deletions(-) create mode 100644 Models/Policy.cs create mode 100644 Store/PolicyStore.cs diff --git a/Models/Channel.cs b/Models/Channel.cs index 835351c..a575583 100644 --- a/Models/Channel.cs +++ b/Models/Channel.cs @@ -7,5 +7,6 @@ namespace HermesSocketServer.Models public string Id { get; set; } public User User { get; set; } public ChatterStore Chatters { get; set; } + public PolicyStore Policies { get; set; } } } \ No newline at end of file diff --git a/Models/Policy.cs b/Models/Policy.cs new file mode 100644 index 0000000..76c9d58 --- /dev/null +++ b/Models/Policy.cs @@ -0,0 +1,12 @@ +namespace HermesSocketServer.Store +{ + public class Policy + { + public string Id { get; set; } + public string UserId { get; set; } + public string GroupId { get; set; } + public string Path { get; set; } + public int Usage { get; set; } + public TimeSpan Span { get; set; } + } +} \ No newline at end of file diff --git a/Services/ChannelManager.cs b/Services/ChannelManager.cs index ea645d7..73a543b 100644 --- a/Services/ChannelManager.cs +++ b/Services/ChannelManager.cs @@ -11,6 +11,7 @@ namespace HermesSocketServer.Services private readonly Database _database; private readonly Serilog.ILogger _logger; private readonly IDictionary _channels; + private readonly object _lock; public ChannelManager(UserStore users, Database database, Serilog.ILogger logger) { @@ -18,32 +19,45 @@ namespace HermesSocketServer.Services _database = database; _logger = logger; _channels = new ConcurrentDictionary(); + _lock = new object(); } - public async Task Add(string userId) + public async Task Add(string userId) { var user = _users.Get(userId); if (user == null) { - return; + return null; } - if (_channels.ContainsKey(userId)) + lock (_lock) { - return; + if (_channels.ContainsKey(userId)) + { + return null; + } } var chatters = new ChatterStore(userId, _database, _logger); - await chatters.Load(); + var policies = new PolicyStore(userId, _database, _logger); + await Task.WhenAll([ + chatters.Load(), + policies.Load(), + ]); var channel = new Channel() { Id = userId, User = user, - Chatters = chatters + Chatters = chatters, + Policies = policies }; - _channels.Add(userId, channel); + lock (_lock) + { + _channels.Add(userId, channel); + } + return channel; } public Channel? Get(string channelId) @@ -52,5 +66,28 @@ namespace HermesSocketServer.Services return channel; return null; } + + public async Task Save(string userId) + { + if (!_channels.TryGetValue(userId, out var channel)) + return; + + await Task.WhenAll([ + channel.Chatters.Save(), + channel.Policies.Save(), + ]); + } + + public async Task Save() + { + foreach (var channel in _channels.Values) + { + _logger.Debug($"Saving channel data to database [channel id: {channel.Id}][channel name: {channel.User.Name}]"); + await Task.WhenAll([ + channel.Chatters.Save(), + channel.Policies.Save(), + ]); + } + } } } \ No newline at end of file diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs index c539bc5..eca05d5 100644 --- a/Services/DatabaseService.cs +++ b/Services/DatabaseService.cs @@ -4,13 +4,15 @@ namespace HermesSocketServer.Services { public class DatabaseService : BackgroundService { + private readonly ChannelManager _channels; private readonly VoiceStore _voices; private readonly UserStore _users; private readonly ServerConfiguration _configuration; private readonly Serilog.ILogger _logger; - public DatabaseService(VoiceStore voices, UserStore users, ServerConfiguration configuration, Serilog.ILogger logger) + public DatabaseService(ChannelManager channels, VoiceStore voices, UserStore users, ServerConfiguration configuration, Serilog.ILogger logger) { + _channels = channels; _voices = voices; _users = users; _configuration = configuration; @@ -26,14 +28,16 @@ namespace HermesSocketServer.Services await Task.Run(async () => { - var tasks = new List(); await Task.Delay(TimeSpan.FromSeconds(_configuration.Database.SaveDelayInSeconds)); + while (true) { - tasks.Add(_voices.Save()); - tasks.Add(_users.Save()); - tasks.Add(Task.Delay(TimeSpan.FromSeconds(_configuration.Database.SaveDelayInSeconds))); - await Task.WhenAll(tasks); + await Task.WhenAll([ + _voices.Save(), + _users.Save(), + _channels.Save(), + Task.Delay(TimeSpan.FromSeconds(_configuration.Database.SaveDelayInSeconds)), + ]); } }); } diff --git a/Socket/Handlers/HermesLoginHandler.cs b/Socket/Handlers/HermesLoginHandler.cs index ba4a5b7..319c5f5 100644 --- a/Socket/Handlers/HermesLoginHandler.cs +++ b/Socket/Handlers/HermesLoginHandler.cs @@ -55,11 +55,14 @@ namespace HermesSocketServer.Socket.Handlers sender.WebLogin = data.WebLogin; } - await _manager.Add(userId); var channel = _manager.Get(userId); if (channel == null) - return; - + { + channel = await _manager.Add(userId); + if (channel == null) + return; + } + sender.Name = channel.User.Name; sender.Admin = channel.User.Role == "ADMIN"; diff --git a/Store/PolicyStore.cs b/Store/PolicyStore.cs new file mode 100644 index 0000000..1b591cc --- /dev/null +++ b/Store/PolicyStore.cs @@ -0,0 +1,105 @@ +using HermesSocketLibrary.db; + +namespace HermesSocketServer.Store +{ + public class PolicyStore : GroupSaveStore + { + private readonly string _userId; + private readonly Database _database; + private readonly Serilog.ILogger _logger; + private readonly GroupSaveSqlGenerator _generator; + + + public PolicyStore(string userId, Database database, Serilog.ILogger logger) : base(logger) + { + _userId = userId; + _database = database; + _logger = logger; + + var ctp = new Dictionary + { + { "id", "Id" }, + { "userId", "UserId" }, + { "groupId", "GroupId" }, + { "path", "Path" }, + { "count", "Usage" }, + { "timespan", "Span" }, + }; + _generator = new GroupSaveSqlGenerator(ctp); + } + + public override async Task Load() + { + var data = new Dictionary() { { "user", _userId } }; + string sql = $"SELECT id, \"groupId\", path, count, timespan FROM \"GroupPermissionPolicy\" WHERE \"userId\" = @user"; + await _database.Execute(sql, data, (reader) => + { + string id = reader.GetString(0).ToString(); + _store.Add(id, new Policy() + { + Id = id, + UserId = _userId, + GroupId = reader.GetString(1), + Path = reader.GetString(2), + Usage = reader.GetInt32(3), + Span = TimeSpan.FromMilliseconds(reader.GetInt32(4)), + }); + }); + _logger.Information($"Loaded {_store.Count} policies from database."); + } + + protected override void OnInitialAdd(string key, Policy value) + { + } + + protected override void OnInitialModify(string key, Policy value) + { + } + + protected override void OnInitialRemove(string key) + { + } + + public override async Task Save() + { + int count = 0; + string sql = string.Empty; + + if (_added.Any()) + { + lock (_lock) + { + count = _added.Count; + sql = _generator.GenerateInsertSql("GroupPermissionPolicy", _added.Select(a => _store[a]), ["id", "userId", "groupId", "path", "count", "timespan"]); + _added.Clear(); + } + + _logger.Debug($"GroupPermissionPolicy - Adding {count} rows to database: {sql}"); + await _database.ExecuteScalar(sql); + } + if (_modified.Any()) + { + lock (_lock) + { + count = _modified.Count; + sql = _generator.GenerateUpdateSql("GroupPermissionPolicy", _modified.Select(m => _store[m]), ["id"], ["userId", "groupId", "path", "count", "timespan"]); + _modified.Clear(); + } + _logger.Debug($"GroupPermissionPolicy - Modifying {count} rows in database: {sql}"); + await _database.ExecuteScalar(sql); + } + if (_deleted.Any()) + { + lock (_lock) + { + count = _deleted.Count; + sql = _generator.GenerateDeleteSql("GroupPermissionPolicy", _deleted, ["id"]); + _deleted.Clear(); + } + _logger.Debug($"GroupPermissionPolicy - Deleting {count} rows from database: {sql}"); + await _database.ExecuteScalar(sql); + } + return true; + } + } +} \ No newline at end of file