2023-07-06 04:01:36 +02:00
using System ;
2018-04-03 04:50:41 +02:00
using System.Linq ;
using System.Threading.Tasks ;
2020-11-17 13:46:23 +01:00
using BTCPayServer.Abstractions.Constants ;
2023-07-06 04:01:36 +02:00
using BTCPayServer.Abstractions.Contracts ;
using BTCPayServer.Abstractions.Extensions ;
2022-07-15 05:38:33 +02:00
using BTCPayServer.Abstractions.Models ;
2021-12-11 04:32:23 +01:00
using BTCPayServer.Client ;
2021-12-31 08:59:02 +01:00
using BTCPayServer.Data ;
2018-04-03 04:50:41 +02:00
using BTCPayServer.Models.AppViewModels ;
2018-08-22 10:26:49 +02:00
using BTCPayServer.Services.Apps ;
2021-10-25 09:54:36 +02:00
using BTCPayServer.Services.Stores ;
2018-08-30 20:16:24 +02:00
using Microsoft.AspNetCore.Authorization ;
2023-07-06 04:01:36 +02:00
using Microsoft.AspNetCore.Http ;
2018-04-03 04:50:41 +02:00
using Microsoft.AspNetCore.Identity ;
using Microsoft.AspNetCore.Mvc ;
2023-01-21 19:08:12 +01:00
using Microsoft.AspNetCore.Mvc.Rendering ;
2018-04-03 04:50:41 +02:00
namespace BTCPayServer.Controllers
{
[AutoValidateAntiforgeryToken]
[Route("apps")]
2022-01-07 04:32:00 +01:00
public partial class UIAppsController : Controller
2018-04-03 04:50:41 +02:00
{
2022-01-07 04:32:00 +01:00
public UIAppsController (
2018-04-03 04:50:41 +02:00
UserManager < ApplicationUser > userManager ,
2021-12-11 04:32:23 +01:00
StoreRepository storeRepository ,
2023-07-06 04:01:36 +02:00
IFileService fileService ,
2023-01-21 19:08:12 +01:00
AppService appService ,
IHtmlHelper html )
2018-04-03 04:50:41 +02:00
{
2021-12-11 04:32:23 +01:00
_userManager = userManager ;
2021-10-25 09:54:36 +02:00
_storeRepository = storeRepository ;
2023-07-06 04:01:36 +02:00
_fileService = fileService ;
2021-12-11 04:32:23 +01:00
_appService = appService ;
2023-01-21 19:08:12 +01:00
Html = html ;
2018-04-03 04:50:41 +02:00
}
2018-08-30 20:16:24 +02:00
2021-12-11 04:32:23 +01:00
private readonly UserManager < ApplicationUser > _userManager ;
2021-10-25 09:54:36 +02:00
private readonly StoreRepository _storeRepository ;
2023-07-06 04:01:36 +02:00
private readonly IFileService _fileService ;
2021-12-11 04:32:23 +01:00
private readonly AppService _appService ;
2018-08-30 20:16:24 +02:00
2018-10-09 16:38:56 +02:00
public string CreatedAppId { get ; set ; }
2023-01-21 19:08:12 +01:00
public IHtmlHelper Html { get ; }
2023-01-06 14:18:07 +01:00
2022-07-18 20:51:53 +02:00
public class AppUpdated
{
public string AppId { get ; set ; }
public object Settings { get ; set ; }
public string StoreId { get ; set ; }
public override string ToString ( )
{
return string . Empty ;
}
}
2023-01-06 14:18:07 +01:00
2022-07-22 15:41:14 +02:00
[HttpGet("/apps/{appId}")]
public async Task < IActionResult > RedirectToApp ( string appId )
{
var app = await _appService . GetApp ( appId , null ) ;
if ( app is null )
return NotFound ( ) ;
2023-04-10 04:07:03 +02:00
2023-03-17 03:56:32 +01:00
var res = await _appService . ViewLink ( app ) ;
if ( res is null )
2022-07-22 15:41:14 +02:00
{
2023-03-17 03:56:32 +01:00
return NotFound ( ) ;
}
return Redirect ( res ) ;
2022-07-22 15:41:14 +02:00
}
2021-12-31 08:59:02 +01:00
2022-07-22 15:41:14 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
2021-12-11 04:32:23 +01:00
[HttpGet("/stores/{storeId}/apps")]
2020-07-19 23:22:26 +02:00
public async Task < IActionResult > ListApps (
2021-12-31 08:59:02 +01:00
string storeId ,
2020-07-19 23:22:26 +02:00
string sortOrder = null ,
2023-09-11 02:59:17 +02:00
string sortOrderColumn = null ,
bool archived = false
2020-07-19 23:22:26 +02:00
)
2018-04-03 04:50:41 +02:00
{
2021-12-20 15:15:32 +01:00
var store = GetCurrentStore ( ) ;
2023-09-11 02:59:17 +02:00
var apps = ( await _appService . GetAllApps ( GetUserId ( ) , false , store . Id , archived ) )
. Where ( app = > app . Archived = = archived ) ;
2020-07-19 23:22:26 +02:00
2021-12-31 08:59:02 +01:00
if ( sortOrder ! = null & & sortOrderColumn ! = null )
2020-07-19 23:22:26 +02:00
{
2021-12-31 08:59:02 +01:00
apps = apps . OrderByDescending ( app = >
2021-12-11 04:32:23 +01:00
{
2023-09-11 02:59:17 +02:00
return sortOrderColumn switch
2020-07-19 23:22:26 +02:00
{
2023-09-11 02:59:17 +02:00
nameof ( app . AppName ) = > app . AppName ,
nameof ( app . StoreName ) = > app . StoreName ,
nameof ( app . AppType ) = > app . AppType ,
_ = > app . Id
} ;
} ) ;
2020-07-19 23:22:26 +02:00
switch ( sortOrder )
{
case "desc" :
ViewData [ $"{sortOrderColumn}SortOrder" ] = "asc" ;
break ;
case "asc" :
2023-09-11 02:59:17 +02:00
apps = apps . Reverse ( ) ;
2020-07-19 23:22:26 +02:00
ViewData [ $"{sortOrderColumn}SortOrder" ] = "desc" ;
break ;
}
}
2021-12-31 08:59:02 +01:00
2021-12-11 04:32:23 +01:00
return View ( new ListAppsViewModel
2018-04-03 04:50:41 +02:00
{
2023-09-11 02:59:17 +02:00
Apps = apps . ToArray ( )
2018-04-03 04:50:41 +02:00
} ) ;
}
2022-07-22 15:41:14 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
2023-03-18 22:36:26 +01:00
[HttpGet("/stores/{storeId}/apps/create/{appType?}")]
2023-03-17 03:56:32 +01:00
public IActionResult CreateApp ( string storeId , string appType = null )
2018-04-03 04:50:41 +02:00
{
2023-03-18 22:36:26 +01:00
var vm = new CreateAppViewModel ( _appService )
{
StoreId = storeId ,
AppType = appType ,
SelectedAppType = appType
} ;
2023-03-17 03:56:32 +01:00
return View ( vm ) ;
2018-04-03 04:50:41 +02:00
}
2022-07-22 15:41:14 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
2023-03-18 22:36:26 +01:00
[HttpPost("/stores/{storeId}/apps/create/{appType?}")]
2021-12-11 04:32:23 +01:00
public async Task < IActionResult > CreateApp ( string storeId , CreateAppViewModel vm )
2018-04-03 04:50:41 +02:00
{
2021-12-20 15:15:32 +01:00
var store = GetCurrentStore ( ) ;
vm . StoreId = store . Id ;
2023-03-18 22:36:26 +01:00
var type = _appService . GetAppType ( vm . AppType ? ? vm . SelectedAppType ) ;
2023-03-20 02:39:26 +01:00
if ( type is null )
2023-03-18 22:36:26 +01:00
{
2018-04-03 04:50:41 +02:00
ModelState . AddModelError ( nameof ( vm . SelectedAppType ) , "Invalid App Type" ) ;
2023-03-18 22:36:26 +01:00
}
2018-04-03 04:50:41 +02:00
if ( ! ModelState . IsValid )
{
return View ( vm ) ;
}
2019-09-02 15:37:52 +02:00
var appData = new AppData
2018-04-03 04:50:41 +02:00
{
2021-12-20 15:15:32 +01:00
StoreDataId = store . Id ,
2021-10-29 12:29:02 +02:00
Name = vm . AppName ,
2023-03-18 22:36:26 +01:00
AppType = type ! . Type
2019-09-02 15:37:52 +02:00
} ;
2021-10-25 09:54:36 +02:00
var defaultCurrency = await GetStoreDefaultCurrentIfEmpty ( appData . StoreDataId , null ) ;
2023-03-17 03:56:32 +01:00
await _appService . SetDefaultSettings ( appData , defaultCurrency ) ;
2021-12-11 04:32:23 +01:00
await _appService . UpdateOrCreateApp ( appData ) ;
2023-04-10 04:07:03 +02:00
2019-10-31 04:29:59 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "App successfully created" ;
2019-09-02 15:37:52 +02:00
CreatedAppId = appData . Id ;
2018-12-29 11:52:07 +01:00
2023-03-20 02:39:26 +01:00
var url = await type . ConfigureLink ( appData ) ;
2023-03-17 03:56:32 +01:00
return Redirect ( url ) ;
2018-04-03 04:50:41 +02:00
}
2022-07-22 15:41:14 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
2021-09-07 04:55:53 +02:00
[HttpGet("{appId}/delete")]
2021-12-16 17:37:19 +01:00
public IActionResult DeleteApp ( string appId )
2018-04-03 04:50:41 +02:00
{
2021-12-20 15:15:32 +01:00
var app = GetCurrentApp ( ) ;
if ( app = = null )
2018-04-03 04:50:41 +02:00
return NotFound ( ) ;
2021-12-31 08:59:02 +01:00
2023-01-21 19:08:12 +01:00
return View ( "Confirm" , new ConfirmModel ( "Delete app" , $"The app <strong>{Html.Encode(app.Name)}</strong> and its settings will be permanently deleted. Are you sure?" , "Delete" ) ) ;
2018-04-03 04:50:41 +02:00
}
2022-07-22 15:41:14 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
2021-12-11 04:32:23 +01:00
[HttpPost("{appId}/delete")]
public async Task < IActionResult > DeleteAppPost ( string appId )
{
2021-12-20 15:15:32 +01:00
var app = GetCurrentApp ( ) ;
if ( app = = null )
2021-12-11 04:32:23 +01:00
return NotFound ( ) ;
2021-12-31 08:59:02 +01:00
2021-12-20 15:15:32 +01:00
if ( await _appService . DeleteApp ( app ) )
2021-12-11 04:32:23 +01:00
TempData [ WellKnownTempData . SuccessMessage ] = "App deleted successfully." ;
2021-12-31 08:59:02 +01:00
2022-07-12 08:18:08 +02:00
return RedirectToAction ( nameof ( UIStoresController . Dashboard ) , "UIStores" , new { storeId = app . StoreDataId } ) ;
2021-12-11 04:32:23 +01:00
}
2023-09-11 02:59:17 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[HttpPost("{appId}/archive")]
public async Task < IActionResult > ToggleArchive ( string appId )
{
var app = GetCurrentApp ( ) ;
if ( app = = null )
return NotFound ( ) ;
var type = _appService . GetAppType ( app . AppType ) ;
if ( type is null )
{
return UnprocessableEntity ( ) ;
}
var archived = ! app . Archived ;
if ( await _appService . SetArchived ( app , archived ) )
{
TempData [ WellKnownTempData . SuccessMessage ] = archived
? "The app has been archived and will no longer appear in the apps list by default."
: "The app has been unarchived and will appear in the apps list by default again." ;
}
else
{
TempData [ WellKnownTempData . ErrorMessage ] = $"Failed to {(archived ? " archive " : " unarchive ")} the app." ;
}
var url = await type . ConfigureLink ( app ) ;
return Redirect ( url ) ;
}
2021-12-11 04:32:23 +01:00
2023-07-06 04:01:36 +02:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[HttpPost("{appId}/upload-file")]
[IgnoreAntiforgeryToken]
public async Task < IActionResult > FileUpload ( IFormFile file )
{
var app = GetCurrentApp ( ) ;
var userId = GetUserId ( ) ;
if ( app is null | | userId is null )
return NotFound ( ) ;
2023-11-02 19:58:03 +01:00
if ( ! file . FileName . IsValidFileName ( ) )
{
return Json ( new { error = "Invalid file name" } ) ;
}
2023-07-06 04:01:36 +02:00
if ( ! file . ContentType . StartsWith ( "image/" , StringComparison . InvariantCulture ) )
{
return Json ( new { error = "The file needs to be an image" } ) ;
}
if ( file . Length > 500_000 )
{
2023-11-02 19:58:03 +01:00
return Json ( new { error = "The file size should be less than 0.5MB" } ) ;
2023-07-06 04:01:36 +02:00
}
var formFile = await file . Bufferize ( ) ;
if ( ! FileTypeDetector . IsPicture ( formFile . Buffer , formFile . FileName ) )
{
return Json ( new { error = "The file needs to be an image" } ) ;
}
try
{
var storedFile = await _fileService . AddFile ( file , userId ) ;
var fileId = storedFile . Id ;
var fileUrl = await _fileService . GetFileUrl ( Request . GetAbsoluteRootUri ( ) , fileId ) ;
return Json ( new { fileId , fileUrl } ) ;
}
catch ( Exception e )
{
return Json ( new { error = $"Could not save file: {e.Message}" } ) ;
}
}
2021-12-11 04:32:23 +01:00
async Task < string > GetStoreDefaultCurrentIfEmpty ( string storeId , string currency )
{
if ( string . IsNullOrWhiteSpace ( currency ) )
{
2023-07-06 04:01:36 +02:00
var store = await _storeRepository . FindStore ( storeId ) ;
currency = store ? . GetStoreBlob ( ) . DefaultCurrency ;
2021-12-11 04:32:23 +01:00
}
2023-07-06 04:01:36 +02:00
return currency ? . Trim ( ) . ToUpperInvariant ( ) ;
2021-12-11 04:32:23 +01:00
}
2021-12-20 15:15:32 +01:00
private string GetUserId ( ) = > _userManager . GetUserId ( User ) ;
2021-12-11 04:32:23 +01:00
2021-12-20 15:15:32 +01:00
private StoreData GetCurrentStore ( ) = > HttpContext . GetStoreData ( ) ;
2021-12-31 08:59:02 +01:00
2021-12-20 15:15:32 +01:00
private AppData GetCurrentApp ( ) = > HttpContext . GetAppData ( ) ;
2018-04-03 04:50:41 +02:00
}
}