V11 / ca6f28eff240
WorkspaceProjectExtensions.cs
Lilith/Core/Workspace/Extensions/WorkspaceProjectExtensions.cs
Changed in this versionCS
using Lilith.Core.Projects;
namespace Lilith.Core.Workspace.Extensions;
internal static class WorkspaceProjectExtensions
{
internal static ProjectBundle ReadProjectBundle(this WorkspaceRequest request, WorkspaceOptions options)
{
if (request.Content.Length > 200000) throw new InvalidDataException("Project proposal exceeds 200000 characters.");
var bundle = ProjectJson.Parse<ProjectBundle>(request.Content);
if (bundle.Files is null || bundle.Files.Length is < 3 or > 16 || bundle.Files.Sum(f => f.Content?.Length ?? 0) > 160000 ||
bundle.Files.Select(f => f.Path).Distinct(StringComparer.OrdinalIgnoreCase).Count() != bundle.Files.Length)
throw new InvalidDataException("Invalid project publication bundle.");
string target = options.ResolvePath(request.Path);
foreach (var file in bundle.Files)
{
if (file.Content is null) throw new InvalidDataException("Missing project file content.");
string path = options.ResolvePath(request.Path + "/" + file.Path);
if (!path.StartsWith(target + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) ||
Path.GetExtension(file.Path).ToLowerInvariant() is not (".cs" or ".csproj" or ".html" or ".css" or ".js" or ".txt" or ".json"))
throw new InvalidDataException("Invalid project publication path.");
}
if (!bundle.Files.Any(f => f.Path == "lilith-project.json")) throw new InvalidDataException("Missing project manifest.");
return bundle;
}
internal static async Task PublishProjectAsync(this WorkspaceRequest request, WorkspaceOptions options, string target, string operationId)
{
var bundle = request.ReadProjectBundle(options);
string temporary = options.GetMetadataPath("project-staging", operationId);
if (Directory.Exists(temporary)) throw new IOException("A previous incomplete publication is retained for review.");
Directory.CreateDirectory(temporary);
foreach (var file in bundle.Files)
{
string path = Path.Combine(temporary, file.Path.Replace('/', Path.DirectorySeparatorChar));
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
await File.WriteAllTextAsync(path, file.Content, CancellationToken.None);
}
// Same workspace volume: publication is a single directory rename and never overwrites a project.
Directory.Move(temporary, target);
}
}