fix(backend): sanitize exception disclosure in changed API endpoints

Replace raw exception-message disclosure (ex.Message) returned to API
callers with a stable sanitized public message plus correlated structured
internal logging, across the endpoints changed in this PR.

- Add SanitizedErrors helper: logs the original exception at Error with a
  generated correlation id and returns a stable public message referencing
  it so support can trace without exposing internals.
- Inject ILogger<T> into the 14 changed controllers and route every
  ex.Message/dbex.Message disclosure through the helper, preserving status
  codes, response shapes, and business data (e.g. OpenWorkOrders).
- Leave FluentValidation (vex.Errors) and existing fixed-message catches
  untouched; out-of-scope controllers (Account/Contact/Employee/Asset/
  PMSchedule) are unchanged.
- Add focused tests proving internal exception text is not returned and
  that Error logging carrying the original exception is invoked.
This commit is contained in:
Alexandre Brandizzi 2026-07-24 14:14:39 -03:00
parent 6f6c7e04cd
commit 32de36f78c
26 changed files with 386 additions and 99 deletions

View file

@ -4,6 +4,7 @@ using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -20,7 +21,7 @@ public class AuthenticationControllerTests
private static AuthenticationController NewController(Mock<IAuthenticationService> service, string? userId = null) private static AuthenticationController NewController(Mock<IAuthenticationService> service, string? userId = null)
{ {
var controller = new AuthenticationController(service.Object); var controller = new AuthenticationController(service.Object, Mock.Of<ILogger<AuthenticationController>>());
if (userId != null) if (userId != null)
{ {
controller.ControllerContext = new ControllerContext controller.ControllerContext = new ControllerContext
@ -82,6 +83,35 @@ public class AuthenticationControllerTests
result.Should().BeOfType<UnauthorizedResult>(); result.Should().BeOfType<UnauthorizedResult>();
} }
[Fact]
public async Task Login_WhenServiceThrows_ReturnsSanitized500AndLogsInternally()
{
var service = new Mock<IAuthenticationService>();
service.Setup(s => s.LoginAsync(It.IsAny<string?>(), It.IsAny<string?>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new InvalidOperationException("SECRET-internal-stack-detail"));
var logger = new Mock<ILogger<AuthenticationController>>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var controller = new AuthenticationController(service.Object, logger.Object);
var result = await controller.Login(new LoginModel { Username = "x", Password = "y" }, CancellationToken.None);
var status = result.Should().BeOfType<ObjectResult>().Subject;
status.StatusCode.Should().Be(500);
var message = (string)status.Value!;
message.Should().NotContain("SECRET");
message.Should().NotContain("internal-stack-detail");
message.Should().Contain("reference");
logger.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.Is<Exception>(ex => ex.Message.Contains("SECRET")),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
[Fact] [Fact]
public async Task ChangePassword_Success_ReturnsSuccessMessageWithTrailingSpace() public async Task ChangePassword_Success_ReturnsSuccessMessageWithTrailingSpace()
{ {

View file

@ -4,6 +4,7 @@ using Api.SeaHavenIndustries.DTOs;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -26,7 +27,7 @@ public class CalendarControllerTests
}; };
private static CalendarController NewController(Mock<ICalendarService> service) => private static CalendarController NewController(Mock<ICalendarService> service) =>
new(service.Object); new(service.Object, Mock.Of<ILogger<CalendarController>>());
private static object Prop(object source, string name) => private static object Prop(object source, string name) =>
source.GetType().GetProperty(name)!.GetValue(source)!; source.GetType().GetProperty(name)!.GetValue(source)!;
@ -45,17 +46,32 @@ public class CalendarControllerTests
} }
[Fact] [Fact]
public async Task AddEvent_WhenServiceThrows_ReturnsBadRequestWithMessage() public async Task AddEvent_WhenServiceThrows_ReturnsSanitizedMessageAndLogsInternally()
{ {
var service = new Mock<ICalendarService>(); var service = new Mock<ICalendarService>();
service.Setup(s => s.CreateEventAsync(It.IsAny<EditEventInput>(), It.IsAny<CancellationToken>())) service.Setup(s => s.CreateEventAsync(It.IsAny<EditEventInput>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new InvalidOperationException("boom")); .ThrowsAsync(new InvalidOperationException("SECRET-internal-db-connection-leak"));
var result = await NewController(service).AddEvent(ValidDto(), CancellationToken.None); var logger = new Mock<ILogger<CalendarController>>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var controller = new CalendarController(service.Object, logger.Object);
var result = await controller.AddEvent(ValidDto(), CancellationToken.None);
var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject; var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject;
var response = bad.Value.Should().BeOfType<Response>().Subject; var response = bad.Value.Should().BeOfType<Response>().Subject;
response.Message.Should().Be("boom"); response.Status.Should().Be("Error");
response.Message.Should().NotContain("SECRET");
response.Message.Should().NotContain("internal-db-connection-leak");
response.Message.Should().Contain("reference");
logger.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.Is<Exception>(ex => ex.Message.Contains("SECRET")),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
} }
[Fact] [Fact]

View file

@ -5,6 +5,7 @@ using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -16,7 +17,7 @@ public class FollowUpControllerTests
{ {
private static FollowUpController NewController(Mock<IFollowUpService> service) private static FollowUpController NewController(Mock<IFollowUpService> service)
{ {
var controller = new FollowUpController(service.Object) var controller = new FollowUpController(service.Object, Mock.Of<ILogger<FollowUpController>>())
{ {
ControllerContext = new ControllerContext ControllerContext = new ControllerContext
{ {
@ -33,7 +34,7 @@ public class FollowUpControllerTests
} }
private static FollowUpController NewControllerNoUser(Mock<IFollowUpService> service) => private static FollowUpController NewControllerNoUser(Mock<IFollowUpService> service) =>
new(service.Object) new(service.Object, Mock.Of<ILogger<FollowUpController>>())
{ {
ControllerContext = new ControllerContext ControllerContext = new ControllerContext
{ {

View file

@ -4,6 +4,7 @@ using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -14,7 +15,7 @@ namespace Api.SeaHavenIndustries.Tests;
public class LocationControllerTests public class LocationControllerTests
{ {
private static LocationController NewController(Mock<ILocationService> service) => private static LocationController NewController(Mock<ILocationService> service) =>
new(service.Object); new(service.Object, Mock.Of<ILogger<LocationController>>());
private static object Prop(object source, string name) => private static object Prop(object source, string name) =>
source.GetType().GetProperty(name)!.GetValue(source)!; source.GetType().GetProperty(name)!.GetValue(source)!;

View file

@ -0,0 +1,71 @@
using Api.SeaHavenIndustries.Helper;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace Api.SeaHavenIndustries.Tests;
public class SanitizedErrorsTests
{
[Fact]
public void Sanitize_ReturnsStableMessageAndNeverExposesExceptionText()
{
var logger = new Mock<ILogger>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var message = logger.Object.Sanitize(new InvalidOperationException("SUPER-SECRET-internal-detail"));
message.Should().NotContain("SUPER-SECRET");
message.Should().NotContain("internal-detail");
message.Should().StartWith("An unexpected error occurred");
message.Should().Contain("reference");
}
[Fact]
public void Sanitize_UsesProvidedPublicMessageInsteadOfExceptionText()
{
var logger = new Mock<ILogger>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var message = logger.Object.Sanitize(
new InvalidOperationException("SUPER-SECRET-internal-detail"),
"Vendor not found");
message.Should().Contain("Vendor not found");
message.Should().NotContain("SUPER-SECRET");
message.Should().Contain("reference");
}
[Fact]
public void Sanitize_LogsOriginalExceptionAtErrorLevel()
{
var logger = new Mock<ILogger>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var exception = new InvalidOperationException("SUPER-SECRET-internal-detail");
logger.Object.Sanitize(exception);
logger.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.Is<Exception>(ex => ex == exception),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
[Fact]
public void Sanitize_ProducesUniqueCorrelationReferencesAcrossCalls()
{
var logger = new Mock<ILogger>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var exception = new InvalidOperationException("detail");
var first = logger.Object.Sanitize(exception);
var second = logger.Object.Sanitize(exception);
first.Should().NotBe(second);
}
}

View file

@ -4,6 +4,7 @@ using Api.SeaHavenIndustries.Helper.ViewModel;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -14,7 +15,7 @@ namespace Api.SeaHavenIndustries.Tests;
public class SettingsControllerTests public class SettingsControllerTests
{ {
private static SettingsController NewController(Mock<ISettingsService> service) => private static SettingsController NewController(Mock<ISettingsService> service) =>
new(service.Object); new(service.Object, Mock.Of<ILogger<SettingsController>>());
[Fact] [Fact]
public async Task GetCategoryList_ReturnsPaginationEnvelopeWithServiceItems() public async Task GetCategoryList_ReturnsPaginationEnvelopeWithServiceItems()

View file

@ -3,6 +3,7 @@ using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -15,7 +16,7 @@ public class UpliftControllerTests
{ {
private static UpliftController NewController(Mock<IUpliftService> service, params string[] roles) private static UpliftController NewController(Mock<IUpliftService> service, params string[] roles)
{ {
var controller = new UpliftController(service.Object); var controller = new UpliftController(service.Object, Mock.Of<ILogger<UpliftController>>());
var claims = new List<Claim> { new(ClaimTypes.NameIdentifier, "42") }; var claims = new List<Claim> { new(ClaimTypes.NameIdentifier, "42") };
claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r))); claims.AddRange(roles.Select(r => new Claim(ClaimTypes.Role, r)));
controller.ControllerContext = new ControllerContext controller.ControllerContext = new ControllerContext
@ -66,6 +67,41 @@ public class UpliftControllerTests
nf.Value.Should().BeOfType<Response>(); nf.Value.Should().BeOfType<Response>();
} }
[Fact]
public async Task Approve_Forbidden_ReturnsSanitized403AndLogsInternally()
{
var service = new Mock<IUpliftService>();
service.Setup(x => x.ApproveAsync(It.IsAny<ClaimsPrincipal>(), 5, null, It.IsAny<CancellationToken>()))
.ThrowsAsync(new UpliftForbiddenException("SECRET-tier-restriction-detail"));
var logger = new Mock<ILogger<UpliftController>>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var controller = new UpliftController(service.Object, logger.Object);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "42") }, "Test"))
}
};
var result = await controller.Approve(5, null);
var status = result.Should().BeOfType<ObjectResult>().Subject;
status.StatusCode.Should().Be(403);
var response = status.Value.Should().BeOfType<Response>().Subject;
response.Message.Should().NotContain("SECRET");
response.Message.Should().Contain("reference");
logger.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.Is<Exception>(ex => ex.Message.Contains("SECRET")),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
[Fact] [Fact]
public async Task Approve_ForbiddenTier_Returns403() public async Task Approve_ForbiddenTier_Returns403()
{ {

View file

@ -4,6 +4,7 @@ using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -20,7 +21,7 @@ public class UserControllerTests
private static UserController NewController(Mock<IUserService> service, string? userId = null) private static UserController NewController(Mock<IUserService> service, string? userId = null)
{ {
var controller = new UserController(service.Object); var controller = new UserController(service.Object, Mock.Of<ILogger<UserController>>());
if (userId != null) if (userId != null)
{ {
controller.ControllerContext = new ControllerContext controller.ControllerContext = new ControllerContext

View file

@ -4,6 +4,7 @@ using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -16,7 +17,7 @@ public class VendorControllerTests
{ {
private static VendorController NewController(Mock<IVendorService> service) private static VendorController NewController(Mock<IVendorService> service)
{ {
var controller = new VendorController(service.Object); var controller = new VendorController(service.Object, Mock.Of<ILogger<VendorController>>());
controller.ControllerContext = new ControllerContext controller.ControllerContext = new ControllerContext
{ {
@ -218,6 +219,44 @@ public class VendorControllerTests
result.Should().BeOfType<NotFoundResult>(); result.Should().BeOfType<NotFoundResult>();
} }
[Fact]
public async Task Delete_WhenDeactivationBlocked_ReturnsSanitizedConflictWithOpenWorkOrdersAndLogs()
{
var openWorkOrders = new List<LinkedWorkOrderDTO> { new() { WorkOrderId = 7 } };
var service = new Mock<IVendorService>();
service.Setup(x => x.DeleteVendorAsync(It.IsAny<int>(), It.IsAny<string>()))
.ThrowsAsync(new VendorDeactivationBlockedException("SECRET-internal-reason", openWorkOrders));
var logger = new Mock<ILogger<VendorController>>();
logger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true);
var controller = new VendorController(service.Object, logger.Object);
controller.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "42") }, "Test"))
}
};
var result = await controller.Delete(5);
var conflict = result.Should().BeOfType<ConflictObjectResult>().Subject;
var value = conflict.Value!;
var message = (string)value.GetType().GetProperty("Message")!.GetValue(value)!;
message.Should().NotContain("SECRET");
message.Should().Contain("reference");
var returnedOpenWorkOrders = (List<LinkedWorkOrderDTO>)value.GetType().GetProperty("OpenWorkOrders")!.GetValue(value)!;
returnedOpenWorkOrders.Should().HaveCount(1);
logger.Verify(
x => x.Log(
LogLevel.Error,
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.Is<Exception>(ex => ex.Message.Contains("SECRET")),
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
Times.Once);
}
[Fact] [Fact]
public async Task Delete_MapsDeactivationBlocked_ToConflict() public async Task Delete_MapsDeactivationBlocked_ToConflict()
{ {
@ -265,7 +304,7 @@ public class VendorControllerTests
public async Task Update_UnauthenticatedUser_ReturnsUnauthorized() public async Task Update_UnauthenticatedUser_ReturnsUnauthorized()
{ {
var service = new Mock<IVendorService>(); var service = new Mock<IVendorService>();
var controller = new VendorController(service.Object); var controller = new VendorController(service.Object, Mock.Of<ILogger<VendorController>>());
controller.ControllerContext = new ControllerContext controller.ControllerContext = new ControllerContext
{ {
HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal() } HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal() }

View file

@ -3,6 +3,7 @@ using Data.SeaHavenIndustries;
using FluentAssertions; using FluentAssertions;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -14,7 +15,7 @@ public class VendorPortalControllerTests
{ {
private static VendorPortalController NewController(Mock<IVendorPortalService> service, string? token = "valid-token") private static VendorPortalController NewController(Mock<IVendorPortalService> service, string? token = "valid-token")
{ {
var controller = new VendorPortalController(service.Object); var controller = new VendorPortalController(service.Object, Mock.Of<ILogger<VendorPortalController>>());
var context = new DefaultHttpContext(); var context = new DefaultHttpContext();
if (token != null) if (token != null)
context.Request.Headers["X-Vendor-Token"] = token; context.Request.Headers["X-Vendor-Token"] = token;
@ -133,7 +134,8 @@ public class VendorPortalControllerTests
var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject; var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject;
var resp = bad.Value.Should().BeOfType<Response>().Subject; var resp = bad.Value.Should().BeOfType<Response>().Subject;
resp.Message.Should().Be("Cannot accept"); resp.Message.Should().NotContain("Cannot accept");
resp.Message.Should().Contain("reference");
} }
[Fact] [Fact]
@ -169,7 +171,8 @@ public class VendorPortalControllerTests
var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject; var bad = result.Should().BeOfType<BadRequestObjectResult>().Subject;
var resp = bad.Value.Should().BeOfType<Response>().Subject; var resp = bad.Value.Should().BeOfType<Response>().Subject;
resp.Message.Should().Contain("Transition"); resp.Message.Should().NotContain("Transition");
resp.Message.Should().Contain("reference");
} }
[Fact] [Fact]

View file

@ -24,4 +24,8 @@
<ProjectReference Include="..\SeaHaven.DataServices\SeaHaven.DataServices.csproj" /> <ProjectReference Include="..\SeaHaven.DataServices\SeaHaven.DataServices.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Api.SeaHavenIndustries.Tests" />
</ItemGroup>
</Project> </Project>

View file

@ -1,7 +1,9 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
using System.Security.Claims; using System.Security.Claims;
@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class AuthenticationController : Controller public class AuthenticationController : Controller
{ {
private readonly IAuthenticationService _authenticationService; private readonly IAuthenticationService _authenticationService;
private readonly ILogger<AuthenticationController> _logger;
public AuthenticationController(IAuthenticationService authenticationService) public AuthenticationController(IAuthenticationService authenticationService, ILogger<AuthenticationController> logger)
{ {
_authenticationService = authenticationService; _authenticationService = authenticationService;
_logger = logger;
} }
[AllowAnonymous] [AllowAnonymous]
@ -46,7 +50,7 @@ namespace Api.SeaHavenIndustries.Controllers
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, ex.Message); return StatusCode(500, _logger.Sanitize(ex));
} }
} }
@ -94,7 +98,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -135,7 +139,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = "Error: " + ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -159,7 +163,7 @@ namespace Api.SeaHavenIndustries.Controllers
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = "Error: " + ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,7 +1,9 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class CalendarController : Controller public class CalendarController : Controller
{ {
private readonly ICalendarService _calendarService; private readonly ICalendarService _calendarService;
private readonly ILogger<CalendarController> _logger;
public CalendarController(ICalendarService calendarService) public CalendarController(ICalendarService calendarService, ILogger<CalendarController> logger)
{ {
_calendarService = calendarService; _calendarService = calendarService;
_logger = logger;
} }
// =================================================================================== // ===================================================================================
@ -34,7 +38,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -62,7 +66,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -86,7 +90,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -109,7 +113,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -135,7 +139,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -154,7 +158,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,7 +1,9 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Api.SeaHavenIndustries.Helper.ViewModel; using Api.SeaHavenIndustries.Helper.ViewModel;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
namespace Api.SeaHavenIndustries.Controllers namespace Api.SeaHavenIndustries.Controllers
@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class CommonController : Controller public class CommonController : Controller
{ {
private readonly ICommonService _commonService; private readonly ICommonService _commonService;
private readonly ILogger<CommonController> _logger;
public CommonController(ICommonService commonService) public CommonController(ICommonService commonService, ILogger<CommonController> logger)
{ {
_commonService = commonService; _commonService = commonService;
_logger = logger;
} }
[HttpGet] [HttpGet]
@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new { Status = "Error", Message = ex.Message }); return BadRequest(new { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -65,7 +69,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new { Status = "Error", Message = ex.Message }); return BadRequest(new { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,6 +1,8 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -12,10 +14,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class DropdownOptionsController : Controller public class DropdownOptionsController : Controller
{ {
private readonly IDropdownOptionsService _dropdownOptionsService; private readonly IDropdownOptionsService _dropdownOptionsService;
private readonly ILogger<DropdownOptionsController> _logger;
public DropdownOptionsController(IDropdownOptionsService dropdownOptionsService) public DropdownOptionsController(IDropdownOptionsService dropdownOptionsService, ILogger<DropdownOptionsController> logger)
{ {
_dropdownOptionsService = dropdownOptionsService; _dropdownOptionsService = dropdownOptionsService;
_logger = logger;
} }
[HttpGet] [HttpGet]
@ -42,7 +46,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new { message = ex.Message }); return BadRequest(new { message = _logger.Sanitize(ex, "The dropdown option could not be saved") });
} }
} }

View file

@ -1,9 +1,11 @@
using System.Security.Claims; using System.Security.Claims;
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -14,10 +16,12 @@ using SeaHaven.Services.Interfaces;
public class FollowUpController : ControllerBase public class FollowUpController : ControllerBase
{ {
private readonly IFollowUpService _followUpService; private readonly IFollowUpService _followUpService;
private readonly ILogger<FollowUpController> _logger;
public FollowUpController(IFollowUpService followUpService) public FollowUpController(IFollowUpService followUpService, ILogger<FollowUpController> logger)
{ {
_followUpService = followUpService; _followUpService = followUpService;
_logger = logger;
} }
[HttpGet("GetFollowUpList")] [HttpGet("GetFollowUpList")]
@ -111,7 +115,7 @@ public class FollowUpController : ControllerBase
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -134,7 +138,7 @@ public class FollowUpController : ControllerBase
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -173,7 +177,7 @@ public class FollowUpController : ControllerBase
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,8 +1,10 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -15,10 +17,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class LocationController : Controller public class LocationController : Controller
{ {
private readonly ILocationService _locationService; private readonly ILocationService _locationService;
private readonly ILogger<LocationController> _logger;
public LocationController(ILocationService locationService) public LocationController(ILocationService locationService, ILogger<LocationController> logger)
{ {
_locationService = locationService; _locationService = locationService;
_logger = logger;
} }
[HttpGet("GetLocationList")] [HttpGet("GetLocationList")]
@ -98,7 +102,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -121,7 +125,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -142,7 +146,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,7 +1,9 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class QuotesController : Controller public class QuotesController : Controller
{ {
private readonly IQuotesService _quotesService; private readonly IQuotesService _quotesService;
private readonly ILogger<QuotesController> _logger;
public QuotesController(IQuotesService quotesService) public QuotesController(IQuotesService quotesService, ILogger<QuotesController> logger)
{ {
_quotesService = quotesService; _quotesService = quotesService;
_logger = logger;
} }
[HttpPost] [HttpPost]
@ -31,7 +35,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -68,7 +72,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,8 +1,10 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Api.SeaHavenIndustries.Helper.ViewModel; using Api.SeaHavenIndustries.Helper.ViewModel;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
namespace Api.SeaHavenIndustries.Controllers namespace Api.SeaHavenIndustries.Controllers
@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class SettingsController : Controller public class SettingsController : Controller
{ {
private readonly ISettingsService _settingsService; private readonly ISettingsService _settingsService;
private readonly ILogger<SettingsController> _logger;
public SettingsController(ISettingsService settingsService) public SettingsController(ISettingsService settingsService, ILogger<SettingsController> logger)
{ {
_settingsService = settingsService; _settingsService = settingsService;
_logger = logger;
} }
[HttpPost] [HttpPost]
@ -30,7 +34,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -47,7 +51,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -64,7 +68,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -109,7 +113,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -145,7 +149,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new { Status = "Error", Message = ex.Message }); return BadRequest(new { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -161,7 +165,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
} }

View file

@ -1,7 +1,9 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -14,10 +16,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class TaskListTemplateController : Controller public class TaskListTemplateController : Controller
{ {
private readonly ITaskListTemplateService _taskListTemplateService; private readonly ITaskListTemplateService _taskListTemplateService;
private readonly ILogger<TaskListTemplateController> _logger;
public TaskListTemplateController(ITaskListTemplateService taskListTemplateService) public TaskListTemplateController(ITaskListTemplateService taskListTemplateService, ILogger<TaskListTemplateController> logger)
{ {
_taskListTemplateService = taskListTemplateService; _taskListTemplateService = taskListTemplateService;
_logger = logger;
} }
[HttpGet] [HttpGet]
@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -67,7 +71,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,6 +1,8 @@
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class UpliftController : Controller public class UpliftController : Controller
{ {
private readonly IUpliftService _upliftService; private readonly IUpliftService _upliftService;
private readonly ILogger<UpliftController> _logger;
public UpliftController(IUpliftService upliftService) public UpliftController(IUpliftService upliftService, ILogger<UpliftController> logger)
{ {
_upliftService = upliftService; _upliftService = upliftService;
_logger = logger;
} }
[HttpGet] [HttpGet]
@ -43,15 +47,15 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (KeyNotFoundException ex) catch (KeyNotFoundException ex)
{ {
return NotFound(new Response { Status = "Error", Message = ex.Message }); return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") });
} }
catch (UpliftForbiddenException ex) catch (UpliftForbiddenException ex)
{ {
return StatusCode(403, new Response { Status = "Error", Message = ex.Message }); return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") });
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") });
} }
} }
@ -65,15 +69,15 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (KeyNotFoundException ex) catch (KeyNotFoundException ex)
{ {
return NotFound(new Response { Status = "Error", Message = ex.Message }); return NotFound(new Response { Status = "Error", Message = _logger.Sanitize(ex, "Uplift request not found") });
} }
catch (UpliftForbiddenException ex) catch (UpliftForbiddenException ex)
{ {
return StatusCode(403, new Response { Status = "Error", Message = ex.Message }); return StatusCode(403, new Response { Status = "Error", Message = _logger.Sanitize(ex, "You are not authorized to perform this action on this uplift request") });
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "This uplift request cannot be modified in its current state") });
} }
} }

View file

@ -1,7 +1,9 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
using System.Security.Claims; using System.Security.Claims;
@ -14,10 +16,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class UserController : Controller public class UserController : Controller
{ {
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly ILogger<UserController> _logger;
public UserController(IUserService userService) public UserController(IUserService userService, ILogger<UserController> logger)
{ {
_userService = userService; _userService = userService;
_logger = logger;
} }
[Route("")] [Route("")]
[HttpGet] [HttpGet]
@ -49,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -73,7 +77,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -93,7 +97,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -109,7 +113,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,8 +1,10 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using System.Security.Claims; using System.Security.Claims;
@ -16,10 +18,12 @@ namespace Api.SeaHavenIndustries.Controllers
public class VendorController : Controller public class VendorController : Controller
{ {
private readonly IVendorService _vendorService; private readonly IVendorService _vendorService;
private readonly ILogger<VendorController> _logger;
public VendorController(IVendorService vendorService) public VendorController(IVendorService vendorService, ILogger<VendorController> logger)
{ {
_vendorService = vendorService; _vendorService = vendorService;
_logger = logger;
} }
[HttpGet("GetVendorList")] [HttpGet("GetVendorList")]
@ -148,7 +152,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -199,7 +203,7 @@ namespace Api.SeaHavenIndustries.Controllers
return Conflict(new return Conflict(new
{ {
Status = "Conflict", Status = "Conflict",
Message = dbex.Message, Message = _logger.Sanitize(dbex, "Vendor cannot be deactivated because it has open work orders"),
OpenWorkOrders = dbex.OpenWorkOrders OpenWorkOrders = dbex.OpenWorkOrders
}); });
} }
@ -209,7 +213,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -236,7 +240,7 @@ namespace Api.SeaHavenIndustries.Controllers
return Conflict(new return Conflict(new
{ {
Status = "Conflict", Status = "Conflict",
Message = dbex.Message, Message = _logger.Sanitize(dbex, "Vendor cannot be deactivated because it has open work orders"),
OpenWorkOrders = dbex.OpenWorkOrders OpenWorkOrders = dbex.OpenWorkOrders
}); });
} }
@ -246,7 +250,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -264,7 +268,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -304,7 +308,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return StatusCode(500, new Response { Status = "Error", Message = ex.Message }); return StatusCode(500, new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -1,5 +1,7 @@
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
@ -13,10 +15,12 @@ namespace Api.SeaHavenIndustries.Controllers
private const string TokenHeader = "X-Vendor-Token"; private const string TokenHeader = "X-Vendor-Token";
private readonly IVendorPortalService _portalService; private readonly IVendorPortalService _portalService;
private readonly ILogger<VendorPortalController> _logger;
public VendorPortalController(IVendorPortalService portalService) public VendorPortalController(IVendorPortalService portalService, ILogger<VendorPortalController> logger)
{ {
_portalService = portalService; _portalService = portalService;
_logger = logger;
} }
[HttpGet("session")] [HttpGet("session")]
@ -71,7 +75,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }
@ -92,7 +96,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }
@ -113,7 +117,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }
@ -134,7 +138,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }
@ -155,7 +159,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }
@ -176,7 +180,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }
@ -197,7 +201,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }
@ -218,7 +222,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The requested action could not be completed for this dispatch") });
} }
} }

View file

@ -1,8 +1,10 @@
using Api.SeaHavenIndustries.DTOs; using Api.SeaHavenIndustries.DTOs;
using Api.SeaHavenIndustries.Helper;
using Data.SeaHavenIndustries; using Data.SeaHavenIndustries;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SeaHaven.Services.DTOs; using SeaHaven.Services.DTOs;
using SeaHaven.Services.Interfaces; using SeaHaven.Services.Interfaces;
using SeaHaven.DataServices.Models; using SeaHaven.DataServices.Models;
@ -18,13 +20,16 @@ namespace Api.SeaHavenIndustries.Controllers
{ {
private readonly IWorkOrderService _workOrderService; private readonly IWorkOrderService _workOrderService;
private readonly IWorkOrderDispatchService _dispatchService; private readonly IWorkOrderDispatchService _dispatchService;
private readonly ILogger<WorkOrderController> _logger;
public WorkOrderController( public WorkOrderController(
IWorkOrderService workOrderService, IWorkOrderService workOrderService,
IWorkOrderDispatchService dispatchService) IWorkOrderDispatchService dispatchService,
ILogger<WorkOrderController> logger)
{ {
_workOrderService = workOrderService; _workOrderService = workOrderService;
_dispatchService = dispatchService; _dispatchService = dispatchService;
_logger = logger;
} }
[HttpPost] [HttpPost]
@ -60,7 +65,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -107,7 +112,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -149,7 +154,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -238,7 +243,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -257,7 +262,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -281,7 +286,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -304,7 +309,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -365,7 +370,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -410,7 +415,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -436,7 +441,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -458,7 +463,7 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }
@ -484,11 +489,11 @@ namespace Api.SeaHavenIndustries.Controllers
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex, "The dispatch signoff could not be added") });
} }
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(new Response { Status = "Error", Message = ex.Message }); return BadRequest(new Response { Status = "Error", Message = _logger.Sanitize(ex) });
} }
} }

View file

@ -0,0 +1,27 @@
using System.Globalization;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Logging;
namespace Api.SeaHavenIndustries.Helper;
internal static class SanitizedErrors
{
private const string DefaultPublicMessage = "An unexpected error occurred";
public static string Sanitize(
this ILogger logger,
Exception exception,
string? publicMessage = null,
[CallerMemberName] string operation = "")
{
var correlationId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
logger.LogError(
exception,
"Unhandled exception in {Operation}. CorrelationId: {CorrelationId}",
operation,
correlationId);
return $"{publicMessage ?? DefaultPublicMessage} (reference {correlationId}).";
}
}