diff --git a/src/BUTR.Site.NexusMods.Client/Shared/Footer.razor b/src/BUTR.Site.NexusMods.Client/Shared/Footer.razor
index b565e40b..06a029f6 100644
--- a/src/BUTR.Site.NexusMods.Client/Shared/Footer.razor
+++ b/src/BUTR.Site.NexusMods.Client/Shared/Footer.razor
@@ -13,5 +13,5 @@
- © 2022-2023 BUTR
+ © 2022-2024 BUTR
diff --git a/src/BUTR.Site.NexusMods.Server/Controllers/RecreateStacktraceController.cs b/src/BUTR.Site.NexusMods.Server/Controllers/RecreateStacktraceController.cs
index 90be7e8d..bf74ba9b 100644
--- a/src/BUTR.Site.NexusMods.Server/Controllers/RecreateStacktraceController.cs
+++ b/src/BUTR.Site.NexusMods.Server/Controllers/RecreateStacktraceController.cs
@@ -20,6 +20,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using BUTR.Site.NexusMods.Server.Utils.BindingSources;
namespace BUTR.Site.NexusMods.Server.Controllers;
@@ -39,7 +40,7 @@ public RecreateStacktraceController(ILogger logger
[HttpGet("Json")]
[Produces("application/json")]
- public async Task?>> GetJsonAsync([FromQuery] CrashReportFileId id, CancellationToken ct)
+ public async Task?>> GetJsonAsync([BindTenant] TenantId tenant, [FromQuery] CrashReportFileId id, CancellationToken ct)
{
if (!HttpContext.OwnsTenantGame())
return ApiResultError("Game is not owned!", StatusCodes.Status401Unauthorized);
@@ -47,7 +48,7 @@ public RecreateStacktraceController(ILogger logger
string crashReportContent;
try
{
- crashReportContent = await _crashReporterClient.GetCrashReportAsync(id, ct);
+ crashReportContent = await _crashReporterClient.GetCrashReportAsync(tenant, id, ct);
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)
{
@@ -76,7 +77,7 @@ public RecreateStacktraceController(ILogger logger
[HttpGet("Html")]
[Produces("text/plain")]
- public async Task> GetHtmlAsync([FromQuery] CrashReportFileId id, CancellationToken ct)
+ public async Task> GetHtmlAsync([BindTenant] TenantId tenant, [FromQuery] CrashReportFileId id, CancellationToken ct)
{
if (!HttpContext.OwnsTenantGame())
return Unauthorized();
@@ -84,7 +85,7 @@ public async Task> GetHtmlAsync([FromQuery] CrashReportFile
string crashReportContent;
try
{
- crashReportContent = await _crashReporterClient.GetCrashReportAsync(id, ct);
+ crashReportContent = await _crashReporterClient.GetCrashReportAsync(tenant, id, ct);
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)
{
diff --git a/src/BUTR.Site.NexusMods.Server/Controllers/ReportsController.cs b/src/BUTR.Site.NexusMods.Server/Controllers/ReportsController.cs
index a9a4e880..20764c1e 100644
--- a/src/BUTR.Site.NexusMods.Server/Controllers/ReportsController.cs
+++ b/src/BUTR.Site.NexusMods.Server/Controllers/ReportsController.cs
@@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
+using BUTR.Site.NexusMods.Server.Utils.BindingSources;
namespace BUTR.Site.NexusMods.Server.Controllers;
@@ -27,7 +28,10 @@ public ReportsController(ILogger logger, ICrashReporterClient
[HttpGet("{id}.html")]
[Produces("text/html")]
- public async Task> GetAllAsync(CrashReportFileId id, CancellationToken ct) => Ok(await _crashReporterClient.GetCrashReportAsync(id, ct));
+ public async Task> GetAllAsync([BindTenant] TenantId tenant, CrashReportFileId id, CancellationToken ct)
+ {
+ return Ok(await _crashReporterClient.GetCrashReportAsync(tenant, id, ct));
+ }
// Just so we have ApiResult type in swagger.json
[HttpGet("BlankRequest")]
diff --git a/src/BUTR.Site.NexusMods.Server/Jobs/CrashReportProcessorJob.cs b/src/BUTR.Site.NexusMods.Server/Jobs/CrashReportProcessorJob.cs
index 4f7e41c7..ad573947 100644
--- a/src/BUTR.Site.NexusMods.Server/Jobs/CrashReportProcessorJob.cs
+++ b/src/BUTR.Site.NexusMods.Server/Jobs/CrashReportProcessorJob.cs
@@ -39,7 +39,7 @@ public async Task Execute(IJobExecutionContext context)
{
await using var scope = _serviceScopeFactory.CreateAsyncScope().WithTenant(tenant);
var crashReportBatchedHandler = scope.ServiceProvider.GetRequiredService();
- await foreach (var batch in _crashReporterClient.GetNewCrashReportMetadatasAsync(DateTime.UtcNow.AddDays(-2), ct).OfType().ChunkAsync(100).WithCancellation(ct))
+ await foreach (var batch in _crashReporterClient.GetNewCrashReportMetadatasAsync(tenant, DateTime.UtcNow.AddDays(-2), ct).OfType().ChunkAsync(100).WithCancellation(ct))
processed += await crashReportBatchedHandler.HandleBatchAsync(batch, ct);
}
diff --git a/src/BUTR.Site.NexusMods.Server/Services/General/ICrashReportBatchedHandler.cs b/src/BUTR.Site.NexusMods.Server/Services/General/ICrashReportBatchedHandler.cs
index c4169c78..bef63fbc 100644
--- a/src/BUTR.Site.NexusMods.Server/Services/General/ICrashReportBatchedHandler.cs
+++ b/src/BUTR.Site.NexusMods.Server/Services/General/ICrashReportBatchedHandler.cs
@@ -148,6 +148,8 @@ await _ignoredCrashReportsChannel.Writer.WriteAsync(new CrashReportIgnoredFileEn
private async Task DownloadCrashReportsAsync(CancellationToken ct)
{
+ var tenant = _tenantContextAccessor.Current;
+
try
{
var exceptions = new ConcurrentQueue();
@@ -163,11 +165,11 @@ await Parallel.ForEachAsync(_toDownloadChannel.Reader.ReadAllAsync(ct), options,
{
if (version <= 12)
{
- content = await _client.GetCrashReportAsync(fileId, ct2);
+ content = await _client.GetCrashReportAsync(tenant, fileId, ct2);
}
else
{
- content = await _client.GetCrashReportJsonAsync(fileId, ct2);
+ content = await _client.GetCrashReportJsonAsync(tenant, fileId, ct2);
}
}
catch (Exception e)
diff --git a/src/BUTR.Site.NexusMods.Server/Services/HttpClients/ICrashReporterClient.cs b/src/BUTR.Site.NexusMods.Server/Services/HttpClients/ICrashReporterClient.cs
index 4790fda7..a72dfa15 100644
--- a/src/BUTR.Site.NexusMods.Server/Services/HttpClients/ICrashReporterClient.cs
+++ b/src/BUTR.Site.NexusMods.Server/Services/HttpClients/ICrashReporterClient.cs
@@ -15,10 +15,10 @@ namespace BUTR.Site.NexusMods.Server.Services;
public interface ICrashReporterClient
{
- Task GetCrashReportAsync(CrashReportFileId id, CancellationToken ct);
- Task GetCrashReportJsonAsync(CrashReportFileId id, CancellationToken ct);
- IAsyncEnumerable GetNewCrashReportMetadatasAsync(DateTime dateTime, CancellationToken ct);
- IAsyncEnumerable GetCrashReportMetadatasAsync(IEnumerable filenames, CancellationToken ct);
+ Task GetCrashReportAsync(TenantId tenant, CrashReportFileId id, CancellationToken ct);
+ Task GetCrashReportJsonAsync(TenantId tenant, CrashReportFileId id, CancellationToken ct);
+ IAsyncEnumerable GetNewCrashReportMetadatasAsync(TenantId tenant, DateTime dateTime, CancellationToken ct);
+ IAsyncEnumerable GetCrashReportMetadatasAsync(TenantId tenant, IEnumerable filenames, CancellationToken ct);
}
public sealed class CrashReporterClient : ICrashReporterClient
@@ -32,20 +32,20 @@ public CrashReporterClient(HttpClient httpClient, IOptions GetCrashReportAsync(CrashReportFileId id, CancellationToken ct) => await _httpClient.GetStringAsync($"{id}.html", ct);
- public async Task GetCrashReportJsonAsync(CrashReportFileId id, CancellationToken ct) => await _httpClient.GetStringAsync($"{id}.json", ct);
+ public async Task GetCrashReportAsync(TenantId tenant, CrashReportFileId id, CancellationToken ct) => await _httpClient.GetStringAsync($"{tenant}/{id}.html", ct);
+ public async Task GetCrashReportJsonAsync(TenantId tenant, CrashReportFileId id, CancellationToken ct) => await _httpClient.GetStringAsync($"{tenant}/{id}.json", ct);
- public async IAsyncEnumerable GetNewCrashReportMetadatasAsync(DateTime dateTime, [EnumeratorCancellation] CancellationToken ct)
+ public async IAsyncEnumerable GetNewCrashReportMetadatasAsync(TenantId tenant, DateTime dateTime, [EnumeratorCancellation] CancellationToken ct)
{
- using var request = new HttpRequestMessage(HttpMethod.Post, "getnewcrashreports");
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{tenant}/getnewcrashreports");
request.Content = JsonContent.Create(new { DateTime = dateTime.ToString("o") }, options: _jsonSerializerOptions);
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
await foreach (var entry in JsonSerializer.DeserializeAsyncEnumerable(await response.Content.ReadAsStreamAsync(ct), _jsonSerializerOptions, ct))
yield return entry;
}
- public async IAsyncEnumerable GetCrashReportMetadatasAsync(IEnumerable filenames, [EnumeratorCancellation] CancellationToken ct)
+ public async IAsyncEnumerable GetCrashReportMetadatasAsync(TenantId tenant, IEnumerable filenames, [EnumeratorCancellation] CancellationToken ct)
{
- using var request = new HttpRequestMessage(HttpMethod.Post, "getmetadata");
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{tenant}/getmetadata");
request.Content = JsonContent.Create(filenames, options: _jsonSerializerOptions);
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
await foreach (var entry in JsonSerializer.DeserializeAsyncEnumerable(await response.Content.ReadAsStreamAsync(ct), _jsonSerializerOptions, ct))