feat(caddy): support renaming configuration files
This commit is contained in:
@@ -106,6 +106,91 @@ public class CaddyService(
|
||||
Content = content
|
||||
});
|
||||
|
||||
/// <inheritdoc />
|
||||
public CaddyOperationResponse RenameCaddyConfiguration(string oldFileName, string newFileName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(newFileName))
|
||||
{
|
||||
return Failure("The configuration file name is required");
|
||||
}
|
||||
|
||||
if (oldFileName == CaddyGlobalConfigName || newFileName == CaddyGlobalConfigName)
|
||||
{
|
||||
return Failure("The global Caddyfile cannot be renamed");
|
||||
}
|
||||
|
||||
if (oldFileName == newFileName)
|
||||
{
|
||||
return new CaddyOperationResponse
|
||||
{
|
||||
Success = true,
|
||||
Message = "Configuration file renamed successfully"
|
||||
};
|
||||
}
|
||||
|
||||
if (IsInvalidFileName(newFileName))
|
||||
{
|
||||
return Failure("The configuration file name contains invalid characters");
|
||||
}
|
||||
|
||||
var oldPath = Path.Combine(Configurations.ConfigDir, $"{oldFileName}.caddy");
|
||||
var newPath = Path.Combine(Configurations.ConfigDir, $"{newFileName}.caddy");
|
||||
|
||||
if (!File.Exists(oldPath))
|
||||
{
|
||||
return Failure("The configuration file to rename does not exist");
|
||||
}
|
||||
|
||||
// On case insensitive file systems the target resolves to the source for a case only rename,
|
||||
// so the collision check has to be skipped there
|
||||
var caseOnlyRename = string.Equals(oldFileName, newFileName, StringComparison.OrdinalIgnoreCase);
|
||||
if (!caseOnlyRename && File.Exists(newPath))
|
||||
{
|
||||
return Failure("The configuration file already exists");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (caseOnlyRename)
|
||||
{
|
||||
// A direct move would be rejected as an existing destination on a case insensitive
|
||||
// file system, so go through an intermediate name that cannot collide
|
||||
var tempPath = Path.Combine(Configurations.ConfigDir, $"{newFileName}.caddy.renaming");
|
||||
File.Move(oldPath, tempPath);
|
||||
File.Move(tempPath, newPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Move(oldPath, newPath);
|
||||
}
|
||||
|
||||
return new CaddyOperationResponse
|
||||
{
|
||||
Success = true,
|
||||
Message = "Configuration file renamed successfully"
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return Failure(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static CaddyOperationResponse Failure(string message) => new()
|
||||
{
|
||||
Success = false,
|
||||
Message = message
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Guards the rename against path traversal and characters the file system would reject
|
||||
/// </summary>
|
||||
private static bool IsInvalidFileName(string fileName) =>
|
||||
fileName.Contains("..") || fileName.IndexOfAny(InvalidFileNameChars) >= 0;
|
||||
|
||||
private static readonly char[] InvalidFileNameChars =
|
||||
[.. Path.GetInvalidFileNameChars().Union(['/', '\\'])];
|
||||
|
||||
/// <inheritdoc />
|
||||
public CaddyDeleteOperationResponse DeleteCaddyConfigurations(List<string> configurationNames)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user