1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2025-05-08 12:34:03 +02:00

Minor code and dependency cleanup

This commit is contained in:
chylex 2022-12-30 18:16:12 +01:00
parent 2cc7975193
commit b1758fb2bb
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
47 changed files with 134 additions and 82 deletions

View File

@ -6,10 +6,11 @@ using Serilog;
namespace Phantom.Agent.Minecraft.Server;
public sealed class MinecraftServerExecutables : IDisposable {
public sealed partial class MinecraftServerExecutables : IDisposable {
private static readonly ILogger Logger = PhantomLogger.Create<MinecraftServerExecutables>();
private static readonly Regex VersionFolderSanitizeRegex = new (@"[^a-zA-Z0-9_\-\.]", RegexOptions.Compiled);
[GeneratedRegex(@"[^a-zA-Z0-9_\-\.]", RegexOptions.Compiled)]
private static partial Regex VersionFolderSanitizeRegex();
private readonly string basePath;
private readonly MinecraftVersions minecraftVersions = new ();
@ -20,7 +21,7 @@ public sealed class MinecraftServerExecutables : IDisposable {
}
internal async Task<string?> DownloadAndGetPath(string version, EventHandler<DownloadProgressEventArgs> progressEventHandler, CancellationToken cancellationToken) {
string serverExecutableFolderPath = Path.Combine(basePath, VersionFolderSanitizeRegex.Replace(version, "_"));
string serverExecutableFolderPath = Path.Combine(basePath, VersionFolderSanitizeRegex().Replace(version, "_"));
string serverExecutableFilePath = Path.Combine(serverExecutableFolderPath, "server.jar");
if (File.Exists(serverExecutableFilePath)) {
@ -49,7 +50,7 @@ public sealed class MinecraftServerExecutables : IDisposable {
runningDownloadersByVersion.Remove(version);
}
};
runningDownloadersByVersion[version] = downloader;
}
}

View File

@ -2,6 +2,7 @@
using NetMQ.Sockets;
using Phantom.Common.Data.Agent;
using Phantom.Common.Messages;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToServer;
using Phantom.Utils.Rpc;
using Phantom.Utils.Rpc.Message;

View File

@ -1,7 +1,7 @@
using NetMQ;
using NetMQ.Sockets;
using Phantom.Common.Messages;
using Phantom.Common.Messages.ToServer;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Utils.Rpc.Message;
namespace Phantom.Agent.Rpc;

View File

@ -2,6 +2,7 @@
using Phantom.Common.Data.Replies;
using Phantom.Common.Logging;
using Phantom.Common.Messages;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToAgent;
using Phantom.Common.Messages.ToServer;
using Phantom.Utils.Rpc.Message;

View File

@ -13,7 +13,7 @@ public sealed partial class AgentAuthToken {
[MemoryPackInclude]
private readonly byte[] bytes;
public AgentAuthToken(byte[]? bytes) {
internal AgentAuthToken(byte[]? bytes) {
if (bytes == null) {
throw new ArgumentNullException(nameof(bytes));
}

View File

@ -4,7 +4,7 @@ using MemoryPack;
namespace Phantom.Common.Data;
[MemoryPackable]
public readonly partial record struct PortRange(
readonly partial record struct PortRange(
[property: MemoryPackOrder(0)] ushort FirstPort,
[property: MemoryPackOrder(1)] ushort LastPort
) {

View File

@ -1,6 +1,6 @@
namespace Phantom.Common.Data.Replies;
public enum ConfigureInstanceResult {
public enum ConfigureInstanceResult : byte {
Success,
InstanceLimitExceeded,
MemoryLimitExceeded

View File

@ -1,6 +1,6 @@
namespace Phantom.Common.Data.Replies;
public enum LaunchInstanceResult {
public enum LaunchInstanceResult : byte {
LaunchInitiated,
InstanceAlreadyLaunching,
InstanceAlreadyRunning,

View File

@ -1,6 +1,6 @@
namespace Phantom.Common.Data.Replies;
public enum SendCommandToInstanceResult {
public enum SendCommandToInstanceResult : byte {
Success,
UnknownError
}

View File

@ -1,6 +1,6 @@
namespace Phantom.Common.Data.Replies;
public enum StopInstanceResult {
public enum StopInstanceResult : byte {
StopInitiated,
InstanceAlreadyStopping,
InstanceAlreadyStopped,

View File

@ -1,7 +1,7 @@
using MemoryPack;
using Phantom.Utils.Rpc.Message;
namespace Phantom.Common.Messages.ToServer;
namespace Phantom.Common.Messages.BiDirectional;
[MemoryPackable]
public sealed partial record ReplyMessage(

View File

@ -1,6 +1,6 @@
using Phantom.Common.Data.Replies;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToAgent;
using Phantom.Common.Messages.ToServer;
using Phantom.Utils.Rpc.Message;
namespace Phantom.Common.Messages;

View File

@ -1,4 +1,5 @@
using Phantom.Common.Messages.ToServer;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToServer;
using Phantom.Utils.Rpc.Message;
namespace Phantom.Common.Messages;

View File

@ -1,5 +1,6 @@
using Phantom.Common.Data.Replies;
using Phantom.Common.Logging;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToAgent;
using Phantom.Common.Messages.ToServer;
using Phantom.Utils.Rpc.Message;

View File

@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace Phantom.Server.Database.Entities;
[Table("Permissions", Schema = "identity")]
public class PermissionEntity {
public sealed class PermissionEntity {
[Key]
public string Id { get; set; }

View File

@ -3,7 +3,7 @@
namespace Phantom.Server.Database.Entities;
[Table("RolePermissions", Schema = "identity")]
public class RolePermissionEntity {
public sealed class RolePermissionEntity {
public string RoleId { get; set; }
public string PermissionId { get; set; }

View File

@ -3,7 +3,7 @@
namespace Phantom.Server.Database.Entities;
[Table("UserPermissions", Schema = "identity")]
public class UserPermissionEntity {
public sealed class UserPermissionEntity {
public string UserId { get; set; }
public string PermissionId { get; set; }

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
@ -6,10 +6,6 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\Phantom.Common.Messages\Phantom.Common.Messages.csproj" />
</ItemGroup>

View File

@ -1,7 +1,7 @@
using NetMQ;
using NetMQ.Sockets;
using Phantom.Common.Messages;
using Phantom.Common.Messages.ToServer;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Utils.Rpc.Message;
namespace Phantom.Server.Rpc;

View File

@ -1,11 +1,12 @@
using NetMQ.Sockets;
using Phantom.Common.Messages;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToServer;
using Phantom.Utils.Rpc;
using Phantom.Utils.Rpc.Message;
using Phantom.Utils.Runtime;
using Serilog;
using Serilog.Events;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Rpc;

View File

@ -10,7 +10,7 @@ using Phantom.Server.Services.Instances;
using Phantom.Utils.Collections;
using Phantom.Utils.Events;
using Phantom.Utils.Runtime;
using Serilog;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Services.Agents;

View File

@ -3,7 +3,7 @@ using Phantom.Common.Data;
using Phantom.Common.Logging;
using Phantom.Server.Services.Instances;
using Phantom.Utils.Events;
using Serilog;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Services.Agents;

View File

@ -1,5 +1,4 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity;
using Phantom.Server.Database.Enums;
namespace Phantom.Server.Services.Audit;
@ -17,9 +16,8 @@ public sealed partial class AuditLog {
AddEvent(userId, AuditEventType.UserLoggedIn, userId);
}
public void AddUserLoggedOutEvent(ClaimsPrincipal user) {
var userId = identityLookup.GetAuthenticatedUserId(user);
AddEvent(userId, AuditEventType.UserLoggedOut, userId ?? string.Empty);
public void AddUserLoggedOutEvent(string userId) {
AddEvent(userId, AuditEventType.UserLoggedOut, userId);
}
public Task AddUserCreatedEvent(IdentityUser user) {

View File

@ -3,7 +3,7 @@ using System.Collections.Immutable;
using Phantom.Common.Logging;
using Phantom.Utils.Collections;
using Phantom.Utils.Events;
using Serilog;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Services.Instances;

View File

@ -10,7 +10,7 @@ using Phantom.Server.Database.Entities;
using Phantom.Server.Services.Agents;
using Phantom.Utils.Collections;
using Phantom.Utils.Events;
using Serilog;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Services.Instances;

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\Phantom.Common.Data\Phantom.Common.Data.csproj" />
<ProjectReference Include="..\..\Common\Phantom.Common.Minecraft\Phantom.Common.Minecraft.csproj" />

View File

@ -1,6 +1,7 @@
using Phantom.Common.Data.Instance;
using Phantom.Common.Data.Replies;
using Phantom.Common.Messages;
using Phantom.Common.Messages.BiDirectional;
using Phantom.Common.Messages.ToAgent;
using Phantom.Common.Messages.ToServer;
using Phantom.Server.Rpc;

View File

@ -2,7 +2,7 @@
namespace Phantom.Server.Web.Components.Utils;
public static class BootstrapEditContext {
static class BootstrapEditContext {
public static EditContext Create(object model) {
EditContext context = new EditContext(model);
context.SetFieldCssClassProvider(ClassProvider);

View File

@ -1,27 +1,35 @@
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Phantom.Common.Logging;
using Phantom.Server.Services.Audit;
using Phantom.Server.Web.Identity.Interfaces;
using Phantom.Utils.Cryptography;
using Serilog;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Web.Identity.Authentication;
public sealed class PhantomLoginManager {
private static readonly ILogger Logger = PhantomLogger.Create<PhantomLoginManager>();
public static bool IsAuthenticated(ClaimsPrincipal user) {
return user.Identity is { IsAuthenticated: true };
}
internal static string? GetAuthenticatedUserId(ClaimsPrincipal user, UserManager<IdentityUser> userManager) {
return IsAuthenticated(user) ? userManager.GetUserId(user) : null;
}
private readonly INavigation navigation;
private readonly PhantomLoginStore loginStore;
private readonly UserManager<IdentityUser> userManager;
private readonly SignInManager<IdentityUser> signInManager;
private readonly AuditLog auditLog;
private readonly ILoginEvents loginEvents;
public PhantomLoginManager(INavigation navigation, PhantomLoginStore loginStore, UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager, AuditLog auditLog) {
public PhantomLoginManager(INavigation navigation, PhantomLoginStore loginStore, UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager, ILoginEvents loginEvents) {
this.navigation = navigation;
this.loginStore = loginStore;
this.userManager = userManager;
this.signInManager = signInManager;
this.auditLog = auditLog;
this.loginEvents = loginEvents;
}
public async Task<SignInResult> SignIn(string username, string password, string? returnUrl = null) {
@ -43,7 +51,10 @@ public sealed class PhantomLoginManager {
}
internal async Task SignOut() {
auditLog.AddUserLoggedOutEvent(signInManager.Context.User);
if (GetAuthenticatedUserId(signInManager.Context.User, userManager) is {} userId) {
loginEvents.UserLoggedOut(userId);
}
await signInManager.SignOutAsync();
}
@ -57,7 +68,7 @@ public sealed class PhantomLoginManager {
var result = await signInManager.PasswordSignInAsync(user, entry.Password, lockoutOnFailure: false, isPersistent: true);
if (result == SignInResult.Success) {
Logger.Information("Successful login for {Username}.", user.UserName);
auditLog.AddUserLoggedInEvent(user.Id);
loginEvents.UserLoggedIn(user.Id);
return entry.ReturnUrl;
}
else {

View File

@ -2,21 +2,24 @@
using System.Diagnostics;
using Microsoft.AspNetCore.Identity;
using Phantom.Common.Logging;
using Phantom.Server.Services;
using Phantom.Utils.Runtime;
using Serilog;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Web.Identity.Authentication;
public sealed class PhantomLoginStore {
private static readonly ILogger Logger = PhantomLogger.Create<PhantomLoginStore>();
private static readonly TimeSpan ExpirationTime = TimeSpan.FromMinutes(1);
internal static Func<IServiceProvider, PhantomLoginStore> Create(CancellationToken cancellationToken) {
return provider => new PhantomLoginStore(provider.GetRequiredService<TaskManager>(), cancellationToken);
}
private readonly ConcurrentDictionary<string, LoginEntry> loginEntries = new ();
private readonly CancellationToken cancellationToken;
public PhantomLoginStore(ServiceConfiguration configuration, TaskManager taskManager) {
this.cancellationToken = configuration.CancellationToken;
private PhantomLoginStore(TaskManager taskManager, CancellationToken cancellationToken) {
this.cancellationToken = cancellationToken;
taskManager.Run(RunExpirationLoop);
}

View File

@ -1,42 +1,40 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Identity;
using Phantom.Server.Database;
using Phantom.Server.Services.Users;
using Phantom.Server.Web.Identity.Authentication;
using Phantom.Server.Web.Identity.Data;
namespace Phantom.Server.Web.Identity.Authorization;
public sealed class PermissionManager {
private readonly DatabaseProvider databaseProvider;
private readonly IdentityLookup identityLookup;
private readonly UserManager<IdentityUser> userManager;
private readonly Dictionary<string, IdentityPermissions> userIdsToPermissionIds = new ();
public PermissionManager(DatabaseProvider databaseProvider, IdentityLookup identityLookup) {
public PermissionManager(DatabaseProvider databaseProvider, UserManager<IdentityUser> userManager) {
this.databaseProvider = databaseProvider;
this.identityLookup = identityLookup;
this.userManager = userManager;
}
private IdentityPermissions FetchPermissions(string userId) {
private IdentityPermissions FetchPermissionsForUserId(string userId) {
using var scope = databaseProvider.CreateScope();
var userPermissions = scope.Ctx.UserPermissions.Where(up => up.UserId == userId).Select(static up => up.PermissionId);
var rolePermissions = scope.Ctx.UserRoles.Where(ur => ur.UserId == userId).Join(scope.Ctx.RolePermissions, static ur => ur.RoleId, static rp => rp.RoleId, static (ur, rp) => rp.PermissionId);
return new IdentityPermissions(userPermissions.Union(rolePermissions));
}
private IdentityPermissions GetPermissionsForUserId(string? userId, bool refreshCache) {
if (userId == null) {
return IdentityPermissions.None;
}
private IdentityPermissions GetPermissionsForUserId(string userId, bool refreshCache) {
if (!refreshCache && userIdsToPermissionIds.TryGetValue(userId, out var userPermissions)) {
return userPermissions;
}
else {
return userIdsToPermissionIds[userId] = FetchPermissions(userId);
return userIdsToPermissionIds[userId] = FetchPermissionsForUserId(userId);
}
}
public IdentityPermissions GetPermissions(ClaimsPrincipal user, bool refreshCache = false) {
return GetPermissionsForUserId(identityLookup.GetAuthenticatedUserId(user), refreshCache);
string? userId = PhantomLoginManager.GetAuthenticatedUserId(user, userManager);
return userId == null ? IdentityPermissions.None : GetPermissionsForUserId(userId, refreshCache);
}
public bool CheckPermission(ClaimsPrincipal user, Permission permission, bool refreshCache = false) {

View File

@ -0,0 +1,6 @@
namespace Phantom.Server.Web.Identity.Interfaces;
public interface ILoginEvents {
void UserLoggedIn(string userId);
void UserLoggedOut(string userId);
}

View File

@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
namespace Phantom.Server.Web.Identity;
namespace Phantom.Server.Web.Identity.Interfaces;
public interface INavigation {
string BasePath { get; }

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
@ -6,6 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
@ -16,7 +20,10 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Phantom.Server.Services\Phantom.Server.Services.csproj" />
<ProjectReference Include="..\..\Common\Phantom.Common.Logging\Phantom.Common.Logging.csproj" />
<ProjectReference Include="..\..\Utils\Phantom.Utils.Cryptography\Phantom.Utils.Cryptography.csproj" />
<ProjectReference Include="..\..\Utils\Phantom.Utils.Runtime\Phantom.Utils.Runtime.csproj" />
<ProjectReference Include="..\Phantom.Server.Database\Phantom.Server.Database.csproj" />
</ItemGroup>
</Project>

View File

@ -1,12 +1,11 @@
using System.Collections.Immutable;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Phantom.Common.Logging;
using Phantom.Server.Database;
using Phantom.Server.Database.Entities;
using Phantom.Server.Web.Identity.Data;
using Phantom.Utils.Runtime;
using Serilog;
using ILogger = Serilog.ILogger;
namespace Phantom.Server.Web.Identity;

View File

@ -13,13 +13,13 @@ using Phantom.Server.Web.Identity.Data;
namespace Phantom.Server.Web.Identity;
public static class PhantomIdentityExtensions {
public static void AddPhantomIdentity<TUser, TRole>(this IServiceCollection services) where TUser : class where TRole : class {
public static void AddPhantomIdentity<TUser, TRole>(this IServiceCollection services, CancellationToken cancellationToken) where TUser : class where TRole : class {
services.AddIdentity<TUser, TRole>(ConfigureIdentity).AddEntityFrameworkStores<ApplicationDbContext>();
services.ConfigureApplicationCookie(ConfigureIdentityCookie);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
services.AddAuthorization(ConfigureAuthorization);
services.AddSingleton<PhantomLoginStore>();
services.AddSingleton(PhantomLoginStore.Create(cancellationToken));
services.AddScoped<PhantomLoginManager>();
services.AddScoped<PhantomIdentityConfigurator>();

View File

@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Http;
using Phantom.Server.Web.Identity.Authentication;
using Phantom.Server.Web.Identity.Interfaces;
namespace Phantom.Server.Web.Identity;

View File

@ -1,4 +1,6 @@
@inject INavigation Nav
@using Phantom.Server.Web.Identity.Interfaces
@using Phantom.Server.Web.Identity.Authentication
@inject INavigation Nav
@inject NavigationManager NavigationManager
<CascadingAuthenticationState>
@ -6,7 +8,7 @@
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@if (context.User.Identity is not { IsAuthenticated: true }) {
@if (!PhantomLoginManager.IsAuthenticated(context.User)) {
var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri).TrimEnd('/');
Nav.NavigateTo("login" + QueryString.Create("return", returnUrl), forceLoad: true);
}

View File

@ -0,0 +1,20 @@
using Phantom.Server.Services.Audit;
using Phantom.Server.Web.Identity.Interfaces;
namespace Phantom.Server.Web.Base;
sealed class LoginEvents : ILoginEvents {
private readonly AuditLog auditLog;
public LoginEvents(AuditLog auditLog) {
this.auditLog = auditLog;
}
public void UserLoggedIn(string userId) {
auditLog.AddUserLoggedInEvent(userId);
}
public void UserLoggedOut(string userId) {
auditLog.AddUserLoggedOutEvent(userId);
}
}

View File

@ -1,9 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Web;
using Microsoft.AspNetCore.Components;
using Phantom.Server.Web.Identity;
using Phantom.Server.Web.Identity.Interfaces;
namespace Phantom.Server.Web;
namespace Phantom.Server.Web.Base;
sealed class Navigation : INavigation {
public static Func<IServiceProvider, Navigation> Create(string basePath) {

View File

@ -2,8 +2,10 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Phantom.Server.Database;
using Phantom.Server.Web.Base;
using Phantom.Server.Web.Components.Utils;
using Phantom.Server.Web.Identity;
using Phantom.Server.Web.Identity.Interfaces;
using Serilog;
namespace Phantom.Server.Web;
@ -35,7 +37,8 @@ public static class Launcher {
builder.Services.AddDbContextPool<ApplicationDbContext>(dbOptionsBuilder, poolSize: 64);
builder.Services.AddSingleton<DatabaseProvider>();
builder.Services.AddPhantomIdentity<IdentityUser, IdentityRole>();
builder.Services.AddPhantomIdentity<IdentityUser, IdentityRole>(config.CancellationToken);
builder.Services.AddScoped<ILoginEvents, LoginEvents>();
builder.Services.AddRazorPages(static options => options.RootDirectory = "/Layout");
builder.Services.AddServerSideBlazor();

View File

@ -1,5 +1,5 @@
@page
@using Phantom.Server.Web.Identity
@using Phantom.Server.Web.Identity.Interfaces
@model Phantom.Server.Web.Layout.ErrorModel
@inject INavigation Navigation

View File

@ -1,4 +1,4 @@
@using Phantom.Server.Web.Identity
@using Phantom.Server.Web.Identity.Interfaces
@namespace Phantom.Server.Web.Layout
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@inject INavigation Navigation

View File

@ -8,6 +8,7 @@
@using System.Collections.Immutable
@using System.ComponentModel.DataAnnotations
@using System.Diagnostics.CodeAnalysis
@using Phantom.Server.Web.Identity.Interfaces
@using Phantom.Common.Data.Java
@using Phantom.Common.Data
@using Phantom.Common.Data.Instance

View File

@ -1,4 +1,5 @@
@page "/login"
@using Phantom.Server.Web.Identity.Interfaces
@using Phantom.Server.Web.Identity.Authentication
@using Microsoft.AspNetCore.Identity
@using System.ComponentModel.DataAnnotations

View File

@ -28,8 +28,9 @@
</tr>
</thead>
<tbody>
@{ var myUserId = IdentityLookup.GetAuthenticatedUserId(context.User); }
@foreach (var user in allUsers) {
var isMe = IdentityLookup.GetAuthenticatedUserId(context.User) == user.Id;
var isMe = myUserId == user.Id;
<tr>
<td>
<code class="text-uppercase">@user.Id</code>

View File

@ -1,18 +1,12 @@
namespace Phantom.Utils.IO;
public readonly record struct FileSize {
public readonly record struct FileSize(ulong Bytes) {
private const int Scale = 1024;
private static readonly string[] Units = {
"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"
};
public ulong Bytes { get; }
public FileSize(ulong bytes) {
Bytes = bytes;
}
public string ToHumanReadable(int decimalPlaces) {
int power = Bytes == 0L ? 0 : (int) Math.Log(Bytes, Scale);
int unit = power >= Units.Length ? Units.Length - 1 : power;