// 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; } /// /// Report last access times to refs store /// /// /// 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. /// /// [HttpPost("startLastAccessRollup")] [ProducesResponseType(type: typeof(UpdatedRecordsResponse), 200)] public async Task 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())); } /// /// Manually run the refs cleanup /// /// /// 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. /// /// [HttpPost("refCleanup")] public async Task 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)); } /// /// Dumps all settings currently in use /// /// [HttpGet("settings")] public async Task SettingsAsync() { ActionResult? result = await _requestHelper.HasAccessForGlobalOperationsAsync(User, new[] { JupiterAclAction.AdminAction }); if (result != null) { return result; } Dictionary> settings = new Dictionary>(); Dictionary ResolveSection(IConfigurationSection section) { Dictionary values = new Dictionary(); 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 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 blobs) { Blobs = blobs.ToArray(); } } public class RemovedRefRecordsResponse { public RemovedRefRecordsResponse(int countOfRemovedRecords) { CountOfRemovedRecords = countOfRemovedRecords; } public int CountOfRemovedRecords { get; } } public class UpdatedRecordsResponse { public UpdatedRecordsResponse(List 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 UpdatedRecords { get; } } }