Initial commit
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using PowderCoating.Shared.Constants;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Infrastructure.Data;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PowderCoating.Web.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// SuperAdmin-only system health and runtime information page.
|
||||
/// Aggregates application version, database connectivity, migration state,
|
||||
/// seed history, user counts, and host environment details into a single view
|
||||
/// to give platform operators a quick operational snapshot without needing
|
||||
/// server or database console access.
|
||||
/// </summary>
|
||||
[Authorize(Policy = AppConstants.Policies.SuperAdminOnly)]
|
||||
public class SystemInfoController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _dbContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
|
||||
public SystemInfoController(
|
||||
ApplicationDbContext dbContext,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IWebHostEnvironment env)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_userManager = userManager;
|
||||
_env = env;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gathers and renders runtime system information into a
|
||||
/// <see cref="SystemInfoViewModel"/>.
|
||||
/// <para>
|
||||
/// Key design decisions:
|
||||
/// <list type="bullet">
|
||||
/// <item>The informational version string (from
|
||||
/// <see cref="AssemblyInformationalVersionAttribute"/>) is preferred over the
|
||||
/// numeric version because it can include Git commit hashes or pre-release labels
|
||||
/// set during CI builds.</item>
|
||||
/// <item>Database connectivity is tested via <c>CanConnectAsync()</c> rather than
|
||||
/// a raw query to keep the check lightweight and independent of schema state.</item>
|
||||
/// <item>Applied migrations are listed (not pending) so the view can report the
|
||||
/// last applied migration without needing write access to the migration history.</item>
|
||||
/// <item><c>SeedData.LastSeedRun</c> is a static field updated by the seed service
|
||||
/// each time seeding completes; it shows <c>null</c> if no seed has run since
|
||||
/// the last application restart.</item>
|
||||
/// <item>Server time is reported in both local and UTC to help operators diagnose
|
||||
/// timezone-related scheduling issues.</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
// App version from assembly
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
var version = assembly?.GetName().Version?.ToString() ?? "Unknown";
|
||||
var infoVersion = assembly?
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||
?.InformationalVersion ?? version;
|
||||
|
||||
// Database connectivity
|
||||
bool dbConnected;
|
||||
string dbStatus;
|
||||
string? lastMigration = null;
|
||||
int migrationCount = 0;
|
||||
try
|
||||
{
|
||||
dbConnected = await _dbContext.Database.CanConnectAsync();
|
||||
dbStatus = dbConnected ? "Connected" : "Unreachable";
|
||||
if (dbConnected)
|
||||
{
|
||||
var applied = await _dbContext.Database.GetAppliedMigrationsAsync();
|
||||
var migrations = applied.ToList();
|
||||
migrationCount = migrations.Count;
|
||||
lastMigration = migrations.LastOrDefault();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
dbConnected = false;
|
||||
dbStatus = $"Error: {ex.Message}";
|
||||
}
|
||||
|
||||
// User counts
|
||||
var totalUsers = await _userManager.Users.CountAsync();
|
||||
var activeUsers = await _userManager.Users.CountAsync(u => u.IsActive);
|
||||
|
||||
var vm = new SystemInfoViewModel
|
||||
{
|
||||
AppVersion = infoVersion,
|
||||
DatabaseConnected = dbConnected,
|
||||
DatabaseStatus = dbStatus,
|
||||
LastAppliedMigration = lastMigration,
|
||||
MigrationCount = migrationCount,
|
||||
LastSeedRun = SeedData.LastSeedRun,
|
||||
ServerTime = DateTime.Now,
|
||||
ServerTimeUtc = DateTime.UtcNow,
|
||||
ServerTimeZone = TimeZoneInfo.Local.DisplayName,
|
||||
ActiveUserCount = activeUsers,
|
||||
TotalUserCount = totalUsers,
|
||||
EnvironmentName = _env.EnvironmentName,
|
||||
MachineName = Environment.MachineName,
|
||||
OsDescription = System.Runtime.InteropServices.RuntimeInformation.OSDescription,
|
||||
RuntimeVersion = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
|
||||
public class SystemInfoViewModel
|
||||
{
|
||||
public string AppVersion { get; set; } = string.Empty;
|
||||
public bool DatabaseConnected { get; set; }
|
||||
public string DatabaseStatus { get; set; } = string.Empty;
|
||||
public string? LastAppliedMigration { get; set; }
|
||||
public int MigrationCount { get; set; }
|
||||
public DateTime? LastSeedRun { get; set; }
|
||||
public DateTime ServerTime { get; set; }
|
||||
public DateTime ServerTimeUtc { get; set; }
|
||||
public string ServerTimeZone { get; set; } = string.Empty;
|
||||
public int ActiveUserCount { get; set; }
|
||||
public int TotalUserCount { get; set; }
|
||||
public string EnvironmentName { get; set; } = string.Empty;
|
||||
public string MachineName { get; set; } = string.Empty;
|
||||
public string OsDescription { get; set; } = string.Empty;
|
||||
public string RuntimeVersion { get; set; } = string.Empty;
|
||||
}
|
||||
Reference in New Issue
Block a user