Initial commit
This commit is contained in:
commit
77d35ff965
51 changed files with 5591 additions and 0 deletions
63
Models/AzureStorageModels.cs
Normal file
63
Models/AzureStorageModels.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
namespace PlanTempusAdmin.Models;
|
||||
|
||||
public class AzureContainer
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public DateTimeOffset? LastModified { get; set; }
|
||||
public long TotalSize { get; set; }
|
||||
public int BlobCount { get; set; }
|
||||
}
|
||||
|
||||
public class AzureBlob
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? ContentType { get; set; }
|
||||
public long Size { get; set; }
|
||||
public DateTimeOffset? LastModified { get; set; }
|
||||
public DateTimeOffset? CreatedOn { get; set; }
|
||||
public string? AccessTier { get; set; }
|
||||
public BlobType BlobType { get; set; }
|
||||
|
||||
// Computed
|
||||
public string FileName => Name.Contains('/') ? Name.Substring(Name.LastIndexOf('/') + 1) : Name;
|
||||
public string? Directory => Name.Contains('/') ? Name.Substring(0, Name.LastIndexOf('/')) : null;
|
||||
public bool IsBackup => Name.EndsWith(".tar.gz") || Name.EndsWith(".sql.gz") || Name.EndsWith(".zip") || Name.EndsWith(".bak");
|
||||
}
|
||||
|
||||
public enum BlobType
|
||||
{
|
||||
Block,
|
||||
Page,
|
||||
Append
|
||||
}
|
||||
|
||||
public class AzureStorageDashboard
|
||||
{
|
||||
public bool IsConnected { get; set; }
|
||||
public string AccountName { get; set; } = string.Empty;
|
||||
|
||||
// Stats
|
||||
public int TotalContainers { get; set; }
|
||||
public long TotalSize { get; set; }
|
||||
public int TotalBlobs { get; set; }
|
||||
|
||||
// Containers
|
||||
public List<AzureContainer> Containers { get; set; } = new();
|
||||
|
||||
// Recent blobs
|
||||
public List<AzureBlob> RecentBlobs { get; set; } = new();
|
||||
|
||||
// Backup specific
|
||||
public int BackupFileCount { get; set; }
|
||||
public long BackupTotalSize { get; set; }
|
||||
public DateTimeOffset? LastBackupUpload { get; set; }
|
||||
}
|
||||
|
||||
public class ContainerDetails
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public long TotalSize { get; set; }
|
||||
public int BlobCount { get; set; }
|
||||
public List<AzureBlob> Blobs { get; set; } = new();
|
||||
public List<string> Prefixes { get; set; } = new(); // Virtual directories
|
||||
}
|
||||
162
Models/BackupLog.cs
Normal file
162
Models/BackupLog.cs
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
namespace PlanTempusAdmin.Models;
|
||||
|
||||
public class BackupLog
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
// Timing
|
||||
public DateTime StartedAt { get; set; }
|
||||
public DateTime? CompletedAt { get; set; }
|
||||
public int? DurationMs { get; set; }
|
||||
|
||||
// Identifikation
|
||||
public string BackupType { get; set; } = string.Empty; // 'forgejo_repos', 'postgres_db', etc.
|
||||
public string SourceName { get; set; } = string.Empty; // repo navn eller db navn
|
||||
public string? SourcePath { get; set; } // /var/lib/forgejo/repositories/user/repo.git
|
||||
|
||||
// Destination
|
||||
public string Destination { get; set; } = string.Empty; // 'azure_blob', 's3', 'local', 'sftp'
|
||||
public string? RemotePath { get; set; } // azure:container/backups/2024-01-31/repo.tar.gz
|
||||
|
||||
// Resultat
|
||||
public string Status { get; set; } = string.Empty; // 'running', 'success', 'failed', 'partial'
|
||||
public long? SizeBytes { get; set; }
|
||||
public int? FileCount { get; set; } // antal filer i backup
|
||||
|
||||
// Fejlhåndtering
|
||||
public string? ErrorMessage { get; set; }
|
||||
public string? ErrorCode { get; set; } // 'AZURE_UPLOAD_FAILED', 'DISK_FULL', 'TAR_FAILED', etc.
|
||||
public int RetryCount { get; set; }
|
||||
|
||||
// Metadata
|
||||
public string? Hostname { get; set; } // hvilken server kørte backup
|
||||
public string? ScriptVersion { get; set; } // version af backup script
|
||||
public string? Checksum { get; set; } // SHA256 af backup fil
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
// Legacy compatibility properties for existing pages
|
||||
public DateTime Timestamp => StartedAt;
|
||||
public string Database => SourceName;
|
||||
public TimeSpan? Duration => DurationMs.HasValue ? TimeSpan.FromMilliseconds(DurationMs.Value) : null;
|
||||
public string? FilePath => RemotePath ?? SourcePath;
|
||||
}
|
||||
|
||||
public class BackupSummary
|
||||
{
|
||||
public int TotalBackups { get; set; }
|
||||
public int SuccessfulBackups { get; set; }
|
||||
public int FailedBackups { get; set; }
|
||||
public DateTime? LastBackup { get; set; }
|
||||
public DateTime? LastSuccessfulBackup { get; set; }
|
||||
public long TotalSizeBytes { get; set; }
|
||||
}
|
||||
|
||||
public class RepositorySummary
|
||||
{
|
||||
public string SourceName { get; set; } = string.Empty;
|
||||
public string BackupType { get; set; } = string.Empty;
|
||||
public int TotalBackups { get; set; }
|
||||
public int SuccessfulBackups { get; set; }
|
||||
public int FailedBackups { get; set; }
|
||||
public DateTime? LastBackup { get; set; }
|
||||
public DateTime? LastSuccessfulBackup { get; set; }
|
||||
public long TotalSizeBytes { get; set; }
|
||||
public long? LastBackupSizeBytes { get; set; }
|
||||
public long? PreviousBackupSizeBytes { get; set; }
|
||||
|
||||
public string SizeTrend
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!LastBackupSizeBytes.HasValue || !PreviousBackupSizeBytes.HasValue)
|
||||
return "unknown";
|
||||
if (LastBackupSizeBytes > PreviousBackupSizeBytes * 1.1m)
|
||||
return "growing";
|
||||
if (LastBackupSizeBytes < PreviousBackupSizeBytes * 0.9m)
|
||||
return "shrinking";
|
||||
return "stable";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BackupDashboard
|
||||
{
|
||||
// Overall stats
|
||||
public int TotalBackups { get; set; }
|
||||
public int SuccessfulBackups { get; set; }
|
||||
public int FailedBackups { get; set; }
|
||||
public int RunningBackups { get; set; }
|
||||
public long TotalSizeBytes { get; set; }
|
||||
public double SuccessRate => TotalBackups > 0 ? (double)SuccessfulBackups / TotalBackups * 100 : 0;
|
||||
|
||||
// Time-based
|
||||
public DateTime? LastBackup { get; set; }
|
||||
public DateTime? LastSuccessfulBackup { get; set; }
|
||||
public int BackupsLast24Hours { get; set; }
|
||||
public int BackupsLast7Days { get; set; }
|
||||
public long SizeLast24Hours { get; set; }
|
||||
public long SizeLast7Days { get; set; }
|
||||
|
||||
// Grouped stats
|
||||
public List<BackupTypeStat> ByType { get; set; } = new();
|
||||
public List<DestinationStat> ByDestination { get; set; } = new();
|
||||
public List<HostStat> ByHost { get; set; } = new();
|
||||
public List<ErrorStat> TopErrors { get; set; } = new();
|
||||
public List<DailyStat> DailyStats { get; set; } = new();
|
||||
|
||||
// Recent activity
|
||||
public List<BackupLog> RunningNow { get; set; } = new();
|
||||
public List<BackupLog> RecentSuccesses { get; set; } = new();
|
||||
public List<BackupLog> RecentFailures { get; set; } = new();
|
||||
}
|
||||
|
||||
public class BackupTypeStat
|
||||
{
|
||||
public string BackupType { get; set; } = string.Empty;
|
||||
public int Total { get; set; }
|
||||
public int Successful { get; set; }
|
||||
public int Failed { get; set; }
|
||||
public int Running { get; set; }
|
||||
public long TotalSize { get; set; }
|
||||
public DateTime? LastBackup { get; set; }
|
||||
public double SuccessRate => Total > 0 ? (double)Successful / Total * 100 : 0;
|
||||
}
|
||||
|
||||
public class DestinationStat
|
||||
{
|
||||
public string Destination { get; set; } = string.Empty;
|
||||
public int Total { get; set; }
|
||||
public int Successful { get; set; }
|
||||
public int Failed { get; set; }
|
||||
public long TotalSize { get; set; }
|
||||
public DateTime? LastBackup { get; set; }
|
||||
}
|
||||
|
||||
public class HostStat
|
||||
{
|
||||
public string Hostname { get; set; } = string.Empty;
|
||||
public int Total { get; set; }
|
||||
public int Successful { get; set; }
|
||||
public int Failed { get; set; }
|
||||
public int Running { get; set; }
|
||||
public DateTime? LastBackup { get; set; }
|
||||
public string? ScriptVersion { get; set; }
|
||||
}
|
||||
|
||||
public class ErrorStat
|
||||
{
|
||||
public string ErrorCode { get; set; } = string.Empty;
|
||||
public int Count { get; set; }
|
||||
public DateTime? LastOccurrence { get; set; }
|
||||
public string? LastMessage { get; set; }
|
||||
}
|
||||
|
||||
public class DailyStat
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
public int Total { get; set; }
|
||||
public int Successful { get; set; }
|
||||
public int Failed { get; set; }
|
||||
public long TotalSize { get; set; }
|
||||
}
|
||||
52
Models/CaddyHost.cs
Normal file
52
Models/CaddyHost.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
namespace PlanTempusAdmin.Models;
|
||||
|
||||
public class CaddyHost
|
||||
{
|
||||
public string Hostname { get; set; } = string.Empty;
|
||||
public string[] Addresses { get; set; } = Array.Empty<string>();
|
||||
public bool Tls { get; set; }
|
||||
public string? Upstream { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyConfig
|
||||
{
|
||||
public CaddyApps? Apps { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyApps
|
||||
{
|
||||
public CaddyHttpApp? Http { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyHttpApp
|
||||
{
|
||||
public Dictionary<string, CaddyServer>? Servers { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyServer
|
||||
{
|
||||
public string[]? Listen { get; set; }
|
||||
public CaddyRoute[]? Routes { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyRoute
|
||||
{
|
||||
public CaddyMatch[]? Match { get; set; }
|
||||
public CaddyHandle[]? Handle { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyMatch
|
||||
{
|
||||
public string[]? Host { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyHandle
|
||||
{
|
||||
public string Handler { get; set; } = string.Empty;
|
||||
public CaddyUpstream[]? Upstreams { get; set; }
|
||||
}
|
||||
|
||||
public class CaddyUpstream
|
||||
{
|
||||
public string Dial { get; set; } = string.Empty;
|
||||
}
|
||||
143
Models/Forgejo.cs
Normal file
143
Models/Forgejo.cs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
namespace PlanTempusAdmin.Models;
|
||||
|
||||
public class ForgejoRepository
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string OwnerName { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string FullName => $"{OwnerName}/{Name}";
|
||||
public string? Description { get; set; }
|
||||
public bool IsPrivate { get; set; }
|
||||
public bool IsFork { get; set; }
|
||||
public bool IsArchived { get; set; }
|
||||
public bool IsMirror { get; set; }
|
||||
|
||||
// Stats
|
||||
public int NumStars { get; set; }
|
||||
public int NumForks { get; set; }
|
||||
public int NumWatches { get; set; }
|
||||
public int NumIssues { get; set; }
|
||||
public int NumClosedIssues { get; set; }
|
||||
public int NumPulls { get; set; }
|
||||
public int NumClosedPulls { get; set; }
|
||||
public long Size { get; set; } // in KB
|
||||
|
||||
// Timestamps
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
// Calculated
|
||||
public int OpenIssues => NumIssues - NumClosedIssues;
|
||||
public int OpenPulls => NumPulls - NumClosedPulls;
|
||||
public string SizeFormatted => FormatSize(Size * 1024); // Size is in KB
|
||||
|
||||
private static string FormatSize(long bytes)
|
||||
{
|
||||
if (bytes == 0) return "0 B";
|
||||
var sizes = new[] { "B", "KB", "MB", "GB", "TB" };
|
||||
var i = (int)Math.Floor(Math.Log(bytes) / Math.Log(1024));
|
||||
return $"{Math.Round(bytes / Math.Pow(1024, i), 1)} {sizes[i]}";
|
||||
}
|
||||
}
|
||||
|
||||
public class ForgejoActionRun
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long RepoId { get; set; }
|
||||
public string RepoName { get; set; } = string.Empty;
|
||||
public string OwnerName { get; set; } = string.Empty;
|
||||
public string FullRepoName => $"{OwnerName}/{RepoName}";
|
||||
public string WorkflowId { get; set; } = string.Empty;
|
||||
public long Index { get; set; }
|
||||
public string TriggerUser { get; set; } = string.Empty;
|
||||
public string Ref { get; set; } = string.Empty;
|
||||
public string CommitSha { get; set; } = string.Empty;
|
||||
public string Event { get; set; } = string.Empty; // push, pull_request, etc.
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
// Status: 0=unknown, 1=waiting, 2=running, 3=success, 4=failure, 5=cancelled, 6=skipped
|
||||
public int Status { get; set; }
|
||||
public string StatusText => Status switch
|
||||
{
|
||||
1 => "waiting",
|
||||
2 => "running",
|
||||
3 => "success",
|
||||
4 => "failure",
|
||||
5 => "cancelled",
|
||||
6 => "skipped",
|
||||
_ => "unknown"
|
||||
};
|
||||
|
||||
public DateTime? Started { get; set; }
|
||||
public DateTime? Stopped { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Updated { get; set; }
|
||||
|
||||
public TimeSpan? Duration => Started.HasValue && Stopped.HasValue
|
||||
? Stopped.Value - Started.Value
|
||||
: Started.HasValue ? DateTime.UtcNow - Started.Value : null;
|
||||
}
|
||||
|
||||
public class ForgejoActionJob
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public long RunId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int Status { get; set; }
|
||||
public string StatusText => Status switch
|
||||
{
|
||||
1 => "waiting",
|
||||
2 => "running",
|
||||
3 => "success",
|
||||
4 => "failure",
|
||||
5 => "cancelled",
|
||||
6 => "skipped",
|
||||
_ => "unknown"
|
||||
};
|
||||
public DateTime? Started { get; set; }
|
||||
public DateTime? Stopped { get; set; }
|
||||
}
|
||||
|
||||
public class ForgejoDashboard
|
||||
{
|
||||
// Repository stats
|
||||
public int TotalRepos { get; set; }
|
||||
public int PublicRepos { get; set; }
|
||||
public int PrivateRepos { get; set; }
|
||||
public int ForkedRepos { get; set; }
|
||||
public int ArchivedRepos { get; set; }
|
||||
public int MirrorRepos { get; set; }
|
||||
public long TotalSize { get; set; }
|
||||
public int TotalStars { get; set; }
|
||||
public int TotalForks { get; set; }
|
||||
public int TotalOpenIssues { get; set; }
|
||||
public int TotalOpenPRs { get; set; }
|
||||
|
||||
// Actions stats
|
||||
public int TotalRuns { get; set; }
|
||||
public int RunsToday { get; set; }
|
||||
public int RunsThisWeek { get; set; }
|
||||
public int SuccessfulRuns { get; set; }
|
||||
public int FailedRunsCount { get; set; }
|
||||
public int RunningNow { get; set; }
|
||||
public double SuccessRate => TotalRuns > 0 ? (double)SuccessfulRuns / TotalRuns * 100 : 0;
|
||||
|
||||
// Recent activity
|
||||
public List<ForgejoRepository> RecentlyUpdated { get; set; } = new();
|
||||
public List<ForgejoRepository> LargestRepos { get; set; } = new();
|
||||
public List<ForgejoActionRun> RecentRuns { get; set; } = new();
|
||||
public List<ForgejoActionRun> FailedRuns { get; set; } = new();
|
||||
public List<ForgejoActionRun> RunningRuns { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ForgejoActionStats
|
||||
{
|
||||
public string WorkflowId { get; set; } = string.Empty;
|
||||
public string RepoName { get; set; } = string.Empty;
|
||||
public int TotalRuns { get; set; }
|
||||
public int Successful { get; set; }
|
||||
public int Failed { get; set; }
|
||||
public double SuccessRate => TotalRuns > 0 ? (double)Successful / TotalRuns * 100 : 0;
|
||||
public DateTime? LastRun { get; set; }
|
||||
public double? AvgDurationSeconds { get; set; }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue