Started creating the docs

This commit is contained in:
2024-07-23 19:15:01 +02:00
parent 643ceeb607
commit e31dccc298
28 changed files with 332 additions and 129 deletions

View File

@@ -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>

View File

@@ -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;");
}
}

View File

@@ -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);
}
}

View File

@@ -24,4 +24,6 @@ public interface IUserService {
Task DeleteUser(User user);
Task<bool> CheckUserPassword(User user, string password);
Task ChangePassword(User user, string password);
}

View File

@@ -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();
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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();
}
}

View File

@@ -3,3 +3,15 @@
flex-direction: row;
justify-content: space-between;
}
th, h3 {
user-select: none;
}
.reload, .sorter {
cursor: pointer;
}
.bold {
font-weight: bold;
}

View File

@@ -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>();

View File

@@ -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

View File

@@ -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")]

View File

@@ -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;");
}
}

View File

@@ -1,4 +1,4 @@
using DatabaseTest;
using RestApiTest;
using HopFrame.Api.Extensions;
using Microsoft.OpenApi.Models;

View 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

View 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

View 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

View 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.
![](img/BaseModels.svg)
## API Models
These are the models used by the REST API and the Blazor API.
![](img/ApiModels.svg)
## Database Models
These are the models that correspond to the scheme in the Database
![](img/DatabaseModels.svg)

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB