refactor(api): inject IEmailSyncService into SyncController; change OAuth2 authorize route to username param; add Swagger UI metadata and description links
This commit is contained in:
@@ -20,20 +20,20 @@ public class OAuth2Controller(IMediator mediator, IHttpContextAccessor httpConte
|
|||||||
/// Step 1: Redirects the user to Google's consent screen for the specified email account.
|
/// Step 1: Redirects the user to Google's consent screen for the specified email account.
|
||||||
/// After consent, Google redirects to <c>/api/oauth2/google/callback</c> with an authorization code.
|
/// After consent, Google redirects to <c>/api/oauth2/google/callback</c> with an authorization code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="accountId">ID of the email account to authorize.</param>
|
/// <param name="username">the email account to authorize.</param>
|
||||||
/// <param name="cancellationToken">Cancellation token.</param>
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
/// <returns>HTTP 302 redirect to Google consent screen.</returns>
|
/// <returns>HTTP 302 redirect to Google consent screen.</returns>
|
||||||
[HttpGet("google/authorize/{accountId:int}")]
|
[HttpGet("google/authorize/{username}")]
|
||||||
[ProducesResponseType(StatusCodes.Status302Found)]
|
[ProducesResponseType(StatusCodes.Status302Found)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> AuthorizeGoogle(int accountId, CancellationToken cancellationToken)
|
public async Task<IActionResult> AuthorizeGoogle([FromRoute] string username, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var redirectUri = BuildCallbackUri();
|
var redirectUri = BuildCallbackUri();
|
||||||
|
|
||||||
var authUrl = await mediator.Send(new GetOAuth2AuthorizationUrlQuery
|
var authUrl = await mediator.Send(new GetOAuth2AuthorizationUrlQuery
|
||||||
{
|
{
|
||||||
AccountId = accountId,
|
Username = username,
|
||||||
RedirectUri = redirectUri
|
RedirectUri = redirectUri
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using DigitalData.MessagingService.Infrastructure.Services.Background;
|
using DigitalData.MessagingService.Application.Common.Interfaces;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
namespace DigitalData.MessagingService.API.Controllers;
|
namespace DigitalData.MessagingService.API.Controllers;
|
||||||
|
|
||||||
@@ -9,7 +8,7 @@ namespace DigitalData.MessagingService.API.Controllers;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class SyncController(IEnumerable<IHostedService> hostedServices) : ControllerBase
|
public class SyncController(IEmailSyncService emailSyncService) : ControllerBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Triggers an immediate email sync cycle for all configured accounts,
|
/// Triggers an immediate email sync cycle for all configured accounts,
|
||||||
@@ -22,14 +21,7 @@ public class SyncController(IEnumerable<IHostedService> hostedServices) : Contro
|
|||||||
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
|
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
|
||||||
public IActionResult TriggerSync()
|
public IActionResult TriggerSync()
|
||||||
{
|
{
|
||||||
var syncWorker = hostedServices.OfType<EmailSyncWorker>().FirstOrDefault();
|
var syncTime = emailSyncService.ForceTriggerSync();
|
||||||
|
return Accepted(new { syncTime });
|
||||||
if (syncWorker is null)
|
|
||||||
return StatusCode(StatusCodes.Status503ServiceUnavailable,
|
|
||||||
new { Message = "EmailSyncWorker is not running." });
|
|
||||||
|
|
||||||
syncWorker.TriggerSync();
|
|
||||||
|
|
||||||
return Accepted(new { Message = "Sync triggered. The next cycle will start immediately." });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,35 @@ try
|
|||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
|
||||||
|
{
|
||||||
|
Title = "DigitalData MessagingService API",
|
||||||
|
Version = "v1",
|
||||||
|
Description = """
|
||||||
|
Die **DigitalData MessagingService API** stellt Endpunkte zur Verwaltung von E-Mail-Konten,
|
||||||
|
E-Mail-Profilen sowie zur Verarbeitung und Nachverfolgung eingehender und ausgehender Nachrichten bereit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Authentifizierung
|
||||||
|
|
||||||
|
Für OAuth2-geschützte Endpunkte ist eine Authentifizierung erforderlich.
|
||||||
|
Rufen Sie den folgenden Endpunkt auf und ersetzen Sie `{E-Mail-Adresse}` durch die zu authentifizierende E-Mail-Adresse:
|
||||||
|
|
||||||
|
`/api/OAuth2/google/authorize/{E-Mail-Adresse}`
|
||||||
|
|
||||||
|
**Beispiel:** **[/api/OAuth2/google/authorize/htek0100@gmail.com →](/api/OAuth2/google/authorize/htek0100@gmail.com)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Weiterführende Links
|
||||||
|
|
||||||
|
- [Serilog Log-Viewer ↗](/serilog-ui)
|
||||||
|
"""
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Required by OAuth2Controller to build callback URIs
|
// Required by OAuth2Controller to build callback URIs
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
@@ -92,14 +120,30 @@ try
|
|||||||
// Add Serilog request logging
|
// Add Serilog request logging
|
||||||
app.UseSerilogRequestLogging();
|
app.UseSerilogRequestLogging();
|
||||||
|
|
||||||
// Configure Swagger — enabled in Development always, and in other environments based on appsettings
|
// Configure Swagger <EFBFBD> enabled in Development always, and in other environments based on appsettings
|
||||||
var swaggerEnabled = app.Environment.IsDevelopment()
|
var swaggerEnabled = app.Environment.IsDevelopment()
|
||||||
|| app.Configuration.GetValue<bool>("Swagger:Enabled");
|
|| app.Configuration.GetValue<bool>("Swagger:Enabled");
|
||||||
|
|
||||||
if (swaggerEnabled)
|
if (swaggerEnabled)
|
||||||
{
|
{
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI(ui =>
|
||||||
|
{
|
||||||
|
ui.SwaggerEndpoint("/swagger/v1/swagger.json", "DigitalData MessagingService API v1");
|
||||||
|
// Inject CSS so all description links open in a new tab
|
||||||
|
ui.InjectStylesheet("data:text/css,.renderedMarkdown a{target:_blank}");
|
||||||
|
ui.InjectJavascript("data:text/javascript," + Uri.EscapeDataString("""
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
document.querySelectorAll('.renderedMarkdown a').forEach(a => {
|
||||||
|
a.setAttribute('target', '_blank');
|
||||||
|
a.setAttribute('rel', 'noopener noreferrer');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
});
|
||||||
|
"""));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|||||||
Reference in New Issue
Block a user