Merge pull request #5641 from sbwalker/dev

add ability to view Migration History
This commit is contained in:
Shaun Walker
2025-09-19 15:00:14 -04:00
committed by GitHub
9 changed files with 346 additions and 204 deletions

View File

@ -56,6 +56,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>(); services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
services.AddScoped<ICookieConsentService, CookieConsentService>(); services.AddScoped<ICookieConsentService, CookieConsentService>();
services.AddScoped<ITimeZoneService, TimeZoneService>(); services.AddScoped<ITimeZoneService, TimeZoneService>();
services.AddScoped<IMigrationHistoryService, MigrationHistoryService>();
services.AddScoped<IOutputCacheService, OutputCacheService>(); services.AddScoped<IOutputCacheService, OutputCacheService>();
// providers // providers

View File

@ -2,9 +2,12 @@
@inherits ModuleBase @inherits ModuleBase
@inject ISystemService SystemService @inject ISystemService SystemService
@inject IInstallationService InstallationService @inject IInstallationService InstallationService
@inject IMigrationHistoryService MigrationHistoryService
@inject IStringLocalizer<Index> Localizer @inject IStringLocalizer<Index> Localizer
@inject IStringLocalizer<SharedResources> SharedLocalizer @inject IStringLocalizer<SharedResources> SharedLocalizer
@if (_initialized)
{
<TabStrip> <TabStrip>
<TabPanel Name="Info" Heading="Info" ResourceKey="Info"> <TabPanel Name="Info" Heading="Info" ResourceKey="Info">
<div class="container"> <div class="container">
@ -170,12 +173,29 @@
<br /><br /> <br /><br />
<button type="button" class="btn btn-danger" @onclick="ClearLog">@Localizer["Clear"]</button> <button type="button" class="btn btn-danger" @onclick="ClearLog">@Localizer["Clear"]</button>
</TabPanel> </TabPanel>
<TabPanel Name="Migrations" Heading="Migrations" ResourceKey="Migrations">
<Pager Items="@_history" SearchProperties="MigrationId">
<Header>
<th>@Localizer["Migration"]</th>
<th>@Localizer["Date"]</th>
<th>@Localizer["Version"]</th>
</Header>
<Row>
<td>@context.MigrationId</td>
<td>@UtcToLocal(context.AppliedDate)</td>
<td>@context.AppliedVersion</td>
</Row>
</Pager>
</TabPanel>
</TabStrip> </TabStrip>
<br /><br /> <br /><br />
}
@code { @code {
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host; public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
private bool _initialized = false;
private string _version = string.Empty; private string _version = string.Empty;
private string _clrversion = string.Empty; private string _clrversion = string.Empty;
private string _osversion = string.Empty; private string _osversion = string.Empty;
@ -199,6 +219,8 @@
private string _log = string.Empty; private string _log = string.Empty;
private List<MigrationHistory> _history;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
_version = Constants.Version; _version = Constants.Version;
@ -236,6 +258,10 @@
{ {
_log = systeminfo["Log"].ToString(); _log = systeminfo["Log"].ToString();
} }
_history = await MigrationHistoryService.GetMigrationHistoryAsync();
_initialized = true;
} }
private async Task SaveConfig() private async Task SaveConfig()

View File

@ -309,4 +309,13 @@
<data name="Endpoints" xml:space="preserve"> <data name="Endpoints" xml:space="preserve">
<value>API Endpoints</value> <value>API Endpoints</value>
</data> </data>
<data name="Migration" xml:space="preserve">
<value>Migration</value>
</data>
<data name="Date" xml:space="preserve">
<value>Date</value>
</data>
<data name="Version" xml:space="preserve">
<value>Framework Version</value>
</data>
</root> </root>

View File

@ -0,0 +1,34 @@
using Oqtane.Models;
using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using Oqtane.Documentation;
using Oqtane.Shared;
namespace Oqtane.Services
{
/// <summary>
/// Service to manage <see cref="MigrationHistory/>s on the Oqtane installation.
/// </summary>
public interface IMigrationHistoryService
{
/// <summary>
/// Get all <see cref="MigrationHistory"/>s
/// </summary>
/// <returns></returns>
Task<List<MigrationHistory>> GetMigrationHistoryAsync();
}
[PrivateApi("Don't show in the documentation, as everything should use the Interface")]
public class MigrationHistoryService : ServiceBase, IMigrationHistoryService
{
public MigrationHistoryService(HttpClient http, SiteState siteState) : base(http, siteState) { }
private string Apiurl => CreateApiUrl("MigrationHistory");
public async Task<List<MigrationHistory>> GetMigrationHistoryAsync()
{
return await GetJsonAsync<List<MigrationHistory>>(Apiurl);
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
using System.Collections.Generic;
using Oqtane.Shared;
using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class MigrationHistoryController : Controller
{
private readonly IMigrationHistoryRepository _history;
public MigrationHistoryController(IMigrationHistoryRepository history)
{
_history = history;
}
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = RoleNames.Host)]
public IEnumerable<MigrationHistory> Get()
{
return _history.GetMigrationHistory();
}
}
}

View File

@ -228,6 +228,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddScoped<IImageService, ImageService>(); services.AddScoped<IImageService, ImageService>();
services.AddScoped<ICookieConsentService, ServerCookieConsentService>(); services.AddScoped<ICookieConsentService, ServerCookieConsentService>();
services.AddScoped<ITimeZoneService, TimeZoneService>(); services.AddScoped<ITimeZoneService, TimeZoneService>();
services.AddScoped<IMigrationHistoryService, MigrationHistoryService>();
// providers // providers
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>(); services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
@ -276,6 +277,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.AddTransient<IVisitorRepository, VisitorRepository>(); services.AddTransient<IVisitorRepository, VisitorRepository>();
services.AddTransient<IUrlMappingRepository, UrlMappingRepository>(); services.AddTransient<IUrlMappingRepository, UrlMappingRepository>();
services.AddTransient<ISearchContentRepository, SearchContentRepository>(); services.AddTransient<ISearchContentRepository, SearchContentRepository>();
services.AddTransient<IMigrationHistoryRepository, MigrationHistoryRepository>();
// managers // managers
services.AddTransient<IDBContextDependencies, DBContextDependencies>(); services.AddTransient<IDBContextDependencies, DBContextDependencies>();

View File

@ -69,6 +69,7 @@ namespace Oqtane.Repository
public virtual DbSet<JobLog> JobLog { get; set; } public virtual DbSet<JobLog> JobLog { get; set; }
public virtual DbSet<Setting> Setting { get; set; } public virtual DbSet<Setting> Setting { get; set; }
public virtual DbSet<Theme> Theme { get; set; } public virtual DbSet<Theme> Theme { get; set; }
public virtual DbSet<MigrationHistory> MigrationHistory { get; set; }
public override int SaveChanges() public override int SaveChanges()
{ {

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface IMigrationHistoryRepository
{
IEnumerable<MigrationHistory> GetMigrationHistory();
}
public class MigrationHistoryRepository : IMigrationHistoryRepository
{
private MasterDBContext _db;
public MigrationHistoryRepository(MasterDBContext context)
{
_db = context;
}
public IEnumerable<MigrationHistory> GetMigrationHistory()
{
return _db.MigrationHistory.ToList();
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace Oqtane.Models
{
[Table("__EFMigrationsHistory")]
[Keyless]
public class MigrationHistory
{
public string MigrationId { get; set; }
public string ProductVersion { get; set; }
public DateTime AppliedDate { get; set; }
public string AppliedVersion { get; set; }
}
}