🐛 Fix file management of the site

This commit is contained in:
2025-11-21 00:40:58 +08:00
parent 3aa5561a07
commit 75b8567a28
2 changed files with 42 additions and 23 deletions

View File

@@ -23,11 +23,18 @@ public class PublicationSiteManager(
private string GetFullPath(Guid siteId, string relativePath) private string GetFullPath(Guid siteId, string relativePath)
{ {
var fullPath = Path.Combine(_basePath, siteId.ToString(), relativePath); // Treat paths starting with separator as relative to site root
var siteDirPath = Path.Combine(_basePath, siteId.ToString()); relativePath = relativePath.TrimStart('/', '\\');
return !Path.GetRelativePath(siteDirPath, fullPath).StartsWith('.') string fullPath = Path.Combine(_basePath, siteId.ToString(), relativePath);
? throw new ArgumentException("Invalid path") string normalizedPath = Path.GetFullPath(fullPath);
: fullPath; string siteDirFull = Path.Combine(_basePath, siteId.ToString());
string normalizedSiteDir = Path.GetFullPath(siteDirFull);
if (!normalizedPath.StartsWith(normalizedSiteDir + Path.DirectorySeparatorChar) &&
!normalizedPath.Equals(normalizedSiteDir))
{
throw new ArgumentException("Path escapes site directory");
}
return normalizedPath;
} }
private async Task EnsureSiteDirectory(Guid siteId) private async Task EnsureSiteDirectory(Guid siteId)
@@ -43,11 +50,11 @@ public class PublicationSiteManager(
public async Task<List<FileEntry>> ListFiles(Guid siteId, string relativePath = "") public async Task<List<FileEntry>> ListFiles(Guid siteId, string relativePath = "")
{ {
await EnsureSiteDirectory(siteId); await EnsureSiteDirectory(siteId);
var dir = Path.Combine(_basePath, siteId.ToString(), relativePath); var targetDir = GetFullPath(siteId, relativePath);
if (!Directory.Exists(dir)) if (!Directory.Exists(targetDir))
throw new DirectoryNotFoundException("Directory not found"); throw new DirectoryNotFoundException("Directory not found");
var entries = (from file in Directory.GetFiles(dir) var entries = (from file in Directory.GetFiles(targetDir)
let fileInfo = new FileInfo(file) let fileInfo = new FileInfo(file)
select new FileEntry select new FileEntry
{ {
@@ -55,7 +62,7 @@ public class PublicationSiteManager(
RelativePath = Path.GetRelativePath(Path.Combine(_basePath, siteId.ToString()), file), RelativePath = Path.GetRelativePath(Path.Combine(_basePath, siteId.ToString()), file),
Size = fileInfo.Length, Modified = fileInfo.LastWriteTimeUtc Size = fileInfo.Length, Modified = fileInfo.LastWriteTimeUtc
}).ToList(); }).ToList();
entries.AddRange(from subDir in Directory.GetDirectories(dir) entries.AddRange(from subDir in Directory.GetDirectories(targetDir)
let dirInfo = new DirectoryInfo(subDir) let dirInfo = new DirectoryInfo(subDir)
select new FileEntry select new FileEntry
{ {
@@ -108,10 +115,9 @@ public class PublicationSiteManager(
return size; return size;
} }
public string GetFullPathForDownload(Guid siteId, string relativePath) public string GetValidatedFullPath(Guid siteId, string relativePath)
{ {
// Internal method to get path without throwing if not exists return GetFullPath(siteId, relativePath);
return Path.Combine(_basePath, siteId.ToString(), relativePath);
} }
public async Task UpdateFile(Guid siteId, string relativePath, string newContent) public async Task UpdateFile(Guid siteId, string relativePath, string newContent)
@@ -130,4 +136,4 @@ public class PublicationSiteManager(
else if (Directory.Exists(fullPath)) else if (Directory.Exists(fullPath))
Directory.Delete(fullPath, true); Directory.Delete(fullPath, true);
} }
} }

View File

@@ -111,7 +111,16 @@ public class SiteManagerController(
var check = await CheckAccess(siteId); var check = await CheckAccess(siteId);
if (check != null) return check; if (check != null) return check;
var fullPath = fileManager.GetFullPathForDownload(siteId, relativePath); string fullPath;
try
{
fullPath = fileManager.GetValidatedFullPath(siteId, relativePath);
}
catch (ArgumentException)
{
return BadRequest("Invalid path");
}
if (!System.IO.File.Exists(fullPath)) if (!System.IO.File.Exists(fullPath))
return NotFound(); return NotFound();
@@ -137,23 +146,27 @@ public class SiteManagerController(
const long maxFileSize = 1048576; // 1MB const long maxFileSize = 1048576; // 1MB
const long maxTotalSize = 26214400; // 25MB const long maxTotalSize = 26214400; // 25MB
var fullPath = fileManager.GetFullPathForDownload(siteId, relativePath);
long oldSize = 0;
if (System.IO.File.Exists(fullPath))
oldSize = new FileInfo(fullPath).Length;
if (request.NewContent.Length > maxFileSize) if (request.NewContent.Length > maxFileSize)
return BadRequest("New content exceeds 1MB limit"); return BadRequest("New content exceeds 1MB limit");
var currentTotal = await fileManager.GetTotalSiteSize(siteId); long oldSize = 0;
if (currentTotal - oldSize + request.NewContent.Length > maxTotalSize)
return BadRequest("Site total size would exceed 25MB limit");
try try
{ {
var fullPath = fileManager.GetValidatedFullPath(siteId, relativePath);
if (System.IO.File.Exists(fullPath))
oldSize = new FileInfo(fullPath).Length;
var currentTotal = await fileManager.GetTotalSiteSize(siteId);
if (currentTotal - oldSize + request.NewContent.Length > maxTotalSize)
return BadRequest("Site total size would exceed 25MB limit");
await fileManager.UpdateFile(siteId, relativePath, request.NewContent); await fileManager.UpdateFile(siteId, relativePath, request.NewContent);
return Ok(); return Ok();
} }
catch (ArgumentException)
{
return BadRequest("Invalid path");
}
catch (Exception ex) catch (Exception ex)
{ {
return BadRequest(ex.Message); return BadRequest(ex.Message);