From 0f3da6952beb7919295d43f413530e8e99540a4d Mon Sep 17 00:00:00 2001 From: Penry Date: Sun, 7 Jun 2026 08:11:02 +0200 Subject: [PATCH] Added all fucking things except Versioning --- .gitea/workflows/linux_arm64_docker.yaml | 11 +- .../Pages/CouchLogSettings.razor | 47 ++--- .../Pages/Dialogs/CreateUserDialog.razor | 68 +++++++ .../AdminSettings/Pages/Index.razor | 5 + .../AdminSettings/Pages/UserManagement.razor | 187 +++++++----------- .../Shared/AdminSettingsLayout.razor | 34 ++-- .../Shared/AdminSettingsNavMenu.razor | 18 -- .../Dialogs/AddNewGlobalEntityDialog.razor | 139 +++++-------- .../Pages/GlobalList/GlobalList.razor | 2 + .../Components/PrivateEntityCard.razor | 85 ++++---- .../Pages/PrivateList/PrivateList.razor | 75 ++++++- CouchLog/CouchLog.csproj | 4 +- CouchLog/OnStartUp.cs | 2 +- README.md | 10 +- 14 files changed, 371 insertions(+), 316 deletions(-) create mode 100644 CouchLog/Components/AdminSettings/Pages/Dialogs/CreateUserDialog.razor delete mode 100644 CouchLog/Components/AdminSettings/Shared/AdminSettingsNavMenu.razor diff --git a/.gitea/workflows/linux_arm64_docker.yaml b/.gitea/workflows/linux_arm64_docker.yaml index 400ea90..22f9cac 100644 --- a/.gitea/workflows/linux_arm64_docker.yaml +++ b/.gitea/workflows/linux_arm64_docker.yaml @@ -4,6 +4,7 @@ on: push: branches: - main + - v0.1.0-beta jobs: build-docker-linux-arm64: @@ -22,6 +23,14 @@ jobs: id: get_repo run: | echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV + + - name: Get version + run: | + VERSION=${GITHUB_REF#refs/tags/v} + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "ASSEMBLY_VERSION=${VERSION}.0" >> $GITHUB_ENV + echo "FILE_VERSION=${VERSION}.0" >> $GITHUB_ENV + echo "REPO_LC=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV - name: Build Linux ARM64 Docker Image run: | @@ -30,7 +39,7 @@ jobs: . - name: Save Docker Image to Tar - run: docker save -o CouchLog-Linux-ARM64-Docker-Image.tar gitea.penry.de/${{ env.REPO_LC }}:latest + run: docker save -o CouchLog-Linux-ARM64-Docker-Image.tar gitea.penry.de/${{ env.REPO_LC }}:nightly - name: Upload Artifact uses: actions/upload-artifact@v3 diff --git a/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor b/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor index ef8642e..4fb8717 100644 --- a/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor +++ b/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor @@ -1,51 +1,38 @@ @page "/AdminSettings/CouchLogSettings" @rendermode InteractiveServer +@using System.Reflection @using CouchLog.Data @using Microsoft.EntityFrameworkCore -@using Microsoft.AspNetCore.Components.Forms -@inject ApplicationDbContext CouchLogDB +@inject ApplicationDbContext CouchLogDb -

CouchLog Settings

+CouchLog Settings -@if (accountsSettings is null) -{ -

Lade Einstellungen…

-} -else -{ - -
- + + + - - - -
-
-} +Version: @_version @code { - private AccountsSettings? accountsSettings; + private AccountsSettings? _accountsSettings; + private readonly string _version = Assembly.GetEntryAssembly().GetCustomAttribute()?.InformationalVersion?.Split('+')[0]; protected override async Task OnInitializedAsync() { - accountsSettings = await CouchLogDB.AccountsSettings.FirstAsync(); + _accountsSettings = await CouchLogDb.AccountsSettings.FirstAsync(); } private async Task OnRegistrationChanged(bool value) { - accountsSettings!.IsRegistrationAllowed = value; + _accountsSettings!.IsRegistrationAllowed = value; - CouchLogDB.AccountsSettings.Update(accountsSettings); - await CouchLogDB.SaveChangesAsync(); + CouchLogDb.AccountsSettings.Update(_accountsSettings); + await CouchLogDb.SaveChangesAsync(); } } diff --git a/CouchLog/Components/AdminSettings/Pages/Dialogs/CreateUserDialog.razor b/CouchLog/Components/AdminSettings/Pages/Dialogs/CreateUserDialog.razor new file mode 100644 index 0000000..6c63bee --- /dev/null +++ b/CouchLog/Components/AdminSettings/Pages/Dialogs/CreateUserDialog.razor @@ -0,0 +1,68 @@ +@using CouchLog.Data +@using Microsoft.AspNetCore.Identity + +@inject UserManager UserManager +@inject ISnackbar Snackbar + + + + Create New User + + + + + + @foreach (var role in Roles) + { + @role.Name + } + + + + + Create user + Cancel + + + +@code +{ + [Parameter] public required List Roles { get; set; } + + [CascadingParameter] + public IMudDialogInstance MudDialog { get; set; } = null!; + + private string _newUsername = string.Empty; + private string _newUserRole = string.Empty; + + private async Task CreateNewUser() + { + if (string.IsNullOrWhiteSpace(_newUsername) || string.IsNullOrWhiteSpace(_newUserRole)) + { + Snackbar.Add("Add Username and Role" , Severity.Warning); + return; + } + + ApplicationUser newUser = new ApplicationUser + { + UserName = _newUsername, + EmailConfirmed = true + }; + + var result = await UserManager.CreateAsync(newUser, "NewPassword123!"); + + if(result.Succeeded) + { + await UserManager.AddToRoleAsync(newUser, _newUserRole); + MudDialog.Close(DialogResult.Ok(newUser)); + return; + } + + //Snackbar.Add("User could not be created", Severity.Error); + } + + private async Task Cancel() + { + MudDialog.Cancel(); + } +} \ No newline at end of file diff --git a/CouchLog/Components/AdminSettings/Pages/Index.razor b/CouchLog/Components/AdminSettings/Pages/Index.razor index bc6dce5..7e88f3d 100644 --- a/CouchLog/Components/AdminSettings/Pages/Index.razor +++ b/CouchLog/Components/AdminSettings/Pages/Index.razor @@ -2,10 +2,15 @@ @using Microsoft.AspNetCore.Authorization +@inject NavigationManager NavigationManager + @attribute [Authorize(Roles = "Admin")]

Index

+CouchLog Settings +User Management + @code { } diff --git a/CouchLog/Components/AdminSettings/Pages/UserManagement.razor b/CouchLog/Components/AdminSettings/Pages/UserManagement.razor index ce9bfba..3c7e676 100644 --- a/CouchLog/Components/AdminSettings/Pages/UserManagement.razor +++ b/CouchLog/Components/AdminSettings/Pages/UserManagement.razor @@ -1,106 +1,84 @@ @page "/AdminSettings/UserManagement" @rendermode InteractiveServer +@using CouchLog.Components.AdminSettings.Pages.Dialogs @using CouchLog.Data @using Microsoft.AspNetCore.Identity @using Microsoft.EntityFrameworkCore -@using Microsoft.AspNetCore.Components.QuickGrid @using Microsoft.AspNetCore.Authorization -@inject ApplicationDbContext CouchLogDB +@inject ApplicationDbContext CouchLogDb @inject UserManager UserManager @inject AuthenticationStateProvider AuthenticationStateProvider @inject NavigationManager NavigationManager +@inject IDialogService DialogService +@inject ISnackbar Snackbar @attribute [Authorize(Roles = "Admin")] -
-

UserManagement

+ + UserManagement + + + - -
+
- - - - + + @code { - - string newUsername = ""; - string newUserRoleId = ""; - List gridUsers = new(); - List roles = new(); - string? currentUserId; + private string? _currentUserId; + private List _users = new(); + private List _roles = new(); + private List _gridUsers = new(); + + public class UserGridItem + { + public int Index { get; set; } + public string UserId { get; set; } = ""; + public string UserName { get; set; } = ""; + public string Role { get; set; } = ""; + } protected override async Task OnInitializedAsync() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); - currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; + _currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; - var users = await CouchLogDB.Users.ToListAsync(); - roles = await CouchLogDB.Roles.ToListAsync(); + var users = await CouchLogDb.Users.ToListAsync(); + _roles = await CouchLogDb.Roles.ToListAsync(); + + await CreateGridUserObjects(); + } + + private async Task CreateGridUserObjects() + { + _gridUsers.Clear(); + + var users = await CouchLogDb.Users.ToListAsync(); int index = 1; - - gridUsers.Clear(); - + foreach (var user in users) { + var role = await UserManager.GetRolesAsync(user); - gridUsers.Add(new UserGridItem + _gridUsers.Add(new UserGridItem { Index = index++, UserId = user.Id, @@ -110,17 +88,26 @@ } } - public class UserGridItem + private async Task OpenCreateUserDialog() { - public int Index { get; set; } - public string UserId { get; set; } = ""; - public string UserName { get; set; } = ""; - public string Role { get; set; } = ""; + var parameters = new DialogParameters + { + { x => x.Roles, _roles } + }; + + var dialog = await DialogService.ShowAsync("Create User", parameters); + var result = await dialog.Result; + if (!result!.Canceled && result.Data is ApplicationUser newUser) + { + Snackbar.Add("User created", Severity.Success); + await CreateGridUserObjects(); + StateHasChanged(); + } } async Task DeleteUser(UserGridItem user) { - if (user.UserId == currentUserId) + if (user.UserId == _currentUserId) return; var identityUser = await UserManager.FindByIdAsync(user.UserId); @@ -132,48 +119,12 @@ if (result.Succeeded) { - gridUsers.Remove(user); + _gridUsers.Remove(user); StateHasChanged(); } else { - // Optional: Fehler anzeigen + Snackbar.Add(result.Errors.ToString()!, Severity.Warning); } } - - private async Task CreateUserAsync() - { - // Validierung - if (string.IsNullOrWhiteSpace(newUsername) || string.IsNullOrWhiteSpace(newUserRoleId)) - { - // Fehlermeldung anzeigen oder abbrechen - Console.WriteLine("Bitte Benutzername und Rolle angeben."); - return; - } - - // Hier deine Logik zum Erstellen des Users - var roleIdToUse = newUserRoleId; - var usernameToUse = newUsername; - - ApplicationUser newUser = new ApplicationUser - { - UserName = newUsername, - EmailConfirmed = true - }; - - var result = await UserManager.CreateAsync(newUser, "NewPassword123!"); - - IdentityRole roleName = roles.FirstOrDefault(r => r.Id == newUserRoleId); - - if(result.Succeeded) - { - await UserManager.AddToRoleAsync(newUser, roleName.Name); - } - - // Modal schließen oder Formular zurücksetzen - newUsername = ""; - newUserRoleId = ""; - - NavigationManager.NavigateTo(NavigationManager.Uri, true); - } } diff --git a/CouchLog/Components/AdminSettings/Shared/AdminSettingsLayout.razor b/CouchLog/Components/AdminSettings/Shared/AdminSettingsLayout.razor index f13c34a..6ddacd9 100644 --- a/CouchLog/Components/AdminSettings/Shared/AdminSettingsLayout.razor +++ b/CouchLog/Components/AdminSettings/Shared/AdminSettingsLayout.razor @@ -5,17 +5,27 @@ @attribute [Authorize(Roles = "Admin")] -

Manage CouchLog

+Manage CouchLog -
-

-
-
-
- -
-
+ + + + + + CouchLog Settings + + + + User Management + + + + + @Body -
-
-
\ No newline at end of file + + + \ No newline at end of file diff --git a/CouchLog/Components/AdminSettings/Shared/AdminSettingsNavMenu.razor b/CouchLog/Components/AdminSettings/Shared/AdminSettingsNavMenu.razor deleted file mode 100644 index 3d71bbe..0000000 --- a/CouchLog/Components/AdminSettings/Shared/AdminSettingsNavMenu.razor +++ /dev/null @@ -1,18 +0,0 @@ -@using Microsoft.AspNetCore.Identity -@using CouchLog.Data - -@inject SignInManager SignInManager - - \ No newline at end of file diff --git a/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor b/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor index 53a185e..dd02e4c 100644 --- a/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor +++ b/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor @@ -11,19 +11,9 @@ @inject ISnackbar Snackbar @@ -36,57 +26,31 @@ - - - - -
- - @if (_imagePreviewUrl is not null) - { -
- Preview -
- - - - Replace Image - - -
-
- } - else - { - - - - Bild hierher ziehen
oder klicken zum Auswählen -
-
- } - -
-
-
-
+ + + + @if (!string.IsNullOrWhiteSpace(_imagePreview)) + { + + } + else + { + + + + } + + - + - -
+ +
@@ -148,14 +112,14 @@ private List _genres = new(); private GlobalEntityFormModel _form = new(); - private MudFileUpload? _fileUpload; - private IBrowserFile? _picture; - private string? _imagePreviewUrl; - private bool _isDragOver; + private string? _imagePreview; private int _selectedMediaTypeId; private bool _isPrivate; private bool _isSaving; - private IEnumerable _selectedGenreIds = new HashSet(); + private IReadOnlyCollection _selectedGenreIds = Array.Empty(); + + //Picture + private IBrowserFile? _pictureFile; private class GlobalEntityFormModel { @@ -164,15 +128,6 @@ public string Title { get; set; } = string.Empty; } - private string GetDropZoneStyle() => - "width: 100%; height: 100%; min-height: 380px; border-radius: 6px; " + - $"border: 2px dashed {(_isDragOver ? "var(--mud-palette-primary)" : "var(--mud-palette-lines-default)")}; " + - $"background-color: {(_isDragOver ? "var(--mud-palette-primary-hover)" : "transparent")}; " + - "transition: border-color .2s ease, background-color .2s ease;"; - - private Task OpenFilePicker() => - _fileUpload?.OpenFilePickerAsync() ?? Task.CompletedTask; - protected override async Task OnInitializedAsync() { _editContext = new EditContext(_form); @@ -194,17 +149,24 @@ _selectedMediaTypeId = _mediaTypes.First().Id; } - private async Task OnPictureUploaded(IBrowserFile file) + private async Task OnPictureUploaded() { - _picture = file; - - const long maxPreviewSize = 5 * 1024 * 1024; - await using var stream = file.OpenReadStream(maxPreviewSize); + if (_pictureFile == null) + { + Snackbar.Add("No file selected!", Severity.Warning); + return; + } + if (_pictureFile.Size > 5242880) + { + Snackbar.Add("File size exceeds 5MB!", Severity.Error); + return; + } + await using var stream = _pictureFile.OpenReadStream(5242880); using var ms = new MemoryStream(); await stream.CopyToAsync(ms); var base64 = Convert.ToBase64String(ms.ToArray()); - _imagePreviewUrl = $"data:{file.ContentType};base64,{base64}"; + _imagePreview = $"data:{_pictureFile.ContentType};base64,{base64}"; } private void Cancel() => MudDialog.Cancel(); @@ -213,7 +175,7 @@ { if (!ctx.Validate()) return; - if (_picture is null) + if (_pictureFile is null) { Snackbar.Add("Please upload a picture.", Severity.Warning); return; @@ -229,13 +191,12 @@ try { - string safeTitle = _form.Title.Replace(" ", "-"); - string fileName = $"{safeTitle}-{Guid.NewGuid()}{Path.GetExtension(_picture.Name)}"; + string fileName = $"{Guid.NewGuid()}{Path.GetExtension(_pictureFile.Name)}"; string picturePath = Path.Combine("wwwroot", "Pictures", fileName); await using (var fs = File.Create(picturePath)) { - await _picture.OpenReadStream(maxAllowedSize: 10_000_000).CopyToAsync(fs); + await _pictureFile.OpenReadStream(maxAllowedSize: 10_000_000).CopyToAsync(fs); } var entity = new GlobalEntity diff --git a/CouchLog/Components/Pages/GlobalList/GlobalList.razor b/CouchLog/Components/Pages/GlobalList/GlobalList.razor index 2704e22..58db7e7 100644 --- a/CouchLog/Components/Pages/GlobalList/GlobalList.razor +++ b/CouchLog/Components/Pages/GlobalList/GlobalList.razor @@ -178,6 +178,8 @@ CreationTime = DateTime.Now, GlobalEntityId = globalEntity.Id, UserWatchStatusId = 1, + //Season = 0, + //Episode = 0 }; CouchLogDb.PrivateEntities.Add(privateEntity); diff --git a/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor b/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor index 1b5b8d4..901fc58 100644 --- a/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor +++ b/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor @@ -1,9 +1,9 @@ @using CouchLog.Data - + - + - - + + - + - @PrivateEntity.GlobalEntity.Title + @PrivateEntity.GlobalEntity.Title - Bearbeiten - Entfernen + Edit + Remove + - - - - - --> + - + @status.Name + } + - + - + int? season = currentSeason + i; + Staffel @season + } + - + - + int? episode = currentEpisode + i; + Episode @episode + } + - + + Size="Size.Small"> Details + @@ -88,4 +99,10 @@ { [Parameter] public PrivateEntity PrivateEntity { get; set; } = null!; [Parameter] public List UserWatchStatuses { get; set; } = null!; + + [Parameter] public EventCallback OnWatchStatusChange { get; set; } + [Parameter] public EventCallback OnSeasonChange { get; set; } + [Parameter] public EventCallback OnEpisodeChange { get; set; } + + [Parameter] public EventCallback OnRemove { get; set; } } diff --git a/CouchLog/Components/Pages/PrivateList/PrivateList.razor b/CouchLog/Components/Pages/PrivateList/PrivateList.razor index ad08a1a..a69d974 100644 --- a/CouchLog/Components/Pages/PrivateList/PrivateList.razor +++ b/CouchLog/Components/Pages/PrivateList/PrivateList.razor @@ -53,10 +53,11 @@ - @*Entity allready added*@ + No Filters avalible yet! + @*To be exstendet*@ @@ -72,7 +73,16 @@ @foreach (var privateEntity in VisibleActiveWatchingEntities) { - + + + } @@ -85,14 +95,24 @@ +
+ Library -
@foreach (var privateEntity in VisibleLibraryEntities) { - + + + }
@@ -119,13 +139,13 @@ //Paging Active Watching private int _currentActiveWatchingPage = 1; - private int _pageSizeActiveWatching = 12; + private readonly int _pageSizeActiveWatching = 12; private int TotalPagesActiveWatching => (int)Math.Ceiling(ActiveWatchingEntities.Count() / (double)_pageSizeActiveWatching); private IEnumerable VisibleActiveWatchingEntities => ActiveWatchingEntities.Skip((_currentActiveWatchingPage - 1) * _pageSizeActiveWatching).Take(_pageSizeActiveWatching); //Paging Library private int _currentLibraryPage = 1; - private int _pageSizeLibrary = 12; + private readonly int _pageSizeLibrary = 12; private int TotalPagesLibrary => (int)Math.Ceiling(LibraryEntities.Count() / (double)_pageSizeLibrary); private IEnumerable VisibleLibraryEntities => LibraryEntities.Skip((_currentLibraryPage - 1) * _pageSizeLibrary).Take(_pageSizeLibrary); @@ -140,6 +160,13 @@ private IEnumerable LibraryEntities => FiltertGlobalEntities.Where(x => x.UserWatchStatus?.Name != "Watching").ToList(); protected override async Task OnInitializedAsync() + { + await GetPrivateEntities(); + + _userWatchStatuses = await CouchLogDb.UserWatchStatuses.OrderBy(entity => entity.Id).ToListAsync(); + } + + private async Task GetPrivateEntities() { var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); _appUser = await UserManager.GetUserAsync(authState.User); @@ -153,8 +180,38 @@ .Include(x => x.GlobalEntity.MediaType) .OrderByDescending(entity => entity.LastChange ?? entity.CreationTime) .ToListAsync(); - - _userWatchStatuses = await CouchLogDb.UserWatchStatuses.OrderByDescending(entity => entity.Id).ToListAsync(); } } + + private async Task RefreshPages() + { + _currentLibraryPage = 1; + _currentActiveWatchingPage = 1; + StateHasChanged(); + } + + private async Task ChangeWatchStatus(PrivateEntity privateEntity, int newUserWatchStatusId) + { + privateEntity.UserWatchStatus = _userWatchStatuses.FirstOrDefault(s => s.Id == newUserWatchStatusId); + await CouchLogDb.SaveChangesAsync(); + } + + private async Task ChangeSeason(PrivateEntity privateEntity, int? newSeason) + { + privateEntity.Season = newSeason; + await CouchLogDb.SaveChangesAsync(); + } + + private async Task ChangeEpisode(PrivateEntity privateEntity, int? newEpisode) + { + privateEntity.Episode = newEpisode; + await CouchLogDb.SaveChangesAsync(); + } + + private async Task RemovePrivateEntity(PrivateEntity privateEntity) + { + CouchLogDb.PrivateEntities.Remove(privateEntity); + await CouchLogDb.SaveChangesAsync(); + await GetPrivateEntities(); + } } \ No newline at end of file diff --git a/CouchLog/CouchLog.csproj b/CouchLog/CouchLog.csproj index 7530ca6..915973e 100644 --- a/CouchLog/CouchLog.csproj +++ b/CouchLog/CouchLog.csproj @@ -7,6 +7,8 @@ Exe True True + 0.1.0-nightly.10 + Penry @@ -77,7 +79,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/CouchLog/OnStartUp.cs b/CouchLog/OnStartUp.cs index f9af309..2a86d60 100644 --- a/CouchLog/OnStartUp.cs +++ b/CouchLog/OnStartUp.cs @@ -140,7 +140,7 @@ namespace CouchLog new() { Name = "Not watched", CreationTime = DateTime.Now }, new() { Name = "Watching", CreationTime = DateTime.Now }, new() { Name = "Finished", CreationTime = DateTime.Now }, - new() { Name = "Paused", CreationTime = DateTime.Now }, + new() { Name = "Waiting for next Season", CreationTime = DateTime.Now }, new() { Name = "Aborted", CreationTime= DateTime.Now }, ]; diff --git a/README.md b/README.md index 941990a..49ea4d1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ # Watchlog -## Work in Progress -### Information -- This Repo has a Mirror on Codeberg, but the Releases a only available on [Gitea](https://gitea.penry.de/Penry/CouchLog) for now \ No newline at end of file +## Information +- This Repo has a Mirror on Codeberg, but the Releases a only available on [Gitea](https://gitea.penry.de/Penry/CouchLog) for now + +## About docker image Releases +There are two options: +- latest: Is the latest release Version +- nightly: Is the latest commit on the main branch \ No newline at end of file