using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using BTCPayServer.Abstractions.Constants; using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Extensions; using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Data; using BTCPayServer.Filters; using BTCPayServer.Models; using BTCPayServer.Models.StoreViewModels; using BTCPayServer.Services; using BTCPayServer.Services.Stores; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.FileProviders; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace BTCPayServer.Controllers { public class UIHomeController( ThemeSettings theme, LanguageService languageService, StoreRepository storeRepository, IWebHostEnvironment environment, SignInManager signInManager, PermissionService permissionService) : Controller { private SignInManager SignInManager { get; } = signInManager; private readonly IFileProvider _webRootFileProvider = environment.WebRootFileProvider; public LanguageService LanguageService { get; } = languageService; [HttpGet("home")] public Task Home() { return Index(); } [Route("")] [DomainMappingConstraint] public async Task Index() { if (theme.FirstRun) { return RedirectToAction(nameof(UIAccountController.Register), "UIAccount"); } if (SignInManager.IsSignedIn(User)) { var userId = HttpContext.User.GetIdOrNull(); var storeId = HttpContext.GetUserPrefsCookie()?.CurrentStoreId; if (storeId != null && userId != null) { // verify the store exists and redirect to it var store = await storeRepository.FindStore(storeId, userId); if (store != null) { return RedirectToAction(nameof(UIStoresController.Index), "UIStores", new { storeId }); } HttpContext.DeleteUserPrefsCookie(); } var stores = await storeRepository.GetStoresByUserId(userId!); var activeStore = stores.FirstOrDefault(s => !s.Archived); return activeStore != null ? RedirectToAction(nameof(UIStoresController.Index), "UIStores", new { storeId = activeStore.Id }) : RedirectToAction(nameof(UIUserStoresController.CreateStore), "UIUserStores"); } return Challenge(); } [Route("misc/lang")] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie + "," + AuthenticationSchemes.Greenfield)] public IActionResult Languages() { return Json(LanguageService.GetLanguages(), new JsonSerializerSettings { Formatting = Formatting.Indented }); } [Route("misc/permissions")] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie + "," + AuthenticationSchemes.Greenfield)] public IActionResult Permissions() { var nodes = new Dictionary(); foreach (var def in permissionService.Definitions.Values) { var m = new PermissionMetadata { PermissionName = def.Policy }; m.SubPermissions = permissionService.PermissionNodesByPolicy[m.PermissionName] .EnumerateDescendants(false).Select(e => e.Definition.Policy) .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(e => e, StringComparer.OrdinalIgnoreCase) .ToList(); nodes.Add(def.Policy, m); } var metadata = nodes.Values.OrderBy(v => v.PermissionName, StringComparer.OrdinalIgnoreCase).ToArray(); return Json(metadata, new JsonSerializerSettings { Formatting = Formatting.Indented }); } [Route("misc/translations/{resource}/{lang}")] [AllowAnonymous] public IActionResult GetTranslations(string resource, string lang) { string path; if (resource.StartsWith("checkout")) path = "locales/checkout"; else return NotFound(); var enLang = Lang(path + "/en.json"); var en = (enLang as JsonResult)?.Value as JObject; if (en is null || lang == "en" || lang == "en-US") return enLang; lang = LanguageService.FindLanguage(lang)?.Code; if (lang is null) return enLang; var oLang = Lang(path + $"/{lang}.json"); var o = (oLang as JsonResult)?.Value as JObject; if (o is null) return enLang; en.Merge(o); return Json(en); } private IActionResult Lang(string path) { var fi = _webRootFileProvider.GetFileInfo(path); try { using var fs = fi.CreateReadStream(); return Json(JObject.Load(new JsonTextReader(new StreamReader(fs, leaveOpen: true)))); } catch { return NotFound(); } } [Route("swagger/v1/swagger.json")] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie + "," + AuthenticationSchemes.Greenfield)] public async Task Swagger([FromServices] IEnumerable swaggerProviders) { JObject json = new(); var res = await Task.WhenAll(swaggerProviders.Select(provider => provider.Fetch())); foreach (var jObject in res) { if (json.ContainsKey("x-webhooks") && jObject.TryGetValue("x-webhooks", out var value)) { (((JObject)json["x-webhooks"])!).Merge(value); } json.Merge(jObject); } var servers = new JArray(); servers.Add(new JObject(new JProperty("url", HttpContext.Request.GetAbsoluteRoot()))); json["servers"] = servers; if (json["tags"] is JArray tags) { json["tags"] = new JArray(tags .Select(o => (name: ((JObject)o)["name"].Value(), o)) .OrderBy(o => o.name) .Select(o => o.o) .ToArray()); } if (json["x-webhooks"] is JObject webhooks) { json["x-webhooks"] = new JObject(webhooks.Properties() .OrderBy(o => o.Name) .ToArray()); } return Json(json); } [Route("docs")] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)] public IActionResult SwaggerDocs() { return View(); } [Route("recovery-seed-backup")] [Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyStoreSettings)] public IActionResult RecoverySeedBackup(RecoverySeedBackupViewModel vm) { return View("RecoverySeedBackup", vm); } public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } }