diff --git a/src/main/java/dev/kruhlmann/imgfloat/service/SystemAdministratorService.java b/src/main/java/dev/kruhlmann/imgfloat/service/SystemAdministratorService.java index bae5648..4e90382 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/service/SystemAdministratorService.java +++ b/src/main/java/dev/kruhlmann/imgfloat/service/SystemAdministratorService.java @@ -2,14 +2,16 @@ package dev.kruhlmann.imgfloat.service; import dev.kruhlmann.imgfloat.model.SystemAdministrator; import dev.kruhlmann.imgfloat.repository.SystemAdministratorRepository; -import jakarta.annotation.PostConstruct; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; +import org.springframework.context.event.EventListener; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service public class SystemAdministratorService { @@ -27,8 +29,13 @@ public class SystemAdministratorService { this.environment = environment; } - @PostConstruct + @EventListener(ApplicationReadyEvent.class) public void initDefaults() { + initDefaultsInTransaction(); + } + + @Transactional + void initDefaultsInTransaction() { if ( Boolean.parseBoolean( environment.getProperty("org.springframework.boot.test.context.SpringBootTestContextBootstrapper") diff --git a/src/main/resources/static/css/styles.css b/src/main/resources/static/css/styles.css index fcd1094..bc7bc39 100644 --- a/src/main/resources/static/css/styles.css +++ b/src/main/resources/static/css/styles.css @@ -2014,6 +2014,17 @@ button:disabled:hover { font-weight: 700; } +.sysadmin-title-row { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} + +.sysadmin-badge { + font-size: 12px; +} + .identity-row { display: flex; align-items: center; diff --git a/src/main/resources/static/js/settings.js b/src/main/resources/static/js/settings.js index 5c87d3c..995b560 100644 --- a/src/main/resources/static/js/settings.js +++ b/src/main/resources/static/js/settings.js @@ -151,17 +151,46 @@ function renderSystemAdministrators(admins) { } admins.forEach((admin) => { + const normalizedAdmin = admin?.trim().toLowerCase(); + const isInitialSysadmin = initialSysadmin && normalizedAdmin === initialSysadmin; const listItem = document.createElement("li"); listItem.classList.add("stacked-list-item"); const text = document.createElement("div"); - text.innerHTML = `
${admin}
System admin access
`; + const titleRow = document.createElement("div"); + titleRow.classList.add("sysadmin-title-row"); + + const title = document.createElement("p"); + title.classList.add("list-title"); + title.textContent = admin; + titleRow.appendChild(title); + + if (isInitialSysadmin) { + const badge = document.createElement("span"); + badge.classList.add("chip", "subtle", "sysadmin-badge"); + badge.textContent = "Initial system admin"; + titleRow.appendChild(badge); + } + + const subtitle = document.createElement("p"); + subtitle.classList.add("muted"); + subtitle.textContent = "System admin access"; + + text.appendChild(titleRow); + text.appendChild(subtitle); const button = document.createElement("button"); button.classList.add("button", "secondary"); button.type = "button"; button.textContent = "Remove"; button.addEventListener("click", () => removeSystemAdministrator(admin)); + button.setAttribute("data-sysadmin-remove", "true"); + button.setAttribute("data-sysadmin-username", admin); + + if (isInitialSysadmin) { + button.disabled = true; + button.title = "The initial system administrator cannot be removed."; + } listItem.appendChild(text); listItem.appendChild(button); @@ -215,6 +244,10 @@ function addSystemAdministrator() { } function removeSystemAdministrator(username) { + if (initialSysadmin && username.trim().toLowerCase() === initialSysadmin) { + showToast("The initial system administrator cannot be removed.", "warning"); + return; + } fetch(`/api/system-administrators/${encodeURIComponent(username)}`, { method: "DELETE" }) .then((r) => { if (!r.ok) { @@ -258,13 +291,3 @@ setFormSettings(currentSettings); updateStatCards(currentSettings); updateSubmitButtonDisabledState(); loadSystemAdministrators(); - -if (initialSysadmin) { - document.querySelectorAll("[data-sysadmin-remove]").forEach((button) => { - const username = button.getAttribute("data-sysadmin-username"); - if (username && username.trim().toLowerCase() === initialSysadmin) { - button.disabled = true; - button.title = "The initial system administrator cannot be removed."; - } - }); -}