1
0

AdminLogsEui.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. using System.Linq;
  2. using System.Threading;
  3. using System.Threading.Tasks;
  4. using Content.Server.Administration.Managers;
  5. using Content.Server.EUI;
  6. using Content.Server.GameTicking;
  7. using Content.Shared.Administration;
  8. using Content.Shared.Administration.Logs;
  9. using Content.Shared.CCVar;
  10. using Content.Shared.Database;
  11. using Content.Shared.Eui;
  12. using Microsoft.Extensions.ObjectPool;
  13. using Robust.Shared.Configuration;
  14. using Robust.Shared.Timing;
  15. using Robust.Shared.Utility;
  16. using static Content.Shared.Administration.Logs.AdminLogsEuiMsg;
  17. namespace Content.Server.Administration.Logs;
  18. public sealed class AdminLogsEui : BaseEui
  19. {
  20. [Dependency] private readonly IAdminLogManager _adminLogs = default!;
  21. [Dependency] private readonly IAdminManager _adminManager = default!;
  22. [Dependency] private readonly ILogManager _logManager = default!;
  23. [Dependency] private readonly IConfigurationManager _configuration = default!;
  24. [Dependency] private readonly IEntityManager _e = default!;
  25. private readonly ISawmill _sawmill;
  26. private int _clientBatchSize;
  27. private bool _isLoading = true;
  28. private readonly Dictionary<Guid, string> _players = new();
  29. private int _roundLogs;
  30. private CancellationTokenSource _logSendCancellation = new();
  31. private LogFilter _filter;
  32. private readonly DefaultObjectPool<List<SharedAdminLog>> _adminLogListPool =
  33. new(new ListPolicy<SharedAdminLog>());
  34. public AdminLogsEui()
  35. {
  36. IoCManager.InjectDependencies(this);
  37. _sawmill = _logManager.GetSawmill(AdminLogManager.SawmillId);
  38. _configuration.OnValueChanged(CCVars.AdminLogsClientBatchSize, ClientBatchSizeChanged, true);
  39. _filter = new LogFilter
  40. {
  41. CancellationToken = _logSendCancellation.Token,
  42. Limit = _clientBatchSize
  43. };
  44. }
  45. private int CurrentRoundId => _e.System<GameTicker>().RoundId;
  46. public override async void Opened()
  47. {
  48. base.Opened();
  49. _adminManager.OnPermsChanged += OnPermsChanged;
  50. var roundId = _filter.Round ?? CurrentRoundId;
  51. await LoadFromDb(roundId);
  52. }
  53. private void ClientBatchSizeChanged(int value)
  54. {
  55. _clientBatchSize = value;
  56. }
  57. private void OnPermsChanged(AdminPermsChangedEventArgs args)
  58. {
  59. if (args.Player == Player && !_adminManager.HasAdminFlag(Player, AdminFlags.Logs))
  60. {
  61. Close();
  62. }
  63. }
  64. public override EuiStateBase GetNewState()
  65. {
  66. if (_isLoading)
  67. {
  68. return new AdminLogsEuiState(CurrentRoundId, new Dictionary<Guid, string>(), 0)
  69. {
  70. IsLoading = true
  71. };
  72. }
  73. var state = new AdminLogsEuiState(CurrentRoundId, _players, _roundLogs);
  74. return state;
  75. }
  76. public override async void HandleMessage(EuiMessageBase msg)
  77. {
  78. base.HandleMessage(msg);
  79. if (!_adminManager.HasAdminFlag(Player, AdminFlags.Logs))
  80. {
  81. return;
  82. }
  83. switch (msg)
  84. {
  85. case LogsRequest request:
  86. {
  87. _sawmill.Info($"Admin log request from admin with id {Player.UserId.UserId} and name {Player.Name}");
  88. _logSendCancellation.Cancel();
  89. _logSendCancellation = new CancellationTokenSource();
  90. _filter = new LogFilter
  91. {
  92. CancellationToken = _logSendCancellation.Token,
  93. Round = request.RoundId,
  94. Search = request.Search,
  95. Types = request.Types,
  96. Impacts = request.Impacts,
  97. Before = request.Before,
  98. After = request.After,
  99. IncludePlayers = request.IncludePlayers,
  100. AnyPlayers = request.AnyPlayers,
  101. AllPlayers = request.AllPlayers,
  102. IncludeNonPlayers = request.IncludeNonPlayers,
  103. LastLogId = null,
  104. Limit = _clientBatchSize
  105. };
  106. var roundId = _filter.Round ??= CurrentRoundId;
  107. await LoadFromDb(roundId);
  108. SendLogs(true);
  109. break;
  110. }
  111. case NextLogsRequest:
  112. {
  113. _sawmill.Info($"Admin log next batch request from admin with id {Player.UserId.UserId} and name {Player.Name}");
  114. SendLogs(false);
  115. break;
  116. }
  117. }
  118. }
  119. public void SetLogFilter(string? search = null, bool invertTypes = false, HashSet<LogType>? types = null)
  120. {
  121. var message = new SetLogFilter(
  122. search,
  123. invertTypes,
  124. types);
  125. SendMessage(message);
  126. }
  127. private async void SendLogs(bool replace)
  128. {
  129. var stopwatch = new Stopwatch();
  130. stopwatch.Start();
  131. var logs = await Task.Run(async () => await _adminLogs.All(_filter, _adminLogListPool.Get),
  132. _filter.CancellationToken);
  133. if (logs.Count > 0)
  134. {
  135. _filter.LogsSent += logs.Count;
  136. var largestId = _filter.DateOrder switch
  137. {
  138. DateOrder.Ascending => 0,
  139. DateOrder.Descending => ^1,
  140. _ => throw new ArgumentOutOfRangeException(nameof(_filter.DateOrder), _filter.DateOrder, null)
  141. };
  142. _filter.LastLogId = logs[largestId].Id;
  143. }
  144. var message = new NewLogs(logs, replace, logs.Count >= _filter.Limit);
  145. SendMessage(message);
  146. _sawmill.Info($"Sent {logs.Count} logs to {Player.Name} in {stopwatch.Elapsed.TotalMilliseconds} ms");
  147. _adminLogListPool.Return(logs);
  148. }
  149. public override void Closed()
  150. {
  151. base.Closed();
  152. _configuration.UnsubValueChanged(CCVars.AdminLogsClientBatchSize, ClientBatchSizeChanged);
  153. _adminManager.OnPermsChanged -= OnPermsChanged;
  154. _logSendCancellation.Cancel();
  155. _logSendCancellation.Dispose();
  156. }
  157. private async Task LoadFromDb(int roundId)
  158. {
  159. _isLoading = true;
  160. StateDirty();
  161. var round = _adminLogs.Round(roundId);
  162. var count = _adminLogs.CountLogs(roundId);
  163. await Task.WhenAll(round, count);
  164. var players = (await round).Players
  165. .ToDictionary(player => player.UserId, player => player.LastSeenUserName);
  166. _players.Clear();
  167. foreach (var (id, name) in players)
  168. {
  169. _players.Add(id, name);
  170. }
  171. _roundLogs = await count;
  172. _isLoading = false;
  173. StateDirty();
  174. }
  175. }