mirror of
https://github.com/claunia/romrepomgr.git
synced 2025-12-16 19:24:51 +00:00
Compare commits
14 Commits
52946eca2e
...
e1ced26bc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
e1ced26bc5
|
|||
|
9c91d76561
|
|||
|
cf7186adbb
|
|||
|
122e397d0a
|
|||
|
a2fc47cc5b
|
|||
|
80f1c0e28e
|
|||
|
f4b87f68ec
|
|||
|
dc4646512a
|
|||
|
00d005ba98
|
|||
|
9877f3886e
|
|||
|
5bb39929b4
|
|||
|
72b2eed55c
|
|||
|
cca4af8d6d
|
|||
|
8df9bb7211
|
@@ -20,6 +20,7 @@
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="2.0.0-preview1-final"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.6"/>
|
||||
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.DataGrid.EntityFrameworkAdapter" Version="4.12.1"/>
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15"/>
|
||||
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0"/>
|
||||
<PackageVersion Include="Mono.Fuse.NETStandard" Version="1.1.0"/>
|
||||
@@ -30,6 +31,7 @@
|
||||
<PackageVersion Include="Roslynator.Formatting.Analyzers" Version="4.13.1"/>
|
||||
<PackageVersion Include="SabreTools.Models" Version="1.5.8"/>
|
||||
<PackageVersion Include="Serilog" Version="4.3.0"/>
|
||||
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0"/>
|
||||
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.2"/>
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0"/>
|
||||
<PackageVersion Include="SharpCompress" Version="0.39.0"/>
|
||||
@@ -46,5 +48,7 @@
|
||||
<PackageVersion Include="SharpCompress" Version="0.38.0"/>
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0"/>
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.6"/>
|
||||
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.12.1"/>
|
||||
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.12.1"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
20
RomRepoMgr.Blazor/Components/App.razor
Normal file
20
RomRepoMgr.Blazor/Components/App.razor
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<base href="/"/>
|
||||
<link href="@Assets["app.css"]" rel="stylesheet"/>
|
||||
<link href="@Assets["RomRepoMgr.Blazor.styles.css"]" rel="stylesheet"/>
|
||||
<ImportMap/>
|
||||
<link href="favicon.ico" rel="icon" type="image/x-icon"/>
|
||||
<HeadOutlet @rendermode="InteractiveServer"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes @rendermode="InteractiveServer"/>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
16
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor
Normal file
16
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor
Normal file
@@ -0,0 +1,16 @@
|
||||
@implements IDialogContentComponent
|
||||
@inject ILogger<ImportDats> Logger
|
||||
|
||||
<FluentDialog Width="800px" Height="400px" Title="Import DATs" Modal="true" TrapFocus="true">
|
||||
<FluentDialogBody>
|
||||
<p hidden="@IsBusy">DAT files will be imported from @path.</p>
|
||||
<FluentLabel Color="@StatusColor">@StatusMessage</FluentLabel>
|
||||
<FluentProgress Max="@ProgressMax" Min="@ProgressMin" Value="@ProgressValue" Visible="@ProgressVisible"/>
|
||||
</FluentDialogBody>
|
||||
<FluentDialogFooter>
|
||||
<FluentStack Orientation="Orientation.Horizontal">
|
||||
<FluentButton OnClick="@StartAsync" Disabled="@IsBusy">Start</FluentButton>
|
||||
<FluentButton OnClick="@CloseAsync" Disabled="@CannotClose">Close</FluentButton>
|
||||
</FluentStack>
|
||||
</FluentDialogFooter>
|
||||
</FluentDialog>
|
||||
108
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor.cs
Normal file
108
RomRepoMgr.Blazor/Components/Dialogs/ImportDats.razor.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using RomRepoMgr.Core.Workers;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
||||
namespace RomRepoMgr.Blazor.Components.Dialogs;
|
||||
|
||||
public partial class ImportDats : ComponentBase
|
||||
{
|
||||
readonly Stopwatch _stopwatch = new();
|
||||
string[] _datFiles;
|
||||
int _listPosition;
|
||||
int _workers;
|
||||
string path;
|
||||
public string StatusMessage { get; set; }
|
||||
public bool IsBusy { get; set; }
|
||||
[CascadingParameter]
|
||||
public FluentDialog Dialog { get; set; }
|
||||
public int? ProgressMax { get; set; }
|
||||
public int? ProgressMin { get; set; }
|
||||
public int? ProgressValue { get; set; }
|
||||
public bool CannotClose { get; set; }
|
||||
public bool ProgressVisible { get; set; }
|
||||
public Color? StatusColor { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
path = Path.Combine(Environment.CurrentDirectory, Consts.IncomingDatFolder);
|
||||
StatusMessage = string.Empty;
|
||||
IsBusy = false;
|
||||
CannotClose = false;
|
||||
ProgressVisible = false;
|
||||
}
|
||||
|
||||
Task StartAsync()
|
||||
{
|
||||
IsBusy = true;
|
||||
CannotClose = true;
|
||||
ProgressVisible = true;
|
||||
ProgressValue = null;
|
||||
StatusMessage = "Searching for files...";
|
||||
|
||||
_stopwatch.Restart();
|
||||
string[] dats = Directory.GetFiles(path, "*.dat", SearchOption.AllDirectories);
|
||||
|
||||
string[] xmls = Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);
|
||||
|
||||
_datFiles = dats.Concat(xmls).Order().ToArray();
|
||||
_stopwatch.Stop();
|
||||
|
||||
Logger.LogDebug("Took {TotalSeconds} to find {Length} DAT files",
|
||||
_stopwatch.Elapsed.TotalSeconds,
|
||||
_datFiles.Length);
|
||||
|
||||
StatusMessage = string.Format("Found {0} files...", _datFiles.Length);
|
||||
|
||||
ProgressMin = 0;
|
||||
ProgressMax = _datFiles.Length;
|
||||
ProgressValue = 0;
|
||||
_listPosition = 0;
|
||||
_workers = 0;
|
||||
StateHasChanged();
|
||||
|
||||
return ImportAsync();
|
||||
}
|
||||
|
||||
async Task ImportAsync()
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
Logger.LogDebug("Starting to import DAT files...");
|
||||
|
||||
Parallel.ForEach(_datFiles,
|
||||
datFile =>
|
||||
{
|
||||
_ = InvokeAsync(() =>
|
||||
{
|
||||
StatusMessage = string.Format("Importing {0}...", Path.GetFileName(datFile));
|
||||
|
||||
ProgressValue = _listPosition;
|
||||
StateHasChanged();
|
||||
});
|
||||
|
||||
var worker = new DatImporter(datFile, null, new SerilogLoggerFactory(Log.Logger));
|
||||
|
||||
worker.Import();
|
||||
|
||||
Interlocked.Increment(ref _listPosition);
|
||||
});
|
||||
|
||||
ProgressVisible = false;
|
||||
StatusMessage = "Finished";
|
||||
CannotClose = false;
|
||||
_stopwatch.Stop();
|
||||
|
||||
Logger.LogDebug("Took {TotalSeconds} seconds to import {Length} DAT files",
|
||||
_stopwatch.Elapsed.TotalSeconds,
|
||||
_datFiles.Length);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
Task CloseAsync() => Dialog.CloseAsync();
|
||||
}
|
||||
24
RomRepoMgr.Blazor/Components/Layout/MainLayout.razor
Normal file
24
RomRepoMgr.Blazor/Components/Layout/MainLayout.razor
Normal file
@@ -0,0 +1,24 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<FluentLayout>
|
||||
<FluentHeader>
|
||||
ROM Repository Manager
|
||||
</FluentHeader>
|
||||
<FluentStack Class="main" Orientation="Orientation.Horizontal" Width="100%">
|
||||
<FluentBodyContent Class="body-content">
|
||||
<div class="content">
|
||||
@Body
|
||||
</div>
|
||||
</FluentBodyContent>
|
||||
</FluentStack>
|
||||
<FluentFooter>
|
||||
<a href="https://www.natportillo.es" target="_blank">Copyright © 2020-2025 Natalia Portillo</a>
|
||||
</FluentFooter>
|
||||
<FluentDialogProvider/>
|
||||
</FluentLayout>
|
||||
|
||||
<div data-nosnippet id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a class="reload" href=".">Reload</a>
|
||||
<span class="dismiss">🗙</span>
|
||||
</div>
|
||||
35
RomRepoMgr.Blazor/Components/Pages/Error.razor
Normal file
35
RomRepoMgr.Blazor/Components/Pages/Error.razor
Normal file
@@ -0,0 +1,35 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if(ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() => RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
36
RomRepoMgr.Blazor/Components/Pages/Home.razor
Normal file
36
RomRepoMgr.Blazor/Components/Pages/Home.razor
Normal file
@@ -0,0 +1,36 @@
|
||||
@page "/"
|
||||
@using RomRepoMgr.Database
|
||||
@rendermode InteractiveServer
|
||||
@inject IDialogService DialogService
|
||||
@inject Context ctx
|
||||
|
||||
<PageTitle>ROM Repository Manager</PageTitle>
|
||||
|
||||
<FluentToolbar>
|
||||
<FluentButton OnClick="@ImportDatsAsync">Import DATs</FluentButton>
|
||||
<FluentButton Disabled="true">Export DAT</FluentButton>
|
||||
<FluentButton Disabled="true">Remove DAT</FluentButton>
|
||||
<FluentButton Disabled="true">Import ROMs</FluentButton>
|
||||
<FluentButton Disabled="true">Export ROMs</FluentButton>
|
||||
</FluentToolbar>
|
||||
|
||||
<FluentDataGrid @ref="romSetsGrid" Items="@RomSets" Style="width: 100%;" AutoFit="true" Pagination="@pagination"
|
||||
AutoItemsPerPage="true" ResizableColumns="true">
|
||||
<PropertyColumn Property="@(p => p.Name)" Title="Name"/>
|
||||
<PropertyColumn Property="@(p => p.Version)" Title="Version"/>
|
||||
<PropertyColumn Property="@(p => p.Author)" Title="Author"/>
|
||||
<PropertyColumn Property="@(p => p.Category)" Title="Category"/>
|
||||
<PropertyColumn Property="@(p => p.Date)" Title="Date"/>
|
||||
<PropertyColumn Property="@(p => p.Description)" Title="Description"/>
|
||||
<PropertyColumn Property="@(p => p.Comment)" Title="Comment"/>
|
||||
<PropertyColumn Property="@(p => p.Homepage)" Title="Homepage"/>
|
||||
<PropertyColumn Property="@(p => p.TotalMachines)" Title="Total machines"/>
|
||||
<PropertyColumn Property="@(p => p.CompleteMachines)" Title="Complete machines"/>
|
||||
<PropertyColumn Property="@(p => p.IncompleteMachines)" Title="Incomplete machines"/>
|
||||
<PropertyColumn Property="@(p => p.TotalRoms)" Title="Total ROMs"/>
|
||||
<PropertyColumn Property="@(p => p.HaveRoms)" Title="Have ROMs"/>
|
||||
<PropertyColumn Property="@(p => p.MissRoms)" Title="Miss ROMs"/>
|
||||
|
||||
</FluentDataGrid>
|
||||
|
||||
<FluentPaginator State="@pagination"/>
|
||||
59
RomRepoMgr.Blazor/Components/Pages/Home.razor.cs
Normal file
59
RomRepoMgr.Blazor/Components/Pages/Home.razor.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using RomRepoMgr.Blazor.Components.Dialogs;
|
||||
using RomRepoMgr.Core.Models;
|
||||
|
||||
namespace RomRepoMgr.Blazor.Components.Pages;
|
||||
|
||||
public partial class Home : ComponentBase
|
||||
{
|
||||
readonly PaginationState pagination = new()
|
||||
{
|
||||
ItemsPerPage = 10
|
||||
};
|
||||
FluentDataGrid<RomSetModel>? romSetsGrid;
|
||||
|
||||
public IQueryable<RomSetModel>? RomSets { get; set; }
|
||||
|
||||
async Task ImportDatsAsync()
|
||||
{
|
||||
IDialogReference dialog = await DialogService.ShowDialogAsync<ImportDats>(new DialogParameters());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
|
||||
romSetsGrid?.SetLoadingState(true);
|
||||
|
||||
RomSets = ctx.RomSets.OrderBy(r => r.Name)
|
||||
.ThenBy(r => r.Version)
|
||||
.ThenBy(r => r.Date)
|
||||
.ThenBy(r => r.Description)
|
||||
.ThenBy(r => r.Comment)
|
||||
.ThenBy(r => r.Filename)
|
||||
.Select(r => new RomSetModel
|
||||
{
|
||||
Id = r.Id,
|
||||
Author = r.Author,
|
||||
Comment = r.Comment,
|
||||
Date = r.Date,
|
||||
Description = r.Description,
|
||||
Filename = r.Filename,
|
||||
Homepage = r.Homepage,
|
||||
Name = r.Name,
|
||||
Sha384 = r.Sha384,
|
||||
Version = r.Version,
|
||||
TotalMachines = r.Statistics.TotalMachines,
|
||||
CompleteMachines = r.Statistics.CompleteMachines,
|
||||
IncompleteMachines = r.Statistics.IncompleteMachines,
|
||||
TotalRoms = r.Statistics.TotalRoms,
|
||||
HaveRoms = r.Statistics.HaveRoms,
|
||||
MissRoms = r.Statistics.MissRoms,
|
||||
Category = r.Category
|
||||
});
|
||||
|
||||
romSetsGrid?.SetLoadingState(false);
|
||||
}
|
||||
}
|
||||
8
RomRepoMgr.Blazor/Components/Routes.razor
Normal file
8
RomRepoMgr.Blazor/Components/Routes.razor
Normal file
@@ -0,0 +1,8 @@
|
||||
<FluentDesignTheme StorageName="theme" Mode="DesignThemeModes.Dark">
|
||||
<Router AppAssembly="typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView DefaultLayout="typeof(Layout.MainLayout)" RouteData="routeData"/>
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
|
||||
</Found>
|
||||
</Router>
|
||||
</FluentDesignTheme>
|
||||
12
RomRepoMgr.Blazor/Components/_Imports.razor
Normal file
12
RomRepoMgr.Blazor/Components/_Imports.razor
Normal file
@@ -0,0 +1,12 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.FluentUI.AspNetCore.Components
|
||||
@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons
|
||||
@using Microsoft.JSInterop
|
||||
@using RomRepoMgr.Blazor
|
||||
@using RomRepoMgr.Blazor.Components
|
||||
11
RomRepoMgr.Blazor/Consts.cs
Normal file
11
RomRepoMgr.Blazor/Consts.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace RomRepoMgr.Blazor;
|
||||
|
||||
public class Consts
|
||||
{
|
||||
public const string DbFolder = "db";
|
||||
public const string DatFolder = "dats";
|
||||
public const string IncomingDatFolder = "incoming-dats";
|
||||
public const string RepositoryFolder = "repo";
|
||||
public const string IncomingRomsFolder = "incoming";
|
||||
public const string TemporaryFolder = "tmp";
|
||||
}
|
||||
127
RomRepoMgr.Blazor/Program.cs
Normal file
127
RomRepoMgr.Blazor/Program.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.FluentUI.AspNetCore.Components;
|
||||
using RomRepoMgr.Blazor;
|
||||
using RomRepoMgr.Blazor.Components;
|
||||
using RomRepoMgr.Database;
|
||||
using RomRepoMgr.Settings;
|
||||
using Serilog;
|
||||
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
#if DEBUG
|
||||
.MinimumLevel.Debug()
|
||||
#else
|
||||
.MinimumLevel.Information()
|
||||
#endif
|
||||
.WriteTo.Console()
|
||||
.Enrich.FromLogContext()
|
||||
.CreateLogger();
|
||||
|
||||
Log.Information("Welcome to ROM Repository Manager!");
|
||||
Log.Information("Copyright © 2020-2025 Natalia Portillo");
|
||||
|
||||
// Ensure the folders exist
|
||||
Log.Information("Ensuring folders exist...");
|
||||
|
||||
string[] folders =
|
||||
[
|
||||
Consts.DbFolder, Consts.DatFolder, Consts.IncomingDatFolder, Consts.RepositoryFolder, Consts.IncomingRomsFolder
|
||||
];
|
||||
|
||||
foreach(string folder in folders)
|
||||
{
|
||||
if(!Directory.Exists(folder))
|
||||
{
|
||||
Log.Debug("Creating folder: {Folder}", folder);
|
||||
Directory.CreateDirectory(folder);
|
||||
}
|
||||
else
|
||||
Log.Debug("Folder already exists: {Folder}", folder);
|
||||
}
|
||||
|
||||
// Ensure the temporary folder exists but it can also be a symlink
|
||||
if(!Directory.Exists(Consts.TemporaryFolder) && !File.Exists(Consts.TemporaryFolder))
|
||||
{
|
||||
Log.Debug("Creating folder: {TemporaryFolder}", Consts.TemporaryFolder);
|
||||
Directory.CreateDirectory(Consts.TemporaryFolder);
|
||||
}
|
||||
else
|
||||
Log.Debug("Folder already exists: {TemporaryFolder}", Consts.TemporaryFolder);
|
||||
|
||||
Log.Debug("Creating the builder...");
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Host.UseSerilog(); // ✅ Plug Serilog into the host
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
||||
builder.Services.AddFluentUIComponents();
|
||||
|
||||
Log.Debug("Creating database context...");
|
||||
|
||||
builder.Services.AddDbContextFactory<Context>(options =>
|
||||
{
|
||||
options.UseSqlite($"Data Source={Consts.DbFolder}/database.db");
|
||||
#if DEBUG
|
||||
options.EnableSensitiveDataLogging();
|
||||
options.LogTo(Log.Debug);
|
||||
#else
|
||||
options.LogTo(Log.Information, LogLevel.Information);
|
||||
#endif
|
||||
});
|
||||
|
||||
builder.Services.AddDataGridEntityFrameworkAdapter();
|
||||
|
||||
Log.Debug("Setting the settings...");
|
||||
|
||||
Settings.Current = new SetSettings
|
||||
{
|
||||
DatabasePath = Path.Combine(Environment.CurrentDirectory, Consts.DbFolder, "database.db"),
|
||||
TemporaryFolder = Path.Combine(Environment.CurrentDirectory, Consts.TemporaryFolder),
|
||||
RepositoryPath = Path.Combine(Environment.CurrentDirectory, Consts.RepositoryFolder),
|
||||
UseInternalDecompressor = true,
|
||||
Compression = CompressionType.Zstd // Todo: Read from configuration
|
||||
};
|
||||
|
||||
Log.Debug("Building the application...");
|
||||
WebApplication app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if(!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error", true);
|
||||
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.MapStaticAssets();
|
||||
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
|
||||
|
||||
Stopwatch stopwatch = new();
|
||||
|
||||
using(IServiceScope scope = app.Services.CreateScope())
|
||||
{
|
||||
IServiceProvider services = scope.ServiceProvider;
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Updating the database...");
|
||||
stopwatch.Start();
|
||||
Context dbContext = services.GetRequiredService<Context>();
|
||||
await dbContext.Database.MigrateAsync();
|
||||
stopwatch.Stop();
|
||||
Log.Debug("Database migration: {Elapsed} seconds", stopwatch.Elapsed.TotalSeconds);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Error(ex, "An error occurred while updating the database");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log.Debug("Running the application...");
|
||||
app.Run();
|
||||
23
RomRepoMgr.Blazor/Properties/launchSettings.json
Normal file
23
RomRepoMgr.Blazor/Properties/launchSettings.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5079",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7057;http://localhost:5079",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RomRepoMgr.Blazor/RomRepoMgr.Blazor.csproj
Normal file
23
RomRepoMgr.Blazor/RomRepoMgr.Blazor.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite"/>
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components"/>
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.DataGrid.EntityFrameworkAdapter"/>
|
||||
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons"/>
|
||||
<PackageReference Include="Serilog"/>
|
||||
<PackageReference Include="Serilog.AspNetCore"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RomRepoMgr.Core\RomRepoMgr.Core.csproj"/>
|
||||
<ProjectReference Include="..\RomRepoMgr.Database\RomRepoMgr.Database.csproj"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
11
RomRepoMgr.Blazor/appsettings.Development.json
Normal file
11
RomRepoMgr.Blazor/appsettings.Development.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"CircuitOptions": {
|
||||
"DetailedErrors": true
|
||||
}
|
||||
}
|
||||
9
RomRepoMgr.Blazor/appsettings.json
Normal file
9
RomRepoMgr.Blazor/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
191
RomRepoMgr.Blazor/wwwroot/app.css
Normal file
191
RomRepoMgr.Blazor/wwwroot/app.css
Normal file
@@ -0,0 +1,191 @@
|
||||
@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';
|
||||
|
||||
body {
|
||||
--body-font: "Segoe UI Variable", "Segoe UI", sans-serif;
|
||||
font-family: var(--body-font);
|
||||
font-size: var(--type-ramp-base-font-size);
|
||||
line-height: var(--type-ramp-base-line-height);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.navmenu-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: calc(100dvh - 86px);
|
||||
color: var(--neutral-foreground-rest);
|
||||
align-items: stretch !important;
|
||||
}
|
||||
|
||||
.body-content {
|
||||
align-self: stretch;
|
||||
height: calc(100dvh - 86px) !important;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 0.5rem 1.5rem;
|
||||
align-self: stretch !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.manage {
|
||||
width: 100dvw;
|
||||
}
|
||||
|
||||
footer {
|
||||
background: var(--neutral-layer-4);
|
||||
color: var(--neutral-foreground-rest);
|
||||
align-items: center;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: var(--neutral-foreground-rest);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer a:focus {
|
||||
outline: 1px dashed;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.alert {
|
||||
border: 1px dashed var(--accent-fill-rest);
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
.blazor-error-boundary {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||
padding: 1rem 1rem 1rem 3.7rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blazor-error-boundary::before {
|
||||
content: "An error has occurred. "
|
||||
}
|
||||
|
||||
.loading-progress {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
margin: 20vh auto 1rem auto;
|
||||
}
|
||||
|
||||
.loading-progress circle {
|
||||
fill: none;
|
||||
stroke: #e0e0e0;
|
||||
stroke-width: 0.6rem;
|
||||
transform-origin: 50% 50%;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.loading-progress circle:last-child {
|
||||
stroke: #1b6ec2;
|
||||
stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
|
||||
transition: stroke-dasharray 0.05s ease-in-out;
|
||||
}
|
||||
|
||||
.loading-progress-text {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
|
||||
}
|
||||
|
||||
.loading-progress-text:after {
|
||||
content: var(--blazor-load-percentage-text, "Loading");
|
||||
}
|
||||
|
||||
code {
|
||||
color: #c02d76;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.header-gutters {
|
||||
margin: 0.5rem 3rem 0.5rem 1.5rem !important;
|
||||
}
|
||||
|
||||
[dir="rtl"] .header-gutters {
|
||||
margin: 0.5rem 1.5rem 0.5rem 3rem !important;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex-direction: column !important;
|
||||
row-gap: 0 !important;
|
||||
}
|
||||
|
||||
nav.sitenav {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#main-menu {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#main-menu > div:first-child:is(.expander) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navmenu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#navmenu-toggle {
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
#navmenu-toggle ~ nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#navmenu-toggle:checked ~ nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navmenu-icon {
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: unset;
|
||||
right: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
[dir="rtl"] .navmenu-icon {
|
||||
left: 20px;
|
||||
right: unset;
|
||||
}
|
||||
}
|
||||
BIN
RomRepoMgr.Blazor/wwwroot/favicon.ico
Normal file
BIN
RomRepoMgr.Blazor/wwwroot/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -32,6 +32,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.DatTools", "Sabr
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Reports", "SabreTools\SabreTools.Reports\SabreTools.Reports.csproj", "{E73767A7-0A65-4F89-B149-A520874F7B32}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RomRepoMgr.Blazor", "RomRepoMgr.Blazor\RomRepoMgr.Blazor.csproj", "{30DA0637-76C5-43DE-8203-403AECF5F859}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -78,5 +80,9 @@ Global
|
||||
{E73767A7-0A65-4F89-B149-A520874F7B32}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E73767A7-0A65-4F89-B149-A520874F7B32}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E73767A7-0A65-4F89-B149-A520874F7B32}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{30DA0637-76C5-43DE-8203-403AECF5F859}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Reference in New Issue
Block a user