2
0

ActivityRepository.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. #pragma warning disable CS1591
  2. #pragma warning disable SA1600
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.IO;
  7. using System.Linq;
  8. using Emby.Server.Implementations.Data;
  9. using MediaBrowser.Controller;
  10. using MediaBrowser.Model.Activity;
  11. using MediaBrowser.Model.IO;
  12. using MediaBrowser.Model.Querying;
  13. using Microsoft.Extensions.Logging;
  14. using SQLitePCL.pretty;
  15. namespace Emby.Server.Implementations.Activity
  16. {
  17. public class ActivityRepository : BaseSqliteRepository, IActivityRepository
  18. {
  19. private static readonly CultureInfo _usCulture = CultureInfo.ReadOnly(new CultureInfo("en-US"));
  20. private readonly IFileSystem _fileSystem;
  21. public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem)
  22. : base(loggerFactory.CreateLogger(nameof(ActivityRepository)))
  23. {
  24. DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
  25. _fileSystem = fileSystem;
  26. }
  27. public void Initialize()
  28. {
  29. try
  30. {
  31. InitializeInternal();
  32. }
  33. catch (Exception ex)
  34. {
  35. Logger.LogError(ex, "Error loading database file. Will reset and retry.");
  36. _fileSystem.DeleteFile(DbFilePath);
  37. InitializeInternal();
  38. }
  39. }
  40. private void InitializeInternal()
  41. {
  42. using (var connection = GetConnection())
  43. {
  44. connection.RunQueries(new[]
  45. {
  46. "create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
  47. "drop index if exists idx_ActivityLogEntries"
  48. });
  49. TryMigrate(connection);
  50. }
  51. }
  52. private void TryMigrate(ManagedConnection connection)
  53. {
  54. try
  55. {
  56. if (TableExists(connection, "ActivityLogEntries"))
  57. {
  58. connection.RunQueries(new[]
  59. {
  60. "INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries",
  61. "drop table if exists ActivityLogEntries"
  62. });
  63. }
  64. }
  65. catch (Exception ex)
  66. {
  67. Logger.LogError(ex, "Error migrating activity log database");
  68. }
  69. }
  70. private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
  71. public void Create(ActivityLogEntry entry)
  72. {
  73. if (entry == null)
  74. {
  75. throw new ArgumentNullException(nameof(entry));
  76. }
  77. using (var connection = GetConnection())
  78. {
  79. connection.RunInTransaction(db =>
  80. {
  81. using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
  82. {
  83. statement.TryBind("@Name", entry.Name);
  84. statement.TryBind("@Overview", entry.Overview);
  85. statement.TryBind("@ShortOverview", entry.ShortOverview);
  86. statement.TryBind("@Type", entry.Type);
  87. statement.TryBind("@ItemId", entry.ItemId);
  88. if (entry.UserId.Equals(Guid.Empty))
  89. {
  90. statement.TryBindNull("@UserId");
  91. }
  92. else
  93. {
  94. statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
  95. }
  96. statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
  97. statement.TryBind("@LogSeverity", entry.Severity.ToString());
  98. statement.MoveNext();
  99. }
  100. }, TransactionMode);
  101. }
  102. }
  103. public void Update(ActivityLogEntry entry)
  104. {
  105. if (entry == null)
  106. {
  107. throw new ArgumentNullException(nameof(entry));
  108. }
  109. using (var connection = GetConnection())
  110. {
  111. connection.RunInTransaction(db =>
  112. {
  113. using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"))
  114. {
  115. statement.TryBind("@Id", entry.Id);
  116. statement.TryBind("@Name", entry.Name);
  117. statement.TryBind("@Overview", entry.Overview);
  118. statement.TryBind("@ShortOverview", entry.ShortOverview);
  119. statement.TryBind("@Type", entry.Type);
  120. statement.TryBind("@ItemId", entry.ItemId);
  121. if (entry.UserId.Equals(Guid.Empty))
  122. {
  123. statement.TryBindNull("@UserId");
  124. }
  125. else
  126. {
  127. statement.TryBind("@UserId", entry.UserId.ToString("N", CultureInfo.InvariantCulture));
  128. }
  129. statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
  130. statement.TryBind("@LogSeverity", entry.Severity.ToString());
  131. statement.MoveNext();
  132. }
  133. }, TransactionMode);
  134. }
  135. }
  136. public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
  137. {
  138. var commandText = BaseActivitySelectText;
  139. var whereClauses = new List<string>();
  140. if (minDate.HasValue)
  141. {
  142. whereClauses.Add("DateCreated>=@DateCreated");
  143. }
  144. if (hasUserId.HasValue)
  145. {
  146. if (hasUserId.Value)
  147. {
  148. whereClauses.Add("UserId not null");
  149. }
  150. else
  151. {
  152. whereClauses.Add("UserId is null");
  153. }
  154. }
  155. var whereTextWithoutPaging = whereClauses.Count == 0 ?
  156. string.Empty :
  157. " where " + string.Join(" AND ", whereClauses.ToArray());
  158. if (startIndex.HasValue && startIndex.Value > 0)
  159. {
  160. var pagingWhereText = whereClauses.Count == 0 ?
  161. string.Empty :
  162. " where " + string.Join(" AND ", whereClauses.ToArray());
  163. whereClauses.Add(
  164. string.Format(
  165. CultureInfo.InvariantCulture,
  166. "Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
  167. pagingWhereText,
  168. startIndex.Value));
  169. }
  170. var whereText = whereClauses.Count == 0 ?
  171. string.Empty :
  172. " where " + string.Join(" AND ", whereClauses.ToArray());
  173. commandText += whereText;
  174. commandText += " ORDER BY DateCreated DESC";
  175. if (limit.HasValue)
  176. {
  177. commandText += " LIMIT " + limit.Value.ToString(_usCulture);
  178. }
  179. var statementTexts = new[]
  180. {
  181. commandText,
  182. "select count (Id) from ActivityLog" + whereTextWithoutPaging
  183. };
  184. var list = new List<ActivityLogEntry>();
  185. var result = new QueryResult<ActivityLogEntry>();
  186. using (var connection = GetConnection(true))
  187. {
  188. connection.RunInTransaction(
  189. db =>
  190. {
  191. var statements = PrepareAll(db, statementTexts).ToList();
  192. using (var statement = statements[0])
  193. {
  194. if (minDate.HasValue)
  195. {
  196. statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
  197. }
  198. foreach (var row in statement.ExecuteQuery())
  199. {
  200. list.Add(GetEntry(row));
  201. }
  202. }
  203. using (var statement = statements[1])
  204. {
  205. if (minDate.HasValue)
  206. {
  207. statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
  208. }
  209. result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
  210. }
  211. },
  212. ReadTransactionMode);
  213. }
  214. result.Items = list;
  215. return result;
  216. }
  217. private static ActivityLogEntry GetEntry(IReadOnlyList<IResultSetValue> reader)
  218. {
  219. var index = 0;
  220. var info = new ActivityLogEntry
  221. {
  222. Id = reader[index].ToInt64()
  223. };
  224. index++;
  225. if (reader[index].SQLiteType != SQLiteType.Null)
  226. {
  227. info.Name = reader[index].ToString();
  228. }
  229. index++;
  230. if (reader[index].SQLiteType != SQLiteType.Null)
  231. {
  232. info.Overview = reader[index].ToString();
  233. }
  234. index++;
  235. if (reader[index].SQLiteType != SQLiteType.Null)
  236. {
  237. info.ShortOverview = reader[index].ToString();
  238. }
  239. index++;
  240. if (reader[index].SQLiteType != SQLiteType.Null)
  241. {
  242. info.Type = reader[index].ToString();
  243. }
  244. index++;
  245. if (reader[index].SQLiteType != SQLiteType.Null)
  246. {
  247. info.ItemId = reader[index].ToString();
  248. }
  249. index++;
  250. if (reader[index].SQLiteType != SQLiteType.Null)
  251. {
  252. info.UserId = new Guid(reader[index].ToString());
  253. }
  254. index++;
  255. info.Date = reader[index].ReadDateTime();
  256. index++;
  257. if (reader[index].SQLiteType != SQLiteType.Null)
  258. {
  259. info.Severity = (LogLevel)Enum.Parse(typeof(LogLevel), reader[index].ToString(), true);
  260. }
  261. return info;
  262. }
  263. }
  264. }