mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2025-12-20 06:51:55 +00:00
Added cache clear button, added cache size estimation
This commit is contained in:
@@ -58,7 +58,7 @@ public class SettingsController : Controller
|
|||||||
}
|
}
|
||||||
AdminSettingsModel currentSettingsModel = await _ldap.GetAdminSettingsModelAsync();
|
AdminSettingsModel currentSettingsModel = await _ldap.GetAdminSettingsModelAsync();
|
||||||
await _ldap.SetAdminSettingsModelAsync(adminSettingsRequestModel.AdminSettingsModel);
|
await _ldap.SetAdminSettingsModelAsync(adminSettingsRequestModel.AdminSettingsModel);
|
||||||
if (adminSettingsRequestModel.AdminSettingsModel.UserImagePreloadType != currentSettingsModel.UserImagePreloadType)
|
if (adminSettingsRequestModel.AdminSettingsModel.UserImagePreloadType != UserImagePreloadType.None)
|
||||||
{
|
{
|
||||||
IEnumerable<UserModel> users = await _ldap.ListUsersAsync();
|
IEnumerable<UserModel> users = await _ldap.ListUsersAsync();
|
||||||
Task _ = ImageHelper.PreloadUsers(users, adminSettingsRequestModel.AdminSettingsModel.UserImagePreloadType);
|
Task _ = ImageHelper.PreloadUsers(users, adminSettingsRequestModel.AdminSettingsModel.UserImagePreloadType);
|
||||||
@@ -67,4 +67,18 @@ public class SettingsController : Controller
|
|||||||
Success = true
|
Success = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize(Roles = "CanManageSettings")]
|
||||||
|
[HttpPost("ClearUserImageCache")]
|
||||||
|
public bool ClearUserImageCache()
|
||||||
|
{
|
||||||
|
ImageHelper.ImageCache = [];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
[Authorize(Roles = "CanManageSettings")]
|
||||||
|
[HttpPost("UserImageCacheSize")]
|
||||||
|
public string UserImageCacheSize()
|
||||||
|
{
|
||||||
|
return ImageHelper.ToHumanReadableSize(ImageHelper.GetImageCacheSize());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -53,4 +53,66 @@ public static class ImageHelper
|
|||||||
Thread.Sleep(10);
|
Thread.Sleep(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long GetImageCacheSize()
|
||||||
|
{
|
||||||
|
long size = 0;
|
||||||
|
int stringOverhead = GetStringOverhead();
|
||||||
|
int arrayOverhead = GetArrayOverhead();
|
||||||
|
foreach (var kvOuter in ImageCache)
|
||||||
|
{
|
||||||
|
foreach (var kvInner in kvOuter.Value)
|
||||||
|
{
|
||||||
|
size += System.Text.ASCIIEncoding.Unicode.GetByteCount(kvInner.Key);
|
||||||
|
size += stringOverhead;
|
||||||
|
|
||||||
|
// byte[] size
|
||||||
|
if (kvInner.Value != null)
|
||||||
|
size += kvInner.Value.Length + arrayOverhead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToHumanReadableSize(long bytes)
|
||||||
|
{
|
||||||
|
string[] sizes = ["B", "KB", "MB", "GB", "TB", "PB"];
|
||||||
|
double len = bytes;
|
||||||
|
int order = 0;
|
||||||
|
|
||||||
|
while (len >= 1024 && order < sizes.Length - 1)
|
||||||
|
{
|
||||||
|
order++;
|
||||||
|
len /= 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{len:0.##} {sizes[order]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetStringOverhead()
|
||||||
|
{
|
||||||
|
// Object header size
|
||||||
|
int header = IntPtr.Size == 8 ? 16 : 8; // 16 bytes on x64, 8 on x86
|
||||||
|
|
||||||
|
int size =
|
||||||
|
header +
|
||||||
|
sizeof(int) + // string length field
|
||||||
|
sizeof(char); // null terminator
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetArrayOverhead()
|
||||||
|
{
|
||||||
|
// On x64 object header = 16 bytes; on x86 = 8 bytes
|
||||||
|
int header = IntPtr.Size == 8 ? 16 : 8;
|
||||||
|
|
||||||
|
// Array length field = 4 bytes
|
||||||
|
int lengthField = sizeof(int);
|
||||||
|
|
||||||
|
// Padding on x64 to align the elements (so the first element is aligned)
|
||||||
|
int padding = IntPtr.Size == 8 ? 4 : 0;
|
||||||
|
|
||||||
|
return header + lengthField + padding;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -101,4 +101,7 @@
|
|||||||
<data name="userImagePreloadType_FullSized" xml:space="preserve">
|
<data name="userImagePreloadType_FullSized" xml:space="preserve">
|
||||||
<value>48x48 + 256x256</value>
|
<value>48x48 + 256x256</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Cache cleared successfully" xml:space="preserve">
|
||||||
|
<value>Cache wurde erfolgreich geleert.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class ImagePreloadService : IHostedService
|
|||||||
if (adminSettingsModel.UserImagePreloadType is not null && adminSettingsModel.UserImagePreloadType != UserImagePreloadType.None)
|
if (adminSettingsModel.UserImagePreloadType is not null && adminSettingsModel.UserImagePreloadType != UserImagePreloadType.None)
|
||||||
{
|
{
|
||||||
IEnumerable<UserModel> users = _ldap.ListUsersAsync().Result;
|
IEnumerable<UserModel> users = _ldap.ListUsersAsync().Result;
|
||||||
_logger.LogTrace("Preloading user images");
|
_logger.LogInformation("Preloading user images with preload type {adminSettingsModel.UserImagePreloadType}", [adminSettingsModel.UserImagePreloadType]);
|
||||||
return ImageHelper.PreloadUsers(users, adminSettingsModel.UserImagePreloadType);
|
return ImageHelper.PreloadUsers(users, adminSettingsModel.UserImagePreloadType);
|
||||||
}
|
}
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
@using Berufsschule_HAM.Helpers
|
||||||
@using Berufsschule_HAM.Services
|
@using Berufsschule_HAM.Services
|
||||||
@using Microsoft.AspNetCore.Html
|
@using Microsoft.AspNetCore.Html
|
||||||
@using Microsoft.AspNetCore.Mvc.Localization
|
@using Microsoft.AspNetCore.Mvc.Localization
|
||||||
@@ -11,12 +12,13 @@
|
|||||||
@{
|
@{
|
||||||
ViewData["Title"] = T["Users"];
|
ViewData["Title"] = T["Users"];
|
||||||
List<string> supportedBarcodeTypes = ["code128c", "ean13", "ean8", "upc", "itf14", "itf"];
|
List<string> supportedBarcodeTypes = ["code128c", "ean13", "ean8", "upc", "itf14", "itf"];
|
||||||
|
string userImageCacheSize = ImageHelper.ToHumanReadableSize(ImageHelper.GetImageCacheSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
<form id="updateSettings" style="margin-bottom: 4rem !important" method="post" asp-controller="Settings" asp-action="Admin">
|
<form id="updateSettings" style="margin-bottom: 4rem !important" method="post" asp-controller="Settings" asp-action="Admin">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<h4 class="fw-bold">@T["General settings"]</h4>
|
<h4 class="fw-bold">@T["General settings"]</h4>
|
||||||
<div class="col-md-6">
|
<div class="col-md-3">
|
||||||
<label class="form-label" for="updateHashAlgorithm">@T["Default hash algorithm"]</label>
|
<label class="form-label" for="updateHashAlgorithm">@T["Default hash algorithm"]</label>
|
||||||
<select type="text" name="DefaultHashAlgorithm" id="updateHashAlgorithm" class="form-control">
|
<select type="text" name="DefaultHashAlgorithm" id="updateHashAlgorithm" class="form-control">
|
||||||
@foreach (string algorithm in ldap.HashAlgorithms.Keys)
|
@foreach (string algorithm in ldap.HashAlgorithms.Keys)
|
||||||
@@ -26,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-3">
|
||||||
<label class="form-label" for="updateBarcodeType">@T["Barcode type"]</label>
|
<label class="form-label" for="updateBarcodeType">@T["Barcode type"]</label>
|
||||||
<span
|
<span
|
||||||
id="barcodeInfoIcon"
|
id="barcodeInfoIcon"
|
||||||
@@ -48,15 +50,15 @@
|
|||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-3">
|
||||||
<label class="form-label" for="updateBarcodeText">@T["Barcode text"]</label>
|
<label class="form-label" for="updateBarcodeText">@T["Barcode text"]</label>
|
||||||
<input type="text" name="BarcodeText" id="updateBarcodeText" class="form-control" value="@Model.BarcodeText" />
|
<input type="text" name="BarcodeText" id="updateBarcodeText" class="form-control" value="@Model.BarcodeText" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<label class="form-label" for="updateMaxDownloadableUserImageSize">@T["Max downloadable user image size"]</label>
|
<label class="form-label" for="updateMaxDownloadableUserImageSize">@T["Max downloadable user image size"]</label>
|
||||||
<input type="number" id="updateMaxDownloadableUserImageSize" name="MaxDownloadableUserImageSize" class="form-control" value="@Model.MaxDownloadableUserImageSize" />
|
<input type="number" id="updateMaxDownloadableUserImageSize" name="MaxDownloadableUserImageSize" class="form-control" value="@Model.MaxDownloadableUserImageSize" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-3">
|
||||||
<label class="form-label" for="updateUserImagePreloadType">@T["User image preloading"]</label>
|
<label class="form-label" for="updateUserImagePreloadType">@T["User image preloading"]</label>
|
||||||
<select type="text" name="UserImagePreloadType" id="updateUserImagePreloadType" class="form-control" />
|
<select type="text" name="UserImagePreloadType" id="updateUserImagePreloadType" class="form-control" />
|
||||||
@foreach (UserImagePreloadType userImagePreloadType in Enum.GetValues(typeof(UserImagePreloadType)))
|
@foreach (UserImagePreloadType userImagePreloadType in Enum.GetValues(typeof(UserImagePreloadType)))
|
||||||
@@ -65,6 +67,10 @@
|
|||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<p class="form-label">@T["Current user image cache utilization:"] <span id="userImageCacheSize">@userImageCacheSize</span></p>
|
||||||
|
<button class="form-control btn btn-danger" type="button" id="clearCacheBtn">@T["Clear user image cache"]</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-3 p-2 bg border rounded d-flex justify-content-between align-items-center"
|
<div class="mt-3 p-2 bg border rounded d-flex justify-content-between align-items-center"
|
||||||
data-bs-toggle="collapse"
|
data-bs-toggle="collapse"
|
||||||
@@ -124,6 +130,40 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<script defer>
|
<script defer>
|
||||||
|
|
||||||
|
document.getElementById("clearCacheBtn").addEventListener("click", async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/Settings/ClearUserImageCache", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Request failed: " + response.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("userImageCacheSize").textContent = "0 B";
|
||||||
|
showToast('@T["Cache cleared successfully"]', 'success');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
showToast('@T["Error contacting server"]', 'danger');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
fetch('/Settings/UserImageCacheSize', {
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
.then(res => res.text())
|
||||||
|
.then(text => {
|
||||||
|
document.querySelector('#userImageCacheSize').textContent = text;
|
||||||
|
})
|
||||||
|
.catch(err => console.error('Cache-size fetch failed:', err));
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const updateForm = document.getElementById('updateSettings');
|
const updateForm = document.getElementById('updateSettings');
|
||||||
updateForm.addEventListener('submit', async e => {
|
updateForm.addEventListener('submit', async e => {
|
||||||
|
|||||||
Reference in New Issue
Block a user