Files
UnrealEngine/Engine/Source/Programs/UnrealCloudDDC/Jupiter/Controllers/AdminController.cs
2025-05-18 13:04:45 +08:00

177 lines
5.0 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Jupiter.Implementation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
namespace Jupiter.Controllers
{
[ApiController]
[Route("api/v1/admin")]
[Authorize]
public class AdminController : Controller
{
private readonly LastAccessServiceReferences _lastAccessService;
private readonly IRefCleanup _refCleanup;
private readonly IConfiguration _configuration;
private readonly IRequestHelper _requestHelper;
public AdminController(LastAccessServiceReferences lastAccessService, IRefCleanup refCleanup, IConfiguration configuration, IRequestHelper requestHelper)
{
_lastAccessService = lastAccessService;
_refCleanup = refCleanup;
_configuration = configuration;
_requestHelper = requestHelper;
}
/// <summary>
/// Report last access times to refs store
/// </summary>
/// <remarks>
/// Manually triggers a aggregation of the last access records and commits these to the refs store. This is done automatically so the only reason to use this endpoint is for debugging purposes.
/// </remarks>
/// <returns></returns>
[HttpPost("startLastAccessRollup")]
[ProducesResponseType(type: typeof(UpdatedRecordsResponse), 200)]
public async Task<IActionResult> StartLastAccessRollupAsync()
{
ActionResult? result = await _requestHelper.HasAccessForGlobalOperationsAsync(User, new[] { JupiterAclAction.AdminAction });
if (result != null)
{
return result;
}
List<(LastAccessRecord, DateTime)>? updatedRecords = await _lastAccessService.ProcessLastAccessRecordsAsync(HttpContext.RequestAborted);
return Ok(new UpdatedRecordsResponse(
updatedRecords?.Select(tuple => new UpdatedRecordsResponse.UpdatedRecord(tuple.Item1, tuple.Item2))
.ToList() ??
new List<UpdatedRecordsResponse.UpdatedRecord>()));
}
/// <summary>
/// Manually run the refs cleanup
/// </summary>
/// <remarks>
/// Manually triggers a cleanup of the refs keys based on last access time. This is done automatically so the only reason to use this endpoint is for debugging purposes.
/// </remarks>
/// <returns></returns>
[HttpPost("refCleanup")]
public async Task<IActionResult> RefCleanupAsync()
{
ActionResult? result = await _requestHelper.HasAccessForGlobalOperationsAsync(User, new[] { JupiterAclAction.AdminAction });
if (result != null)
{
return result;
}
int countOfDeletedRecords = await _refCleanup.Cleanup(CancellationToken.None);
return Ok(new RemovedRefRecordsResponse(countOfDeletedRecords));
}
/// <summary>
/// Dumps all settings currently in use
/// </summary>
/// <returns></returns>
[HttpGet("settings")]
public async Task<IActionResult> SettingsAsync()
{
ActionResult? result = await _requestHelper.HasAccessForGlobalOperationsAsync(User, new[] { JupiterAclAction.AdminAction });
if (result != null)
{
return result;
}
Dictionary<string, Dictionary<string, object>> settings = new Dictionary<string, Dictionary<string, object>>();
Dictionary<string, object> ResolveSection(IConfigurationSection section)
{
Dictionary<string, object> values = new Dictionary<string, object>();
foreach (IConfigurationSection childSection in section.GetChildren())
{
if (childSection.Value == null)
{
values.Add(childSection.Key, ResolveSection(childSection));
}
else
{
values.Add(childSection.Key, childSection.Value);
}
}
return values;
}
foreach (IConfigurationSection section in _configuration.GetChildren())
{
Dictionary<string, object> values = ResolveSection(section);
if (values.Count != 0)
{
settings.Add(section.Key, values);
}
}
return new JsonResult(new
{
Settings = settings
}, new System.Text.Json.JsonSerializerOptions
{
WriteIndented = true
})
{
StatusCode = (int)HttpStatusCode.OK
};
}
}
public class RemovedBlobRecords
{
public BlobId[] Blobs { get; }
public RemovedBlobRecords(IEnumerable<BlobId> blobs)
{
Blobs = blobs.ToArray();
}
}
public class RemovedRefRecordsResponse
{
public RemovedRefRecordsResponse(int countOfRemovedRecords)
{
CountOfRemovedRecords = countOfRemovedRecords;
}
public int CountOfRemovedRecords { get; }
}
public class UpdatedRecordsResponse
{
public UpdatedRecordsResponse(List<UpdatedRecord> updatedRecords)
{
UpdatedRecords = updatedRecords;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Only used by serialization")]
public class UpdatedRecord
{
public UpdatedRecord(LastAccessRecord record, DateTime time)
{
Record = record;
Time = time;
}
public LastAccessRecord Record { get; }
public DateTime Time { get; }
}
public List<UpdatedRecord> UpdatedRecords { get; }
}
}