Added Authentication with user-auth and apikey-auth
All checks were successful
Gitea CI/CD / dotnet-build-and-test (push) Successful in 10m5s
Gitea CI/CD / Set Tag Name (push) Successful in 5s
Gitea CI/CD / docker-build-and-push (push) Successful in 11m28s
Gitea CI/CD / Create Tag (push) Successful in 5s

This commit is contained in:
2026-01-09 10:18:06 +01:00
parent 05e5a209da
commit d8b164e3eb
25 changed files with 1809 additions and 5 deletions

View File

@@ -0,0 +1,217 @@
@model watcher_monitoring.Models.User
@{
ViewData["Title"] = "Benutzerdetails";
}
<div class="container-fluid px-4">
<div class="row">
<div class="col-lg-10">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="section-title mb-0">Benutzerdetails</h1>
<div>
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn" style="background-color: var(--warning); color: #fff; margin-right: 0.5rem;">Bearbeiten</a>
<a asp-action="Index" class="btn" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary);">Zurück</a>
</div>
</div>
@if (TempData["Success"] != null)
{
<div class="alert" style="background-color: rgba(63, 185, 80, 0.15); border: 1px solid var(--success); color: var(--success); border-radius: 6px; padding: 1rem; margin-bottom: 1rem; position: relative;">
@TempData["Success"]
@if (TempData["NewApiKey"] != null)
{
<hr style="border-color: var(--border-color); margin: 1rem 0;">
<strong>API-Key (wird nur einmal angezeigt!):</strong>
<div class="input-group mt-2" style="display: flex; gap: 0.5rem;">
<input type="text" class="form-control font-monospace" value="@TempData["NewApiKey"]" id="newApiKey" readonly style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px; flex: 1;">
<button class="btn" type="button" onclick="copyApiKey()" style="background-color: var(--accent-primary); color: #fff; border: none; padding: 0.75rem 1rem; border-radius: 6px;">
Kopieren
</button>
</div>
}
<button type="button" class="btn-close" data-bs-dismiss="alert" style="position: absolute; top: 1rem; right: 1rem; color: var(--success);"></button>
</div>
}
@if (TempData["Error"] != null)
{
<div class="alert" style="background-color: rgba(248, 81, 73, 0.15); border: 1px solid var(--danger); color: var(--danger); border-radius: 6px; padding: 1rem; margin-bottom: 1rem; position: relative;">
@TempData["Error"]
<button type="button" class="btn-close" data-bs-dismiss="alert" style="position: absolute; top: 1rem; right: 1rem; color: var(--danger);"></button>
</div>
}
<div class="card mb-4">
<h2 class="card-title">Benutzerinformationen</h2>
<div style="color: var(--text-secondary);">
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
<div class="col-sm-4"><strong style="color: var(--text-primary);">Benutzername:</strong></div>
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.Username</div>
</div>
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
<div class="col-sm-4"><strong style="color: var(--text-primary);">E-Mail:</strong></div>
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.Email</div>
</div>
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
<div class="col-sm-4"><strong style="color: var(--text-primary);">Status:</strong></div>
<div class="col-sm-8">
@if (Model.IsActive)
{
<span class="status-badge status-online">Aktiv</span>
}
else
{
<span class="status-badge status-offline">Inaktiv</span>
}
</div>
</div>
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
<div class="col-sm-4"><strong style="color: var(--text-primary);">Erstellt am:</strong></div>
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.CreatedAt.ToString("dd.MM.yyyy HH:mm:ss")</div>
</div>
<div class="row mb-3" style="padding-bottom: 1rem; border-bottom: 1px solid var(--border-color);">
<div class="col-sm-4"><strong style="color: var(--text-primary);">Letzter Login:</strong></div>
<div class="col-sm-8" style="color: var(--text-secondary);">@Model.LastLogin.ToString("dd.MM.yyyy HH:mm:ss")</div>
</div>
<div class="row">
<div class="col-sm-12">
<form asp-action="ToggleActive" asp-route-id="@Model.Id" method="post" class="d-inline">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm" style="background-color: @(Model.IsActive ? "var(--warning)" : "var(--success)"); color: #fff; margin-right: 0.5rem;">
@(Model.IsActive ? "Deaktivieren" : "Aktivieren")
</button>
</form>
<form asp-action="Delete" asp-route-id="@Model.Id" method="post" class="d-inline" onsubmit="return confirm('Möchten Sie diesen Benutzer wirklich löschen? Alle API-Keys werden ebenfalls gelöscht.');">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm" style="background-color: var(--danger); color: #fff;">Löschen</button>
</form>
</div>
</div>
</div>
</div>
<div class="card">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="card-title mb-0">API-Keys (@Model.ApiKeys.Count)</h2>
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#createApiKeyModal">
Neuer API-Key
</button>
</div>
@if (Model.ApiKeys.Any())
{
<div class="table-responsive">
<table class="table table-hover" style="color: var(--text-primary); margin-bottom: 0;">
<thead style="border-bottom: 1px solid var(--border-color);">
<tr>
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Name</th>
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Beschreibung</th>
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Status</th>
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Erstellt</th>
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Läuft ab</th>
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Zuletzt verwendet</th>
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Aktionen</th>
</tr>
</thead>
<tbody>
@foreach (var key in Model.ApiKeys.OrderByDescending(k => k.CreatedAt))
{
<tr style="border-bottom: 1px solid var(--border-color);">
<td style="padding: 1rem;"><strong style="color: var(--text-primary);">@key.Name</strong></td>
<td style="padding: 1rem; color: var(--text-secondary);">@(key.Description ?? "-")</td>
<td style="padding: 1rem;">
@if (key.IsExpired)
{
<span class="status-badge status-offline">Abgelaufen</span>
}
else if (!key.IsActive)
{
<span class="status-badge" style="background-color: rgba(139, 148, 158, 0.15); color: var(--text-secondary); border: 1px solid var(--text-secondary);">Inaktiv</span>
}
else
{
<span class="status-badge status-online">Aktiv</span>
}
</td>
<td style="padding: 1rem; color: var(--text-secondary);">@key.CreatedAt.ToString("dd.MM.yyyy")</td>
<td style="padding: 1rem; color: var(--text-secondary);">@(key.ExpiresAt?.ToString("dd.MM.yyyy") ?? "Nie")</td>
<td style="padding: 1rem; color: var(--text-secondary);">@(key.LastUsedAt?.ToString("dd.MM.yyyy HH:mm") ?? "Nie")</td>
<td style="padding: 1rem;">
<form asp-action="ToggleApiKey" asp-route-userId="@Model.Id" asp-route-keyId="@key.Id" method="post" class="d-inline">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm" style="background-color: @(key.IsActive ? "var(--warning)" : "var(--success)"); color: #fff; margin-right: 0.5rem;">
@(key.IsActive ? "Deaktivieren" : "Aktivieren")
</button>
</form>
<form asp-action="DeleteApiKey" asp-route-userId="@Model.Id" asp-route-keyId="@key.Id" method="post" class="d-inline" onsubmit="return confirm('Möchten Sie diesen API-Key wirklich löschen?');">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm" style="background-color: var(--danger); color: #fff;">Löschen</button>
</form>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<p style="color: var(--text-muted); margin-bottom: 0;">Noch keine API-Keys vorhanden.</p>
}
</div>
</div>
</div>
</div>
<!-- Modal für neuen API-Key -->
<div class="modal fade" id="createApiKeyModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content" style="background-color: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary);">
<form asp-action="GenerateApiKey" asp-route-id="@Model.Id" method="post">
@Html.AntiForgeryToken()
<div class="modal-header" style="border-bottom: 1px solid var(--border-color);">
<h5 class="modal-title" style="color: var(--text-primary);">Neuen API-Key erstellen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" style="filter: invert(1);"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="keyName" class="form-label" style="color: var(--text-primary); font-weight: 500;">Name *</label>
<input type="text" class="form-control" id="keyName" name="keyName" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;" required>
<div style="color: var(--text-muted); font-size: 0.875rem; margin-top: 0.25rem;">Ein eindeutiger Name für diesen API-Key</div>
</div>
<div class="mb-3">
<label for="description" class="form-label" style="color: var(--text-primary); font-weight: 500;">Beschreibung</label>
<textarea class="form-control" id="description" name="description" rows="2" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;"></textarea>
</div>
<div class="mb-3">
<label for="expiresAt" class="form-label" style="color: var(--text-primary); font-weight: 500;">Ablaufdatum (optional)</label>
<input type="datetime-local" class="form-control" id="expiresAt" name="expiresAt" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;">
<div style="color: var(--text-muted); font-size: 0.875rem; margin-top: 0.25rem;">Leer lassen für unbegrenzte Gültigkeit</div>
</div>
</div>
<div class="modal-footer" style="border-top: 1px solid var(--border-color);">
<button type="button" class="btn" data-bs-dismiss="modal" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary);">Abbrechen</button>
<button type="submit" class="btn btn-primary">API-Key erstellen</button>
</div>
</form>
</div>
</div>
</div>
@section Scripts {
<script>
function copyApiKey() {
var copyText = document.getElementById("newApiKey");
copyText.select();
copyText.setSelectionRange(0, 99999);
navigator.clipboard.writeText(copyText.value);
var btn = event.target;
var originalText = btn.textContent;
btn.textContent = "Kopiert!";
setTimeout(function() {
btn.textContent = originalText;
}, 2000);
}
</script>
}