306 lines
14 KiB
Plaintext
306 lines
14 KiB
Plaintext
@using Server.Models
|
|
@using System.Web
|
|
@using Shared.Models
|
|
@using Server.Services
|
|
@using Server
|
|
|
|
@inject LocalizationService T
|
|
@model HomeIndexViewModel
|
|
@{
|
|
ViewData["Title"] = "Home Page";
|
|
bool hasName = User.Identity?.Name is not null;
|
|
string name = "";
|
|
if (hasName && User.Identity is not null && User.Identity.Name is not null)
|
|
{
|
|
name = User.Identity.Name;
|
|
}
|
|
}
|
|
|
|
<div class="container py-4">
|
|
<h1 class="visually-hidden">Searchdomains</h1>
|
|
<p class="mb-4 fs-3">
|
|
@(hasName ? T["Hi, {0}!", name] : T["Hi!"])
|
|
</p>
|
|
|
|
<div class="row g-4">
|
|
|
|
<!-- Server -->
|
|
<div class="col-md-6">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-body">
|
|
<h2 class="card-title fs-5">@T["Server"]</h2>
|
|
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>@T["Total RAM usage"]</span>
|
|
<strong id="serverMemorySize"></strong>
|
|
</div>
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>@T["Total Database size"]</span>
|
|
<strong id="serverDatabaseSize"></strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Embedding Cache -->
|
|
<div class="col-md-6">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-body">
|
|
<h2 class="card-title fs-5">@T["Embedding Cache"]</h2>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<span>@T["Size"]</span>
|
|
<strong id="embeddingcacheSize"></strong>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>
|
|
@T["Strings"]
|
|
<i class="bi bi-info-circle-fill text-info"
|
|
data-bs-toggle="tooltip"
|
|
title="@T["stringsCountInfo"]"></i>
|
|
</span>
|
|
<strong id="embeddingcacheElementCount"></strong>
|
|
</div>
|
|
|
|
<div class="progress mt-3" style="height: 8px;">
|
|
<div id="embeddingcacheElementCountProgressBar" class="progress-bar"
|
|
style="width: 0.00%"></div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>@T["Embeddings"]</span>
|
|
<strong id="embeddingcacheEmbeddingCount"></strong>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Health Checks -->
|
|
<div class="col-md-6">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-body">
|
|
<h2 class="card-title fs-5">@T["Health Checks"]</h2>
|
|
|
|
<ul class="list-group list-group-flush">
|
|
<li class="list-group-item d-flex justify-content-between">
|
|
<span>@T["Server"]</span>
|
|
<span id="healthchecksServer"
|
|
class="badge bg-warning">
|
|
???????
|
|
</span>
|
|
</li>
|
|
<li class="list-group-item d-flex justify-content-between">
|
|
<span>@T["AI Providers"]</span>
|
|
<span id="healthchecksAiProvider"
|
|
class="badge bg-warning">
|
|
???????
|
|
</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Searchdomains -->
|
|
<div class="col-md-6">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-body">
|
|
<h2 class="card-title fs-5">@T["Searchdomains"]</h2>
|
|
|
|
<div class="d-flex justify-content-between">
|
|
<span>@T["Count"]</span>
|
|
<strong id="searchdomainCount"></strong>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>@T["Total Entities"]</span>
|
|
<strong id="searchdomainEntityCount"></strong>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>@T["Total query cache utilization"]</span>
|
|
<strong id="totalQuerycacheUtilization"></strong>
|
|
</div>
|
|
|
|
<!-- Query cache -->
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>@T["Query cache entry count"]</span>
|
|
<strong id="querycacheCount"></strong>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>
|
|
@T["Query cache capacity (loaded)"]
|
|
<i class="bi bi-info-circle-fill text-info"
|
|
data-bs-toggle="tooltip"
|
|
title="@T["queryCacheEntryCountLoadedInfo"]"></i>
|
|
</span>
|
|
<strong id="querycacheLoadedMaxElementCount"></strong>
|
|
</div>
|
|
|
|
<div class="progress mt-3" style="height: 8px;">
|
|
<div id="querycacheLoadedMaxElementCountProgressBar" class="progress-bar"
|
|
style="width: 0.00%"></div>
|
|
</div>
|
|
|
|
|
|
<div class="d-flex justify-content-between mt-2">
|
|
<span>
|
|
@T["Query cache capacity (all)"]
|
|
<i class="bi bi-info-circle-fill text-info"
|
|
data-bs-toggle="tooltip"
|
|
title="@T["queryCacheEntryCountAllInfo"]"></i>
|
|
</span>
|
|
<strong id="querycacheMaxElementCount"></strong>
|
|
</div>
|
|
|
|
<div class="progress mt-3" style="height: 8px;">
|
|
<div id="querycacheMaxElementCountProgressBar" class="progress-bar"
|
|
style="width: 0.00%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var searchdomains = null;
|
|
|
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
let searchdomainCount = document.getElementById("searchdomainCount");
|
|
showThrobber(searchdomainCount);
|
|
let searchdomainEntityCount = document.getElementById("searchdomainEntityCount");
|
|
showThrobber(searchdomainEntityCount);
|
|
let totalQuerycacheUtilization = document.getElementById("totalQuerycacheUtilization");
|
|
showThrobber(totalQuerycacheUtilization);
|
|
let embeddingcacheSize = document.getElementById("embeddingcacheSize");
|
|
showThrobber(embeddingcacheSize);
|
|
let embeddingcacheElementCount = document.getElementById("embeddingcacheElementCount");
|
|
showThrobber(embeddingcacheElementCount);
|
|
let embeddingcacheEmbeddingCount = document.getElementById("embeddingcacheEmbeddingCount");
|
|
showThrobber(embeddingcacheEmbeddingCount);
|
|
let embeddingcacheElementCountProgressBar = document.getElementById("embeddingcacheElementCountProgressBar");
|
|
|
|
let querycacheCount = document.getElementById("querycacheCount");
|
|
showThrobber(querycacheCount);
|
|
let querycacheMaxElementCount = document.getElementById("querycacheMaxElementCount");
|
|
showThrobber(querycacheMaxElementCount);
|
|
let querycacheMaxElementCountProgressBar = document.getElementById("querycacheMaxElementCountProgressBar");
|
|
let querycacheLoadedMaxElementCount = document.getElementById("querycacheLoadedMaxElementCount");
|
|
showThrobber(querycacheLoadedMaxElementCount);
|
|
let querycacheLoadedElementCountProgressBar = document.getElementById("querycacheLoadedElementCountProgressBar");
|
|
|
|
let serverMemorySize = document.getElementById("serverMemorySize");
|
|
showThrobber(serverMemorySize);
|
|
let serverDatabaseSize = document.getElementById("serverDatabaseSize");
|
|
showThrobber(serverDatabaseSize);
|
|
|
|
let healthchecksServer = document.getElementById("healthchecksServer");
|
|
let healthchecksAiProvider = document.getElementById("healthchecksAiProvider");
|
|
|
|
(async() => {
|
|
listSearchdomains().then(async result => {
|
|
searchdomains = result.Searchdomains;
|
|
hideThrobber(searchdomainCount);
|
|
searchdomainCount.textContent = searchdomains.length;
|
|
});
|
|
getServerStats().then(result => {
|
|
let utilization = result.EmbeddingCacheUtilization;
|
|
let embeddingCacheMaxElementCount = result.EmbeddingCacheMaxElementCount;
|
|
let embeddingCacheElementCount = result.ElementCount;
|
|
let embeddingCount = result.EmbeddingsCount;
|
|
let entityCount = result.EntityCount;
|
|
let queryCacheUtilization = result.QueryCacheUtilization;
|
|
let queryCacheElementCount = result.QueryCacheElementCount;
|
|
let queryCacheMaxElementCountAll = result.QueryCacheMaxElementCountAll;
|
|
let queryCacheMaxElementCountLoadedSearchdomainsOnly = result.QueryCacheMaxElementCountLoadedSearchdomainsOnly;
|
|
hideThrobber(embeddingcacheSize);
|
|
embeddingcacheSize.textContent = NumberOfBytesAsHumanReadable(utilization);
|
|
hideThrobber(embeddingcacheElementCount);
|
|
embeddingcacheElementCount.textContent = `${embeddingCacheElementCount.toLocaleString()} / ${embeddingCacheMaxElementCount.toLocaleString()}`;
|
|
hideThrobber(embeddingcacheEmbeddingCount);
|
|
embeddingcacheEmbeddingCount.textContent = embeddingCount;
|
|
embeddingcacheElementCountProgressBar.style.width = `${embeddingCacheElementCount / embeddingCacheMaxElementCount * 100}%`;
|
|
hideThrobber(searchdomainEntityCount);
|
|
searchdomainEntityCount.textContent = entityCount;
|
|
hideThrobber(totalQuerycacheUtilization);
|
|
totalQuerycacheUtilization.textContent = NumberOfBytesAsHumanReadable(queryCacheUtilization);
|
|
hideThrobber(querycacheMaxElementCount);
|
|
querycacheCount.textContent = queryCacheElementCount;
|
|
hideThrobber(querycacheCount);
|
|
querycacheMaxElementCount.textContent = queryCacheMaxElementCountAll.toLocaleString();
|
|
querycacheMaxElementCountProgressBar.style.width = `${queryCacheElementCount / queryCacheMaxElementCountAll * 100}%`;
|
|
hideThrobber(querycacheLoadedMaxElementCount);
|
|
querycacheLoadedMaxElementCount.textContent = queryCacheMaxElementCountLoadedSearchdomainsOnly.toLocaleString();
|
|
querycacheLoadedMaxElementCountProgressBar.style.width = `${queryCacheElementCount / queryCacheMaxElementCountLoadedSearchdomainsOnly * 100}%`;
|
|
serverMemorySize.textContent = NumberOfBytesAsHumanReadable(result.RamTotalSize);
|
|
hideThrobber(serverMemorySize);
|
|
serverDatabaseSize.textContent = NumberOfBytesAsHumanReadable(result.DatabaseTotalSize);
|
|
hideThrobber(serverDatabaseSize);
|
|
});
|
|
getHealthCheckStatusAndApply(healthchecksServer, "/healthz/Database");
|
|
getHealthCheckStatusAndApply(healthchecksAiProvider, "/healthz/AIProvider");
|
|
})();
|
|
});
|
|
|
|
|
|
async function listSearchdomains() {
|
|
return await fetch(`/Searchdomains`)
|
|
.then(r => r.json());
|
|
}
|
|
|
|
async function listEntities(searchdomain) {
|
|
return await fetch(`/Entities?searchdomain=${searchdomain}`)
|
|
.then(r => r.json());
|
|
}
|
|
|
|
async function getQuerycacheUtilization(searchdomain) {
|
|
return await fetch(`/Searchdomain/QueryCache/Size?searchdomain=${searchdomain}`)
|
|
.then(r => r.json());
|
|
}
|
|
|
|
async function getServerStats() {
|
|
return await fetch(`/Server/Stats`)
|
|
.then(r => r.json());
|
|
}
|
|
|
|
async function getHealthCheckStatusAndApply(element, url) {
|
|
let text = await fetch(url)
|
|
.then(r => r.text());
|
|
let states = {"Healthy": "bg-success", "Degraded": "bg-warning", "Unhealthy": "bg-danger"};
|
|
for (let state in states) {
|
|
element.classList.remove(states[state]);
|
|
}
|
|
if (states[text]) {
|
|
element.classList.add(states[text]);
|
|
} else {
|
|
element.classList.add("bg-danger");
|
|
}
|
|
element.textContent = text;
|
|
}
|
|
|
|
function showThrobber(element = null) {
|
|
if (element == null) element = document;
|
|
element.classList.add('spinner');
|
|
}
|
|
|
|
function hideThrobber(element = null) {
|
|
if (element == null) element = document;
|
|
element.classList.remove('spinner');
|
|
}
|
|
|
|
function NumberOfBytesAsHumanReadable(bytes, decimals = 2) {
|
|
if (bytes === 0) return '0 B';
|
|
if (bytes > 1.20892581961*(10**27)) return "∞ B";
|
|
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
|
const unitIndex = Math.floor(Math.log2(bytes) / 10);
|
|
const unit = units[Math.min(unitIndex, units.length - 1)];
|
|
const value = bytes / Math.pow(1024, unitIndex);
|
|
|
|
return `${value.toFixed(decimals)} ${unit}`;
|
|
}
|
|
|
|
</script> |