This commit is contained in:
Mateusz Gruszczyński committed 2026-10-10 20:56:36 +02:00
1 parent f213d853e2
commit 76b8bb7ee2
78 files changed
+7750 -5204

No files matched your search

+58 -7
View File
@@ -9,7 +9,7 @@ const ADMIN_APPEARANCE_DEFAULTS = Object.freeze({
button_style: "normal", table_density: "normal", dashboard_density: "compact", dashboard_widgets: [...ADMIN_DASHBOARD_WIDGETS], dashboard_hidden_widgets: [],
});
const CHART_STYLE_KEYS = Object.freeze({ default: "chartStyleDefault", soft: "chartStyleSoft", technical: "chartStyleTechnical", high_contrast: "chartStyleHighContrast", neon: "chartStyleNeon", monochrome: "chartStyleMonochrome", warm: "chartStyleWarm", cool: "chartStyleCool" });
let adminPreferences = null, adminPreferencesLoading = null, adminPreferencesWrite = Promise.resolve();
let adminPreferences = null, adminPreferencesLoading = null, adminPreferencesWrite = Promise.resolve(), adminPreferencesUnavailable = false;
function normalizeAdminTheme(value) { return ADMIN_THEMES.has(value) ? value : "dark"; }
function normalizeAdminLanguage(value) { return value === "auto" || /^[A-Za-z0-9_-]{1,64}$/.test(value || "") ? value : "auto"; }
@@ -122,7 +122,7 @@ function refreshChartStylePalette(active = adminPreferences?.chart_style || "def
});
target.replaceChildren(...buttons);
}
function refreshAppearanceUi() { refreshChartStylePalette(); renderDashboardWidgetSettings(); window.rustProxySettings?.refreshSearch?.(); }
function refreshAppearanceUi() { refreshChartStylePalette(); renderDashboardWidgetSettings(); renderRefreshPreferences(adminPreferences?.refresh); window.rustProxySettings?.refreshSearch?.(); }
function formatDateTime(value) { const date = value instanceof Date ? value : new Date(value); return date.toLocaleString(locale(), { hour12: (adminPreferences?.time_format || "24h") === "12h" }); }
function formatTime(value) { const date = value instanceof Date ? value : new Date(value); return date.toLocaleTimeString(locale(), { hour12: (adminPreferences?.time_format || "24h") === "12h" }); }
function applyAdminPreferences(value, cache = true) {
@@ -130,11 +130,14 @@ function applyAdminPreferences(value, cache = true) {
const dashboard = applyDashboardLayout(value.dashboard_widgets, value.dashboard_hidden_widgets);
const normalized = {
...value,
refresh: normalizeRefreshPreferences(value.refresh),
theme: applyAdminTheme(value.theme, cache), language: applyAdminLanguage(value.language, cache, false), ui_scale: applyAdminUiScale(value.ui_scale, cache),
time_format: applyAdminTimeFormat(value.time_format), chart_style: applyAdminChartStyle(value.chart_style), button_style: applyAdminButtonStyle(value.button_style), table_density: applyAdminTableDensity(value.table_density),
dashboard_density: applyAdminDashboardDensity(value.dashboard_density), dashboard_widgets: dashboard.order, dashboard_hidden_widgets: dashboard.hidden,
};
adminPreferences = normalized;
window.rustProxyRefresh?.update(normalized.refresh);
renderRefreshPreferences(normalized.refresh);
window.rustProxyVhosts?.applyPreferences(normalized);
window.rustProxyDocker?.applyPreferences(normalized);
window.rustProxyRedirects?.applyPreferences(normalized);
@@ -152,9 +155,18 @@ async function loadAdminPreferences(force = false) {
if (adminPreferencesLoading) return adminPreferencesLoading;
const epoch = authEpoch;
adminPreferencesLoading = (async () => {
const value = await api("/api/preferences");
if (!authenticated || epoch !== authEpoch) return adminPreferences;
return applyAdminPreferences(value);
try {
const value = await api("/api/preferences");
if (!authenticated || epoch !== authEpoch) return adminPreferences;
adminPreferencesUnavailable = false;
return applyAdminPreferences(value);
} catch (error) {
if (error.status === 401 || !authenticated || epoch !== authEpoch) throw error;
// Preferences are optional for startup. Keep UI and live telemetry usable.
console.warn("Preferences unavailable; using defaults", error);
adminPreferencesUnavailable = true;
return applyAdminPreferences({ ...ADMIN_APPEARANCE_DEFAULTS, refresh: REFRESH_DEFAULTS });
}
})();
try { return await adminPreferencesLoading; } finally { adminPreferencesLoading = null; }
}
@@ -162,9 +174,11 @@ function updateAdminPreferences(patch) {
const epoch = authEpoch;
const task = adminPreferencesWrite.then(async () => {
if (!authenticated || epoch !== authEpoch) throw new Error(t("auth.required"));
const current = await loadAdminPreferences();
const current = await loadAdminPreferences(adminPreferencesUnavailable);
if (adminPreferencesUnavailable) throw new Error("Cannot persist preferences while the preference service is unavailable");
if (!current || !authenticated || epoch !== authEpoch) throw new Error(t("auth.required"));
const next = { ...current, ...patch };
next.refresh = normalizeRefreshPreferences(next.refresh);
next.theme = normalizeAdminTheme(next.theme); next.language = normalizeAdminLanguage(next.language); next.ui_scale = normalizeAdminUiScale(next.ui_scale);
next.time_format = normalizeAdminTimeFormat(next.time_format); next.chart_style = normalizeAdminChartStyle(next.chart_style); next.button_style = normalizeAdminButtonStyle(next.button_style); next.table_density = normalizeAdminDensity(next.table_density);
next.dashboard_density = normalizeAdminDensity(next.dashboard_density, "compact"); next.dashboard_widgets = normalizeDashboardWidgets(next.dashboard_widgets); next.dashboard_hidden_widgets = normalizeHiddenDashboardWidgets(next.dashboard_hidden_widgets);
@@ -174,7 +188,44 @@ function updateAdminPreferences(patch) {
});
adminPreferencesWrite = task.catch(() => undefined); return task;
}
function resetAdminPreferences() { adminPreferences = null; adminPreferencesLoading = null; adminPreferencesWrite = Promise.resolve(); }
function resetAdminPreferences() { adminPreferences = null; adminPreferencesLoading = null; adminPreferencesUnavailable = false; adminPreferencesWrite = Promise.resolve(); window.rustProxyRefresh?.update(REFRESH_DEFAULTS); }
function renderRefreshPreferences(value = REFRESH_DEFAULTS) {
const refresh = normalizeRefreshPreferences(value);
const switcher = $("refresh-live-enabled"); if (switcher) switcher.checked = refresh.live_enabled;
for (const field of Object.keys(REFRESH_CHOICES)) {
const input = $(`refresh-${field.replaceAll("_", "-")}`);
if (input) input.value = String(refresh[field]);
}
const manual = refresh.system_info_seconds === 0 || !refresh.live_enabled;
const hint = $("system-info-refresh-note");
if (hint) hint.textContent = t(manual ? "refreshManualHint" : "refreshSystemHint");
}
for (const control of document.querySelectorAll("[data-refresh-preference]")) {
control.addEventListener("change", () => {
const key = control.dataset.refreshPreference;
const previous = normalizeRefreshPreferences(adminPreferences?.refresh);
const value = key === "live_enabled" ? control.checked : Number(control.value);
if (key !== "live_enabled" && !REFRESH_CHOICES[key]?.includes(value)) {
renderRefreshPreferences(previous); return;
}
const next = { ...previous, [key]: value };
if (adminPreferences) adminPreferences.refresh = next;
window.rustProxyRefresh?.update(next); renderRefreshPreferences(next);
void saveAppearancePreference({ refresh: next }, control, () => {
if (adminPreferences) adminPreferences.refresh = previous;
window.rustProxyRefresh?.update(previous); renderRefreshPreferences(previous);
});
});
}
$("refresh-reset")?.addEventListener("click", () => {
const previous = normalizeRefreshPreferences(adminPreferences?.refresh), next = { ...REFRESH_DEFAULTS };
if (adminPreferences) adminPreferences.refresh = next;
window.rustProxyRefresh?.update(next); renderRefreshPreferences(next);
void saveAppearancePreference({ refresh: next }, $("refresh-reset"), () => {
if (adminPreferences) adminPreferences.refresh = previous;
window.rustProxyRefresh?.update(previous); renderRefreshPreferences(previous);
});
});
window.rustProxyPreferences = { load: loadAdminPreferences, update: updateAdminPreferences, current: () => adminPreferences, applyTheme: applyAdminTheme, refreshAppearanceUi };
async function saveAppearancePreference(patch, control, rollback) {