Initial commit
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
@model IEnumerable<PowderCoating.Core.Entities.BannedIp>
|
||||
@{
|
||||
ViewData["Title"] = "Banned IPs";
|
||||
ViewData["PageIcon"] = "bi-slash-circle";
|
||||
var now = DateTime.UtcNow;
|
||||
var active = Model.Where(b => b.IsActive && (b.ExpiresAt == null || b.ExpiresAt > now)).ToList();
|
||||
var inactive = Model.Where(b => !b.IsActive || (b.ExpiresAt.HasValue && b.ExpiresAt <= now)).ToList();
|
||||
}
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
|
||||
@* Add new ban form *@
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h5 class="mb-0"><i class="bi bi-plus-circle"></i> Add IP Ban</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form asp-action="Add" method="post">
|
||||
@Html.AntiForgeryToken()
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">IP Address <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control font-monospace" name="ipAddress"
|
||||
id="ipAddressInput" placeholder="e.g. 203.0.113.42" required />
|
||||
<button type="button" class="btn btn-outline-secondary" id="fillMyIp" title="Fill with your current IP">
|
||||
<i class="bi bi-geo-alt"></i> My IP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label">Reason</label>
|
||||
<input type="text" class="form-control" name="reason"
|
||||
placeholder="e.g. Competitor snooping, scraping, abuse" maxlength="500" />
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Expires (leave blank = permanent)</label>
|
||||
<input type="datetime-local" class="form-control" name="expiresAt" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-danger w-100">
|
||||
<i class="bi bi-slash-circle"></i> Ban IP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Active bans *@
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="bi bi-slash-circle-fill text-danger"></i> Active Bans <span class="badge bg-danger ms-1">@active.Count</span></h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if (active.Any())
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<th>Reason</th>
|
||||
<th>Banned</th>
|
||||
<th>Expires</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var ban in active)
|
||||
{
|
||||
<tr>
|
||||
<td><code>@ban.IpAddress</code></td>
|
||||
<td>@(ban.Reason ?? "<em class=\"text-muted\">No reason given</em>")</td>
|
||||
<td><small class="text-muted">@ban.BannedAt.ToString("MMM dd, yyyy HH:mm")</small></td>
|
||||
<td>
|
||||
@if (ban.ExpiresAt.HasValue)
|
||||
{
|
||||
<span class="badge bg-warning text-dark">@ban.ExpiresAt.Value.ToString("MMM dd, yyyy HH:mm")</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">Permanent</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group btn-group-sm">
|
||||
<form asp-action="Lift" asp-route-id="@ban.Id" method="post" class="d-inline"
|
||||
onsubmit="return confirm('Lift the ban on @ban.IpAddress?')">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-outline-success" title="Lift ban">
|
||||
<i class="bi bi-check-circle"></i> Lift
|
||||
</button>
|
||||
</form>
|
||||
<form asp-action="Delete" asp-route-id="@ban.Id" method="post" class="d-inline"
|
||||
onsubmit="return confirm('Delete ban record for @ban.IpAddress?')">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-outline-danger" title="Delete record">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-center py-4 text-muted">
|
||||
<i class="bi bi-check-circle display-4"></i>
|
||||
<p class="mt-2">No active IP bans.</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Lifted / expired bans *@
|
||||
@if (inactive.Any())
|
||||
{
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0 text-muted"><i class="bi bi-clock-history"></i> Lifted / Expired Bans</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>IP Address</th>
|
||||
<th>Reason</th>
|
||||
<th>Banned</th>
|
||||
<th>Status</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var ban in inactive)
|
||||
{
|
||||
<tr class="text-muted">
|
||||
<td><code>@ban.IpAddress</code></td>
|
||||
<td><small>@(ban.Reason ?? "—")</small></td>
|
||||
<td><small>@ban.BannedAt.ToString("MMM dd, yyyy")</small></td>
|
||||
<td>
|
||||
@if (!ban.IsActive)
|
||||
{
|
||||
<span class="badge bg-success">Lifted</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">Expired</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<form asp-action="Delete" asp-route-id="@ban.Id" method="post" class="d-inline"
|
||||
onsubmit="return confirm('Delete ban record for @ban.IpAddress?')">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger" title="Delete record">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
document.getElementById('fillMyIp').addEventListener('click', function () {
|
||||
fetch('@Url.Action("MyIp", "BannedIps")')
|
||||
.then(r => r.json())
|
||||
.then(d => { document.getElementById('ipAddressInput').value = d.ip; });
|
||||
});
|
||||
</script>
|
||||
}
|
||||
Reference in New Issue
Block a user