From 41275dc0033c3ebf1b12ee80b7bf2ac92dc1f045 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 7 Nov 2025 09:57:23 +0100 Subject: [PATCH] download page --- .../Controllers/AppDownloadController.cs | 145 +++++++++++++++ .../Admin/Models/App/AppDownloadModel.cs | 14 ++ .../Areas/Admin/Models/App/AppVersionModel.cs | 12 ++ .../Admin/Views/AppDownload/Index.cshtml | 167 ++++++++++++++++++ .../Infrastructure/RouteProvider.cs | 10 ++ .../Nop.Plugin.Misc.FruitBankPlugin.csproj | 3 + 6 files changed, 351 insertions(+) create mode 100644 Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/AppDownloadController.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppDownloadModel.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppVersionModel.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/AppDownload/Index.cshtml diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/AppDownloadController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/AppDownloadController.cs new file mode 100644 index 0000000..ffebb93 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/AppDownloadController.cs @@ -0,0 +1,145 @@ +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]}"; + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppDownloadModel.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppDownloadModel.cs new file mode 100644 index 0000000..f20306b --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppDownloadModel.cs @@ -0,0 +1,14 @@ +namespace Nop.Plugin.Misc.FruitBankPlugin.Models.App +{ + public class AppDownloadModel + { + public string CurrentVersion { get; set; } + public string DownloadUrl { get; set; } + public string FileName { get; set; } // Add this + public long FileSizeBytes { get; set; } + public string FileSizeFormatted { get; set; } + public DateTime ReleaseDate { get; set; } + public string Changelog { get; set; } + public List PreviousVersions { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppVersionModel.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppVersionModel.cs new file mode 100644 index 0000000..ca46a81 --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Models/App/AppVersionModel.cs @@ -0,0 +1,12 @@ +namespace Nop.Plugin.Misc.FruitBankPlugin.Models.App +{ + + public class AppVersionModel + { + public string Version { get; set; } + public string DownloadUrl { get; set; } + public string FileName { get; set; } // Add this + public string FileSizeFormatted { get; set; } + public DateTime ReleaseDate { get; set; } + } +} diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/AppDownload/Index.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/AppDownload/Index.cshtml new file mode 100644 index 0000000..6f1ba3e --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/AppDownload/Index.cshtml @@ -0,0 +1,167 @@ +@using System.IO +@model Nop.Plugin.Misc.FruitBankPlugin.Models.App.AppDownloadModel + +@{ + Layout = "_AdminLayout"; + NopHtml.SetActiveMenuItemSystemName("FruitBank.AppDownload"); +} + +
+

+ Android App letöltés +

+
+ +
+
+ @if (!string.IsNullOrEmpty(Model.CurrentVersion)) + { + +
+
+

+ + Legújabb verzió: @Model.CurrentVersion +

+
+
+
+
+
+ + Telepítési útmutató: +
    +
  1. Engedélyezd az "Ismeretlen forrásokból" való telepítést a táblagépen (Beállítások → Biztonság)
  2. +
  3. Kattints a "Letöltés" gombra
  4. +
  5. Nyisd meg a letöltött APK fájlt
  6. +
  7. Kövesd a telepítési utasításokat
  8. +
+
+ +
+ Verzió: @Model.CurrentVersion
+ Méret: @Model.FileSizeFormatted
+ Kiadás dátuma: @Model.ReleaseDate.ToString("yyyy-MM-dd HH:mm") +
+ + @if (!string.IsNullOrEmpty(Model.Changelog)) + { +
+ Változások: +
@Model.Changelog
+
+ } + + + + Letöltés (@Model.FileSizeFormatted) + +
+ +
+
+ +
+

Szkenneld be a QR kódot a táblagéppel a közvetlen letöltéshez

+
+
+
+
+
+ + @if (Model.PreviousVersions != null && Model.PreviousVersions.Any()) + { + +
+
+

+ + Korábbi verziók +

+
+ +
+
+ +
+ } + } + else + { +
+
+
+ + Nincs elérhető app verzió. Kérlek, helyezd el az APK fájlt a wwwroot/app/versions/ mappában. +
+
+
+ } +
+
+ + + + + + + + \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs index 88132a5..5c0f27b 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs @@ -156,6 +156,16 @@ public class RouteProvider : IRouteProvider name: "Plugin.FruitBank.Admin.ManagementPage.ProcessShippingDocument", pattern: "Admin/ManagamentPage/ProcessShippingDocument/{id}", defaults: new { controller = "ManagementPage", action = "ProcessShippingdocument", area = AreaNames.ADMIN }); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.AppDownload", + pattern: "Admin/AppDownload", + defaults: new { controller = "AppDownload", action = "Index", area = AreaNames.ADMIN }); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.AppDownload.Download", + pattern: "Admin/AppDownload/Download/{version}/{fileName}", + defaults: new { controller = "AppDownload", action = "Download", area = AreaNames.ADMIN }); } /// diff --git a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj index 1bb38be..2e10b5d 100644 --- a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj +++ b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj @@ -167,6 +167,9 @@ + + Always + Always