// Copyright Epic Games, Inc. All Rights Reserved. using System.Net.Mime; using System.Threading.Tasks; using EpicGames.AspNet; using EpicGames.Horde.Storage; using Jupiter.Implementation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using ContentId = Jupiter.Implementation.ContentId; namespace Jupiter.Controllers { [ApiController] [FormatFilter] [Produces(MediaTypeNames.Application.Json, MediaTypeNames.Application.Octet, CustomMediaTypeNames.UnrealCompactBinary)] [Route("api/v1/content-id")] [Authorize] public class ContentIdController : ControllerBase { private readonly IRequestHelper _requestHelper; private readonly IContentIdStore _contentIdStore; public ContentIdController(IRequestHelper requestHelper, IContentIdStore contentIdStore) { _requestHelper = requestHelper; _contentIdStore = contentIdStore; } /// /// Returns which blobs a content id maps to /// /// Namespace. Each namespace is completely separated from each other. Use for different types of data that is never expected to be similar (between two different games for instance). Example: `uc4.ddc` /// The content id to resolve [HttpGet("{ns}/{contentId}.{format?}", Order = 500)] [ProducesDefaultResponseType] [ProducesResponseType(type: typeof(ProblemDetails), 400)] public async Task ResolveAsync(NamespaceId ns, ContentId contentId) { ActionResult? result = await _requestHelper.HasAccessToNamespaceAsync(User, Request, ns, new[] { JupiterAclAction.ReadObject }); if (result != null) { return result; } BlobId[]? blobs = await _contentIdStore.ResolveAsync(ns, contentId, mustBeContentId: true); if (blobs == null) { return NotFound(new ProblemDetails { Title = $"Unable to resolve content id {contentId} ({ns})." }); } return Ok(new ResolvedContentIdResponse(blobs)); } /// /// Update a content id mapping /// /// Namespace. Each namespace is completely separated from each other. Use for different types of data that is never expected to be similar (between two different games for instance). Example: `uc4.ddc` /// The content id to resolve /// The blob identifier to map the content id to /// The weight of this mapping, higher means more important [HttpPut("{ns}/{contentId}/update/{blobIdentifier}/{contentWeight}", Order = 500)] [ProducesDefaultResponseType] [ProducesResponseType(type: typeof(ProblemDetails), 400)] public async Task UpdateContentIdMappingAsync(NamespaceId ns, ContentId contentId, BlobId blobIdentifier, int contentWeight) { ActionResult? result = await _requestHelper.HasAccessToNamespaceAsync(User, Request, ns, new[] { JupiterAclAction.WriteObject }); if (result != null) { return result; } await _contentIdStore.PutAsync(ns, contentId, new BlobId[] {blobIdentifier}, contentWeight); return Ok(); } } public class ResolvedContentIdResponse { public BlobId[] Blobs { get; set; } public ResolvedContentIdResponse(BlobId[] blobs) { Blobs = blobs; } } }