Adds a solid start Foundation with a GlobalList.razor and OnStartUp.cs

This commit is contained in:
2025-10-27 02:01:55 +01:00
parent d78958f7c6
commit 70587b2af6
32 changed files with 627 additions and 286 deletions

View File

@@ -23,12 +23,12 @@
<input type="text" value="@username" id="username" class="form-control" placeholder="Choose your username." disabled />
<label for="username" class="form-label">Username</label>
</div>
<div class="form-floating mb-3">
<!--<div class="form-floating mb-3">
<InputText @bind-Value="Input.PhoneNumber" id="Input.PhoneNumber" class="form-control" placeholder="Enter your phone number" />
<label for="Input.PhoneNumber" class="form-label">Phone number</label>
<ValidationMessage For="() => Input.PhoneNumber" class="text-danger" />
</div>
<button type="submit" class="w-100 btn btn-lg btn-primary">Save</button>
<button type="submit" class="w-100 btn btn-lg btn-primary">Save</button>-->
</EditForm>
</div>
</div>
@@ -36,7 +36,7 @@
@code {
private ApplicationUser user = default!;
private string? username;
private string? phoneNumber;
//private string? phoneNumber;
[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;
@@ -48,21 +48,21 @@
{
user = await UserAccessor.GetRequiredUserAsync(HttpContext);
username = await UserManager.GetUserNameAsync(user);
phoneNumber = await UserManager.GetPhoneNumberAsync(user);
//phoneNumber = await UserManager.GetPhoneNumberAsync(user);
Input.PhoneNumber ??= phoneNumber;
//Input.PhoneNumber ??= phoneNumber;
}
private async Task OnValidSubmitAsync()
{
if (Input.PhoneNumber != phoneNumber)
/*if (Input.PhoneNumber != phoneNumber)
{
var setPhoneResult = await UserManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
if (!setPhoneResult.Succeeded)
{
RedirectManager.RedirectToCurrentPageWithStatus("Error: Failed to set phone number.", HttpContext);
}
}
}*/
await SignInManager.RefreshSignInAsync(user);
RedirectManager.RedirectToCurrentPageWithStatus("Your profile has been updated", HttpContext);

View File

@@ -7,9 +7,11 @@
<li class="nav-item">
<NavLink class="nav-link" href="Account/Manage" Match="NavLinkMatch.All">Profile</NavLink>
</li>
<!--
<li class="nav-item">
<NavLink class="nav-link" href="Account/Manage/Email">Email</NavLink>
</li>
</li>
-->
<li class="nav-item">
<NavLink class="nav-link" href="Account/Manage/ChangePassword">Password</NavLink>
</li>
@@ -19,9 +21,11 @@
<NavLink class="nav-link" href="Account/Manage/ExternalLogins">External logins</NavLink>
</li>
}
<!--
<li class="nav-item">
<NavLink class="nav-link" href="Account/Manage/TwoFactorAuthentication">Two-factor authentication</NavLink>
</li>
-->
<li class="nav-item">
<NavLink class="nav-link" href="Account/Manage/PersonalData">Personal data</NavLink>
</li>

View File

@@ -9,13 +9,15 @@
<link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["CouchLog.styles.css"]" />
<ImportMap />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
<script src="lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="lib/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="lib/bootstrap/dist/js/site.js"></script>
</body>
</html>

View File

@@ -1,6 +1,10 @@
@implements IDisposable
@using CouchLog.Data
@using Microsoft.AspNetCore.Identity
@implements IDisposable
@inject NavigationManager NavigationManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject UserManager<ApplicationUser> UserManager
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
@@ -17,24 +21,24 @@
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
<NavLink class="nav-link" href="GlobalList">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Global List
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="PrivateList">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Private List
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="auth">
<span class="bi bi-lock-nav-menu" aria-hidden="true"></span> Auth Required
</NavLink>
</div>
<AuthorizeView Roles="Admin">
<div class="nav-item px-3">
<NavLink class="nav-link" href="AdminSettings">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Admin Settings
</NavLink>
</div>
</AuthorizeView>
<AuthorizeView>
<Authorized>

View File

@@ -0,0 +1,7 @@
@page "/AdminSettings"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>Admin Settings</PageTitle>

View File

@@ -1,13 +0,0 @@
@page "/auth"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>Auth</PageTitle>
<h1>You are authenticated</h1>
<AuthorizeView>
Hello @context.User.Identity?.Name!
</AuthorizeView>

View File

@@ -1,23 +0,0 @@
@page "/counter"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@@ -0,0 +1,147 @@
@page "/GlobalList"
@rendermode InteractiveServer
@using Microsoft.AspNetCore.Authorization
@using CouchLog.Data
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@inject ApplicationDbContext CouchLogDB
@inject UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@attribute [Authorize]
<PageTitle>GlobalList</PageTitle>
<div class="container-fluid mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Global List</h2>
<button class="btn btn-primary" type="button" @onclick="ToogleCollapseNewGlobalEntity">@(isCollapseNewGlobalEntityOpen ? "X" : "Add Entity")</button>
</div>
<div class="collapse @(isCollapseNewGlobalEntityOpen ? "show" : "")" id="CollapseCreateNewGlobalEntity">
<div class="mb-4 align-items-center CreateNewGlobalEntity-Container">
<EditForm Model="@GlobalEntity" OnSubmit="CreateNewGlobalEntity" FormName="CreateNewGlobalEntityForm">
<div class="mb-3">
<label for="Title" class="form-label">Title</label>
<InputText id="Title" class="form-control" @bind-Value="GlobalEntity.Title"></InputText>
<ValidationMessage For="@(() => GlobalEntity.Title)"></ValidationMessage>
</div>
<div class="mb-3">
<label for="Type" class="form-label">Type</label>
<select>
@foreach(MediaType Type in MediaTypes)
{
<option value="@Type.Id">@Type.Name</option>
}
</select>
</div>
<button type="submit">Create new Global Entity</button>
</EditForm>
</div>
</div>
<div class="row">
@foreach (var Entity in GlobalEntities)
{
<div name="Enity-Container" class="col-12 col-md-6 col-lg-3 mb-4 Entity-Container">
<div name="Entity-Container-Card" class="Entity-Container-Card">
<div name="Enity-Container-Image" class="">
<a href="javascript:void(0)" class="Entity-Container-Image">
<img src="@Entity.PicturePath" alt="" class="Entity-Container-Image" />
</a>
</div>
<div name="Entity-Container-Data" class="">
<h3 class="">@Entity.Title</h3>
</div>
<div name="Entity-Container-Button" class="d-flex Entity-Container-Button" style="gap: 10px;">
<button class="btn btn-primary" type="button" @onclick="() => AddToPrivateList(Entity)">
@(IsInPrivateList(Entity.Id) ? "Added" : "Add to Private List")
</button>
<button class="btn btn-primary" type="button">Add to Shared List</button>
</div>
</div>
</div>
}
</div>
</div>
@code
{
private List<MediaType> MediaTypes = new List<MediaType>();
public List<GlobalEntity> GlobalEntities = new List<GlobalEntity>();
private HashSet<int> UserPrivateEntityIds = new();
protected override async Task OnInitializedAsync()
{
GlobalEntities = await CouchLogDB.GlobalEntities.OrderByDescending(Entity => Entity.Id).ToListAsync();
MediaTypes = await CouchLogDB.MediaType.OrderByDescending(Type => Type.Id).ToListAsync();
var AuthState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var User = AuthState.User;
var AppUser = await UserManager.GetUserAsync(User);
if (AppUser == null)
{
throw new NotImplementedException();
}
var TempUserPrivateEntityIds = await CouchLogDB.PrivateEntities
.Where(p => p.UserId == AppUser.Id)
.Select(p => p.GlobalEntityId)
.ToListAsync();
UserPrivateEntityIds = TempUserPrivateEntityIds.ToHashSet();
}
private bool isCollapseNewGlobalEntityOpen = false;
private void ToogleCollapseNewGlobalEntity()
{
isCollapseNewGlobalEntityOpen = !isCollapseNewGlobalEntityOpen;
}
private GlobalEntity GlobalEntity = new();
private void CreateNewGlobalEntity()
{
}
private bool IsInPrivateList(int GolbalEntityId)
{
return UserPrivateEntityIds.Contains(GolbalEntityId);
}
private async Task AddToPrivateList(GlobalEntity GlobalEntity)
{
var AuthState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var User = AuthState.User;
if(User.Identity?.IsAuthenticated == true)
{
//var AppUser = await UserManager.GetUserAsync(User);
if(!IsInPrivateList(GlobalEntity.Id))
{
PrivateEntity PrivateEntity = new()
{
UserId = (await UserManager.GetUserAsync(User))!.Id,
CreationTime = DateTime.Now,
GlobalEntityId = GlobalEntity.Id,
};
CouchLogDB.PrivateEntities.Add(PrivateEntity);
await CouchLogDB.SaveChangesAsync();
UserPrivateEntityIds.Add(GlobalEntity.Id);
StateHasChanged();
}
}
}
}

View File

@@ -0,0 +1,77 @@
/*For Development*/
/** {
outline: 1px solid red !important;
}*/
.Entity-Container {
color:aliceblue
}
.Entity-Container:hover {
color: rgba(0,255,255,0.5);
}
.Entity-Container-Card {
position: relative;
max-width: 275px;
background: #111;
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
border-radius: 15px;
transition: all 0.5s ease;
text-align: center;
gap: 0.75rem;
padding-top: 10px;
padding-bottom: 24px;
}
.Entity-Container-Card h2 {
color: #0ff;
font-size: 2rem;
position: relative;
z-index: 2;
margin: 0;
}
.Entity-Container-Card::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient( 0deg, transparent, transparent 30%, rgba(0,255,255,0.3) );
transform: rotate(-45deg);
transition: all 0.5s ease;
opacity: 0;
}
.Entity-Container-Card:hover {
transform: scale(1.05);
box-shadow: 0 0 20px rgba(0,255,255,0.5);
}
.Entity-Container-Card:hover::before {
opacity: 1;
transform: rotate(-45deg) translateY(100%);
}
.Entity-Container-Image {
margin: 4px;
margin-top: 6px;
max-width: 256px;
max-height: 256px;
}
.Entity-Container-Button {
display: flex;
flex-direction: column
}
.CreateNewGlobalEntity-Container {
background: #111;
border-radius: 15px;
}

View File

@@ -0,0 +1,99 @@
@page "/PrivateList"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>PrivateList</PageTitle>
<!--
<div class='col-12 col-md-6 col-lg-3 mb-4' name='ContainerFromList'>
<!-- <div class='item item-horizontal'>
<!-- <div class='item-image'>
<!-- <a href='#' class='item-details-trigger'>
<!-- $bild = !empty($row['PicturePath']) ? htmlspecialchars($row['PicturePath']) : './pictures/placeholder.jpg';
<img src='". $bild. "' alt='".htmlspecialchars($row['Titel']). "'>
<img src="@Enitiy.PicturePath" alt="" />
</a>
</div>
<div class='item-details'>
<h3 class='item-title'>@Enitiy.Title</h3>
<input type='hidden' name='Id' value='@Enitiy.Id'>
<input type='hidden' name='Titel' value='@Enitiy.Title'>
<input type='hidden' name='PicturePath' value='@Enitiy.PicturePath'>
<input type='hidden' name='TypeId' value='@Enitiy.TypeId'>
<div class='item-meta'>
if ($row['Type'] == $_SESSION['validTypes'][1] || $row['Type'] == $_SESSION['validTypes'][2]) {
<div class='meta-group'>
<h6>Season:</h6>
<form method='post' action='./handler/update_season.php' class='d-inline'>
<input type='hidden' name='id' value='". $row['ID']. "'>
<select name='Season' onchange='this.form.submit()'>
if ($row['Season'] > 0) {
<option value='". ($row['Season'] - 1). "'>Season ". ($row['Season'] - 1). "</option>
}
<option value='". $row['Season']. "' selected>Season ". $row['Season']."</option>
<option value='". ($row['Season'] + 1). "'>Season ". ($row['Season'] + 1)."</option>
</select>
</form>
</div>
<div class='meta-group'>
<h6>Episode:</h6>
<form method='post' action='./handler/update_episode.php' class='d-inline'>
<input type='hidden' name='id' value='". $row['ID']. "'>
<select name='Episode' onchange='this.form.submit()'>
if ($row['Episode'] > 0) {
<option value='". ($row['Episode'] - 1). "'>". ($row['Episode'] - 1)."</option>
}
<option value='". $row['Episode']. "' selected>". $row['Episode']."</option>
for ($i = 1; $i <= 5; $i++)
{
<option value='". ($row['Episode'] + $i). "'>". ($row['Episode'] + $i)."</option>
}
</select>
</form>
</div>
}
<div class='meta-group'>
<h6>Tag:</h6>
if (isset($_SESSION['validTags']) && !empty($_SESSION['validTags']))
{
<form method='post' action='./handler/update_tag.php' class='d-inline'>
<input type='hidden' name='id' value='". $row['ID']. "'>
<select name='Tag' onchange='this.form.submit()'>
$noTagSelected = (empty($row['Tag']) || !isset($_SESSION['validTags'][$row['Tag']])) ? 'selected' : '';
<option value='' ". $noTagSelected. ">-- Kein Tag --</option>
foreach ($_SESSION['validTags'] as $tagId => $tagName) {
$selected = (!empty($row['Tag']) && $row['Tag'] == $tagId) ? 'selected' : '';
<option value='".htmlspecialchars($tagId). "' ". $selected. ">".htmlspecialchars($tagName). "</option>
}
</select>
</form>
}
else
{
<span>Keine Tags verfügbar</span>
}
</div>"
<div class='meta-group'>
<h6>Status:</h6>
<form method='post' action='./handler/update_status.php' class='d-inline'>
<input type='hidden' name='id' value='". $row['ID']. "'>
<select name='watchedStatus' onchange='this.form.submit()'>
foreach ($_SESSION['validWatchedStatuses'] as $status) {
$selected = ($row['WatchedStatus'] == $status) ? 'selected' : '';
<option value='".htmlspecialchars($status). "' ". $selected. ">".htmlspecialchars($status). "</option>
}
</select>
</form>
</div>
</div>
</div>
<a href='./handler/change_favorite.php?id=". $row['ID']. "' class='button-favorite'>
<img src='./pictures/icons/". ($row['Favorite'] ? "favorit-fill.png" : "favorit.png"). "' alt='Favorisieren'>
</a>
<a id=". $row['ID']. "' class='button-edit' data-bs-toggle='modal' data-bs-target='#editEntryModal'>
<img src='./pictures/icons/edit.png' alt='Bearbeiten'>
</a>
</div>
</div> -->

View File

@@ -1,68 +0,0 @@
@page "/weather"
@attribute [StreamRendering]
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin")]
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th aria-label="Temperature in Celsius">Temp. (C)</th>
<th aria-label="Temperature in Farenheit">Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}

View File

@@ -7,17 +7,28 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.10" />
<PackageReference Include="Microsoft.Build" Version="17.14.28" />
<PackageReference Include="Microsoft.DotNet.Scaffolding.Shared" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.10">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.10">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Data\DatabaseModels\Shared\" />
<Folder Include="Data\DatabaseModels\Private\" />
<Folder Include="wwwroot\Pictures\" />
</ItemGroup>
</Project>

Binary file not shown.

View File

@@ -1,9 +1,11 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics.CodeAnalysis;
namespace CouchLog.Data
{
public class GlobalEntity
[method: SetsRequiredMembers]
public class GlobalEntity()
{
[Key]
public int Id { get; set; }

102
CouchLog/OnStartUp.cs Normal file
View File

@@ -0,0 +1,102 @@
using CouchLog.Data;
namespace CouchLog
{
public class OnStartUp
{
private ApplicationDbContext CouchLogDB;
public OnStartUp(ApplicationDbContext CouchLogDB)
{
this.CouchLogDB = CouchLogDB;
}
public void AddBasicDatabaseEntries()
{
//##################
//### MediaTypes ###
//##################
List<MediaType> MediaTypes = new List<MediaType>
{
//Video based
new() { Name="Movie" },
new() { Name="Series" },
new() { Name="Anime" },
new() { Name="Series" },
new() { Name="Documentary" },
new() { Name="Short Film" },
//new() { Name="Music Video" },
//new() { Name="Live Performance" },
//Audio based
new() { Name="Podcast" },
new() { Name="Audiobook" },
new() { Name="Music Album" },
//Interactive
new() { Name="Video Game" },
//Text based
new() { Name = "Book" },
new() { Name = "Comic" },
};
foreach (MediaType MediaType in MediaTypes)
{
CouchLogDB.Add(MediaType);
}
//##############
//### Genres ###
//##############
List<Genre> Genres = new List<Genre>
{
new() { Name = "Action", CreationTime = DateTime.Now },
new() { Name = "Animation", CreationTime = DateTime.Now },
new() { Name = "Comedy", CreationTime = DateTime.Now },
new() { Name = "Crime", CreationTime = DateTime.Now },
new() { Name = "Drama", CreationTime = DateTime.Now },
new() { Name = "Fantasy", CreationTime = DateTime.Now },
new() { Name = "History", CreationTime = DateTime.Now },
new() { Name = "Horror", CreationTime = DateTime.Now },
new() { Name = "Musical", CreationTime = DateTime.Now },
new() { Name = "Miniseries", CreationTime = DateTime.Now },
new() { Name = "Mystery", CreationTime = DateTime.Now },
new() { Name = "Romance", CreationTime = DateTime.Now },
new() { Name = "Science Fiction", CreationTime = DateTime.Now },
new() { Name = "Thriller", CreationTime = DateTime.Now },
new() { Name = "War", CreationTime = DateTime.Now },
new() { Name = "Western", CreationTime = DateTime.Now },
};
foreach(Genre Genre in Genres)
{
CouchLogDB.Add(Genre);
}
//##########################
//### StreamingPlatforms ###
//##########################
List<StreamingPlatform> StreamingPlatforms = new List<StreamingPlatform>
{
new() { Name = "Netflix", PicturePath="StreamingPlatforms/Netflix.png", CreationTime = DateTime.Now },
new() { Name = "Prime Video", PicturePath="StreamingPlatforms/Prime-Video.png", CreationTime = DateTime.Now},
new() { Name = "Disney+", PicturePath="StreamingPlatforms/Disney+.png", CreationTime = DateTime.Now},
new() { Name = "Apple TV", PicturePath="StreamingPlatforms/AppleTV.png", CreationTime = DateTime.Now},
new() { Name = "WOW TV", PicturePath="StreamingPlatforms/WOWTV.png", CreationTime = DateTime.Now},
new() { Name = "Paramount+", PicturePath="StreamingPlatforms/Paramount+.png", CreationTime = DateTime.Now},
new() { Name = "Joyn", PicturePath="StreamingPlatforms/Joyn.png", CreationTime = DateTime.Now},
};
foreach(StreamingPlatform StreamingPlatform in StreamingPlatforms)
{
CouchLogDB.Add(StreamingPlatform);
}
CouchLogDB.SaveChangesAsync();
}
}
}

View File

@@ -7,7 +7,6 @@ using CouchLog.Data;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
@@ -29,7 +28,7 @@ builder.Services.AddDbContext<ApplicationDbContext>(options =>
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>() // <-- Das ist der wichtige Zusatz
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
@@ -38,7 +37,6 @@ builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSe
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
@@ -46,7 +44,6 @@ if (app.Environment.IsDevelopment())
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
@@ -59,13 +56,12 @@ app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
using (var scope = app.Services.CreateScope())
{
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>(); // UserManager hinzufügen
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "Admin", "User" };
IdentityResult roleResult;
@@ -79,32 +75,22 @@ using (var scope = app.Services.CreateScope())
}
}
// --- HIER BEGINNT DER NEUE TEIL ---
// Erstellt den Admin-Benutzer und weist ihm die Admin-Rolle zu.
// WICHTIG: Ändere hier die E-Mail-Adresse und das Passwort!
var adminUsername = "Admin";
var normalUsername = "User";
var adminEmail = "admin@deine-app.de";
var adminPassword = "EinSehrSicheresPasswort123!"; // Nur für lokale Entwicklung, besser aus Konfiguration laden
var normalUserEmail = "user@deine-app.de";
var adminPassword = "EinSehrSicheresPasswort123!";
// Sucht nach dem Benutzer anhand der E-Mail.
var adminUser = await userManager.FindByNameAsync(adminUsername);
var normalUser = await userManager.FindByNameAsync(normalUsername);
// Wenn der Admin-Benutzer NICHT existiert, erstellen wir ihn.
if (adminUser == null)
{
adminUser = new ApplicationUser
{
UserName = adminUsername,
//Email = adminEmail,
EmailConfirmed = true // Wichtig, damit er sich direkt einloggen kann
EmailConfirmed = true
};
// Erstellt den Benutzer mit dem definierten Passwort.
var createResult = await userManager.CreateAsync(adminUser, adminPassword);
// Wenn die Erstellung erfolgreich war, weisen wir die Admin-Rolle zu.
if (createResult.Succeeded)
{
await userManager.AddToRoleAsync(adminUser, "Admin");
@@ -115,7 +101,6 @@ using (var scope = app.Services.CreateScope())
normalUser = new ApplicationUser
{
UserName = normalUsername,
//Email = normalUserEmail,
EmailConfirmed = true
};
var createResult = await userManager.CreateAsync(normalUser, adminPassword);
@@ -129,11 +114,39 @@ using (var scope = app.Services.CreateScope())
{
await userManager.AddToRoleAsync(normalUser, "User");
}
// Optional: Wenn der Benutzer bereits existiert, aber kein Admin ist.
else if (!await userManager.IsInRoleAsync(adminUser, "Admin"))
{
await userManager.AddToRoleAsync(adminUser, "Admin");
}
var CouchLogDB = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
MediaType mediaType = new()
{
Name = "Movie"
};
if (!CouchLogDB.MediaType.Any())
CouchLogDB.MediaType.Add(mediaType);
await CouchLogDB.SaveChangesAsync();
GlobalEntity globalEntity = new()
{
Title = "Inception",
CreationTime = DateTime.Now,
CreatorId = (await userManager.FindByNameAsync("Admin"))!.Id,
TypeId = 1,
PicturePath = "Pictures/Inception.jpg"
};
if (!CouchLogDB.GlobalEntities.Any())
CouchLogDB.GlobalEntities.Add(globalEntity);
await CouchLogDB.SaveChangesAsync();
}
Console.WriteLine("ALL UP");
app.Run();

View File

@@ -1,8 +1,3 @@
{
"dependencies": {
"mssql1": {
"type": "mssql",
"connectionId": "ConnectionStrings:DefaultConnection"
}
}
"dependencies": {}
}

View File

@@ -1,8 +1,3 @@
{
"dependencies": {
"mssql1": {
"type": "mssql.local",
"connectionId": "ConnectionStrings:DefaultConnection"
}
}
"dependencies": {}
}

View File

@@ -1,9 +1,4 @@
{
"dependencies": {
"mssql1": {
"restored": true,
"restoreTime": "2025-09-23T16:40:52.2029789Z"
}
},
"dependencies": {},
"parameters": {}
}

5
CouchLog/libman.json Normal file
View File

@@ -0,0 +1,5 @@
{
"version": "3.0",
"defaultProvider": "cdnjs",
"libraries": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -1,5 +1,7 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: #2d2d2d;
color: white
}
a, .btn-link {

View File

@@ -0,0 +1,8 @@
window.initializeBootstrapCollapse = () => {
var collapseElementList = [].slice.call(document.querySelectorAll('[data-bs-toggle="collapse"]'))
var collapseList = collapseElementList.map(function (collapseEl) {
return new bootstrap.Collapse(document.querySelector(collapseEl.dataset.bsTarget), {
toggle: false
})
})
};