From c62ed191f3fcd6c8591f748c13391bcf2b25444c Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 22 Nov 2025 16:00:30 +0800 Subject: [PATCH] :sparkles: File deploy smart mode --- .../Publication/PublicationSiteManager.cs | 5 ++ .../Publication/SiteManagerController.cs | 74 +++++++++++++++++-- 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/DysonNetwork.Zone/Publication/PublicationSiteManager.cs b/DysonNetwork.Zone/Publication/PublicationSiteManager.cs index a78ca25..ab41e7b 100644 --- a/DysonNetwork.Zone/Publication/PublicationSiteManager.cs +++ b/DysonNetwork.Zone/Publication/PublicationSiteManager.cs @@ -121,6 +121,11 @@ public class PublicationSiteManager( return GetFullPath(siteId, relativePath); } + public string GetSiteDirectory(Guid siteId) + { + return Path.Combine(_basePath, siteId.ToString()); + } + public async Task UpdateFile(Guid siteId, string relativePath, string newContent) { await EnsureSiteDirectory(siteId); diff --git a/DysonNetwork.Zone/Publication/SiteManagerController.cs b/DysonNetwork.Zone/Publication/SiteManagerController.cs index 1921e6f..f2da083 100644 --- a/DysonNetwork.Zone/Publication/SiteManagerController.cs +++ b/DysonNetwork.Zone/Publication/SiteManagerController.cs @@ -1,3 +1,4 @@ +using System.IO.Compression; using DysonNetwork.Shared.Models; using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Registry; @@ -24,7 +25,8 @@ public class SiteManagerController( if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); var accountId = Guid.Parse(currentUser.Id); - var isMember = await remotePublisherService.IsMemberWithRole(site.PublisherId, accountId, PublisherMemberRole.Editor); + var isMember = + await remotePublisherService.IsMemberWithRole(site.PublisherId, accountId, PublisherMemberRole.Editor); return !isMember ? Forbid() : null; } @@ -85,7 +87,11 @@ public class SiteManagerController( [HttpPost("deploy")] [Authorize] [Consumes("multipart/form-data")] - public async Task Deploy(Guid siteId, IFormFile? zipFile) + public async Task Deploy( + Guid siteId, + [FromForm(Name = "file")] IFormFile? zipFile, + [FromQuery] bool smart = true + ) { var check = await CheckAccess(siteId); if (check != null) return check; @@ -115,9 +121,67 @@ public class SiteManagerController( // This is a rough check. The actual uncompressed size might be much larger. // Consider adding a more sophisticated check if this is a concern. if (currentTotal + zipFile.Length * 3 > maxTotalSiteSizeAfterExtract) // Heuristic: assume 3x expansion - return BadRequest($"Deployment would exceed total site size limit of {maxTotalSiteSizeAfterExtract / (1024 * 1024)}MB."); + return BadRequest( + $"Deployment would exceed total site size limit of {maxTotalSiteSizeAfterExtract / (1024 * 1024)}MB."); + + var siteDir = fileManager.GetSiteDirectory(siteId); + Directory.CreateDirectory(siteDir); // Ensure site directory exists + + if (smart) + { + // Smart mode: Extract to temp directory and flatten if single folder wrapper + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + try + { + await using (var archive = new ZipArchive(zipFile.OpenReadStream(), ZipArchiveMode.Read)) + { + await archive.ExtractToDirectoryAsync(tempDir, true); + } + + // Check if temp directory has exactly one subdirectory and no files at root + var rootEntries = Directory.GetFileSystemEntries(tempDir); + if (rootEntries.Length == 1 && Directory.Exists(rootEntries[0])) + { + var innerDir = rootEntries[0]; + // Flatten: move contents of innerDir to siteDir + foreach (var file in Directory.GetFiles(innerDir, "*", SearchOption.AllDirectories)) + { + string relPath = Path.GetRelativePath(innerDir, file); + string destFile = Path.Combine(siteDir, relPath); + string? destDir = Path.GetDirectoryName(destFile); + if (!string.IsNullOrEmpty(destDir) && !Directory.Exists(destDir)) + Directory.CreateDirectory(destDir); + System.IO.File.Move(file, destFile, true); + } + + // Also create empty directories + foreach (var dir in Directory.GetDirectories(innerDir, "*", SearchOption.AllDirectories)) + { + string relPath = Path.GetRelativePath(innerDir, dir); + string destDirPath = Path.Combine(siteDir, relPath); + Directory.CreateDirectory(destDirPath); + } + } + else + { + // No smart flattening needed, extract directly to siteDir + using (var archive = new ZipArchive(zipFile.OpenReadStream(), ZipArchiveMode.Read)) + { + archive.ExtractToDirectory(siteDir, true); + } + } + } + finally + { + Directory.Delete(tempDir, true); + } + } + else + { + await fileManager.DeployZip(siteId, zipFile); + } - await fileManager.DeployZip(siteId, zipFile); return Ok("Deployment successful."); } catch (Exception ex) @@ -257,4 +321,4 @@ public class SiteManagerController( public class UpdateFileRequest { public string NewContent { get; set; } = null!; -} +} \ No newline at end of file