Merge pull request #5541 from duplicati/feature/fix-swagger-output
Fix Swagger exposed API.
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
|
||||
namespace Duplicati.WebserverCore;
|
||||
|
||||
//Useful for debugging ASP.net magically loading controllers
|
||||
public class ApplicationPartsLogger(ILogger<ApplicationPartsLogger> logger, ApplicationPartManager partManager)
|
||||
: IHostedService
|
||||
{
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Get the names of all the application parts. This is the short assembly name for AssemblyParts
|
||||
var applicationParts = partManager.ApplicationParts.Select(x => x.Name);
|
||||
|
||||
// Create a controller feature, and populate it from the application parts
|
||||
var controllerFeature = new ControllerFeature();
|
||||
partManager.PopulateFeature(controllerFeature);
|
||||
|
||||
// Get the names of all of the controllers
|
||||
var controllers = controllerFeature.Controllers.Select(x => x.Name);
|
||||
|
||||
// Log the application parts and controllers
|
||||
logger.LogInformation(
|
||||
"Found the following application parts: '{ApplicationParts}' with the following controllers: '{Controllers}'",
|
||||
string.Join(", ", applicationParts), string.Join(", ", controllers));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Required by the interface
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Duplicati.Server.Database;
|
||||
@@ -9,6 +9,7 @@ using Duplicati.WebserverCore.Middlewares;
|
||||
using Duplicati.WebserverCore.Services;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http.Json;
|
||||
using Microsoft.Extensions.Configuration.Json;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
@@ -70,24 +71,13 @@ public partial class DuplicatiWebserver
|
||||
});
|
||||
});
|
||||
|
||||
//builder.Host.UseRESTHandlers();
|
||||
builder.Services.ConfigureHttpJsonOptions(opt =>
|
||||
builder.Services.Configure<JsonOptions>(opt =>
|
||||
{
|
||||
opt.SerializerOptions.PropertyNamingPolicy = null;
|
||||
opt.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
opt.SerializerOptions.Converters.Add(new DayOfWeekStringEnumConverter());
|
||||
opt.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
});
|
||||
|
||||
builder.Services.AddControllers()
|
||||
// This app gets launched by a different assembly, so we need to tell it to look in this one
|
||||
.AddApplicationPart(GetType().Assembly)
|
||||
.AddJsonOptions(opt =>
|
||||
{
|
||||
opt.JsonSerializerOptions.PropertyNamingPolicy = null;
|
||||
opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
opt.JsonSerializerOptions.Converters.Add(new DayOfWeekStringEnumConverter());
|
||||
});
|
||||
|
||||
// Generate JWTConfig with signing key if not present
|
||||
if (string.IsNullOrWhiteSpace(connection.ApplicationSettings.JWTConfig))
|
||||
connection.ApplicationSettings.JWTConfig = JsonSerializer.Serialize(JWTConfig.Create());
|
||||
@@ -96,7 +86,6 @@ public partial class DuplicatiWebserver
|
||||
?? throw new Exception("Failed to deserialize JWTConfig");
|
||||
|
||||
builder.Services
|
||||
.AddHostedService<ApplicationPartsLogger>()
|
||||
#if DEBUG
|
||||
.AddEndpointsApiExplorer()
|
||||
.AddSwaggerGen(c =>
|
||||
@@ -115,6 +104,7 @@ public partial class DuplicatiWebserver
|
||||
.AddHttpContextAccessor()
|
||||
.AddSingleton<IHostnameValidator>(new HostnameValidator(settings.AllowedHostnames))
|
||||
.AddSingleton(jwtConfig)
|
||||
.AddAuthorization()
|
||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
@@ -163,6 +153,9 @@ public partial class DuplicatiWebserver
|
||||
App = builder.Build();
|
||||
Provider = App.Services;
|
||||
|
||||
App.UseAuthentication();
|
||||
App.UseAuthorization();
|
||||
|
||||
#if DEBUG
|
||||
App.UseSwagger();
|
||||
App.UseSwaggerUI(c =>
|
||||
|
||||
@@ -14,24 +14,9 @@ public class BackupPutDelete : IEndpointV1
|
||||
{
|
||||
public static void Map(RouteGroupBuilder group)
|
||||
{
|
||||
// TODO: Figure out why the JSON deserialization is not working here
|
||||
// group.MapPut("/backup/{id}", ([FromServices] Connection connection, [FromRoute] string id, [FromBody] Dto.BackupAndScheduleInputDto input)
|
||||
// => ExecutePut(connection, input);
|
||||
|
||||
group.MapPut("/backup/{id}", async ([FromServices] Connection connection, [FromServices] IHttpContextAccessor httpContextAccessor, [FromRoute] string id) =>
|
||||
{
|
||||
var opts = new JsonSerializerOptions()
|
||||
{
|
||||
Converters = { new DayOfWeekStringEnumConverter() }
|
||||
};
|
||||
|
||||
var input = (await JsonSerializer.DeserializeAsync<Dto.BackupAndScheduleInputDto>(httpContextAccessor.HttpContext!.Request.Body, opts))
|
||||
?? throw new BadRequestException("No data found in request body");
|
||||
|
||||
ExecutePut(GetBackup(connection, id), connection, input);
|
||||
})
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapPut("/backup/{id}", ([FromServices] Connection connection, [FromRoute] string id, [FromBody] Dto.BackupAndScheduleInputDto input)
|
||||
=> ExecutePut(GetBackup(connection, id), connection, input))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapDelete("/backup/{id}", ([FromServices] Connection connection, [FromServices] IWorkerThreadsManager workerThreadsManager, [FromServices] ICaptchaProvider captchaProvider, [FromServices] LiveControls liveControls, [FromServices] IHttpContextAccessor httpContextAccessor, [FromRoute] string id, [FromQuery(Name = "delete-remote-files")] bool? delete_remote_files, [FromQuery(Name = "delete-local-db")] bool? delete_local_db, [FromQuery(Name = "captcha-token")] string? captcha_token, [FromQuery(Name = "captcha-answer")] string? captcha_answer, [FromQuery] bool? force) =>
|
||||
{
|
||||
@@ -41,7 +26,6 @@ public class BackupPutDelete : IEndpointV1
|
||||
return res;
|
||||
})
|
||||
.RequireAuthorization();
|
||||
|
||||
}
|
||||
|
||||
private static IBackup GetBackup(Connection connection, string id)
|
||||
|
||||
@@ -16,20 +16,8 @@ public class Backups : IEndpointV1
|
||||
=> ExecuteGet(connection))
|
||||
.RequireAuthorization();
|
||||
|
||||
// TODO: Figure out why the JSON deserialization is not working here
|
||||
// group.MapPost("/backups", ([FromServices] Connection connection, [FromBody] Dto.BackupAndScheduleInputDto input, [FromQuery] bool? temporary, [FromQuery] bool? existingdb)
|
||||
// => ExecuteAdd(connection, input, temporary ?? false, existingdb ?? false)).RequireAuthorization();
|
||||
|
||||
group.MapPost("/backups", async ([FromServices] Connection connection, [FromQuery] bool? temporary, [FromQuery] bool? existingdb, [FromServices] IHttpContextAccessor httpContextAccessor) =>
|
||||
{
|
||||
var opts = new JsonSerializerOptions()
|
||||
{
|
||||
Converters = { new DayOfWeekStringEnumConverter() }
|
||||
};
|
||||
var input = await JsonSerializer.DeserializeAsync<Dto.BackupAndScheduleInputDto>(httpContextAccessor.HttpContext!.Request.Body, opts)
|
||||
?? throw new BadRequestException("No data found in request body");
|
||||
return ExecuteAdd(connection, input, temporary ?? false, existingdb ?? false);
|
||||
})
|
||||
group.MapPost("/backups", ([FromServices] Connection connection, [FromBody] Dto.BackupAndScheduleInputDto input, [FromQuery] bool? temporary, [FromQuery] bool? existingdb)
|
||||
=> ExecuteAdd(connection, input, temporary ?? false, existingdb ?? false))
|
||||
.RequireAuthorization();
|
||||
|
||||
group.MapPost("/backups/import", ([FromBody] Dto.ImportBackupInputDto input, [FromServices] IJWTTokenProvider jWTTokenProvider, [FromServices] Connection connection, [FromServices] IHttpContextAccessor httpContextAccessor) =>
|
||||
|
||||
@@ -18,17 +18,13 @@ public static class WebApplicationExtensions
|
||||
typeof(WebApplicationExtensions).Assembly.DefinedTypes
|
||||
.Where(t => t.ImplementedInterfaces.Contains(mapperInterfaceType))
|
||||
.ToArray();
|
||||
if (endpoints.Length == 0)
|
||||
{
|
||||
return application;
|
||||
}
|
||||
|
||||
var group = application.MapGroup("/api/v1")
|
||||
.AddEndpointFilter<LanguageFilter>()
|
||||
.AddEndpointFilter<HostnameFilter>();
|
||||
|
||||
foreach (var endpoint in endpoints)
|
||||
{
|
||||
var group = application.MapGroup("/api/v1")
|
||||
.AddEndpointFilter<LanguageFilter>()
|
||||
.AddEndpointFilter<HostnameFilter>();
|
||||
|
||||
var methodMap = endpoint.GetMethod(nameof(IEndpointV1.Map), BindingFlags.Static | BindingFlags.Public);
|
||||
methodMap!.Invoke(null, [group]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user