Added permission groups. Cleaned security ui. Added some permission stuff
This commit is contained in:
parent
0015001d7c
commit
d3b55d155b
10 changed files with 430 additions and 245 deletions
|
@ -22,6 +22,10 @@ public class PermissionStorage
|
|||
{
|
||||
return BitHelper.ReadBit(Data, permission.Index);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Verbose("Error reading permissions. (Can be intentional)");
|
||||
|
@ -37,4 +41,15 @@ public class PermissionStorage
|
|||
Data = BitHelper.WriteBit(Data, permission.Index, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAnyPermissions()
|
||||
{
|
||||
foreach (var permission in Permissions.GetAllPermissions())
|
||||
{
|
||||
if (this[permission])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -261,10 +261,10 @@ public static class Permissions
|
|||
Description = "Manage mail settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysMalware = new()
|
||||
public static Permission AdminSecurityMalware = new()
|
||||
{
|
||||
Index = 39,
|
||||
Name = "Admin system Malware",
|
||||
Name = "Admin security Malware",
|
||||
Description = "Manage malware settings in the admin area"
|
||||
};
|
||||
|
||||
|
@ -275,11 +275,11 @@ public static class Permissions
|
|||
Description = "View system resources in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysSecurity = new()
|
||||
public static Permission AdminSecurity = new()
|
||||
{
|
||||
Index = 41,
|
||||
Name = "Admin system Security",
|
||||
Description = "Manage security settings in the admin area"
|
||||
Name = "Admin Security",
|
||||
Description = "View security logs in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysSentry = new()
|
||||
|
@ -379,6 +379,20 @@ public static class Permissions
|
|||
Name = "Admin Webspaces Server New",
|
||||
Description = "Create a new webspace server in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityIpBans = new()
|
||||
{
|
||||
Index = 56,
|
||||
Name = "Admin security ip bans",
|
||||
Description = "Manage ip bans in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityPermissionGroups = new()
|
||||
{
|
||||
Index = 57,
|
||||
Name = "Admin security permission groups",
|
||||
Description = "View, add and delete permission groups"
|
||||
};
|
||||
|
||||
public static Permission? FromString(string name)
|
||||
{
|
||||
|
|
|
@ -242,11 +242,22 @@ public class IdentityService
|
|||
Permissions.IsReadyOnly = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Permissions = new PermissionStorage(BitHelper.OverwriteByteArrays(
|
||||
UserPermissions.Data,
|
||||
GroupPermissions.Data),
|
||||
true
|
||||
);
|
||||
|
||||
Permissions = new(Array.Empty<byte>());
|
||||
|
||||
foreach (var permission in Perms.Permissions.GetAllPermissions())
|
||||
{
|
||||
Permissions[permission] = GroupPermissions[permission];
|
||||
}
|
||||
|
||||
foreach (var permission in Perms.Permissions.GetAllPermissions())
|
||||
{
|
||||
if (UserPermissions[permission])
|
||||
{
|
||||
Permissions[permission] = true;
|
||||
}
|
||||
}
|
||||
|
||||
Permissions.IsReadyOnly = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<div class="card mb-5 mb-xl-10">
|
||||
<div class="card-body pt-0 pb-0">
|
||||
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/security">
|
||||
<TL>Overview</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/security/malware">
|
||||
<TL>Malware</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 2 ? "active" : "")" href="/admin/security/ipbans">
|
||||
<TL>Ip bans</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/admin/security/permissiongroups">
|
||||
<TL>Permission groups</TL>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public int Index { get; set; } = 0;
|
||||
}
|
|
@ -69,7 +69,7 @@ else
|
|||
</a>
|
||||
</div>
|
||||
|
||||
if (User.Admin)
|
||||
if (IdentityService.Permissions.HasAnyPermissions())
|
||||
{
|
||||
<div class="menu-item pt-5">
|
||||
<div class="menu-content">
|
||||
|
@ -92,6 +92,14 @@ else
|
|||
<span class="menu-title"><TL>System</TL></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a class="menu-link" href="/admin/security">
|
||||
<span class="menu-icon">
|
||||
<i class="bx bx-shield"></i>
|
||||
</span>
|
||||
<span class="menu-title"><TL>Security</TL></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="menu-item">
|
||||
<a class="menu-link" href="/admin/servers">
|
||||
<span class="menu-icon">
|
||||
|
|
7
Moonlight/Shared/Views/Admin/Security/Index.razor
Normal file
7
Moonlight/Shared/Views/Admin/Security/Index.razor
Normal file
|
@ -0,0 +1,7 @@
|
|||
@page "/admin/security"
|
||||
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminSecurity))]
|
||||
|
||||
<AdminSecurityNavigation Index="0" />
|
|
@ -1,4 +1,4 @@
|
|||
@page "/admin/system/security"
|
||||
@page "/admin/security/ipbans"
|
||||
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using BlazorTable
|
||||
|
@ -13,9 +13,9 @@
|
|||
@inject EventSystem Event
|
||||
@inject ToastService ToastService
|
||||
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminSysSecurity))]
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminSecurityIpBans))]
|
||||
|
||||
<AdminSystemNavigation Index="3"/>
|
||||
<AdminSecurityNavigation Index="2"/>
|
||||
|
||||
<div class="card mb-5">
|
||||
<div class="card-header">
|
||||
|
@ -41,31 +41,33 @@
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
<Table TableItem="IpBan" Items="IpBans" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="IpBan" Title="@(SmartTranslateService.Translate("Ip"))" Field="@(x => x.Ip)" Filterable="true" Sortable="false"/>
|
||||
<Column TableItem="IpBan" Title="" Field="@(x => x.Id)" Filterable="false" Sortable="false">
|
||||
<Template>
|
||||
<div class="text-end">
|
||||
<DeleteButton Confirm="true" OnClick="() => DeleteIpBan(context)"></DeleteButton>
|
||||
</div>
|
||||
</Template>
|
||||
</Column>
|
||||
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
|
||||
</Table>
|
||||
<div class="table-responsive">
|
||||
<Table TableItem="IpBan" Items="AllIpBans" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="IpBan" Title="@(SmartTranslateService.Translate("Ip"))" Field="@(x => x.Ip)" Filterable="true" Sortable="false"/>
|
||||
<Column TableItem="IpBan" Title="" Field="@(x => x.Id)" Filterable="false" Sortable="false">
|
||||
<Template>
|
||||
<div class="text-end">
|
||||
<DeleteButton Confirm="true" OnClick="() => DeleteIpBan(context)"></DeleteButton>
|
||||
</div>
|
||||
</Template>
|
||||
</Column>
|
||||
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
|
||||
</Table>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
private IpBan[] IpBans;
|
||||
private IpBan[] AllIpBans;
|
||||
private string Ip;
|
||||
|
||||
private LazyLoader LazyLoader;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
IpBans = IpBanRepository.Get().ToArray();
|
||||
AllIpBans = IpBanRepository.Get().ToArray();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@page "/admin/system/malware"
|
||||
@page "/admin/security/malware"
|
||||
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using Moonlight.App.Services.Background
|
||||
|
@ -14,9 +14,9 @@
|
|||
|
||||
@implements IDisposable
|
||||
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminSysMalware))]
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminSecurityMalware))]
|
||||
|
||||
<AdminSystemNavigation Index="2"/>
|
||||
<AdminSecurityNavigation Index="1"/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6">
|
132
Moonlight/Shared/Views/Admin/Security/PermissionGroups.razor
Normal file
132
Moonlight/Shared/Views/Admin/Security/PermissionGroups.razor
Normal file
|
@ -0,0 +1,132 @@
|
|||
@page "/admin/security/permissiongroups"
|
||||
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using BlazorTable
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.App.Services.Interop
|
||||
@using Moonlight.App.Services.Sessions
|
||||
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject Repository<PermissionGroup> PermissionGroupRepository
|
||||
@inject SessionServerService SessionServerService
|
||||
@inject Repository<User> UserRepository
|
||||
@inject AlertService AlertService
|
||||
@inject ToastService ToastService
|
||||
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminSecurityPermissionGroups))]
|
||||
|
||||
<AdminSecurityNavigation Index="3"/>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<TL>Permission groups</TL>
|
||||
</span>
|
||||
<div class="card-toolbar">
|
||||
<WButton Text="@(SmartTranslateService.Translate("New"))"
|
||||
CssClasses="btn-sm btn-success"
|
||||
OnClick="NewGroupPermission">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
<div class="table-responsive">
|
||||
<Table TableItem="PermissionGroup" Items="AllPermissionGroups" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="PermissionGroup" Title="@(SmartTranslateService.Translate("Name"))" Field="@(x => x.Name)" Filterable="true" Sortable="false"/>
|
||||
<Column TableItem="PermissionGroup" Title="" Field="@(x => x.Id)" Filterable="false" Sortable="false">
|
||||
<Template>
|
||||
<div class="text-end">
|
||||
<WButton Text="@(SmartTranslateService.Translate("Edit permissions"))"
|
||||
CssClasses="btn-primary me-2"
|
||||
OnClick="() => EditPermissions(context)">
|
||||
</WButton>
|
||||
<DeleteButton Confirm="true" OnClick="() => DeletePermissionGroup(context)"></DeleteButton>
|
||||
</div>
|
||||
</Template>
|
||||
</Column>
|
||||
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
|
||||
</Table>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PermissionEditor @ref="PermissionEditor" OnSave="OnSave"/>
|
||||
|
||||
@code
|
||||
{
|
||||
private PermissionGroup[] AllPermissionGroups;
|
||||
private PermissionGroup CurrentPermissionGroup;
|
||||
private LazyLoader LazyLoader;
|
||||
private PermissionEditor PermissionEditor;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
AllPermissionGroups = PermissionGroupRepository
|
||||
.Get()
|
||||
.ToArray();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task EditPermissions(PermissionGroup group)
|
||||
{
|
||||
CurrentPermissionGroup = group;
|
||||
PermissionEditor.InitialData = CurrentPermissionGroup.Permissions;
|
||||
|
||||
await PermissionEditor.Launch();
|
||||
}
|
||||
|
||||
private async Task DeletePermissionGroup(PermissionGroup group)
|
||||
{
|
||||
PermissionGroupRepository.Delete(group);
|
||||
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
|
||||
private async Task OnSave(byte[] data)
|
||||
{
|
||||
CurrentPermissionGroup.Permissions = data;
|
||||
PermissionGroupRepository.Update(CurrentPermissionGroup);
|
||||
|
||||
await ToastService.Success("Successfully modified permissions");
|
||||
|
||||
var usersWithTheGroup = UserRepository
|
||||
.Get()
|
||||
.Include(x => x.PermissionGroup)
|
||||
.Where(x => x.PermissionGroup != null)
|
||||
.Where(x => x.PermissionGroup!.Id == CurrentPermissionGroup.Id)
|
||||
.ToArray();
|
||||
|
||||
foreach (var user in usersWithTheGroup)
|
||||
{
|
||||
await SessionServerService.ReloadUserSessions(user);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NewGroupPermission()
|
||||
{
|
||||
var name = await AlertService.Text(
|
||||
SmartTranslateService.Translate("Enter the name for the new group"),
|
||||
"",
|
||||
""
|
||||
);
|
||||
|
||||
if(string.IsNullOrEmpty(name))
|
||||
return;
|
||||
|
||||
var group = new PermissionGroup()
|
||||
{
|
||||
Name = name,
|
||||
Permissions = Array.Empty<byte>()
|
||||
};
|
||||
|
||||
PermissionGroupRepository.Add(group);
|
||||
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
}
|
|
@ -1,245 +1,201 @@
|
|||
@page "/admin/users/view/{Id:int}"
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Helpers
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Repositories.Servers
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.App.Repositories.Domains
|
||||
@using Moonlight.App.Helpers
|
||||
@using Moonlight.App.Services.Files
|
||||
|
||||
@inject UserRepository UserRepository
|
||||
@inject ServerRepository ServerRepository
|
||||
@inject DomainRepository DomainRepository
|
||||
@inject Repository<User> UserRepository
|
||||
@inject Repository<Server> ServerRepository
|
||||
@inject Repository<Domain> DomainRepository
|
||||
@inject Repository<WebSpace> WebSpaceRepository
|
||||
@inject ResourceService ResourceService
|
||||
|
||||
@attribute [PermissionRequired(nameof(Permissions.AdminUserView))]
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
@if (User == null)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<TL>No user with this id found</TL>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card card-body mb-5">
|
||||
<div class="d-flex flex-column align-items-center text-center">
|
||||
<img src="/api/moonlight/avatar/@(User.Id)" class="rounded-circle" alt="Profile picture" width="150">
|
||||
@if (User == null)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
<TL>No user with this id found</TL>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex flex-column flex-lg-row">
|
||||
<div class="flex-column flex-lg-row-auto w-lg-250px w-xl-350px mb-10">
|
||||
<div class="card mb-5 mb-xl-8">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-center flex-column py-5">
|
||||
<div class="symbol symbol-100px symbol-circle mb-7">
|
||||
<img src="@(ResourceService.Avatar(User))" alt="Avatar">
|
||||
</div>
|
||||
<span class="fs-3 text-gray-800 fw-bold mb-3">
|
||||
@(User.FirstName) @(User.LastName)
|
||||
</span>
|
||||
@if (User.Admin)
|
||||
{
|
||||
<div class="mb-5">
|
||||
<div class="badge badge-lg badge-light-primary d-inline">
|
||||
<TL>Admin</TL>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<div class="pb-5 fs-6">
|
||||
<div class="fw-bold mt-5">
|
||||
<TL>Account ID</TL>
|
||||
</div>
|
||||
<div class="text-gray-600">@(User.Id)</div>
|
||||
|
||||
<div class="fw-bold mt-5">Email</div>
|
||||
<div class="text-gray-600">
|
||||
@(User.Email)
|
||||
</div>
|
||||
|
||||
<div class="fw-bold mt-5">
|
||||
<TL>Address</TL>
|
||||
</div>
|
||||
<div class="text-gray-600">@(User.Address), <br>@(User.City)<br>@(User.State)<br>@(User.Country)</div>
|
||||
|
||||
<div class="fw-bold mt-5">
|
||||
<TL>Status</TL>
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
@(User.Status)
|
||||
</div>
|
||||
|
||||
<div class="fw-bold mt-5">
|
||||
<TL>TOTP</TL>
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
@(User.TotpEnabled)
|
||||
</div>
|
||||
|
||||
<div class="fw-bold mt-5">
|
||||
<TL>Discord ID</TL>
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
@(User.DiscordId)
|
||||
</div>
|
||||
|
||||
<div class="fw-bold mt-5">
|
||||
<TL>Last Login IP</TL>
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
@(User.LastIp) <TL>at</TL> @(Formatter.FormatDate(User.LastVisitedAt))
|
||||
</div>
|
||||
|
||||
<div class="fw-bold mt-5">
|
||||
<TL>Register IP</TL>
|
||||
</div>
|
||||
<div class="text-gray-600">
|
||||
@(User.RegisterIp) <TL>at</TL> @(Formatter.FormatDate(User.CreatedAt))
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-body mb-5">
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-primary" href="/admin/users/edit/@(User.Id)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-lg-row-fluid ms-lg-15">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header border-0">
|
||||
<span class="card-title">
|
||||
<TL>Services</TL>
|
||||
</span>
|
||||
<div class="card-toolbar">
|
||||
<a href="/admin/users/edit/@(User.Id)" class="btn btn-primary">
|
||||
<TL>Edit</TL>
|
||||
</a>
|
||||
<a class="btn btn-secondary" href="/admin/users">
|
||||
<TL>Back to list</TL>
|
||||
</a>
|
||||
<a class="btn btn-primary" href="/admin/support/view/@(User.Id)">
|
||||
<TL>Open support</TL>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-xl-stretch mb-5">
|
||||
<div class="card-header border-0">
|
||||
<h3 class="card-title fw-bold text-dark">
|
||||
</div>
|
||||
|
||||
<div class="accordion mb-3" id="serversList">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="serversList-header">
|
||||
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#serversList-body" aria-expanded="false" aria-controls="serversList-body">
|
||||
<TL>Servers</TL>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body pt-2">
|
||||
@foreach (var server in Servers)
|
||||
{
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-grow-1">
|
||||
<a href="/server/@(server.Uuid)" class="fs-6">@(server.Name) - @(server.Image.Name)</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (server != Servers.Last())
|
||||
</button>
|
||||
</h2>
|
||||
<div id="serversList-body" class="accordion-collapse collapse" aria-labelledby="serversList-header" data-bs-parent="#serversList">
|
||||
<div class="accordion-body">
|
||||
@foreach (var server in Servers)
|
||||
{
|
||||
<div class="separator my-4"></div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-grow-1">
|
||||
<a href="/server/@(server.Uuid)" class="fs-6">@(server.Name) - @(server.Image.Name)</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (server != Servers.Last())
|
||||
{
|
||||
<div class="separator my-4"></div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-xl-stretch">
|
||||
<div class="card-header border-0">
|
||||
<h3 class="card-title fw-bold text-dark">
|
||||
</div>
|
||||
|
||||
<div class="accordion mb-3" id="domainsList">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="domainsList-header">
|
||||
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#domainsList-body" aria-expanded="false" aria-controls="domainsList-body">
|
||||
<TL>Domains</TL>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body pt-2">
|
||||
@foreach (var domain in Domains)
|
||||
{
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-grow-1">
|
||||
<a href="/domain/@(domain.Id)" class="fs-6">@(domain.Name).@(domain.SharedDomain.Name)</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (domain != Domains.Last())
|
||||
</button>
|
||||
</h2>
|
||||
<div id="domainsList-body" class="accordion-collapse collapse" aria-labelledby="domainsList-header" data-bs-parent="#domainsList">
|
||||
<div class="accordion-body">
|
||||
@foreach (var domain in Domains)
|
||||
{
|
||||
<div class="separator my-4"></div>
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-grow-1">
|
||||
<a href="/domain/@(domain.Id)" class="fs-6">@(domain.Name).@(domain.SharedDomain.Name)</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (domain != Domains.Last())
|
||||
{
|
||||
<div class="separator my-4"></div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body fs-6">
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>First name</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.FirstName)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Last name</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.LastName)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Email</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.Email)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Address</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.Address)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>City</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.City)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>State</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.State)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Country</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.Country)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Admin</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">
|
||||
@if (User.Admin)
|
||||
{
|
||||
<span>✅</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>❌</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Status</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.Status)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Totp</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.TotpEnabled)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Discord</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.DiscordId)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Subscription</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Created at</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(Formatter.FormatDate(User.CreatedAt))</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Register ip</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.RegisterIp)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="row">
|
||||
<label class="col-lg-4 fw-semibold text-muted">
|
||||
<TL>Last ip</TL>
|
||||
</label>
|
||||
<div class="col-lg-8">
|
||||
<span class="fw-bold fs-6 text-gray-800">@(User.LastIp)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion mb-3" id="webspacesList">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="webspacesList-header">
|
||||
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#webspacesList-body" aria-expanded="false" aria-controls="webspacesList-body">
|
||||
<TL>Webspaces</TL>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="webspacesList-body" class="accordion-collapse collapse" aria-labelledby="webspacesList-header" data-bs-parent="#webspacesList">
|
||||
<div class="accordion-body">
|
||||
@foreach (var webSpace in WebSpaces)
|
||||
{
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="flex-grow-1">
|
||||
<a href="/webspace/@(webSpace.Id)" class="fs-6">@(webSpace.Domain) - @(webSpace.CloudPanel.Name)</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
if (webSpace != WebSpaces.Last())
|
||||
{
|
||||
<div class="separator my-4"></div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
|
@ -249,6 +205,7 @@
|
|||
private User? User;
|
||||
private Server[] Servers;
|
||||
private Domain[] Domains;
|
||||
private WebSpace[] WebSpaces;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
|
@ -269,6 +226,13 @@
|
|||
.Include(x => x.Owner)
|
||||
.Where(x => x.Owner.Id == User.Id)
|
||||
.ToArray();
|
||||
|
||||
WebSpaces = WebSpaceRepository
|
||||
.Get()
|
||||
.Include(x => x.CloudPanel)
|
||||
.Include(x => x.Owner)
|
||||
.Where(x => x.Owner.Id == User.Id)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
Loading…
Add table
Reference in a new issue