refactor(storage): scope exports by owner
This commit is contained in:
@@ -8,7 +8,7 @@ public sealed record CvPdfArtifact(string FileName, string StoragePath, byte[] B
|
||||
|
||||
public interface ICvPdfExporter
|
||||
{
|
||||
Task<CvPdfArtifact> ExportAsync(TailoredCvRenderResult renderResult, CancellationToken cancellationToken);
|
||||
Task<CvPdfArtifact> ExportAsync(string ownerUserId, TailoredCvRenderResult renderResult, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed class PlaywrightCvPdfExporter : ICvPdfExporter
|
||||
@@ -36,17 +36,18 @@ public sealed class PlaywrightCvPdfExporter : ICvPdfExporter
|
||||
_retentionDays = Math.Clamp(configuration.GetValue("CvExports:RetainDays", 30), 1, 365);
|
||||
}
|
||||
|
||||
public async Task<CvPdfArtifact> ExportAsync(TailoredCvRenderResult renderResult, CancellationToken cancellationToken)
|
||||
public async Task<CvPdfArtifact> ExportAsync(string ownerUserId, TailoredCvRenderResult renderResult, CancellationToken cancellationToken)
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
PruneExpiredExports(DateOnly.FromDateTime(now.UtcDateTime).AddDays(-_retentionDays));
|
||||
var folder = Path.Combine(_paths.CvExportsRoot, now.ToString("yyyyMMdd"));
|
||||
var folder = Path.Combine(_paths.GetOwnerCvExportsRoot(ownerUserId), now.ToString("yyyyMMdd"));
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
var fileName = string.IsNullOrWhiteSpace(renderResult.SuggestedFileName)
|
||||
var suggestedFileName = string.IsNullOrWhiteSpace(renderResult.SuggestedFileName)
|
||||
? $"tailored-cv-{now:yyyyMMddHHmmss}.pdf"
|
||||
: renderResult.SuggestedFileName;
|
||||
var storagePath = Path.Combine(folder, fileName);
|
||||
: Path.GetFileName(renderResult.SuggestedFileName);
|
||||
var fileName = string.IsNullOrWhiteSpace(suggestedFileName) ? $"tailored-cv-{now:yyyyMMddHHmmss}.pdf" : suggestedFileName;
|
||||
var storagePath = Path.Combine(folder, $"{Guid.NewGuid():N}.pdf");
|
||||
|
||||
var tempRoot = Path.Combine(Path.GetTempPath(), "jobtracker-cv-pdf", Guid.NewGuid().ToString("n"));
|
||||
var htmlPath = Path.Combine(tempRoot, "document.html");
|
||||
@@ -118,19 +119,36 @@ public sealed class PlaywrightCvPdfExporter : ICvPdfExporter
|
||||
foreach (var directory in Directory.EnumerateDirectories(_paths.CvExportsRoot))
|
||||
{
|
||||
var name = Path.GetFileName(directory);
|
||||
if (!DateOnly.TryParseExact(name, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date) || date >= cutoff) continue;
|
||||
if (DateOnly.TryParseExact(name, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var legacyDate))
|
||||
{
|
||||
TryDeleteExpired(directory, legacyDate, cutoff);
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
foreach (var datedDirectory in Directory.EnumerateDirectories(directory))
|
||||
{
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Could not prune expired CV export directory {Directory}", directory);
|
||||
var datedName = Path.GetFileName(datedDirectory);
|
||||
if (DateOnly.TryParseExact(datedName, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date))
|
||||
{
|
||||
TryDeleteExpired(datedDirectory, date, cutoff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryDeleteExpired(string directory, DateOnly date, DateOnly cutoff)
|
||||
{
|
||||
if (date >= cutoff) return;
|
||||
try
|
||||
{
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Could not prune expired CV export directory {Directory}", directory);
|
||||
}
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> BuildArguments(string storagePath, string htmlPath)
|
||||
{
|
||||
return new[]
|
||||
|
||||
Reference in New Issue
Block a user