// Copyright Epic Games, Inc. All Rights Reserved. using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Horde.Server.Acls; using Horde.Server.Configuration; using Horde.Server.Server; using Horde.Server.Streams; using Horde.Server.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.Options; namespace Horde.Server.Projects { /// /// Controller for the /api/v1/projects endpoint /// [ApiController] [Authorize] [Route("[controller]")] public class ProjectsController : HordeControllerBase { private readonly IOptionsSnapshot _globalConfig; private static readonly FileExtensionContentTypeProvider s_contentTypeProvider = new FileExtensionContentTypeProvider(); /// /// Constructor /// public ProjectsController(IOptionsSnapshot globalConfig) { _globalConfig = globalConfig; } /// /// Query all the projects /// /// Whether to include streams in the response /// Whether to include categories in the response /// Filter for the properties to return /// Information about all the projects [HttpGet] [Route("/api/v1/projects")] [ProducesResponseType(typeof(List), 200)] public ActionResult> GetProjects([FromQuery(Name = "Streams")] bool includeStreams = false, [FromQuery(Name = "Categories")] bool includeCategories = false, [FromQuery] PropertyFilter? filter = null) { GlobalConfig globalConfig = _globalConfig.Value; List responses = new List(); foreach (ProjectConfig projectConfig in globalConfig.Projects) { if (projectConfig.Authorize(AclAction.ViewProject, User)) { List? visibleStreams = null; if (includeStreams || includeCategories) { visibleStreams = projectConfig.Streams.Where(x => x.Authorize(AclAction.ViewStream, User)).ToList(); } responses.Add(GetProjectResponse.FromConfig(projectConfig, includeStreams, includeCategories, visibleStreams).ApplyFilter(filter)); } } return responses; } /// /// Retrieve information about a specific project /// /// Id of the project to get information about /// Filter for the properties to return /// Information about the requested project [HttpGet] [Route("/api/v1/projects/{projectId}")] [ProducesResponseType(typeof(List), 200)] public ActionResult GetProject(ProjectId projectId, [FromQuery] PropertyFilter? filter = null) { ProjectConfig? projectConfig; if(!_globalConfig.Value.TryGetProject(projectId, out projectConfig)) { return NotFound(projectId); } if (!projectConfig.Authorize(AclAction.ViewProject, User)) { return Forbid(AclAction.ViewProject, projectId); } bool includeStreams = PropertyFilter.Includes(filter, nameof(GetProjectResponse.Streams)); bool includeCategories = PropertyFilter.Includes(filter, nameof(GetProjectResponse.Categories)); List? visibleStreams = null; if (includeStreams || includeCategories) { visibleStreams = projectConfig.Streams.Where(x => x.Authorize(AclAction.ViewStream, User)).ToList(); } return GetProjectResponse.FromConfig(projectConfig, includeStreams, includeCategories, visibleStreams).ApplyFilter(filter); } /// /// Retrieve information about a specific project /// /// Id of the project to get information about /// Information about the requested project [HttpGet] [Route("/api/v1/projects/{projectId}/logo")] public ActionResult GetProjectLogo(ProjectId projectId) { ProjectConfig? projectConfig; if (!_globalConfig.Value.TryGetProject(projectId, out projectConfig)) { return NotFound(projectId); } if (!projectConfig.Authorize(AclAction.ViewProject, User)) { return Forbid(AclAction.ViewProject, projectId); } ConfigResource? logoResource = projectConfig.Logo; if (logoResource == null || logoResource.Path == null || logoResource.Data.Length == 0) { return NotFound("Missing logo resource data"); } string? contentType; if (!s_contentTypeProvider.TryGetContentType(logoResource.Path, out contentType)) { contentType = "application/octet-stream"; } return new FileContentResult(logoResource.Data.ToArray(), contentType); } } }