Moved BaseUrl from appsettings.json to Database and fixed an issue in UI for setting base url based on a hack, rather than asking backend for it. (#644)

This commit is contained in:
Joseph Milazzo 2021-10-06 11:03:14 -07:00 committed by GitHub
parent e8e838d125
commit 977e364d5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 52 additions and 26 deletions

View File

@ -33,6 +33,14 @@ namespace API.Controllers
_accountService = accountService; _accountService = accountService;
} }
[AllowAnonymous]
[HttpGet("base-url")]
public async Task<ActionResult<string>> GetBaseUrl()
{
var settingsDto = await _unitOfWork.SettingsRepository.GetSettingsDtoAsync();
return Ok(settingsDto.BaseUrl);
}
[Authorize(Policy = "RequireAdminRole")] [Authorize(Policy = "RequireAdminRole")]
[HttpGet] [HttpGet]
public async Task<ActionResult<ServerSettingDto>> GetSettings() public async Task<ActionResult<ServerSettingDto>> GetSettings()
@ -91,8 +99,6 @@ namespace API.Controllers
? $"/{updateSettingsDto.BaseUrl}" ? $"/{updateSettingsDto.BaseUrl}"
: updateSettingsDto.BaseUrl; : updateSettingsDto.BaseUrl;
setting.Value = path; setting.Value = path;
// BaseUrl is managed in appSetting.json
Configuration.BaseUrl = updateSettingsDto.BaseUrl;
_unitOfWork.SettingsRepository.Update(setting); _unitOfWork.SettingsRepository.Update(setting);
} }

View File

@ -27,8 +27,8 @@
/// </summary> /// </summary>
public bool EnableAuthentication { get; set; } public bool EnableAuthentication { get; set; }
/// <summary> /// <summary>
/// Base Url for the kavita. Defaults to "/". Managed in appsettings.json.Requires restart to take effect. /// Base Url for the kavita. Requires restart to take effect.
/// </summary> /// </summary>
public string BaseUrl { get; set; } = "/"; public string BaseUrl { get; set; }
} }
} }

View File

@ -35,6 +35,15 @@ namespace API.Data.Repositories
return _mapper.Map<ServerSettingDto>(settings); return _mapper.Map<ServerSettingDto>(settings);
} }
public ServerSettingDto GetSettingsDto()
{
var settings = _context.ServerSetting
.Select(x => x)
.AsNoTracking()
.ToList();
return _mapper.Map<ServerSettingDto>(settings);
}
public Task<ServerSetting> GetSettingAsync(ServerSettingKey key) public Task<ServerSetting> GetSettingAsync(ServerSettingKey key)
{ {
return _context.ServerSetting.SingleOrDefaultAsync(x => x.Key == key); return _context.ServerSetting.SingleOrDefaultAsync(x => x.Key == key);

View File

@ -50,7 +50,7 @@ namespace API.Data
new () {Key = ServerSettingKey.AllowStatCollection, Value = "true"}, new () {Key = ServerSettingKey.AllowStatCollection, Value = "true"},
new () {Key = ServerSettingKey.EnableOpds, Value = "false"}, new () {Key = ServerSettingKey.EnableOpds, Value = "false"},
new () {Key = ServerSettingKey.EnableAuthentication, Value = "true"}, new () {Key = ServerSettingKey.EnableAuthentication, Value = "true"},
new () {Key = ServerSettingKey.BaseUrl, Value = ""},// Not used from DB, but DB is sync with appSettings.json new () {Key = ServerSettingKey.BaseUrl, Value = "/"},
}; };
foreach (var defaultSetting in defaultSettings) foreach (var defaultSetting in defaultSettings)
@ -64,20 +64,11 @@ namespace API.Data
await context.SaveChangesAsync(); await context.SaveChangesAsync();
if (string.IsNullOrEmpty(Configuration.BaseUrl))
{
Configuration.BaseUrl = "/";
}
// Port and LoggingLevel are managed in appSettings.json. Update the DB values to match // Port and LoggingLevel are managed in appSettings.json. Update the DB values to match
context.ServerSetting.First(s => s.Key == ServerSettingKey.Port).Value = context.ServerSetting.First(s => s.Key == ServerSettingKey.Port).Value =
Configuration.Port + string.Empty; Configuration.Port + string.Empty;
context.ServerSetting.First(s => s.Key == ServerSettingKey.LoggingLevel).Value = context.ServerSetting.First(s => s.Key == ServerSettingKey.LoggingLevel).Value =
Configuration.LogLevel + string.Empty; Configuration.LogLevel + string.Empty;
context.ServerSetting.First(s => s.Key == ServerSettingKey.BaseUrl).Value =
Configuration.BaseUrl;
await context.SaveChangesAsync(); await context.SaveChangesAsync();

View File

@ -10,6 +10,7 @@ namespace API.Interfaces.Repositories
{ {
void Update(ServerSetting settings); void Update(ServerSetting settings);
Task<ServerSettingDto> GetSettingsDtoAsync(); Task<ServerSettingDto> GetSettingsDtoAsync();
ServerSettingDto GetSettingsDto();
Task<ServerSetting> GetSettingAsync(ServerSettingKey key); Task<ServerSetting> GetSettingAsync(ServerSettingKey key);
Task<IEnumerable<ServerSetting>> GetSettingsAsync(); Task<IEnumerable<ServerSetting>> GetSettingsAsync();

View File

@ -5,6 +5,8 @@ using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using API.Extensions; using API.Extensions;
using API.Interfaces;
using API.Interfaces.Repositories;
using API.Middleware; using API.Middleware;
using API.Services; using API.Services;
using API.Services.HostedServices; using API.Services.HostedServices;
@ -121,7 +123,7 @@ namespace API
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env, public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env,
IHostApplicationLifetime applicationLifetime) IHostApplicationLifetime applicationLifetime, IServiceProvider serviceProvider)
{ {
app.UseMiddleware<ExceptionMiddleware>(); app.UseMiddleware<ExceptionMiddleware>();
@ -160,7 +162,9 @@ namespace API
app.UseDefaultFiles(); app.UseDefaultFiles();
if (!string.IsNullOrEmpty(Configuration.BaseUrl)) var service = serviceProvider.GetRequiredService<IUnitOfWork>();
var settings = service.SettingsRepository.GetSettingsDto();
if (!string.IsNullOrEmpty(settings.BaseUrl) && !settings.BaseUrl.Equals("/"))
{ {
var path = !Configuration.BaseUrl.StartsWith("/") var path = !Configuration.BaseUrl.StartsWith("/")
? $"/{Configuration.BaseUrl}" ? $"/{Configuration.BaseUrl}"

View File

@ -0,0 +1,10 @@
/**
* This is for base url only. Not to be used my applicaiton, only loading and bootstrapping app
*/
export class ConfigData {
baseUrl: string = '/';
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
}

View File

@ -31,6 +31,8 @@ import { CardsModule } from './cards/cards.module';
import { CollectionsModule } from './collections/collections.module'; import { CollectionsModule } from './collections/collections.module';
import { ReadingListModule } from './reading-list/reading-list.module'; import { ReadingListModule } from './reading-list/reading-list.module';
import { SAVER, getSaver } from './shared/_providers/saver.provider'; import { SAVER, getSaver } from './shared/_providers/saver.provider';
import { ConfigData } from './_models/config-data';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -83,7 +85,7 @@ import { SAVER, getSaver } from './shared/_providers/saver.provider';
{provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true},
Title, Title,
{provide: SAVER, useFactory: getSaver}, {provide: SAVER, useFactory: getSaver},
{ provide: APP_BASE_HREF, useValue: window['_app_base' as keyof Window] || '/' }, { provide: APP_BASE_HREF, useFactory: (config: ConfigData) => config.baseUrl, deps: [ConfigData] },
], ],
entryComponents: [], entryComponents: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@ -40,9 +40,4 @@
<app-root></app-root> <app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript> <noscript>Please enable JavaScript to continue using this application.</noscript>
</body> </body>
<script>
(function() {
window['_app_base'] = '/' + window.location.pathname.split('/')[1];
})();
</script>
</html> </html>

View File

@ -2,13 +2,21 @@ import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module'; import { AppModule } from './app/app.module';
import { ConfigData } from './app/_models/config-data';
import { environment } from './environments/environment'; import { environment } from './environments/environment';
if (environment.production) { if (environment.production) {
enableProdMode(); enableProdMode();
} }
platformBrowserDynamic().bootstrapModule(AppModule) function fetchConfig(): Promise<ConfigData> {
return fetch(environment.apiUrl + 'settings/base-url')
.then(response => response.text())
.then(response => new ConfigData(response));
}
fetchConfig().then(config => {
platformBrowserDynamic([ { provide: ConfigData, useValue: config } ])
.bootstrapModule(AppModule)
.catch(err => console.error(err)); .catch(err => console.error(err));
});