diff --git a/DysonNetwork.Zone/Publication/PublicationSiteManager.cs b/DysonNetwork.Zone/Publication/PublicationSiteManager.cs index fcf4b77..a78ca25 100644 --- a/DysonNetwork.Zone/Publication/PublicationSiteManager.cs +++ b/DysonNetwork.Zone/Publication/PublicationSiteManager.cs @@ -1,4 +1,5 @@ using DysonNetwork.Shared.Models; +using System.IO.Compression; namespace DysonNetwork.Zone.Publication; @@ -136,4 +137,42 @@ public class PublicationSiteManager( else if (Directory.Exists(fullPath)) Directory.Delete(fullPath, true); } + + public async Task PurgeSite(Guid siteId) + { + await EnsureSiteDirectory(siteId); // This ensures site exists and is self-managed + var siteDirectory = Path.Combine(_basePath, siteId.ToString()); + if (Directory.Exists(siteDirectory)) + { + Directory.Delete(siteDirectory, true); // true for recursive delete + Directory.CreateDirectory(siteDirectory); // Recreate empty directory + } + } + + public async Task DeployZip(Guid siteId, IFormFile zipFile) + { + await EnsureSiteDirectory(siteId); + var siteDirectory = Path.Combine(_basePath, siteId.ToString()); + + // Create a temporary file for the uploaded zip + var tempFilePath = Path.GetTempFileName(); + try + { + await using (var stream = new FileStream(tempFilePath, FileMode.Create)) + { + await zipFile.CopyToAsync(stream); + } + + // Extract the zip file to the site's directory, overwriting existing files + await ZipFile.ExtractToDirectoryAsync(tempFilePath, siteDirectory, true); + } + finally + { + // Clean up the temporary file + if (File.Exists(tempFilePath)) + { + File.Delete(tempFilePath); + } + } + } } diff --git a/DysonNetwork.Zone/Publication/SiteManagerController.cs b/DysonNetwork.Zone/Publication/SiteManagerController.cs index a2583e6..1921e6f 100644 --- a/DysonNetwork.Zone/Publication/SiteManagerController.cs +++ b/DysonNetwork.Zone/Publication/SiteManagerController.cs @@ -82,6 +82,68 @@ public class SiteManagerController( } } + [HttpPost("deploy")] + [Authorize] + [Consumes("multipart/form-data")] + public async Task Deploy(Guid siteId, IFormFile? zipFile) + { + var check = await CheckAccess(siteId); + if (check != null) return check; + + if (zipFile == null || zipFile.Length == 0) + return BadRequest("No file provided."); + + if (Path.GetExtension(zipFile.FileName).ToLowerInvariant() != ".zip") + return BadRequest("Only .zip files are allowed for deployment."); + + // Define size limits + const long maxZipFileSize = 52428800; // 50MB for the zip file itself + const long maxTotalSiteSizeAfterExtract = 104857600; // 100MB total size after extraction + + if (zipFile.Length > maxZipFileSize) + return BadRequest($"Zip file size exceeds {maxZipFileSize / (1024 * 1024)}MB limit."); + + try + { + // For now, we'll only check the zip file size. + // A more robust solution might involve extracting to a temp location + // and checking the uncompressed size before moving, but that's more complex. + + // Get current site size before deployment + long currentTotal = await fileManager.GetTotalSiteSize(siteId); + + // 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."); + + await fileManager.DeployZip(siteId, zipFile); + return Ok("Deployment successful."); + } + catch (Exception ex) + { + return BadRequest($"Deployment failed: {ex.Message}"); + } + } + + [HttpDelete("purge")] + [Authorize] + public async Task Purge(Guid siteId) + { + var check = await CheckAccess(siteId); + if (check != null) return check; + + try + { + await fileManager.PurgeSite(siteId); + return Ok("Site content purged successfully."); + } + catch (Exception ex) + { + return BadRequest($"Purge failed: {ex.Message}"); + } + } + [HttpGet("content/{**relativePath}")] [Authorize] public async Task> GetFileContent(Guid siteId, string relativePath)