Added Authentication with user-auth and apikey-auth
This commit is contained in:
18
watcher-monitoring/Views/Auth/AccessDenied.cshtml
Normal file
18
watcher-monitoring/Views/Auth/AccessDenied.cshtml
Normal file
@@ -0,0 +1,18 @@
|
||||
@{
|
||||
ViewData["Title"] = "Zugriff verweigert";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-danger">
|
||||
<div class="card-body text-center">
|
||||
<h1 class="display-1 text-danger">🚫</h1>
|
||||
<h2 class="card-title">Zugriff verweigert</h2>
|
||||
<p class="card-text">Sie haben keine Berechtigung, auf diese Seite zuzugreifen.</p>
|
||||
<a asp-controller="Auth" asp-action="Login" class="btn btn-primary">Zurück zum Login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
108
watcher-monitoring/Views/Auth/Login.cshtml
Normal file
108
watcher-monitoring/Views/Auth/Login.cshtml
Normal file
@@ -0,0 +1,108 @@
|
||||
@model watcher_monitoring.Models.LoginViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Login";
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - Watcher Monitoring</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.login-container {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
.login-card {
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
|
||||
padding: 40px;
|
||||
}
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.login-header h2 {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
.login-header p {
|
||||
color: #666;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.btn-login {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
}
|
||||
.btn-login:hover {
|
||||
background: linear-gradient(135deg, #5568d3 0%, #6a3f8f 100%);
|
||||
}
|
||||
.form-control:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<h2>🔒 Watcher Monitoring</h2>
|
||||
<p>Bitte melden Sie sich an</p>
|
||||
</div>
|
||||
|
||||
@if (TempData["Error"] != null)
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
@TempData["Error"]
|
||||
</div>
|
||||
}
|
||||
|
||||
<form asp-action="Login" asp-controller="Auth" method="post">
|
||||
<div class="form-group mb-3">
|
||||
<label asp-for="Username" class="form-label">Benutzername</label>
|
||||
<input asp-for="Username" class="form-control" placeholder="Benutzername eingeben" autofocus />
|
||||
<span asp-validation-for="Username" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-4">
|
||||
<label asp-for="Password" class="form-label">Passwort</label>
|
||||
<input asp-for="Password" type="password" class="form-control" placeholder="Passwort eingeben" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
<input asp-for="RememberMe" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="RememberMe" class="form-check-label">
|
||||
Angemeldet bleiben
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-login">Anmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
||||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@@ -8,11 +9,13 @@
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/watcher_monitoring.styles.css" asp-append-version="true" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark">
|
||||
<div class="container-fluid px-4">
|
||||
<a class="navbar-brand fw-bold" asp-area="" asp-controller="Home" asp-action="Index">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="me-2" style="display: inline-block; vertical-align: middle;">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
class="me-2" style="display: inline-block; vertical-align: middle;">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<path d="M12 6v6l4 2"></path>
|
||||
</svg>
|
||||
@@ -26,9 +29,47 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-area="" asp-controller="Home" asp-action="Index">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-area="" asp-controller="Home" asp-action="Index">Settings</a>
|
||||
</li>
|
||||
@if (User.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
|
||||
data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" style="display: inline-block; vertical-align: middle;">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
@User.Identity.Name
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<li>
|
||||
<form asp-controller="Auth" asp-action="Logout" method="post" style="display:inline;">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="dropdown-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2"
|
||||
style="display: inline-block; vertical-align: middle; margin-right: 8px;">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16 17 21 12 16 7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
Abmelden
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<a style="display: inline-block; vertical-align: middle; margin-right: 8px;">
|
||||
asp-area="" asp-controller="User" asp-action="Index"><svg width="16" height="16"
|
||||
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
style="display: inline-block; vertical-align: middle; margin-right: 8px;">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16 17 21 12 16 7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>Settings</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,4 +90,5 @@
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
47
watcher-monitoring/Views/User/Create.cshtml
Normal file
47
watcher-monitoring/Views/User/Create.cshtml
Normal file
@@ -0,0 +1,47 @@
|
||||
@model watcher_monitoring.Models.User
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Neuer Benutzer";
|
||||
}
|
||||
|
||||
<div class="container-fluid px-4">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<h1 class="section-title mb-4">Neuen Benutzer erstellen</h1>
|
||||
|
||||
<div class="card">
|
||||
<form asp-action="Create" method="post">
|
||||
<div asp-validation-summary="ModelOnly" 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;"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label asp-for="Username" class="form-label" style="color: var(--text-primary); font-weight: 500; margin-bottom: 0.5rem;">Benutzername</label>
|
||||
<input asp-for="Username" class="form-control" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;" required />
|
||||
<span asp-validation-for="Username" style="color: var(--danger); font-size: 0.875rem;"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label asp-for="Email" class="form-label" style="color: var(--text-primary); font-weight: 500; margin-bottom: 0.5rem;">E-Mail-Adresse</label>
|
||||
<input asp-for="Email" type="email" class="form-control" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;" required />
|
||||
<span asp-validation-for="Email" style="color: var(--danger); font-size: 0.875rem;"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label asp-for="Password" class="form-label" style="color: var(--text-primary); font-weight: 500; margin-bottom: 0.5rem;">Passwort</label>
|
||||
<input asp-for="Password" type="password" class="form-control" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;" required />
|
||||
<span asp-validation-for="Password" style="color: var(--danger); font-size: 0.875rem;"></span>
|
||||
<div style="color: var(--text-muted); font-size: 0.875rem; margin-top: 0.25rem;">Mindestens 8 Zeichen empfohlen</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Erstellen</button>
|
||||
<a asp-action="Index" class="btn" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.5rem 1rem; border-radius: 6px; text-decoration: none;">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
|
||||
}
|
||||
217
watcher-monitoring/Views/User/Details.cshtml
Normal file
217
watcher-monitoring/Views/User/Details.cshtml
Normal 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>
|
||||
}
|
||||
46
watcher-monitoring/Views/User/Edit.cshtml
Normal file
46
watcher-monitoring/Views/User/Edit.cshtml
Normal file
@@ -0,0 +1,46 @@
|
||||
@model watcher_monitoring.Models.User
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Benutzer bearbeiten";
|
||||
}
|
||||
|
||||
<div class="container-fluid px-4">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<h1 class="section-title mb-4">Benutzer bearbeiten</h1>
|
||||
|
||||
<div class="card">
|
||||
<form asp-action="Edit" method="post">
|
||||
<div asp-validation-summary="ModelOnly" 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;"></div>
|
||||
<input type="hidden" asp-for="Id" />
|
||||
|
||||
<div class="mb-3">
|
||||
<label asp-for="Username" class="form-label" style="color: var(--text-primary); font-weight: 500; margin-bottom: 0.5rem;">Benutzername</label>
|
||||
<input asp-for="Username" class="form-control" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;" required />
|
||||
<span asp-validation-for="Username" style="color: var(--danger); font-size: 0.875rem;"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label asp-for="Email" class="form-label" style="color: var(--text-primary); font-weight: 500; margin-bottom: 0.5rem;">E-Mail-Adresse</label>
|
||||
<input asp-for="Email" type="email" class="form-control" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.75rem; border-radius: 6px;" required />
|
||||
<span asp-validation-for="Email" style="color: var(--danger); font-size: 0.875rem;"></span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input asp-for="IsActive" type="checkbox" class="form-check-input" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color);" />
|
||||
<label asp-for="IsActive" class="form-check-label" style="color: var(--text-primary); margin-left: 0.5rem;">Benutzer ist aktiv</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
<a asp-action="Details" asp-route-id="@Model.Id" class="btn" style="background-color: var(--bg-tertiary); border: 1px solid var(--border-color); color: var(--text-primary); padding: 0.5rem 1rem; border-radius: 6px; text-decoration: none;">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
|
||||
}
|
||||
62
watcher-monitoring/Views/User/Index.cshtml
Normal file
62
watcher-monitoring/Views/User/Index.cshtml
Normal file
@@ -0,0 +1,62 @@
|
||||
@model IEnumerable<watcher_monitoring.Models.User>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Benutzerverwaltung";
|
||||
}
|
||||
|
||||
<div class="container-fluid px-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="section-title mb-0">Benutzerverwaltung</h1>
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
Neuer Benutzer
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<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;">Benutzername</th>
|
||||
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">E-Mail</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;">API-Keys</th>
|
||||
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Erstellt am</th>
|
||||
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Letzter Login</th>
|
||||
<th style="color: var(--text-secondary); font-weight: 600; padding: 1rem;">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var user in Model)
|
||||
{
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 1rem;">
|
||||
<strong style="color: var(--text-primary);">@user.Username</strong>
|
||||
</td>
|
||||
<td style="padding: 1rem; color: var(--text-secondary);">@user.Email</td>
|
||||
<td style="padding: 1rem;">
|
||||
@if (user.IsActive)
|
||||
{
|
||||
<span class="status-badge status-online">Aktiv</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="status-badge status-offline">Inaktiv</span>
|
||||
}
|
||||
</td>
|
||||
<td style="padding: 1rem;">
|
||||
<span class="status-badge" style="background-color: rgba(88, 166, 255, 0.15); color: var(--info); border: 1px solid var(--info);">@user.ApiKeys.Count</span>
|
||||
</td>
|
||||
<td style="padding: 1rem; color: var(--text-secondary);">@user.CreatedAt.ToString("dd.MM.yyyy HH:mm")</td>
|
||||
<td style="padding: 1rem; color: var(--text-secondary);">@user.LastLogin.ToString("dd.MM.yyyy HH:mm")</td>
|
||||
<td style="padding: 1rem;">
|
||||
<a asp-action="Details" asp-route-id="@user.Id" class="btn btn-sm" style="background-color: var(--info); color: #fff; margin-right: 0.5rem;">Details</a>
|
||||
<a asp-action="Edit" asp-route-id="@user.Id" class="btn btn-sm" style="background-color: var(--warning); color: #fff;">Bearbeiten</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user