sqlinsight-pro/src/SQLInsight.Web/Components/Pages/Home.razor
Søren Eggert Lundsteen Olsen f3ef0cbec7 Web: Servere og databaser, and the same in API v1 (scope config:write)
A page under Overvågning lists the monitored servers (from SQLInsight Web and,
read-only, from the agent's configuration file), adds a server (name\instance,
optional port, Windows via the agent's account or a SQL login, encryption),
pauses, resumes and removes it, and finds instances via the agent (SQL Server
Browser). Each server's page shows the agent's connection test with the rights
it has and lacks, the least-privilege GRANT script for the login the agent
arrives as, and the discovered databases (state, size, recovery model,
compatibility level, Dynamics AX) to tick for monitoring, with "overvåg
automatisk nye databaser" and AX findings to the hub per database. Pages
refresh while the agent works on a request.

Only SQLInsight:Web:AdminGroups (new) may change anything; others see it.
SQL login passwords are sealed with the agent's public key on arrival and
never shown or returned; a new server or login needs the password again.

API v1: /monitored-instances (list, add, get, patch pause/monitor-new,
connection, delete), …/connection-test (start, latest with missing
permissions), …/discovery, …/databases (read, choose), …/grant-script,
/instance-discovery and /agent-requests/{id}. Changes need the new token scope
config:write, or AdminGroups for Windows users.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:41 +02:00

82 lines
4.2 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@page "/"
@inject InsightRepository Repository
@inject Display Display
<PageTitle>SQLInsight Pro</PageTitle>
<h1>Overblik</h1>
<p class="lead">Status for alle overvågede SQL Server-instanser.</p>
@if (_instances.Count == 0)
{
<div class="empty">
<p>Der er endnu ingen data. Tilføj en server under <a href="/opsaetning/instanser">Servere og databaser</a>, og tjek, at SQLInsight Agent kører.</p>
</div>
}
else
{
<PrivilegeAlert Findings="_active" ShowInstance="true" />
<div class="kpis">
<div class="kpi"><span class="kpi-icon"><Icon Name="servers" /></span><div><strong>@_instances.Count</strong><span>instanser</span></div></div>
<div class="kpi sev-critical"><span class="kpi-icon"><Icon Name="alert" /></span><div><strong>@_instances.Sum(i => i.Critical)</strong><span>kritiske fund</span></div></div>
<div class="kpi sev-high"><span class="kpi-icon"><Icon Name="alert" /></span><div><strong>@_instances.Sum(i => i.High)</strong><span>høje fund</span></div></div>
<div class="kpi sev-info"><span class="kpi-icon"><Icon Name="unplug" /></span><div><strong>@_instances.Count(i => i.IsFailing)</strong><span>kan ikke nås</span></div></div>
<div class="kpi sev-info"><span class="kpi-icon"><Icon Name="gauge" /></span><div><strong>@_instances.Sum(i => i.NotMeasurable)</strong><span>regler ikke målbare</span></div></div>
<div class="kpi sev-info"><span class="kpi-icon"><Icon Name="list" /></span><div><strong>@_instances.Sum(i => i.ManualChecks)</strong><span>skal tjekkes manuelt</span></div></div>
</div>
<section class="card">
<div class="card-head"><h2>Instanser</h2><span class="muted">sorteret efter alvor</span></div>
<table class="instances">
<thead>
<tr><th>Status</th><th>Instans</th><th>Version</th><th>Fund</th><th>Ikke målbar</th><th>Sidst set</th></tr>
</thead>
<tbody>
@foreach (var i in _instances.OrderByDescending(i => i.IsFailing).ThenByDescending(i => i.WorstSeverity ?? (Severity)(-1)).ThenBy(i => i.Name))
{
<tr>
<td>
@if (i.IsFailing)
{
<span class="badge sev-critical" title="@i.LastError">Fejler</span>
}
else
{
<SeverityBadge Severity="i.WorstSeverity" />
}
</td>
<td><a href="/instanser/@Uri.EscapeDataString(i.Name)"><strong>@i.Name</strong></a><div class="muted">@i.ServerName</div></td>
<td>@(i.ProductVersion is null ? "–" : Display.Release(i.ProductVersion, i.EngineEdition))<div class="muted">@i.ProductVersion @i.ProductLevel @i.ProductUpdateLevel</div></td>
<td><div class="counts">
@if (i.Critical > 0) { <SeverityBadge Severity="Severity.Critical" Count="i.Critical" /> }
@if (i.High > 0) { <SeverityBadge Severity="Severity.High" Count="i.High" /> }
@if (i.Medium > 0) { <SeverityBadge Severity="Severity.Medium" Count="i.Medium" /> }
@if (i.Low > 0) { <SeverityBadge Severity="Severity.Low" Count="i.Low" /> }
</div></td>
<td>@i.NotMeasurable</td>
<td title="@Display.Time(i.LastSeenAt)">@Display.Ago(i.LastSeenAt)</td>
</tr>
}
</tbody>
</table>
</section>
<section class="card">
<div class="card-head"><h2>Kritiske og høje fund</h2><a href="/fund">Se alle fund</a></div>
<FindingList Findings="_urgent" ShowInstance="true" Empty="Ingen kritiske eller høje fund." />
</section>
}
@code {
private IReadOnlyList<InstanceOverview> _instances = [];
private IReadOnlyList<StoredFinding> _active = [];
private IReadOnlyList<StoredFinding> _urgent = [];
protected override async Task OnInitializedAsync()
{
_instances = await Repository.GetInstancesAsync();
_active = await Repository.GetActiveFindingsAsync();
_urgent = _active.Where(f => f.Severity >= Severity.High).ToList();
}
}