Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misc Polishing #413

Merged
merged 6 commits into from
Jul 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 13 additions & 21 deletions API/Controllers/SettingsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using API.Data;
using API.DTOs;
using API.Entities.Enums;
using API.Extensions;
Expand All @@ -13,7 +12,6 @@
using Kavita.Common.Extensions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace API.Controllers
Expand All @@ -24,26 +22,24 @@ public class SettingsController : BaseApiController
private readonly ILogger<SettingsController> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ITaskScheduler _taskScheduler;
private readonly IConfiguration _configuration;

public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler, IConfiguration configuration)
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler)
{
_logger = logger;
_unitOfWork = unitOfWork;
_taskScheduler = taskScheduler;
_configuration = configuration;
}

[HttpGet("")]
[HttpGet]
public async Task<ActionResult<ServerSettingDto>> GetSettings()
{
var settingsDto = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
settingsDto.Port = Configuration.GetPort(Program.GetAppSettingFilename());
settingsDto.LoggingLevel = Configuration.GetLogLevel(Program.GetAppSettingFilename());
settingsDto.Port = Configuration.Port;
settingsDto.LoggingLevel = Configuration.LogLevel;
return Ok(settingsDto);
}

[HttpPost("")]
[HttpPost]
public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDto updateSettingsDto)
{
_logger.LogInformation("{UserName} is updating Server Settings", User.GetUsername());
Expand All @@ -61,9 +57,6 @@ public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDt
// We do not allow CacheDirectory changes, so we will ignore.
var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync();

var logLevelOptions = new LogLevelOptions();
_configuration.GetSection("Logging:LogLevel").Bind(logLevelOptions);

foreach (var setting in currentSettings)
{
if (setting.Key == ServerSettingKey.TaskBackup && updateSettingsDto.TaskBackup != setting.Value)
Expand All @@ -78,24 +71,24 @@ public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDt
_unitOfWork.SettingsRepository.Update(setting);
}

if (setting.Key == ServerSettingKey.Port && updateSettingsDto.Port + "" != setting.Value)
if (setting.Key == ServerSettingKey.Port && updateSettingsDto.Port + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.Port + "";
setting.Value = updateSettingsDto.Port + string.Empty;
// Port is managed in appSetting.json
Configuration.UpdatePort(Program.GetAppSettingFilename(), updateSettingsDto.Port);
Configuration.Port = updateSettingsDto.Port;
_unitOfWork.SettingsRepository.Update(setting);
}

if (setting.Key == ServerSettingKey.LoggingLevel && updateSettingsDto.LoggingLevel + "" != setting.Value)
if (setting.Key == ServerSettingKey.LoggingLevel && updateSettingsDto.LoggingLevel + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.LoggingLevel + "";
Configuration.UpdateLogLevel(Program.GetAppSettingFilename(), updateSettingsDto.LoggingLevel);
setting.Value = updateSettingsDto.LoggingLevel + string.Empty;
Configuration.LogLevel = updateSettingsDto.LoggingLevel;
_unitOfWork.SettingsRepository.Update(setting);
}

if (setting.Key == ServerSettingKey.AllowStatCollection && updateSettingsDto.AllowStatCollection + "" != setting.Value)
if (setting.Key == ServerSettingKey.AllowStatCollection && updateSettingsDto.AllowStatCollection + string.Empty != setting.Value)
{
setting.Value = updateSettingsDto.AllowStatCollection + "";
setting.Value = updateSettingsDto.AllowStatCollection + string.Empty;
_unitOfWork.SettingsRepository.Update(setting);
if (!updateSettingsDto.AllowStatCollection)
{
Expand All @@ -108,7 +101,6 @@ public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDt
}
}

_configuration.GetSection("Logging:LogLevel:Default").Value = updateSettingsDto.LoggingLevel + "";
if (!_unitOfWork.HasChanges()) return Ok("Nothing was updated");

if (!_unitOfWork.HasChanges() || !await _unitOfWork.CommitAsync())
Expand Down
17 changes: 8 additions & 9 deletions API/Data/Seed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ public static async Task SeedRoles(RoleManager<AppRole> roleManager)
var exists = await roleManager.RoleExistsAsync(role.Name);
if (!exists)
{
await roleManager.CreateAsync(role);
await roleManager.CreateAsync(role);
}
}
}

public static async Task SeedSettings(DataContext context)
{
await context.Database.EnsureCreatedAsync();

IList<ServerSetting> defaultSettings = new List<ServerSetting>()
{
new() {Key = ServerSettingKey.CacheDirectory, Value = CacheService.CacheDirectory},
Expand All @@ -46,7 +46,7 @@ public static async Task SeedSettings(DataContext context)
new () {Key = ServerSettingKey.TaskBackup, Value = "weekly"},
new () {Key = ServerSettingKey.BackupDirectory, Value = Path.GetFullPath(Path.Join(Directory.GetCurrentDirectory(), "backups/"))},
new () {Key = ServerSettingKey.Port, Value = "5000"}, // Not used from DB, but DB is sync with appSettings.json
new () {Key = ServerSettingKey.AllowStatCollection, Value = "true"},
new () {Key = ServerSettingKey.AllowStatCollection, Value = "true"},
};

foreach (var defaultSetting in defaultSettings)
Expand All @@ -61,14 +61,13 @@ public static async Task SeedSettings(DataContext context)
await context.SaveChangesAsync();

// Port and LoggingLevel are managed in appSettings.json. Update the DB values to match
var configFile = Program.GetAppSettingFilename();
context.ServerSetting.FirstOrDefault(s => s.Key == ServerSettingKey.Port).Value =
Configuration.GetPort(configFile) + "";
Configuration.Port + string.Empty;
context.ServerSetting.FirstOrDefault(s => s.Key == ServerSettingKey.LoggingLevel).Value =
Configuration.GetLogLevel(configFile);
Configuration.LogLevel + string.Empty;

await context.SaveChangesAsync();

}
}
}
}
2 changes: 1 addition & 1 deletion API/Extensions/ApplicationServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private static void AddSqLite(this IServiceCollection services, IConfiguration c
services.AddDbContext<DataContext>(options =>
{
options.UseSqlite(config.GetConnectionString("DefaultConnection"));
options.EnableSensitiveDataLogging(env.IsDevelopment() || Configuration.GetLogLevel(Program.GetAppSettingFilename()).Equals("Debug"));
options.EnableSensitiveDataLogging(env.IsDevelopment() || Configuration.LogLevel.Equals("Debug"));
});
}

Expand Down
233 changes: 109 additions & 124 deletions API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,132 +18,117 @@

namespace API
{
public class Program
{
private static int _httpPort;

protected Program()
{
}

public static string GetAppSettingFilename()
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var isDevelopment = environment == Environments.Development;
return "appsettings" + (isDevelopment ? ".Development" : "") + ".json";
}

public static async Task Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;

// Before anything, check if JWT has been generated properly or if user still has default
if (!Configuration.CheckIfJwtTokenSet(GetAppSettingFilename()) && Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != Environments.Development)
{
Console.WriteLine("Generating JWT TokenKey for encrypting user sessions...");
var rBytes = new byte[128];
using (var crypto = new RNGCryptoServiceProvider()) crypto.GetBytes(rBytes);
var base64 = Convert.ToBase64String(rBytes).Replace("/", "");
Configuration.UpdateJwtToken(GetAppSettingFilename(), base64);
}

// Get HttpPort from Config
_httpPort = Configuration.GetPort(GetAppSettingFilename());


var host = CreateHostBuilder(args).Build();

using var scope = host.Services.CreateScope();
var services = scope.ServiceProvider;

try
public class Program
{
private static readonly int HttpPort = Configuration.Port;

protected Program()
{
}

public static async Task Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;

// Before anything, check if JWT has been generated properly or if user still has default
if (!Configuration.CheckIfJwtTokenSet() &&
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != Environments.Development)
{
Console.WriteLine("Generating JWT TokenKey for encrypting user sessions...");
var rBytes = new byte[128];
using (var crypto = new RNGCryptoServiceProvider()) crypto.GetBytes(rBytes);
Configuration.JwtToken = Convert.ToBase64String(rBytes).Replace("/", string.Empty);
}

var host = CreateHostBuilder(args).Build();

using var scope = host.Services.CreateScope();
var services = scope.ServiceProvider;

try
{
var context = services.GetRequiredService<DataContext>();
var roleManager = services.GetRequiredService<RoleManager<AppRole>>();
// Apply all migrations on startup
await context.Database.MigrateAsync();
await Seed.SeedRoles(roleManager);
await Seed.SeedSettings(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred during migration");
}

await host.RunAsync();
}

private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
var context = services.GetRequiredService<DataContext>();
var roleManager = services.GetRequiredService<RoleManager<AppRole>>();
// Apply all migrations on startup
await context.Database.MigrateAsync();
await Seed.SeedRoles(roleManager);
await Seed.SeedSettings(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService <ILogger<Program>>();
logger.LogError(ex, "An error occurred during migration");
}

await host.RunAsync();
}

private static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel((opts) =>
{
opts.ListenAnyIP(_httpPort, options =>
{
options.Protocols = HttpProtocols.Http1AndHttp2;
});
});

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (environment != Environments.Development)
{
webBuilder.UseSentry(options =>
webBuilder.UseKestrel((opts) =>
{
opts.ListenAnyIP(HttpPort, options => { options.Protocols = HttpProtocols.Http1AndHttp2; });
});

var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
if (environment != Environments.Development)
{
webBuilder.UseSentry(options =>
{
options.Dsn = "https://[email protected]/5757423";
options.MaxBreadcrumbs = 200;
options.AttachStacktrace = true;
options.Debug = false;
options.SendDefaultPii = false;
options.DiagnosticLevel = SentryLevel.Debug;
options.ShutdownTimeout = TimeSpan.FromSeconds(5);
options.Release = BuildInfo.Version.ToString();
options.AddExceptionFilterForType<OutOfMemoryException>();
options.AddExceptionFilterForType<NetVips.VipsException>();
options.AddExceptionFilterForType<InvalidDataException>();
options.AddExceptionFilterForType<KavitaException>();

options.BeforeSend = sentryEvent =>
{
if (sentryEvent.Exception != null
&& sentryEvent.Exception.Message.StartsWith("[GetCoverImage]")
&& sentryEvent.Exception.Message.StartsWith("[BookService]")
&& sentryEvent.Exception.Message.StartsWith("[ExtractArchive]")
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
&& sentryEvent.Exception.Message.StartsWith("[GetNumberOfPagesFromArchive]")
&& sentryEvent.Exception.Message.Contains("EPUB parsing error")
&& sentryEvent.Exception.Message.Contains("Unsupported EPUB version")
&& sentryEvent.Exception.Message.Contains("Incorrect EPUB")
&& sentryEvent.Exception.Message.Contains("Access is Denied"))
{
options.Dsn = "https://[email protected]/5757423";
options.MaxBreadcrumbs = 200;
options.AttachStacktrace = true;
options.Debug = false;
options.SendDefaultPii = false;
options.DiagnosticLevel = SentryLevel.Debug;
options.ShutdownTimeout = TimeSpan.FromSeconds(5);
options.Release = BuildInfo.Version.ToString();
options.AddExceptionFilterForType<OutOfMemoryException>();
options.AddExceptionFilterForType<NetVips.VipsException>();
options.AddExceptionFilterForType<InvalidDataException>();
options.AddExceptionFilterForType<KavitaException>();
return null; // Don't send this event to Sentry
}

options.BeforeSend = sentryEvent =>
{
if (sentryEvent.Exception != null
&& sentryEvent.Exception.Message.StartsWith("[GetCoverImage]")
&& sentryEvent.Exception.Message.StartsWith("[BookService]")
&& sentryEvent.Exception.Message.StartsWith("[ExtractArchive]")
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
&& sentryEvent.Exception.Message.StartsWith("[GetNumberOfPagesFromArchive]")
&& sentryEvent.Exception.Message.Contains("EPUB parsing error")
&& sentryEvent.Exception.Message.Contains("Unsupported EPUB version")
&& sentryEvent.Exception.Message.Contains("Incorrect EPUB")
&& sentryEvent.Exception.Message.Contains("Access is Denied"))
{
return null; // Don't send this event to Sentry
}
sentryEvent.ServerName = null; // Never send Server Name to Sentry
return sentryEvent;
};

sentryEvent.ServerName = null; // Never send Server Name to Sentry
return sentryEvent;
};

options.ConfigureScope(scope =>
{
scope.User = new User()
{
Id = HashUtil.AnonymousToken()
};
scope.Contexts.App.Name = BuildInfo.AppName;
scope.Contexts.App.Version = BuildInfo.Version.ToString();
scope.Contexts.App.StartTime = DateTime.UtcNow;
scope.Contexts.App.Hash = HashUtil.AnonymousToken();
scope.Contexts.App.Build = BuildInfo.Release;
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
scope.SetTag("branch", BuildInfo.Branch);
});

});
}

webBuilder.UseStartup<Startup>();
});
}
options.ConfigureScope(scope =>
{
scope.User = new User()
{
Id = HashUtil.AnonymousToken()
};
scope.Contexts.App.Name = BuildInfo.AppName;
scope.Contexts.App.Version = BuildInfo.Version.ToString();
scope.Contexts.App.StartTime = DateTime.UtcNow;
scope.Contexts.App.Hash = HashUtil.AnonymousToken();
scope.Contexts.App.Build = BuildInfo.Release;
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
scope.SetTag("branch", BuildInfo.Branch);
});
});
}

webBuilder.UseStartup<Startup>();
});
}
}
Loading