using Microsoft.AspNetCore.Mvc; using Nop.Core; using Nop.Plugin.Misc.FruitBankPlugin.Models.App; using Nop.Services.Security; using Nop.Web.Framework; using Nop.Web.Framework.Controllers; using Nop.Web.Framework.Mvc.Filters; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers { [Area(AreaNames.ADMIN)] [AuthorizeAdmin] [AutoValidateAntiforgeryToken] public class AppDownloadController : BasePluginController { private readonly IPermissionService _permissionService; private readonly IWebHelper _webHelper; private readonly IWorkContext _workContext; public AppDownloadController( IPermissionService permissionService, IWebHelper webHelper, IWorkContext workContext) { _permissionService = permissionService; _webHelper = webHelper; _workContext = workContext; } [HttpGet] public virtual async Task Index() { if (!await _permissionService.AuthorizeAsync(StandardPermission.Orders.ORDERS_CREATE_EDIT_DELETE)) return AccessDeniedView(); var model = new AppDownloadModel { PreviousVersions = new List() }; var appPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "app", "versions"); if (Directory.Exists(appPath)) { var versionFolders = Directory.GetDirectories(appPath) .Select(d => new DirectoryInfo(d)) .OrderByDescending(d => d.Name) .ToList(); foreach (var folder in versionFolders) { var apkFile = Directory.GetFiles(folder.FullName, "*.apk").FirstOrDefault(); if (apkFile != null) { var fileInfo = new FileInfo(apkFile); var fileName = Path.GetFileName(apkFile); var versionModel = new AppVersionModel { Version = folder.Name, DownloadUrl = $"/app/versions/{folder.Name}/{fileName}", FileName = fileName, // Add this FileSizeFormatted = FormatFileSize(fileInfo.Length), ReleaseDate = fileInfo.CreationTime }; // Set the latest version if (model.CurrentVersion == null) { model.CurrentVersion = folder.Name; model.DownloadUrl = versionModel.DownloadUrl; model.FileName = fileName; // Add this model.FileSizeBytes = fileInfo.Length; model.FileSizeFormatted = versionModel.FileSizeFormatted; model.ReleaseDate = versionModel.ReleaseDate; // Read changelog if exists var changelogFile = Path.Combine(folder.FullName, "changelog.txt"); if (System.IO.File.Exists(changelogFile)) { model.Changelog = await System.IO.File.ReadAllTextAsync(changelogFile); } } else { model.PreviousVersions.Add(versionModel); } } } } return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/AppDownload/Index.cshtml", model); } [HttpGet] public virtual IActionResult Download(string version, string fileName) { // Validate inputs if (string.IsNullOrEmpty(version) || string.IsNullOrEmpty(fileName)) return BadRequest(); // Prevent directory traversal if (version.Contains("..") || fileName.Contains("..")) return BadRequest(); if (!fileName.EndsWith(".apk", StringComparison.OrdinalIgnoreCase)) return BadRequest(); var filePath = Path.Combine( Directory.GetCurrentDirectory(), "wwwroot", "app", "versions", version, fileName); if (!System.IO.File.Exists(filePath)) return NotFound(); var fileBytes = System.IO.File.ReadAllBytes(filePath); // Set proper headers for APK download Response.Headers.Add("Content-Disposition", $"attachment; filename=\"{fileName}\""); return File(fileBytes, "application/vnd.android.package-archive", fileName); } private string FormatFileSize(long bytes) { string[] sizes = { "B", "KB", "MB", "GB", "TB" }; double len = bytes; int order = 0; while (len >= 1024 && order < sizes.Length - 1) { order++; len = len / 1024; } return $"{len:0.##} {sizes[order]}"; } } }