Started creating the docs
This commit is contained in:
4
.idea/.idea.HopFrame/.idea/indexLayout.xml
generated
4
.idea/.idea.HopFrame/.idea/indexLayout.xml
generated
@@ -1,7 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<attachedFolders>
|
||||
<Path>docs</Path>
|
||||
</attachedFolders>
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
|
||||
@@ -7,6 +7,6 @@ public class DatabaseContext : HopDbContextBase {
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
|
||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\DatabaseTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
namespace HopFrame.Api.Models;
|
||||
|
||||
public struct SingleValueResult<T>(T value) {
|
||||
public T Value { get; set; } = value;
|
||||
public struct SingleValueResult<TValue>(TValue value) {
|
||||
public TValue Value { get; set; } = value;
|
||||
|
||||
public static implicit operator T(SingleValueResult<T> v) {
|
||||
public static implicit operator TValue(SingleValueResult<TValue> v) {
|
||||
return v.Value;
|
||||
}
|
||||
|
||||
public static implicit operator SingleValueResult<T>(T v) {
|
||||
return new SingleValueResult<T>(v);
|
||||
public static implicit operator SingleValueResult<TValue>(TValue v) {
|
||||
return new SingleValueResult<TValue>(v);
|
||||
}
|
||||
}
|
||||
@@ -24,4 +24,6 @@ public interface IUserService {
|
||||
Task DeleteUser(User user);
|
||||
|
||||
Task<bool> CheckUserPassword(User user, string password);
|
||||
|
||||
Task ChangePassword(User user, string password);
|
||||
}
|
||||
@@ -113,4 +113,16 @@ internal sealed class UserService<TDbContext>(TDbContext context) : IUserService
|
||||
|
||||
return entry.Password == hash;
|
||||
}
|
||||
|
||||
public async Task ChangePassword(User user, string password) {
|
||||
var entry = await context.Users
|
||||
.Where(entry => entry.Id == user.Id.ToString())
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
if (entry is null) return;
|
||||
|
||||
var hash = EncryptionManager.Hash(password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
|
||||
entry.Password = hash;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
@switch (Type) {
|
||||
case HopIcon.Reload:
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/>
|
||||
</svg>
|
||||
break;
|
||||
|
||||
case HopIcon.ArrowUp:
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
||||
</svg>
|
||||
break;
|
||||
|
||||
case HopIcon.ArrowDown:
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
||||
</svg>
|
||||
break;
|
||||
|
||||
case HopIcon.Cross:
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||
</svg>
|
||||
break;
|
||||
|
||||
case HopIcon.User:
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6"/>
|
||||
</svg>
|
||||
break;
|
||||
|
||||
case HopIcon.Group:
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||
<path d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5"/>
|
||||
</svg>
|
||||
break;
|
||||
|
||||
case HopIcon.Logout:
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M6 12.5a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-8a.5.5 0 0 0-.5.5v2a.5.5 0 0 1-1 0v-2A1.5 1.5 0 0 1 6.5 2h8A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 5 12.5v-2a.5.5 0 0 1 1 0z"/>
|
||||
<path fill-rule="evenodd" d="M.146 8.354a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L1.707 7.5H10.5a.5.5 0 0 1 0 1H1.707l2.147 2.146a.5.5 0 0 1-.708.708z"/>
|
||||
</svg>
|
||||
break;
|
||||
}
|
||||
|
||||
<style>
|
||||
svg.bi-nav {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
|
||||
@code {
|
||||
[Parameter] public HopIcon Type { get; set; }
|
||||
[Parameter] public bool NavIcon { get; set; }
|
||||
|
||||
public enum HopIcon {
|
||||
Reload,
|
||||
ArrowUp,
|
||||
ArrowDown,
|
||||
User,
|
||||
Group,
|
||||
Logout,
|
||||
Cross
|
||||
}
|
||||
|
||||
private string GetClass() {
|
||||
return NavIcon ? "bi-nav" : "bi";
|
||||
}
|
||||
}
|
||||
@@ -21,20 +21,20 @@ main {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.top-row ::deep a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.top-row ::deep a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row {
|
||||
@@ -88,9 +88,9 @@ main {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using HopFrame.Web.Pages.Administration.Components
|
||||
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">HopFrame</a>
|
||||
<a class="navbar-brand" href="/administration">HopFrame</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,26 +13,19 @@
|
||||
<nav class="flex-column" style="display: flex; height: 100%">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="administration/users">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-fill" viewBox="0 0 16 16">
|
||||
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6"/>
|
||||
</svg> Users
|
||||
<HopIconDisplay Type="HopIconDisplay.HopIcon.User" NavIcon="true"/> Users
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="administration/groups">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-people-fill" viewBox="0 0 16 16">
|
||||
<path d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5"/>
|
||||
</svg> Groups
|
||||
<HopIconDisplay Type="HopIconDisplay.HopIcon.Group" NavIcon="true"/> Groups
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3" style="margin-top: auto">
|
||||
<NavLink class="nav-link" href="login">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M6 12.5a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-8a.5.5 0 0 0-.5.5v2a.5.5 0 0 1-1 0v-2A1.5 1.5 0 0 1 6.5 2h8A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 5 12.5v-2a.5.5 0 0 1 1 0z"/>
|
||||
<path fill-rule="evenodd" d="M.146 8.354a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L1.707 7.5H10.5a.5.5 0 0 1 0 1H1.707l2.147 2.146a.5.5 0 0 1-.708.708z"/>
|
||||
</svg> Logout
|
||||
<HopIconDisplay Type="HopIconDisplay.HopIcon.Logout" NavIcon="true"/> Logout
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
.top-row {
|
||||
height: 3.5rem;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
@@ -34,50 +34,38 @@
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested-nav-menu {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link {
|
||||
color: #d7d7d7;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
.nav-item ::deep .nav-link {
|
||||
color: #d7d7d7;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
background-color: rgba(255, 255, 255, 0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep .nav-link:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using HopFrame.Web.Components
|
||||
@using HopFrame.Web.Pages.Administration.Components
|
||||
|
||||
@layout AdminLayout
|
||||
@rendermode InteractiveServer
|
||||
@@ -34,6 +35,10 @@
|
||||
<InputText type="text" class="form-control" id="username" required @bind-Value="User.Username"/>
|
||||
<ValidationMessage For="() => User.Username"/>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<InputText type="password" class="form-control" id="password" required @bind-Value="_password"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="groups" class="form-label">Groups</label>
|
||||
@@ -43,9 +48,7 @@
|
||||
@foreach (var group in _groups) {
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-danger btn-sm" style="margin-right: 15px" @onclick="() => RemoveGroup(group)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||
</svg>
|
||||
<HopIconDisplay Type="HopIconDisplay.HopIcon.Cross"/>
|
||||
</button>
|
||||
|
||||
<span>@group.Name.Replace("group.", "")</span>
|
||||
@@ -78,9 +81,7 @@
|
||||
@foreach (var perm in User.Permissions.Where(perm => !perm.PermissionName.StartsWith("group."))) {
|
||||
<li class="list-group-item">
|
||||
<button type="button" class="btn btn-danger btn-sm" style="margin-right: 15px" @onclick="() => RemovePermission(perm)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||
</svg>
|
||||
<HopIconDisplay Type="HopIconDisplay.HopIcon.Cross"/>
|
||||
</button>
|
||||
|
||||
<span>@perm.PermissionName</span>
|
||||
@@ -119,6 +120,7 @@
|
||||
private IList<PermissionGroup> _allGroups = new List<PermissionGroup>();
|
||||
private string _selectedGroup;
|
||||
private string _permissionToAdd;
|
||||
private string _password;
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
if (Guid.TryParse(UserId, out var guid)) {
|
||||
@@ -165,6 +167,10 @@
|
||||
if (result.IsConfirmed) {
|
||||
await Users.UpdateUser(User);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_password)) {
|
||||
await Users.ChangePassword(User, _password);
|
||||
}
|
||||
|
||||
await Alerts.FireAsync(new SweetAlertOptions {
|
||||
Title = "User edited!",
|
||||
Icon = SweetAlertIcon.Success,
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
<AuthorizedView Permission="@AdminPermissions.ViewUsers" RedirectIfUnauthorized="login?redirect=/administration/users"/>
|
||||
|
||||
<div class="title">
|
||||
<h3 style="user-select: none">
|
||||
<h3>
|
||||
Users administration
|
||||
<span style="cursor: pointer" @onclick="Reload">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/>
|
||||
</svg>
|
||||
<span class="reload" @onclick="Reload">
|
||||
<HopIconDisplay Type="HopIconDisplay.HopIcon.Reload"/>
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
@@ -36,75 +33,48 @@
|
||||
<table class="table table-stripped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="user-select: none">#</th>
|
||||
<th scope="col" style="user-select: none">
|
||||
<span style="cursor: pointer;" @onclick="() => OrderBy(OrderType.Email)">E-Mail</span>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">
|
||||
<span class="sorter" @onclick="() => OrderBy(OrderType.Email)">E-Mail</span>
|
||||
@if (_currentOrder == OrderType.Email) {
|
||||
@if (_currentOrderDirection == OrderDirection.Asc) {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
|
||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
||||
</svg>
|
||||
}
|
||||
else {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-up-fill" viewBox="0 0 16 16">
|
||||
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
||||
</svg>
|
||||
}
|
||||
<HopIconDisplay Type="_currentOrderDirection == OrderDirection.Desc ? HopIconDisplay.HopIcon.ArrowDown : HopIconDisplay.HopIcon.ArrowUp"/>
|
||||
}
|
||||
</th>
|
||||
<th scope="col" style="user-select: none">
|
||||
<span style="cursor: pointer;" @onclick="() => OrderBy(OrderType.Username)">Username</span>
|
||||
<th scope="col">
|
||||
<span class="sorter" @onclick="() => OrderBy(OrderType.Username)">Username</span>
|
||||
@if (_currentOrder == OrderType.Username) {
|
||||
@if (_currentOrderDirection == OrderDirection.Asc) {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
|
||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
||||
</svg>
|
||||
}
|
||||
else {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-up-fill" viewBox="0 0 16 16">
|
||||
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
||||
</svg>
|
||||
}
|
||||
<HopIconDisplay Type="_currentOrderDirection == OrderDirection.Desc ? HopIconDisplay.HopIcon.ArrowDown : HopIconDisplay.HopIcon.ArrowUp"/>
|
||||
}
|
||||
</th>
|
||||
<th scope="col" style="user-select: none">
|
||||
<span style="cursor: pointer;" @onclick="() => OrderBy(OrderType.Registered)">Registered</span>
|
||||
<th scope="col">
|
||||
<span class="sorter" @onclick="() => OrderBy(OrderType.Registered)">Registered</span>
|
||||
@if (_currentOrder == OrderType.Registered) {
|
||||
@if (_currentOrderDirection == OrderDirection.Asc) {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
|
||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
||||
</svg>
|
||||
}
|
||||
else {
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-up-fill" viewBox="0 0 16 16">
|
||||
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
||||
</svg>
|
||||
}
|
||||
<HopIconDisplay Type="_currentOrderDirection == OrderDirection.Desc ? HopIconDisplay.HopIcon.ArrowDown : HopIconDisplay.HopIcon.ArrowUp"/>
|
||||
}
|
||||
</th>
|
||||
<th scope="col" style="user-select: none">Primary Group</th>
|
||||
|
||||
<th scope="col">Primary Group</th>
|
||||
|
||||
@if (_hasEditPrivileges || _hasDeletePrivileges) {
|
||||
<th scope="col" style="user-select: none">Actions</th>
|
||||
<th scope="col">Actions</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var user in _users) {
|
||||
<tr>
|
||||
<th scope="row">@user.Id</th>
|
||||
<td class="bold">@user.Id</td>
|
||||
<td>@user.Email</td>
|
||||
<td>@user.Username</td>
|
||||
<td>@user.CreatedAt</td>
|
||||
<td>@GetFriendlyGroupName(user)</td>
|
||||
|
||||
|
||||
@if (_hasEditPrivileges || _hasDeletePrivileges) {
|
||||
<td>
|
||||
<div class="btn-group" role="group" aria-label="Basic example">
|
||||
@if (_hasEditPrivileges) {
|
||||
<button type="button" class="btn btn-warning" @onclick="() => EditUser(user)">Edit</button>
|
||||
}
|
||||
|
||||
|
||||
@if (_hasDeletePrivileges) {
|
||||
<button type="button" class="btn btn-danger" @onclick="() => Delete(user)">Delete</button>
|
||||
}
|
||||
@@ -210,7 +180,7 @@
|
||||
Timer = 1500,
|
||||
ShowConfirmButton = false
|
||||
});
|
||||
|
||||
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,3 +3,15 @@
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
th, h3 {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.reload, .sorter {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ public static class ServiceCollectionExtensions {
|
||||
services.AddHttpClient();
|
||||
services.AddScoped<IAuthService, AuthService<TDbContext>>();
|
||||
services.AddTransient<AuthMiddleware>();
|
||||
|
||||
// Component library's
|
||||
services.AddSweetAlert2();
|
||||
|
||||
//TODO: Use https://blazorstrap.io/V5/V5
|
||||
|
||||
services.AddHopFrameAuthentication<TDbContext>();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Database", "HopFra
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{58703056-8DAD-4221-BBE3-42425D2F4929}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DatabaseTest", "DatabaseTest\DatabaseTest.csproj", "{921159CE-AF75-44C3-A3F9-6B9B1A4E85CF}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApiTest", "RestApiTest\RestApiTest.csproj", "{921159CE-AF75-44C3-A3F9-6B9B1A4E85CF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Security", "HopFrame.Security\HopFrame.Security.csproj", "{7F82E1C6-4A42-4337-9E03-2EE6429D004F}"
|
||||
EndProject
|
||||
|
||||
@@ -3,7 +3,7 @@ using HopFrame.Security.Authorization;
|
||||
using HopFrame.Security.Claims;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DatabaseTest.Controllers;
|
||||
namespace RestApiTest.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("test")]
|
||||
@@ -1,12 +1,12 @@
|
||||
using HopFrame.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DatabaseTest;
|
||||
namespace RestApiTest;
|
||||
|
||||
public class DatabaseContext : HopDbContextBase {
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
|
||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\DatabaseTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using DatabaseTest;
|
||||
using RestApiTest;
|
||||
using HopFrame.Api.Extensions;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
34
docs/Diagrams/Models/ApiModels.puml
Normal file
34
docs/Diagrams/Models/ApiModels.puml
Normal file
@@ -0,0 +1,34 @@
|
||||
@startuml ApiModels
|
||||
|
||||
namespace HopFrame.Security {
|
||||
class UserLogin {
|
||||
+Email: string
|
||||
+Password: string
|
||||
}
|
||||
|
||||
class UserRegister {
|
||||
+Username: string
|
||||
+Email: string
|
||||
+Password: string
|
||||
}
|
||||
}
|
||||
|
||||
namespace HopFrame.Web {
|
||||
class RegisterData {
|
||||
+RepeatedPassword: string
|
||||
}
|
||||
}
|
||||
|
||||
namespace HopFrame.Api {
|
||||
class SingleValueResult<TValue> {
|
||||
+Value: TValue
|
||||
}
|
||||
|
||||
class UserPasswordValidation {
|
||||
+Password: string
|
||||
}
|
||||
}
|
||||
|
||||
UserRegister <|-- RegisterData
|
||||
|
||||
@enduml
|
||||
37
docs/Diagrams/Models/BaseModels.puml
Normal file
37
docs/Diagrams/Models/BaseModels.puml
Normal file
@@ -0,0 +1,37 @@
|
||||
@startuml BaseModels
|
||||
set namespaceSeparator none
|
||||
|
||||
namespace HopFrame.Database {
|
||||
class User {
|
||||
+Id: Guid
|
||||
+Username: string
|
||||
+Email: string
|
||||
+CreatedAt: DateTime
|
||||
+Permissions: IList<Permission>
|
||||
}
|
||||
|
||||
class Permission {
|
||||
+Id: long
|
||||
+PermissionName: string
|
||||
+Owner: Guid
|
||||
+GrantedAt: DateTime
|
||||
}
|
||||
|
||||
class PermissionGroup {
|
||||
+Name: string
|
||||
+IsDefaultGroup: bool
|
||||
+Description: string
|
||||
+CreatedAt: DateTime
|
||||
+Permissions: IList<Permission>
|
||||
}
|
||||
|
||||
interface IPermissionOwner {}
|
||||
}
|
||||
|
||||
IPermissionOwner <|-- User
|
||||
IPermissionOwner <|-- PermissionGroup
|
||||
|
||||
User .. Permission
|
||||
PermissionGroup .. Permission
|
||||
|
||||
@enduml
|
||||
41
docs/Diagrams/Models/DatabaseModels.puml
Normal file
41
docs/Diagrams/Models/DatabaseModels.puml
Normal file
@@ -0,0 +1,41 @@
|
||||
@startuml DatabaseModels
|
||||
set namespaceSeparator none
|
||||
|
||||
namespace HopFrame.Database {
|
||||
class UserEntry {
|
||||
+Id: string
|
||||
+Username: string
|
||||
+Email: string
|
||||
+Password: string
|
||||
+CreatedAt: DateTime
|
||||
}
|
||||
|
||||
class TokenEntry {
|
||||
{static} +RefreshTokenType: int = 0
|
||||
{static} +AccessTokenType: int = 1
|
||||
|
||||
+Type: int
|
||||
+Token: string
|
||||
+UserId: string
|
||||
+CreatedAt: DateTime
|
||||
}
|
||||
|
||||
class PermissionEntry {
|
||||
+RecordId: long
|
||||
+PermissionText: string
|
||||
+UserId: string
|
||||
+GrantedAt: DateTime
|
||||
}
|
||||
|
||||
class GroupEntry {
|
||||
+Name: string
|
||||
+Default: bool
|
||||
+Description: string
|
||||
+CreatedAt: DateTime
|
||||
}
|
||||
}
|
||||
|
||||
UserEntry *-- TokenEntry
|
||||
UserEntry *-- PermissionEntry
|
||||
|
||||
@enduml
|
||||
21
docs/Diagrams/Models/README.md
Normal file
21
docs/Diagrams/Models/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Models for HopFrame
|
||||
|
||||
This page shows all models that HopFrame uses.
|
||||
|
||||
|
||||
## Base Models
|
||||
These are the models used by the various database services.
|
||||
|
||||

|
||||
|
||||
|
||||
## API Models
|
||||
These are the models used by the REST API and the Blazor API.
|
||||
|
||||

|
||||
|
||||
|
||||
## Database Models
|
||||
These are the models that correspond to the scheme in the Database
|
||||
|
||||

|
||||
1
docs/Diagrams/Models/img/ApiModels.svg
Normal file
1
docs/Diagrams/Models/img/ApiModels.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
1
docs/Diagrams/Models/img/BaseModels.svg
Normal file
1
docs/Diagrams/Models/img/BaseModels.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
1
docs/Diagrams/Models/img/DatabaseModels.svg
Normal file
1
docs/Diagrams/Models/img/DatabaseModels.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 14 KiB |
Reference in New Issue
Block a user