Skip to content

Commit b8165b3

Browse files
authored
Misc Polishing (#413)
* Ensure that after we assign a role to a user, we show it immediately * Cached libraryType api as that is not going to change in a viewing session. Moved some components around to tighten bundles. * Cleaned up more TODOs * Refactored Configuration to use getter and setters so that the interface is a lot cleaner. Updated HashUtil to use JWT Secret instead of Machine name (as docker machine name is random each boot).
1 parent ef5b22b commit b8165b3

29 files changed

+411
-310
lines changed

API/Controllers/SettingsController.cs

+13-21
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.IO;
44
using System.Linq;
55
using System.Threading.Tasks;
6-
using API.Data;
76
using API.DTOs;
87
using API.Entities.Enums;
98
using API.Extensions;
@@ -13,7 +12,6 @@
1312
using Kavita.Common.Extensions;
1413
using Microsoft.AspNetCore.Authorization;
1514
using Microsoft.AspNetCore.Mvc;
16-
using Microsoft.Extensions.Configuration;
1715
using Microsoft.Extensions.Logging;
1816

1917
namespace API.Controllers
@@ -24,26 +22,24 @@ public class SettingsController : BaseApiController
2422
private readonly ILogger<SettingsController> _logger;
2523
private readonly IUnitOfWork _unitOfWork;
2624
private readonly ITaskScheduler _taskScheduler;
27-
private readonly IConfiguration _configuration;
2825

29-
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler, IConfiguration configuration)
26+
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler)
3027
{
3128
_logger = logger;
3229
_unitOfWork = unitOfWork;
3330
_taskScheduler = taskScheduler;
34-
_configuration = configuration;
3531
}
3632

37-
[HttpGet("")]
33+
[HttpGet]
3834
public async Task<ActionResult<ServerSettingDto>> GetSettings()
3935
{
4036
var settingsDto = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
41-
settingsDto.Port = Configuration.GetPort(Program.GetAppSettingFilename());
42-
settingsDto.LoggingLevel = Configuration.GetLogLevel(Program.GetAppSettingFilename());
37+
settingsDto.Port = Configuration.Port;
38+
settingsDto.LoggingLevel = Configuration.LogLevel;
4339
return Ok(settingsDto);
4440
}
4541

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

64-
var logLevelOptions = new LogLevelOptions();
65-
_configuration.GetSection("Logging:LogLevel").Bind(logLevelOptions);
66-
6760
foreach (var setting in currentSettings)
6861
{
6962
if (setting.Key == ServerSettingKey.TaskBackup && updateSettingsDto.TaskBackup != setting.Value)
@@ -78,24 +71,24 @@ public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDt
7871
_unitOfWork.SettingsRepository.Update(setting);
7972
}
8073

81-
if (setting.Key == ServerSettingKey.Port && updateSettingsDto.Port + "" != setting.Value)
74+
if (setting.Key == ServerSettingKey.Port && updateSettingsDto.Port + string.Empty != setting.Value)
8275
{
83-
setting.Value = updateSettingsDto.Port + "";
76+
setting.Value = updateSettingsDto.Port + string.Empty;
8477
// Port is managed in appSetting.json
85-
Configuration.UpdatePort(Program.GetAppSettingFilename(), updateSettingsDto.Port);
78+
Configuration.Port = updateSettingsDto.Port;
8679
_unitOfWork.SettingsRepository.Update(setting);
8780
}
8881

89-
if (setting.Key == ServerSettingKey.LoggingLevel && updateSettingsDto.LoggingLevel + "" != setting.Value)
82+
if (setting.Key == ServerSettingKey.LoggingLevel && updateSettingsDto.LoggingLevel + string.Empty != setting.Value)
9083
{
91-
setting.Value = updateSettingsDto.LoggingLevel + "";
92-
Configuration.UpdateLogLevel(Program.GetAppSettingFilename(), updateSettingsDto.LoggingLevel);
84+
setting.Value = updateSettingsDto.LoggingLevel + string.Empty;
85+
Configuration.LogLevel = updateSettingsDto.LoggingLevel;
9386
_unitOfWork.SettingsRepository.Update(setting);
9487
}
9588

96-
if (setting.Key == ServerSettingKey.AllowStatCollection && updateSettingsDto.AllowStatCollection + "" != setting.Value)
89+
if (setting.Key == ServerSettingKey.AllowStatCollection && updateSettingsDto.AllowStatCollection + string.Empty != setting.Value)
9790
{
98-
setting.Value = updateSettingsDto.AllowStatCollection + "";
91+
setting.Value = updateSettingsDto.AllowStatCollection + string.Empty;
9992
_unitOfWork.SettingsRepository.Update(setting);
10093
if (!updateSettingsDto.AllowStatCollection)
10194
{
@@ -108,7 +101,6 @@ public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDt
108101
}
109102
}
110103

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

114106
if (!_unitOfWork.HasChanges() || !await _unitOfWork.CommitAsync())

API/Data/Seed.cs

+8-9
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ public static async Task SeedRoles(RoleManager<AppRole> roleManager)
2929
var exists = await roleManager.RoleExistsAsync(role.Name);
3030
if (!exists)
3131
{
32-
await roleManager.CreateAsync(role);
32+
await roleManager.CreateAsync(role);
3333
}
3434
}
3535
}
3636

3737
public static async Task SeedSettings(DataContext context)
3838
{
3939
await context.Database.EnsureCreatedAsync();
40-
40+
4141
IList<ServerSetting> defaultSettings = new List<ServerSetting>()
4242
{
4343
new() {Key = ServerSettingKey.CacheDirectory, Value = CacheService.CacheDirectory},
@@ -46,7 +46,7 @@ public static async Task SeedSettings(DataContext context)
4646
new () {Key = ServerSettingKey.TaskBackup, Value = "weekly"},
4747
new () {Key = ServerSettingKey.BackupDirectory, Value = Path.GetFullPath(Path.Join(Directory.GetCurrentDirectory(), "backups/"))},
4848
new () {Key = ServerSettingKey.Port, Value = "5000"}, // Not used from DB, but DB is sync with appSettings.json
49-
new () {Key = ServerSettingKey.AllowStatCollection, Value = "true"},
49+
new () {Key = ServerSettingKey.AllowStatCollection, Value = "true"},
5050
};
5151

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

6363
// Port and LoggingLevel are managed in appSettings.json. Update the DB values to match
64-
var configFile = Program.GetAppSettingFilename();
6564
context.ServerSetting.FirstOrDefault(s => s.Key == ServerSettingKey.Port).Value =
66-
Configuration.GetPort(configFile) + "";
65+
Configuration.Port + string.Empty;
6766
context.ServerSetting.FirstOrDefault(s => s.Key == ServerSettingKey.LoggingLevel).Value =
68-
Configuration.GetLogLevel(configFile);
69-
67+
Configuration.LogLevel + string.Empty;
68+
7069
await context.SaveChangesAsync();
71-
70+
7271
}
7372
}
74-
}
73+
}

API/Extensions/ApplicationServiceExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ private static void AddSqLite(this IServiceCollection services, IConfiguration c
4343
services.AddDbContext<DataContext>(options =>
4444
{
4545
options.UseSqlite(config.GetConnectionString("DefaultConnection"));
46-
options.EnableSensitiveDataLogging(env.IsDevelopment() || Configuration.GetLogLevel(Program.GetAppSettingFilename()).Equals("Debug"));
46+
options.EnableSensitiveDataLogging(env.IsDevelopment() || Configuration.LogLevel.Equals("Debug"));
4747
});
4848
}
4949

API/Program.cs

+109-124
Original file line numberDiff line numberDiff line change
@@ -18,132 +18,117 @@
1818

1919
namespace API
2020
{
21-
public class Program
22-
{
23-
private static int _httpPort;
24-
25-
protected Program()
26-
{
27-
}
28-
29-
public static string GetAppSettingFilename()
30-
{
31-
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
32-
var isDevelopment = environment == Environments.Development;
33-
return "appsettings" + (isDevelopment ? ".Development" : "") + ".json";
34-
}
35-
36-
public static async Task Main(string[] args)
37-
{
38-
Console.OutputEncoding = System.Text.Encoding.UTF8;
39-
40-
// Before anything, check if JWT has been generated properly or if user still has default
41-
if (!Configuration.CheckIfJwtTokenSet(GetAppSettingFilename()) && Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != Environments.Development)
42-
{
43-
Console.WriteLine("Generating JWT TokenKey for encrypting user sessions...");
44-
var rBytes = new byte[128];
45-
using (var crypto = new RNGCryptoServiceProvider()) crypto.GetBytes(rBytes);
46-
var base64 = Convert.ToBase64String(rBytes).Replace("/", "");
47-
Configuration.UpdateJwtToken(GetAppSettingFilename(), base64);
48-
}
49-
50-
// Get HttpPort from Config
51-
_httpPort = Configuration.GetPort(GetAppSettingFilename());
52-
53-
54-
var host = CreateHostBuilder(args).Build();
55-
56-
using var scope = host.Services.CreateScope();
57-
var services = scope.ServiceProvider;
58-
59-
try
21+
public class Program
22+
{
23+
private static readonly int HttpPort = Configuration.Port;
24+
25+
protected Program()
26+
{
27+
}
28+
29+
public static async Task Main(string[] args)
30+
{
31+
Console.OutputEncoding = System.Text.Encoding.UTF8;
32+
33+
// Before anything, check if JWT has been generated properly or if user still has default
34+
if (!Configuration.CheckIfJwtTokenSet() &&
35+
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") != Environments.Development)
36+
{
37+
Console.WriteLine("Generating JWT TokenKey for encrypting user sessions...");
38+
var rBytes = new byte[128];
39+
using (var crypto = new RNGCryptoServiceProvider()) crypto.GetBytes(rBytes);
40+
Configuration.JwtToken = Convert.ToBase64String(rBytes).Replace("/", string.Empty);
41+
}
42+
43+
var host = CreateHostBuilder(args).Build();
44+
45+
using var scope = host.Services.CreateScope();
46+
var services = scope.ServiceProvider;
47+
48+
try
49+
{
50+
var context = services.GetRequiredService<DataContext>();
51+
var roleManager = services.GetRequiredService<RoleManager<AppRole>>();
52+
// Apply all migrations on startup
53+
await context.Database.MigrateAsync();
54+
await Seed.SeedRoles(roleManager);
55+
await Seed.SeedSettings(context);
56+
}
57+
catch (Exception ex)
58+
{
59+
var logger = services.GetRequiredService<ILogger<Program>>();
60+
logger.LogError(ex, "An error occurred during migration");
61+
}
62+
63+
await host.RunAsync();
64+
}
65+
66+
private static IHostBuilder CreateHostBuilder(string[] args) =>
67+
Host.CreateDefaultBuilder(args)
68+
.ConfigureWebHostDefaults(webBuilder =>
6069
{
61-
var context = services.GetRequiredService<DataContext>();
62-
var roleManager = services.GetRequiredService<RoleManager<AppRole>>();
63-
// Apply all migrations on startup
64-
await context.Database.MigrateAsync();
65-
await Seed.SeedRoles(roleManager);
66-
await Seed.SeedSettings(context);
67-
}
68-
catch (Exception ex)
69-
{
70-
var logger = services.GetRequiredService <ILogger<Program>>();
71-
logger.LogError(ex, "An error occurred during migration");
72-
}
73-
74-
await host.RunAsync();
75-
}
76-
77-
private static IHostBuilder CreateHostBuilder(string[] args) =>
78-
Host.CreateDefaultBuilder(args)
79-
.ConfigureWebHostDefaults(webBuilder =>
80-
{
81-
webBuilder.UseKestrel((opts) =>
82-
{
83-
opts.ListenAnyIP(_httpPort, options =>
84-
{
85-
options.Protocols = HttpProtocols.Http1AndHttp2;
86-
});
87-
});
88-
89-
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
90-
if (environment != Environments.Development)
91-
{
92-
webBuilder.UseSentry(options =>
70+
webBuilder.UseKestrel((opts) =>
71+
{
72+
opts.ListenAnyIP(HttpPort, options => { options.Protocols = HttpProtocols.Http1AndHttp2; });
73+
});
74+
75+
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
76+
if (environment != Environments.Development)
77+
{
78+
webBuilder.UseSentry(options =>
79+
{
80+
options.Dsn = "https://[email protected]/5757423";
81+
options.MaxBreadcrumbs = 200;
82+
options.AttachStacktrace = true;
83+
options.Debug = false;
84+
options.SendDefaultPii = false;
85+
options.DiagnosticLevel = SentryLevel.Debug;
86+
options.ShutdownTimeout = TimeSpan.FromSeconds(5);
87+
options.Release = BuildInfo.Version.ToString();
88+
options.AddExceptionFilterForType<OutOfMemoryException>();
89+
options.AddExceptionFilterForType<NetVips.VipsException>();
90+
options.AddExceptionFilterForType<InvalidDataException>();
91+
options.AddExceptionFilterForType<KavitaException>();
92+
93+
options.BeforeSend = sentryEvent =>
94+
{
95+
if (sentryEvent.Exception != null
96+
&& sentryEvent.Exception.Message.StartsWith("[GetCoverImage]")
97+
&& sentryEvent.Exception.Message.StartsWith("[BookService]")
98+
&& sentryEvent.Exception.Message.StartsWith("[ExtractArchive]")
99+
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
100+
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
101+
&& sentryEvent.Exception.Message.StartsWith("[GetNumberOfPagesFromArchive]")
102+
&& sentryEvent.Exception.Message.Contains("EPUB parsing error")
103+
&& sentryEvent.Exception.Message.Contains("Unsupported EPUB version")
104+
&& sentryEvent.Exception.Message.Contains("Incorrect EPUB")
105+
&& sentryEvent.Exception.Message.Contains("Access is Denied"))
93106
{
94-
options.Dsn = "https://[email protected]/5757423";
95-
options.MaxBreadcrumbs = 200;
96-
options.AttachStacktrace = true;
97-
options.Debug = false;
98-
options.SendDefaultPii = false;
99-
options.DiagnosticLevel = SentryLevel.Debug;
100-
options.ShutdownTimeout = TimeSpan.FromSeconds(5);
101-
options.Release = BuildInfo.Version.ToString();
102-
options.AddExceptionFilterForType<OutOfMemoryException>();
103-
options.AddExceptionFilterForType<NetVips.VipsException>();
104-
options.AddExceptionFilterForType<InvalidDataException>();
105-
options.AddExceptionFilterForType<KavitaException>();
107+
return null; // Don't send this event to Sentry
108+
}
106109

107-
options.BeforeSend = sentryEvent =>
108-
{
109-
if (sentryEvent.Exception != null
110-
&& sentryEvent.Exception.Message.StartsWith("[GetCoverImage]")
111-
&& sentryEvent.Exception.Message.StartsWith("[BookService]")
112-
&& sentryEvent.Exception.Message.StartsWith("[ExtractArchive]")
113-
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
114-
&& sentryEvent.Exception.Message.StartsWith("[GetSummaryInfo]")
115-
&& sentryEvent.Exception.Message.StartsWith("[GetNumberOfPagesFromArchive]")
116-
&& sentryEvent.Exception.Message.Contains("EPUB parsing error")
117-
&& sentryEvent.Exception.Message.Contains("Unsupported EPUB version")
118-
&& sentryEvent.Exception.Message.Contains("Incorrect EPUB")
119-
&& sentryEvent.Exception.Message.Contains("Access is Denied"))
120-
{
121-
return null; // Don't send this event to Sentry
122-
}
110+
sentryEvent.ServerName = null; // Never send Server Name to Sentry
111+
return sentryEvent;
112+
};
123113

124-
sentryEvent.ServerName = null; // Never send Server Name to Sentry
125-
return sentryEvent;
126-
};
127-
128-
options.ConfigureScope(scope =>
129-
{
130-
scope.User = new User()
131-
{
132-
Id = HashUtil.AnonymousToken()
133-
};
134-
scope.Contexts.App.Name = BuildInfo.AppName;
135-
scope.Contexts.App.Version = BuildInfo.Version.ToString();
136-
scope.Contexts.App.StartTime = DateTime.UtcNow;
137-
scope.Contexts.App.Hash = HashUtil.AnonymousToken();
138-
scope.Contexts.App.Build = BuildInfo.Release;
139-
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
140-
scope.SetTag("branch", BuildInfo.Branch);
141-
});
142-
143-
});
144-
}
145-
146-
webBuilder.UseStartup<Startup>();
147-
});
148-
}
114+
options.ConfigureScope(scope =>
115+
{
116+
scope.User = new User()
117+
{
118+
Id = HashUtil.AnonymousToken()
119+
};
120+
scope.Contexts.App.Name = BuildInfo.AppName;
121+
scope.Contexts.App.Version = BuildInfo.Version.ToString();
122+
scope.Contexts.App.StartTime = DateTime.UtcNow;
123+
scope.Contexts.App.Hash = HashUtil.AnonymousToken();
124+
scope.Contexts.App.Build = BuildInfo.Release;
125+
scope.SetTag("culture", Thread.CurrentThread.CurrentCulture.Name);
126+
scope.SetTag("branch", BuildInfo.Branch);
127+
});
128+
});
129+
}
130+
131+
webBuilder.UseStartup<Startup>();
132+
});
133+
}
149134
}

0 commit comments

Comments
 (0)