2019-01-25 21:33:58 +01:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
2016-11-18 09:39:20 +01:00
using System.IO ;
using System.Linq ;
2019-01-25 21:33:58 +01:00
using Emby.Server.Implementations.Data ;
using MediaBrowser.Controller ;
2016-11-18 09:39:20 +01:00
using MediaBrowser.Model.Activity ;
2019-01-25 21:33:58 +01:00
using MediaBrowser.Model.IO ;
using MediaBrowser.Model.Querying ;
using Microsoft.Extensions.Logging ;
using SQLitePCL.pretty ;
2016-11-18 09:39:20 +01:00
namespace Emby.Server.Implementations.Activity
{
2020-04-15 21:00:45 +02:00
/// <summary>
/// The activity log repository.
/// </summary>
2019-01-25 21:33:58 +01:00
public class ActivityRepository : BaseSqliteRepository , IActivityRepository
2016-11-18 09:39:20 +01:00
{
2020-04-11 12:03:10 +02:00
private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog" ;
2019-03-11 23:07:38 +01:00
private readonly IFileSystem _fileSystem ;
2016-11-18 09:39:20 +01:00
2020-04-14 21:04:54 +02:00
/// <summary>
/// Initializes a new instance of the <see cref="ActivityRepository"/> class.
/// </summary>
2020-04-22 15:44:20 +02:00
/// <param name="logger">The logger.</param>
2020-04-14 21:04:54 +02:00
/// <param name="appPaths">The server application paths.</param>
/// <param name="fileSystem">The filesystem.</param>
2020-04-11 12:03:10 +02:00
public ActivityRepository ( ILogger < ActivityRepository > logger , IServerApplicationPaths appPaths , IFileSystem fileSystem )
: base ( logger )
2019-01-25 21:33:58 +01:00
{
DbFilePath = Path . Combine ( appPaths . DataPath , "activitylog.db" ) ;
2019-03-11 23:07:38 +01:00
_fileSystem = fileSystem ;
2019-01-25 21:33:58 +01:00
}
2016-11-18 09:39:20 +01:00
2020-04-14 21:04:54 +02:00
/// <summary>
/// Initializes the <see cref="ActivityRepository"/>.
/// </summary>
2019-01-25 21:33:58 +01:00
public void Initialize ( )
2017-09-22 07:54:57 +02:00
{
2019-01-25 21:33:58 +01:00
try
{
InitializeInternal ( ) ;
}
catch ( Exception ex )
{
Logger . LogError ( ex , "Error loading database file. Will reset and retry." ) ;
2019-03-11 23:07:38 +01:00
_fileSystem . DeleteFile ( DbFilePath ) ;
2019-01-25 21:33:58 +01:00
InitializeInternal ( ) ;
}
2017-09-22 07:54:57 +02:00
}
2019-01-25 21:33:58 +01:00
private void InitializeInternal ( )
2016-11-18 09:39:20 +01:00
{
2020-04-14 21:04:54 +02:00
using var connection = GetConnection ( ) ;
connection . RunQueries ( new [ ]
2019-01-25 21:33:58 +01:00
{
2020-04-14 21:04:54 +02:00
"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)" ,
"drop index if exists idx_ActivityLogEntries"
} ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
TryMigrate ( connection ) ;
2019-01-25 21:33:58 +01:00
}
2016-11-18 09:39:20 +01:00
2019-02-26 18:30:13 +01:00
private void TryMigrate ( ManagedConnection connection )
2019-01-25 21:33:58 +01:00
{
try
{
if ( TableExists ( connection , "ActivityLogEntries" ) )
{
connection . RunQueries ( new [ ]
{
"INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries" ,
"drop table if exists ActivityLogEntries"
} ) ;
}
}
catch ( Exception ex )
{
Logger . LogError ( ex , "Error migrating activity log database" ) ;
}
2018-09-12 19:26:21 +02:00
}
2019-01-25 21:33:58 +01:00
private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog" ;
2020-04-14 21:04:54 +02:00
/// <inheritdoc />
2019-01-25 21:33:58 +01:00
public void Create ( ActivityLogEntry entry )
2016-11-18 09:39:20 +01:00
{
2019-01-25 21:33:58 +01:00
if ( entry = = null )
{
throw new ArgumentNullException ( nameof ( entry ) ) ;
}
2020-04-14 21:04:54 +02:00
using var connection = GetConnection ( ) ;
connection . RunInTransaction ( db = >
2019-01-25 21:33:58 +01:00
{
2020-04-14 21:04:54 +02:00
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)" ) ;
statement . TryBind ( "@Name" , entry . Name ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
statement . TryBind ( "@Overview" , entry . Overview ) ;
statement . TryBind ( "@ShortOverview" , entry . ShortOverview ) ;
statement . TryBind ( "@Type" , entry . Type ) ;
statement . TryBind ( "@ItemId" , entry . ItemId ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
if ( entry . UserId . Equals ( Guid . Empty ) )
{
statement . TryBindNull ( "@UserId" ) ;
}
else
{
statement . TryBind ( "@UserId" , entry . UserId . ToString ( "N" , CultureInfo . InvariantCulture ) ) ;
}
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
statement . TryBind ( "@DateCreated" , entry . Date . ToDateTimeParamValue ( ) ) ;
statement . TryBind ( "@LogSeverity" , entry . Severity . ToString ( ) ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
statement . MoveNext ( ) ;
} , TransactionMode ) ;
2019-01-25 21:33:58 +01:00
}
2020-04-14 21:04:54 +02:00
/// <summary>
/// Adds the provided <see cref="ActivityLogEntry"/> to this repository.
/// </summary>
/// <param name="entry">The activity log entry.</param>
/// <exception cref="ArgumentNullException">If entry is null.</exception>
2019-01-25 21:33:58 +01:00
public void Update ( ActivityLogEntry entry )
{
if ( entry = = null )
{
throw new ArgumentNullException ( nameof ( entry ) ) ;
}
2020-04-14 21:04:54 +02:00
using var connection = GetConnection ( ) ;
connection . RunInTransaction ( db = >
2019-01-25 21:33:58 +01:00
{
2020-04-14 21:04:54 +02:00
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" ) ;
statement . TryBind ( "@Id" , entry . Id ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
statement . TryBind ( "@Name" , entry . Name ) ;
statement . TryBind ( "@Overview" , entry . Overview ) ;
statement . TryBind ( "@ShortOverview" , entry . ShortOverview ) ;
statement . TryBind ( "@Type" , entry . Type ) ;
statement . TryBind ( "@ItemId" , entry . ItemId ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
if ( entry . UserId . Equals ( Guid . Empty ) )
{
statement . TryBindNull ( "@UserId" ) ;
}
else
{
statement . TryBind ( "@UserId" , entry . UserId . ToString ( "N" , CultureInfo . InvariantCulture ) ) ;
}
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
statement . TryBind ( "@DateCreated" , entry . Date . ToDateTimeParamValue ( ) ) ;
statement . TryBind ( "@LogSeverity" , entry . Severity . ToString ( ) ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
statement . MoveNext ( ) ;
} , TransactionMode ) ;
2019-01-25 21:33:58 +01:00
}
2020-04-14 21:04:54 +02:00
/// <inheritdoc />
2019-01-25 21:33:58 +01:00
public QueryResult < ActivityLogEntry > GetActivityLogEntries ( DateTime ? minDate , bool? hasUserId , int? startIndex , int? limit )
{
2019-08-18 22:05:06 +02:00
var commandText = BaseActivitySelectText ;
var whereClauses = new List < string > ( ) ;
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
if ( minDate . HasValue )
{
whereClauses . Add ( "DateCreated>=@DateCreated" ) ;
}
2020-04-14 21:04:54 +02:00
2019-08-18 22:05:06 +02:00
if ( hasUserId . HasValue )
{
2020-04-14 21:04:54 +02:00
whereClauses . Add ( hasUserId . Value ? "UserId not null" : "UserId is null" ) ;
2019-08-18 22:05:06 +02:00
}
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
var whereTextWithoutPaging = whereClauses . Count = = 0 ?
string . Empty :
" where " + string . Join ( " AND " , whereClauses . ToArray ( ) ) ;
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
if ( startIndex . HasValue & & startIndex . Value > 0 )
{
var pagingWhereText = whereClauses . Count = = 0 ?
2019-01-25 21:33:58 +01:00
string . Empty :
" where " + string . Join ( " AND " , whereClauses . ToArray ( ) ) ;
2019-08-18 22:05:06 +02:00
whereClauses . Add (
string . Format (
CultureInfo . InvariantCulture ,
"Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})" ,
pagingWhereText ,
startIndex . Value ) ) ;
}
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
var whereText = whereClauses . Count = = 0 ?
string . Empty :
" where " + string . Join ( " AND " , whereClauses . ToArray ( ) ) ;
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
commandText + = whereText ;
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
commandText + = " ORDER BY DateCreated DESC" ;
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
if ( limit . HasValue )
{
2020-04-11 12:03:10 +02:00
commandText + = " LIMIT " + limit . Value . ToString ( CultureInfo . InvariantCulture ) ;
2019-08-18 22:05:06 +02:00
}
var statementTexts = new [ ]
{
commandText ,
"select count (Id) from ActivityLog" + whereTextWithoutPaging
} ;
2019-01-25 21:33:58 +01:00
2019-08-18 22:05:06 +02:00
var list = new List < ActivityLogEntry > ( ) ;
var result = new QueryResult < ActivityLogEntry > ( ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
using var connection = GetConnection ( true ) ;
connection . RunInTransaction (
db = >
{
var statements = PrepareAll ( db , statementTexts ) . ToList ( ) ;
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
using ( var statement = statements [ 0 ] )
{
if ( minDate . HasValue )
2019-01-25 21:33:58 +01:00
{
2020-04-14 21:04:54 +02:00
statement . TryBind ( "@DateCreated" , minDate . Value . ToDateTimeParamValue ( ) ) ;
2019-01-25 21:33:58 +01:00
}
2020-04-14 21:04:54 +02:00
list . AddRange ( statement . ExecuteQuery ( ) . Select ( GetEntry ) ) ;
}
2019-01-25 21:33:58 +01:00
2020-04-14 21:04:54 +02:00
using ( var statement = statements [ 1 ] )
{
if ( minDate . HasValue )
{
statement . TryBind ( "@DateCreated" , minDate . Value . ToDateTimeParamValue ( ) ) ;
2019-08-18 22:05:06 +02:00
}
2020-04-14 21:04:54 +02:00
result . TotalRecordCount = statement . ExecuteQuery ( ) . SelectScalarInt ( ) . First ( ) ;
}
} ,
ReadTransactionMode ) ;
2019-08-18 22:05:06 +02:00
2019-09-02 08:19:29 +02:00
result . Items = list ;
2019-08-18 22:05:06 +02:00
return result ;
2016-11-18 09:39:20 +01:00
}
2019-01-25 21:33:58 +01:00
private static ActivityLogEntry GetEntry ( IReadOnlyList < IResultSetValue > reader )
{
var index = 0 ;
var info = new ActivityLogEntry
{
Id = reader [ index ] . ToInt64 ( )
} ;
index + + ;
if ( reader [ index ] . SQLiteType ! = SQLiteType . Null )
{
info . Name = reader [ index ] . ToString ( ) ;
}
index + + ;
if ( reader [ index ] . SQLiteType ! = SQLiteType . Null )
{
info . Overview = reader [ index ] . ToString ( ) ;
}
index + + ;
if ( reader [ index ] . SQLiteType ! = SQLiteType . Null )
{
info . ShortOverview = reader [ index ] . ToString ( ) ;
}
index + + ;
if ( reader [ index ] . SQLiteType ! = SQLiteType . Null )
{
info . Type = reader [ index ] . ToString ( ) ;
}
index + + ;
if ( reader [ index ] . SQLiteType ! = SQLiteType . Null )
{
info . ItemId = reader [ index ] . ToString ( ) ;
}
index + + ;
if ( reader [ index ] . SQLiteType ! = SQLiteType . Null )
{
info . UserId = new Guid ( reader [ index ] . ToString ( ) ) ;
}
index + + ;
info . Date = reader [ index ] . ReadDateTime ( ) ;
index + + ;
if ( reader [ index ] . SQLiteType ! = SQLiteType . Null )
{
2020-04-11 12:03:10 +02:00
info . Severity = Enum . Parse < LogLevel > ( reader [ index ] . ToString ( ) , true ) ;
2019-01-25 21:33:58 +01:00
}
return info ;
}
2016-11-18 09:39:20 +01:00
}
}