using Microsoft.AspNetCore.Mvc; using PlotLine.Services; using PlotLine.ViewModels; namespace PlotLine.Controllers; public sealed class HelpDrawerController( IHelpService help, IHelpMarkdownRenderer markdown, IWebHostEnvironment environment) : Controller { public IActionResult Article(string key) { var article = help.GetByContextKey(key); return PartialView("_HelpDrawerArticle", new HelpDrawerArticleViewModel { Article = article, RequestedContextKey = key, RenderedHtml = article is null ? string.Empty : markdown.Render(article.MarkdownContent), RelatedArticles = article is null ? [] : help.GetRelatedArticles(article) }); } public IActionResult Test() { return View(); } public IActionResult Image(string file) { if (string.IsNullOrWhiteSpace(file) || file.Contains("..") || Path.IsPathRooted(file)) { return NotFound(); } var imageRoot = Path.Combine(environment.ContentRootPath, "Help", "Images"); var fullPath = Path.GetFullPath(Path.Combine(imageRoot, file)); var safeRoot = Path.GetFullPath(imageRoot).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; if (!fullPath.StartsWith(safeRoot, StringComparison.OrdinalIgnoreCase) || !System.IO.File.Exists(fullPath)) { return NotFound(); } var contentType = Path.GetExtension(fullPath).ToLowerInvariant() switch { ".png" => "image/png", ".jpg" or ".jpeg" => "image/jpeg", ".gif" => "image/gif", ".webp" => "image/webp", ".svg" => "image/svg+xml", _ => "application/octet-stream" }; return PhysicalFile(fullPath, contentType); } }