Rilascia gestione completa progetti e aggiornamenti 0.4.0

This commit is contained in:
livio committed 2026-09-24 17:21:25 +02:00
1 parent 620a4bec26
commit 205a520708
10 files changed
+1366 -65

No files matched your search

+54 -6
View File
@@ -68,7 +68,7 @@ public sealed class ProjectStorageService
}
var document = new MapProjectDocument
{
SchemaVersion = 2,
SchemaVersion = 3,
Id = id,
Name = normalizedName,
CreatedAt = existing?.Document.CreatedAt ?? now,
@@ -76,9 +76,15 @@ public sealed class ProjectStorageService
IncludesCadastralParcels = includesCadastralParcels,
Folder = existing?.Document.Folder ?? string.Empty,
IsArchived = existing?.Document.IsArchived ?? false,
AccessMode = existing?.Document.AccessMode ?? "editable",
Map = snapshotDocument.RootElement.Clone()
};
if (existing?.Document.AccessMode == "readonly")
{
throw new InvalidOperationException("Il progetto e' in sola lettura. Duplicalo per modificarlo.");
}
var path = existing?.Path ?? Path.Combine(ProjectsDirectory, BuildFileName(normalizedName, id));
var temporaryPath = path + ".tmp";
try
@@ -131,6 +137,7 @@ public sealed class ProjectStorageService
UpdatedAt = now,
IncludesCadastralParcels = source.Document.IncludesCadastralParcels,
Folder = source.Document.Folder,
AccessMode = "editable",
Map = source.Document.Map.Clone()
};
var path = Path.Combine(ProjectsDirectory, BuildFileName(name, id));
@@ -240,7 +247,7 @@ public sealed class ProjectStorageService
finally { _gate.Release(); }
}
public async Task<string> CreateProjectPackageAsync(string projectId)
public async Task<string> CreateProjectPackageAsync(string projectId, bool readOnly = false)
{
await _gate.WaitAsync();
try
@@ -249,8 +256,22 @@ public sealed class ProjectStorageService
?? throw new FileNotFoundException("Il progetto selezionato non esiste piu'.");
var directory = Path.Combine(FileSystem.CacheDirectory, "Projects");
Directory.CreateDirectory(directory);
var path = Path.Combine(directory, Path.GetFileName(project.Path));
File.Copy(project.Path, path, true);
var mode = readOnly ? "readonly" : "editable";
var path = Path.Combine(directory, $"{Path.GetFileNameWithoutExtension(project.Path)}-{mode}{ProjectExtension}");
var package = new MapProjectDocument
{
SchemaVersion = 3,
Id = project.Document.Id,
Name = project.Document.Name,
CreatedAt = project.Document.CreatedAt,
UpdatedAt = project.Document.UpdatedAt,
IncludesCadastralParcels = project.Document.IncludesCadastralParcels,
Folder = project.Document.Folder,
IsArchived = false,
AccessMode = mode,
Map = project.Document.Map.Clone()
};
await File.WriteAllTextAsync(path, JsonSerializer.Serialize(package, _jsonOptions), new UTF8Encoding(false));
return path;
}
finally { _gate.Release(); }
@@ -269,6 +290,7 @@ public sealed class ProjectStorageService
source.Name = NormalizeName(source.Name);
source.CreatedAt = source.UpdatedAt = DateTimeOffset.UtcNow;
source.IsArchived = false;
source.AccessMode = source.AccessMode == "readonly" ? "readonly" : "editable";
Directory.CreateDirectory(ProjectsDirectory);
var target = Path.Combine(ProjectsDirectory, BuildFileName(source.Name, source.Id));
await File.WriteAllTextAsync(target, JsonSerializer.Serialize(source, _jsonOptions), new UTF8Encoding(false));
@@ -316,6 +338,30 @@ public sealed class ProjectStorageService
}
}
public async Task<string> CreateScheduledBackupAsync(int keep = 10)
{
var source = await CreateBackupAsync();
var directory = Path.Combine(FileSystem.AppDataDirectory, "AutomaticBackups");
Directory.CreateDirectory(directory);
var target = Path.Combine(directory, $"LME-auto-{DateTime.Now:yyyyMMdd-HHmmss}.lmebackup.zip");
File.Copy(source, target, true);
foreach (var stale in Directory.EnumerateFiles(directory, "*.lmebackup.zip")
.OrderByDescending(File.GetLastWriteTimeUtc).Skip(Math.Clamp(keep, 2, 30)))
{
File.Delete(stale);
}
return target;
}
public DateTimeOffset? GetLatestScheduledBackupTime()
{
var directory = Path.Combine(FileSystem.AppDataDirectory, "AutomaticBackups");
if (!Directory.Exists(directory)) return null;
var latest = Directory.EnumerateFiles(directory, "*.lmebackup.zip")
.OrderByDescending(File.GetLastWriteTimeUtc).FirstOrDefault();
return latest is null ? null : File.GetLastWriteTimeUtc(latest);
}
private async Task<IReadOnlyList<ProjectFileEntry>> ReadProjectFilesAsync()
{
Directory.CreateDirectory(ProjectsDirectory);
@@ -397,9 +443,10 @@ public sealed class ProjectStorageService
public bool IncludesCadastralParcels { get; set; }
public string Folder { get; set; } = string.Empty;
public bool IsArchived { get; set; }
public string AccessMode { get; set; } = "editable";
public JsonElement Map { get; set; }
public MapProjectSummary ToSummary() => new(Id, Name, UpdatedAt, IncludesCadastralParcels, Folder, IsArchived);
public MapProjectSummary ToSummary() => new(Id, Name, UpdatedAt, IncludesCadastralParcels, Folder, IsArchived, AccessMode == "readonly");
}
}
@@ -409,6 +456,7 @@ public sealed record MapProjectSummary(
DateTimeOffset UpdatedAt,
bool IncludesCadastralParcels,
string Folder,
bool IsArchived);
bool IsArchived,
bool IsReadOnly);
public sealed record MapProjectVersion(string Id, DateTimeOffset UpdatedAt);