Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0d1186b92 | ||
|
|
04ab4ee60f | ||
|
|
e62074cc26 |
@@ -42,8 +42,8 @@ jobs:
|
||||
release_name_cli="ErsatzTV.CommandLine-$tag-${{ matrix.target }}"
|
||||
|
||||
# Build everything
|
||||
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name"
|
||||
dotnet publish ErsatzTV.CommandLine/ErsatzTV.CommandLine.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name_cli"
|
||||
dotnet publish ErsatzTV/ErsatzTV.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name" /property:InformationalVersion="${tag:1}-${{ matrix.target }}"
|
||||
dotnet publish ErsatzTV.CommandLine/ErsatzTV.CommandLine.csproj --framework net5.0 --runtime "${{ matrix.target }}" -c Release -o "$release_name_cli" /property:InformationalVersion="${tag:1}-${{ matrix.target }}"
|
||||
|
||||
# Pack files
|
||||
if [ "${{ matrix.target }}" == "win-x64" ]; then
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<InformationalVersion>develop</InformationalVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
+2
-1
@@ -26,7 +26,8 @@ COPY ErsatzTV.Core/. ./ErsatzTV.Core/
|
||||
COPY ErsatzTV.Core.Tests/. ./ErsatzTV.Core.Tests/
|
||||
COPY ErsatzTV.Infrastructure/. ./ErsatzTV.Infrastructure/
|
||||
WORKDIR /source/ErsatzTV
|
||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore
|
||||
ARG INFO_VERSION="unknown"
|
||||
RUN dotnet publish -c release -o /app -r linux-x64 --self-contained false --no-restore /p:InformationalVersion=${INFO_VERSION}
|
||||
|
||||
# final stage/image
|
||||
FROM runtime-base
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace ErsatzTV.Application.Logs
|
||||
{
|
||||
public record LogEntryViewModel(
|
||||
int Id,
|
||||
DateTime Timestamp,
|
||||
string Level,
|
||||
string Exception,
|
||||
string RenderedMessage,
|
||||
string Properties);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Application.Logs
|
||||
{
|
||||
internal static class Mapper
|
||||
{
|
||||
internal static LogEntryViewModel ProjectToViewModel(LogEntry logEntry) =>
|
||||
new(
|
||||
logEntry.Id,
|
||||
logEntry.Timestamp,
|
||||
logEntry.Level,
|
||||
logEntry.Exception,
|
||||
logEntry.RenderedMessage,
|
||||
logEntry.Properties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MediatR;
|
||||
|
||||
namespace ErsatzTV.Application.Logs.Queries
|
||||
{
|
||||
public record GetRecentLogEntries : IRequest<List<LogEntryViewModel>>;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using LanguageExt;
|
||||
using MediatR;
|
||||
using static ErsatzTV.Application.Logs.Mapper;
|
||||
|
||||
namespace ErsatzTV.Application.Logs.Queries
|
||||
{
|
||||
public class GetRecentLogEntriesHandler : IRequestHandler<GetRecentLogEntries, List<LogEntryViewModel>>
|
||||
{
|
||||
private readonly ILogRepository _logRepository;
|
||||
|
||||
public GetRecentLogEntriesHandler(ILogRepository logRepository) => _logRepository = logRepository;
|
||||
|
||||
public Task<List<LogEntryViewModel>> Handle(GetRecentLogEntries request, CancellationToken cancellationToken) =>
|
||||
_logRepository.GetRecentLogEntries().Map(list => list.Map(ProjectToViewModel).ToList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace ErsatzTV.Core.Domain
|
||||
{
|
||||
public record LogEntry(
|
||||
int Id,
|
||||
DateTime Timestamp,
|
||||
string Level,
|
||||
string Exception,
|
||||
string RenderedMessage,
|
||||
string Properties);
|
||||
}
|
||||
@@ -13,6 +13,8 @@ namespace ErsatzTV.Core
|
||||
|
||||
public static readonly string DatabasePath = Path.Combine(AppDataFolder, "ersatztv.sqlite3");
|
||||
|
||||
public static readonly string LogDatabasePath = Path.Combine(AppDataFolder, "logs.sqlite3");
|
||||
|
||||
public static readonly string ImageCacheFolder = Path.Combine(AppDataFolder, "cache", "images");
|
||||
|
||||
public static readonly string PlexSecretsPath = Path.Combine(AppDataFolder, "plex-secrets.json");
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
|
||||
namespace ErsatzTV.Core.Interfaces.Repositories
|
||||
{
|
||||
public interface ILogRepository
|
||||
{
|
||||
public Task<List<LogEntry>> GetRecentLogEntries();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Data.Configurations
|
||||
{
|
||||
public class LogEntryConfiguration : IEntityTypeConfiguration<LogEntry>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<LogEntry> builder) =>
|
||||
builder.ToTable("Logs");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Data
|
||||
{
|
||||
public class LogContext : DbContext
|
||||
{
|
||||
public LogContext(DbContextOptions<LogContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<LogEntry> LogEntries { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
builder.ApplyConfigurationsFromAssembly(typeof(LogContext).Assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
{
|
||||
public class LogRepository : ILogRepository
|
||||
{
|
||||
private readonly LogContext _logContext;
|
||||
|
||||
public LogRepository(LogContext logContext) => _logContext = logContext;
|
||||
|
||||
public Task<List<LogEntry>> GetRecentLogEntries() =>
|
||||
_logContext.LogEntries.OrderByDescending(e => e.Id).Take(100).ToListAsync();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Sinks.SQLite" Version="5.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
@page "/system/logs"
|
||||
@using ErsatzTV.Application.Logs
|
||||
@using ErsatzTV.Application.Logs.Queries
|
||||
@inject IMediator Mediator
|
||||
|
||||
<MudTable FixedHeader="true" Dense="true" Items="_logEntries">
|
||||
<HeaderContent>
|
||||
<MudTh>Timestamp</MudTh>
|
||||
<MudTh>Level</MudTh>
|
||||
<MudTh>Message</MudTh>
|
||||
<MudTh>Properties</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Timestamp">@context.Timestamp</MudTd>
|
||||
<MudTd DataLabel="Level">@context.Level</MudTd>
|
||||
<MudTd DataLabel="Message">@context.RenderedMessage</MudTd>
|
||||
<MudTd DataLabel="Message">@context.Properties</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager/>
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
|
||||
@code {
|
||||
private List<LogEntryViewModel> _logEntries;
|
||||
|
||||
protected override async Task OnInitializedAsync() => _logEntries = await Mediator.Send(new GetRecentLogEntries());
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Extensions;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -25,6 +26,7 @@ namespace ErsatzTV
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(Configuration)
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.SQLite(FileSystemLayout.LogDatabasePath, retentionPeriod: TimeSpan.FromDays(1))
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@inherits LayoutComponentBase
|
||||
@using System.Reflection
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<MudThemeProvider Theme="_ersatzTvTheme"/>
|
||||
<MudDialogProvider DisableBackdropClick="true"/>
|
||||
@@ -32,6 +33,12 @@
|
||||
</MudNavGroup>
|
||||
<MudNavLink Href="/schedules">Schedules</MudNavLink>
|
||||
<MudNavLink Href="/playouts">Playouts</MudNavLink>
|
||||
<MudNavLink Href="/system/logs">Logs</MudNavLink>
|
||||
<MudDivider Class="my-6" DividerType="DividerType.Middle" />
|
||||
<MudContainer Style="text-align: right" Class="mr-6">
|
||||
<MudText Typo="Typo.body2">ErsatzTV Version</MudText>
|
||||
<MudText Typo="Typo.body2" Color="Color.Info">@InfoVersion</MudText>
|
||||
</MudContainer>
|
||||
</MudNavMenu>
|
||||
</MudDrawer>
|
||||
<MudMainContent>
|
||||
@@ -42,7 +49,9 @@
|
||||
</MudLayout>
|
||||
|
||||
@code {
|
||||
bool _drawerOpen = true;
|
||||
private static readonly string InfoVersion = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "unknown";
|
||||
|
||||
private bool _drawerOpen = true;
|
||||
|
||||
private void DrawerToggle() => _drawerOpen = !_drawerOpen;
|
||||
|
||||
|
||||
@@ -108,6 +108,9 @@ namespace ErsatzTV
|
||||
o.MigrationsAssembly("ErsatzTV.Infrastructure");
|
||||
}));
|
||||
|
||||
services.AddDbContext<LogContext>(
|
||||
options => options.UseSqlite($"Data Source={FileSystemLayout.LogDatabasePath}"));
|
||||
|
||||
services.AddMediatR(typeof(GetAllChannels).Assembly);
|
||||
|
||||
services.AddRefitClient<IPlexTvApi>()
|
||||
@@ -156,6 +159,7 @@ namespace ErsatzTV
|
||||
services.AddScoped<IConfigElementRepository, ConfigElementRepository>();
|
||||
services.AddScoped<IProgramScheduleRepository, ProgramScheduleRepository>();
|
||||
services.AddScoped<IPlayoutRepository, PlayoutRepository>();
|
||||
services.AddScoped<ILogRepository, LogRepository>();
|
||||
services.AddScoped<IFFmpegLocator, FFmpegLocator>();
|
||||
services.AddScoped<ISmartCollectionBuilder, SmartCollectionBuilder>();
|
||||
services.AddScoped<ILocalMetadataProvider, LocalMetadataProvider>();
|
||||
|
||||
@@ -4,6 +4,8 @@ services:
|
||||
ersatztv:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
INFO_VERSION: "docker-compose-develop"
|
||||
ports:
|
||||
- "8409:8409"
|
||||
volumes:
|
||||
|
||||
Reference in New Issue
Block a user