Initial commit
This commit is contained in:
commit
77d35ff965
51 changed files with 5591 additions and 0 deletions
333
Pages/Index.cshtml
Normal file
333
Pages/Index.cshtml
Normal file
|
|
@ -0,0 +1,333 @@
|
|||
@page
|
||||
@model PlanTempusAdmin.Pages.IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
}
|
||||
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Dashboard</h1>
|
||||
<p class="page-subtitle">PlanTempus SaaS Infrastructure Status</p>
|
||||
</div>
|
||||
|
||||
<!-- Top Status Grid -->
|
||||
<div class="status-grid">
|
||||
<div class="status-item">
|
||||
<div class="status-label">Caddy Server</div>
|
||||
<div class="status-value @(Model.CaddyRunning ? "success" : "danger")">
|
||||
@(Model.CaddyRunning ? "ONLINE" : "OFFLINE")
|
||||
</div>
|
||||
<div class="status-detail">@Model.HostCount hosts</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-label">Forgejo</div>
|
||||
<div class="status-value @(Model.ForgejoConnected ? "success" : "danger")">
|
||||
@if (Model.ForgejoConnected)
|
||||
{
|
||||
@if (Model.ForgejoDashboard.RunningNow > 0)
|
||||
{
|
||||
<span class="pulse">●</span> @Model.ForgejoDashboard.RunningNow <text> CI</text>
|
||||
}
|
||||
else
|
||||
{
|
||||
<text>ONLINE</text>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<text>OFFLINE</text>
|
||||
}
|
||||
</div>
|
||||
<div class="status-detail">@Model.ForgejoDashboard.TotalRepos repos</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-label">Backup</div>
|
||||
<div class="status-value @(Model.BackupDbConnected ? (Model.LastBackupOk ? "success" : "warning") : "danger")">
|
||||
@if (Model.BackupDbConnected)
|
||||
{
|
||||
@(Model.LastBackupAge ?? "INGEN")
|
||||
}
|
||||
else
|
||||
{
|
||||
<text>OFFLINE</text>
|
||||
}
|
||||
</div>
|
||||
<div class="status-detail">@Model.BackupSummary.SuccessfulBackups OK / @Model.BackupSummary.FailedBackups fejl</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-label">Azure Storage</div>
|
||||
<div class="status-value @(Model.AzureConnected ? "success" : "danger")">
|
||||
@(Model.AzureConnected ? "ONLINE" : "OFFLINE")
|
||||
</div>
|
||||
<div class="status-detail">@FormatSize(Model.AzureDashboard.TotalSize)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cards Grid -->
|
||||
<div class="dashboard-cards mt-2">
|
||||
<!-- Forgejo Card -->
|
||||
<div class="card">
|
||||
<div class="card-header">Forgejo Git</div>
|
||||
<div class="card-body">
|
||||
@if (Model.ForgejoConnected)
|
||||
{
|
||||
<div class="mini-stats">
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@Model.ForgejoDashboard.TotalRepos</div>
|
||||
<div class="mini-stat-label">Repos</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@Model.ForgejoDashboard.TotalOpenIssues</div>
|
||||
<div class="mini-stat-label">Issues</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@Model.ForgejoDashboard.TotalOpenPRs</div>
|
||||
<div class="mini-stat-label">PRs</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@FormatSize(Model.ForgejoDashboard.TotalSize * 1024)</div>
|
||||
<div class="mini-stat-label">Størrelse</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.ForgejoDashboard.RunningRuns.Count > 0)
|
||||
{
|
||||
<div class="running-list mt-1">
|
||||
@foreach (var run in Model.ForgejoDashboard.RunningRuns.Take(3))
|
||||
{
|
||||
<div class="running-item">
|
||||
<span class="pulse">●</span>
|
||||
<code>@run.FullRepoName</code>
|
||||
<span class="text-muted">@run.WorkflowId</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<a href="/Forgejo" class="btn btn-primary mt-1">Se detaljer</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-danger">Forgejo database ikke tilgængelig</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Caddy Card -->
|
||||
<div class="card">
|
||||
<div class="card-header">Caddy Reverse Proxy</div>
|
||||
<div class="card-body">
|
||||
@if (Model.CaddyRunning)
|
||||
{
|
||||
<p>Server kører og håndterer <strong>@Model.HostCount</strong> host(s).</p>
|
||||
<a href="/Caddy" class="btn btn-primary mt-1">Se detaljer</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-danger">Caddy server er ikke tilgængelig.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Backup Card -->
|
||||
<div class="card">
|
||||
<div class="card-header">Backup Status</div>
|
||||
<div class="card-body">
|
||||
@if (Model.BackupDbConnected)
|
||||
{
|
||||
<div class="mini-stats">
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value success">@Model.BackupSummary.SuccessfulBackups</div>
|
||||
<div class="mini-stat-label">Success</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value @(Model.BackupSummary.FailedBackups > 0 ? "danger" : "")">@Model.BackupSummary.FailedBackups</div>
|
||||
<div class="mini-stat-label">Fejlet</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@FormatSize(Model.BackupSummary.TotalSizeBytes)</div>
|
||||
<div class="mini-stat-label">Total</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/Backup" class="btn btn-primary mt-1">Se detaljer</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-danger">Backup database er ikke tilgængelig.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CI/CD Card -->
|
||||
<div class="card">
|
||||
<div class="card-header">CI/CD Actions</div>
|
||||
<div class="card-body">
|
||||
@if (Model.ForgejoConnected)
|
||||
{
|
||||
<div class="mini-stats">
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value success">@Model.ForgejoDashboard.SuccessfulRuns</div>
|
||||
<div class="mini-stat-label">Success</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value @(Model.ForgejoDashboard.FailedRunsCount > 0 ? "danger" : "")">@Model.ForgejoDashboard.FailedRunsCount</div>
|
||||
<div class="mini-stat-label">Fejlet</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@Model.ForgejoDashboard.RunsThisWeek</div>
|
||||
<div class="mini-stat-label">Denne uge</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.ForgejoDashboard.FailedRuns.Count > 0)
|
||||
{
|
||||
<div class="failed-list mt-1">
|
||||
<div class="failed-header">Seneste fejl:</div>
|
||||
@foreach (var run in Model.ForgejoDashboard.FailedRuns.Take(2))
|
||||
{
|
||||
<div class="failed-item">
|
||||
<span class="badge badge-danger">FEJL</span>
|
||||
<code>@run.FullRepoName</code>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<a href="/Forgejo/Actions" class="btn btn-primary mt-1">Se detaljer</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted">Ikke tilgængelig</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Azure Storage Card -->
|
||||
<div class="card">
|
||||
<div class="card-header">Azure Blob Storage</div>
|
||||
<div class="card-body">
|
||||
@if (Model.AzureConnected)
|
||||
{
|
||||
<div class="mini-stats">
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@Model.AzureDashboard.TotalContainers</div>
|
||||
<div class="mini-stat-label">Containers</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@Model.AzureDashboard.TotalBlobs</div>
|
||||
<div class="mini-stat-label">Blobs</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@FormatSize(Model.AzureDashboard.TotalSize)</div>
|
||||
<div class="mini-stat-label">Størrelse</div>
|
||||
</div>
|
||||
<div class="mini-stat">
|
||||
<div class="mini-stat-value">@Model.AzureDashboard.BackupFileCount</div>
|
||||
<div class="mini-stat-label">Backups</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.AzureDashboard.RecentBlobs.Count > 0)
|
||||
{
|
||||
<div class="running-list mt-1">
|
||||
@foreach (var blob in Model.AzureDashboard.RecentBlobs.Take(2))
|
||||
{
|
||||
<div class="running-item">
|
||||
<span>📄</span>
|
||||
<code>@blob.FileName</code>
|
||||
<span class="text-muted">@FormatSize(blob.Size)</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<a href="/Azure" class="btn btn-primary mt-1">Se detaljer</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-danger">Azure Storage ikke tilgængelig</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.status-detail {
|
||||
font-size: 10px;
|
||||
color: var(--muted-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.pulse {
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
||||
@@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
.dashboard-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.mini-stats {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.mini-stat {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mini-stat-value {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mini-stat-value.success { color: var(--success-color); }
|
||||
.mini-stat-value.danger { color: var(--danger-color); }
|
||||
|
||||
.mini-stat-label {
|
||||
font-size: 10px;
|
||||
color: var(--muted-color);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.running-list, .failed-list {
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.running-item, .failed-item {
|
||||
font-size: 11px;
|
||||
padding: 4px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.failed-header {
|
||||
font-size: 10px;
|
||||
color: var(--muted-color);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
@@media (max-width: 900px) {
|
||||
.dashboard-cards {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@functions {
|
||||
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]}";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue