Sube carpetas API, API.Tests, Application y Domain del Backend
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a backup step fails in an expected, handled way that callers must catch and log instead of letting it crash the host.
|
||||
/// </summary>
|
||||
public sealed class BackupExecutionException : Exception
|
||||
{
|
||||
public BackupExecutionException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public BackupExecutionException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Metadata describing a single stored backup dump: its stored name and creation timestamp.
|
||||
/// </summary>
|
||||
public sealed record BackupFile(string Name, DateTimeOffset Timestamp);
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Outcome of a single IBackupOperationsService call, mapped explicitly by controllers to an HTTP status code.
|
||||
/// </summary>
|
||||
public enum BackupOperationOutcome
|
||||
{
|
||||
Completed,
|
||||
Busy,
|
||||
NotFound,
|
||||
Failed,
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Application.DTOs.Backup.Response;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Result of a create, delete, or restore attempt, with Record populated only when Outcome is Completed.
|
||||
/// </summary>
|
||||
public sealed record BackupOperationResult(
|
||||
BackupOperationOutcome Outcome, BackupRecordResponse? Record, string? Message);
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Non-secret backup configuration bound from the Backup configuration section.
|
||||
/// </summary>
|
||||
public sealed class BackupOptions
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public int IntervalHours { get; set; } = 24;
|
||||
public int RetentionCount { get; set; } = 7;
|
||||
public string StorageTarget { get; set; } = BackupStorageTargets.Local;
|
||||
public string LocalStoragePath { get; set; } = "backups";
|
||||
public string PgDumpPath { get; set; } = "pg_dump";
|
||||
public string PsqlPath { get; set; } = "psql";
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Named BackupOptions.StorageTarget values, where any value other than Supabase resolves to the local storage backend so a configuration typo fails safe.
|
||||
/// </summary>
|
||||
public static class BackupStorageTargets
|
||||
{
|
||||
public const string Local = "Local";
|
||||
public const string Supabase = "Supabase";
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Domain.Entities.Models;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Durable, queryable per-backup record store that is the source of truth for the admin backup listing.
|
||||
/// </summary>
|
||||
public interface IBackupCatalog
|
||||
{
|
||||
Task<BackupRecord> AddAsync(BackupRecord record, CancellationToken ct = default);
|
||||
|
||||
Task<BackupRecord?> GetByIdAsync(Guid id, CancellationToken ct = default);
|
||||
|
||||
Task<IReadOnlyList<BackupRecord>> ListNewestFirstAsync(CancellationToken ct = default);
|
||||
|
||||
Task RemoveAsync(Guid id, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Domain.Entities.Models;
|
||||
using Domain.Enums;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// The single shared write path for backup operations, serialized by BackupOperationLock so a concurrent attempt returns Busy instead of running alongside another.
|
||||
/// </summary>
|
||||
public interface IBackupOperationsService
|
||||
{
|
||||
Task<IReadOnlyList<BackupRecord>> ListNewestFirstAsync(CancellationToken ct = default);
|
||||
|
||||
Task<BackupOperationResult> CreateBackupAsync(BackupOrigin origin, CancellationToken ct = default);
|
||||
|
||||
Task<BackupOperationResult> DeleteBackupAsync(Guid id, CancellationToken ct = default);
|
||||
|
||||
Task<BackupOperationResult> RestoreBackupAsync(Guid id, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Pure keep-last-N pruning decision over an existing list of backup files; implementations must not perform I/O.
|
||||
/// </summary>
|
||||
public interface IBackupRetentionPolicy
|
||||
{
|
||||
IReadOnlyList<BackupFile> SelectForDeletion(IReadOnlyList<BackupFile> existing, int retainCount);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Persists, lists, and deletes backup dumps in durable off-host storage, whether cloud object storage or a local or mounted directory.
|
||||
/// </summary>
|
||||
public interface IBackupStorage
|
||||
{
|
||||
Task StoreAsync(string name, Stream content, CancellationToken ct = default);
|
||||
|
||||
Task<IReadOnlyList<BackupFile>> ListAsync(CancellationToken ct = default);
|
||||
|
||||
Task DeleteAsync(string name, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Opens a readable stream over the stored backup named name, for the restore flow to copy into a local temp file before invoking IDatabaseRestoreService.
|
||||
/// </summary>
|
||||
Task<Stream> OpenReadAsync(string name, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Produces a single database dump without ever letting an underlying failure crash the caller.
|
||||
/// </summary>
|
||||
public interface IDatabaseBackupService
|
||||
{
|
||||
Task<Stream> CreateDumpAsync(CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Restores the database from a local plain-SQL dump file.
|
||||
/// </summary>
|
||||
public interface IDatabaseRestoreService
|
||||
{
|
||||
/// <summary>
|
||||
/// Restores from the plain-SQL dump at dumpFilePath.
|
||||
/// </summary>
|
||||
Task RestoreAsync(string dumpFilePath, CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Process-wide maintenance-mode flag registered as a singleton, in-memory only.
|
||||
/// </summary>
|
||||
public interface IMaintenanceModeState
|
||||
{
|
||||
bool IsActive { get; }
|
||||
|
||||
string? Reason { get; }
|
||||
|
||||
DateTimeOffset? EnteredAtUtc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Enters maintenance mode with reason.
|
||||
/// </summary>
|
||||
void Enter(string reason);
|
||||
|
||||
/// <summary>
|
||||
/// Exits maintenance mode and is safe to call even when not currently active.
|
||||
/// </summary>
|
||||
void Exit();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Thin abstraction over external process execution.
|
||||
/// </summary>
|
||||
public interface IProcessRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs fileName with the given args, passed as a vector so no argument can be reinterpreted as shell syntax by the child process.
|
||||
/// </summary>
|
||||
Task<ProcessResult> RunAsync(
|
||||
string fileName,
|
||||
IReadOnlyList<string> args,
|
||||
IReadOnlyDictionary<string, string>? environmentVariables = null,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Application.Interfaces.Backup;
|
||||
|
||||
/// <summary>
|
||||
/// Result of running an external process via IProcessRunner, where callers must not treat StdOut as valid output unless ExitCode is 0.
|
||||
/// </summary>
|
||||
public sealed record ProcessResult(int ExitCode, string StdOut, string StdErr);
|
||||
Reference in New Issue
Block a user