fix(ui): persist SMTP restriction in shared shell

This commit is contained in:
sechmachine
2026-08-15 04:33:37 +07:00
parent ec99586393
commit ddf688a533
10 changed files with 303 additions and 61 deletions
+2
View File
@@ -152,6 +152,8 @@
.badge-warning { color: var(--warning); }
.badge-danger { color: var(--danger); }
.alert { margin: .75rem 0; border: 1px solid var(--border); border-radius: .6rem; padding: .75rem .9rem; }
.alert-warning { border-color: color-mix(in srgb, var(--warning), transparent 55%); color: var(--warning); }
.alert-action { margin-left: .6rem; font-weight: 700; }
.alert-error { border-color: color-mix(in srgb, var(--danger), transparent 60%); color: var(--danger); }
.empty-state { padding: 2.5rem 1rem; text-align: center; }
.empty-state p { margin: .3rem auto 0; color: var(--muted); }
File diff suppressed because one or more lines are too long
@@ -10,7 +10,6 @@
<a id="primary-action" class="button" th:href="@{/dashboard}">Back to overview</a>
<main>
<p class="page-description">Create a role-specific account and send its one-time activation link.</p>
<p class="alert alert-error" th:if="${smtpRestricted}" role="alert">This is a restricted installation until tested SMTP is active.</p>
<p class="alert" th:if="${param.created}" role="status">Account created and activation email sent.</p>
<p class="alert alert-error" th:if="${param.deliveryFailed}" role="alert">Account created, but activation delivery failed.</p>
<form class="panel form-panel form-grid" method="post" th:action="@{/admin/accounts}" th:object="${accountForm}">
@@ -52,6 +52,11 @@
<div class="page-heading-copy"><h1 class="page-title" th:text="${pageTitle}">Page</h1></div>
<div class="primary-action" th:if="${primaryAction != null}" th:replace="${primaryAction}"></div>
</div>
<div class="alert alert-warning" role="alert" sec:authorize="hasRole('ADMIN')"
th:if="${smtpRestricted}">
<strong>This installation remains restricted until tested SMTP is active.</strong>
<a class="alert-action" th:href="@{/admin/smtp}">Configure SMTP</a>
</div>
<th:block th:replace="${content}"></th:block>
</div>
</div>
+25 -16
View File
@@ -1,22 +1,31 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><title>Defer SMTP configuration</title><link rel="icon" th:href="@{/assets/icons.svg}" type="image/svg+xml"></head>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: shell(
pageTitle='Defer SMTP configuration',
section='Admin',
activeNav='smtp',
primaryAction=~{::#primary-action},
content=~{::main})}">
<body>
<a id="primary-action" class="button button-primary" th:href="@{/admin/smtp}">Configure SMTP</a>
<main>
<h1>Defer SMTP configuration</h1>
<p role="alert" th:text="${deferralWarning}"></p>
<p th:text="|Confirmation ${deferralStep} of 5|"></p>
<a th:href="@{/admin/smtp}">Configure SMTP</a>
<form th:if="${deferralStep > 1}" method="post" th:action="@{/admin/smtp/defer/back}">
<button type="submit">Back</button>
</form>
<p th:if="${deferralStep == 1}"><a th:href="@{/admin/smtp}">Back</a></p>
<form th:if="${deferralStep < 5}" method="post" th:action="@{/admin/smtp/defer/next}">
<button type="submit">I understand; continue</button>
</form>
<form th:if="${deferralStep == 5}" method="post" th:action="@{/admin/smtp/defer/finish}">
<button type="submit">Finish without SMTP</button>
</form>
<p class="page-description">Review each consequence before continuing without an active mail service.</p>
<section class="panel form-panel form-grid" aria-labelledby="smtp-deferral-progress">
<p class="alert alert-warning" role="alert" th:text="${deferralWarning}"></p>
<p id="smtp-deferral-progress" th:text="|Confirmation ${deferralStep} of 5|"></p>
<div class="form-actions">
<form th:if="${deferralStep > 1}" method="post" th:action="@{/admin/smtp/defer/back}">
<button class="button" type="submit">Back</button>
</form>
<a class="button" th:if="${deferralStep == 1}" th:href="@{/admin/smtp}">Back</a>
<form th:if="${deferralStep < 5}" method="post" th:action="@{/admin/smtp/defer/next}">
<button class="button button-primary" type="submit">I understand; continue</button>
</form>
<form th:if="${deferralStep == 5}" method="post" th:action="@{/admin/smtp/defer/finish}">
<button class="button button-primary" type="submit">Finish without SMTP</button>
</form>
</div>
</section>
</main>
</body>
</html>
+81 -37
View File
@@ -1,45 +1,89 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><title>SMTP configuration</title><link rel="icon" th:href="@{/assets/icons.svg}" type="image/svg+xml"></head>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
th:replace="~{fragments/layout :: shell(
pageTitle='SMTP configuration',
section='Admin',
activeNav='smtp',
primaryAction=~{::#primary-action},
content=~{::main})}">
<body>
<a id="primary-action" class="button" th:href="@{/dashboard}">Back to overview</a>
<main>
<h1>SMTP configuration</h1>
<p th:if="${smtpRestricted}" role="alert">This is a restricted installation until tested SMTP is active.</p>
<p th:if="${param.onboarding}">Configure SMTP now to enable account onboarding and recovery.</p>
<p th:if="${smtpStatus.active}" role="status">SMTP is active.</p>
<p th:if="${smtpStatus.draftId != null}" role="status">Draft saved.</p>
<p th:if="${smtpStatus.tested}" role="status">Test passed.</p>
<p th:if="${smtpActionError}" th:text="${smtpActionError}" role="alert"></p>
<form method="post" th:action="@{/admin/smtp/draft}" th:object="${smtpForm}">
<div th:if="${#fields.hasGlobalErrors()}" role="alert">
<p th:each="error : ${#fields.globalErrors()}" th:text="${error}"></p>
<p class="page-description" th:if="${param.onboarding}">Configure SMTP now to enable account onboarding and recovery.</p>
<p class="alert" th:if="${smtpStatus.active}" role="status">SMTP is active.</p>
<p class="alert" th:if="${smtpStatus.draftId != null}" role="status">Draft saved.</p>
<p class="alert" th:if="${smtpStatus.tested}" role="status">Test passed.</p>
<p class="alert alert-error" th:if="${smtpActionError}" th:text="${smtpActionError}" role="alert"></p>
<form class="panel form-panel form-grid" method="post" th:action="@{/admin/smtp/draft}" th:object="${smtpForm}">
<div id="smtp-form-error-summary" class="alert alert-error" role="alert"
aria-labelledby="smtp-form-error-summary-title" th:if="${#fields.hasAnyErrors()}">
<strong id="smtp-form-error-summary-title">Please correct the highlighted SMTP settings.</strong>
<ul><li th:each="fieldError : ${#fields.allErrors()}" th:text="${fieldError}"></li></ul>
</div>
<label>Host <input th:field="*{host}" required></label>
<p th:if="${#fields.hasErrors('host')}" th:errors="*{host}" role="alert"></p>
<label>Port <input th:field="*{port}" type="number" min="1" max="65535" required></label>
<p th:if="${#fields.hasErrors('port')}" th:errors="*{port}" role="alert"></p>
<label>Security <select th:field="*{securityMode}"><option value="STARTTLS">STARTTLS</option><option value="TLS">TLS</option><option value="NONE">NONE</option></select></label>
<p th:if="${#fields.hasErrors('securityMode')}" th:errors="*{securityMode}" role="alert"></p>
<label>Username <input th:field="*{username}" autocomplete="username"></label>
<label>Password <input name="password" type="password" autocomplete="new-password"></label>
<p th:if="${#fields.hasErrors('authenticationComplete')}" th:errors="*{authenticationComplete}" role="alert"></p>
<label>From address <input th:field="*{fromAddress}" type="email" required></label>
<p th:if="${#fields.hasErrors('fromAddress')}" th:errors="*{fromAddress}" role="alert"></p>
<label>From name <input th:field="*{fromName}" required></label>
<p th:if="${#fields.hasErrors('fromName')}" th:errors="*{fromName}" role="alert"></p>
<button type="submit">Save draft</button>
<div class="form-grid form-grid-three">
<div class="field">
<label class="field-label" for="smtp-host">Host</label>
<input class="control" id="smtp-host" th:field="*{host}" required
th:attr="aria-invalid=${#fields.hasErrors('host')},aria-describedby=${#fields.hasErrors('host') ? 'smtp-host-error' : null}">
<p class="field-error" id="smtp-host-error" th:if="${#fields.hasErrors('host')}" th:errors="*{host}" role="alert"></p>
</div>
<div class="field">
<label class="field-label" for="smtp-port">Port</label>
<input class="control" id="smtp-port" th:field="*{port}" type="number" min="1" max="65535" required
th:attr="aria-invalid=${#fields.hasErrors('port')},aria-describedby=${#fields.hasErrors('port') ? 'smtp-port-error' : null}">
<p class="field-error" id="smtp-port-error" th:if="${#fields.hasErrors('port')}" th:errors="*{port}" role="alert"></p>
</div>
<div class="field">
<label class="field-label" for="smtp-security-mode">Security</label>
<select class="control" id="smtp-security-mode" th:field="*{securityMode}"
th:attr="aria-invalid=${#fields.hasErrors('securityMode')},aria-describedby=${#fields.hasErrors('securityMode') ? 'smtp-security-mode-error' : null}">
<option value="STARTTLS">STARTTLS</option>
<option value="TLS">TLS</option>
<option value="NONE">NONE</option>
</select>
<p class="field-error" id="smtp-security-mode-error" th:if="${#fields.hasErrors('securityMode')}" th:errors="*{securityMode}" role="alert"></p>
</div>
</div>
<div class="form-grid form-grid-three">
<div class="field">
<label class="field-label" for="smtp-username">Username</label>
<input class="control" id="smtp-username" th:field="*{username}" autocomplete="username"
th:attr="aria-invalid=${#fields.hasErrors('authenticationComplete')},aria-describedby=${#fields.hasErrors('authenticationComplete') ? 'smtp-authentication-error' : null}">
</div>
<div class="field">
<label class="field-label" for="smtp-password">Password</label>
<input class="control" id="smtp-password" name="password" type="password" autocomplete="new-password"
th:attr="aria-invalid=${#fields.hasErrors('authenticationComplete')},aria-describedby=${#fields.hasErrors('authenticationComplete') ? 'smtp-authentication-error' : null}">
<p class="field-error" id="smtp-authentication-error" th:if="${#fields.hasErrors('authenticationComplete')}" th:errors="*{authenticationComplete}" role="alert"></p>
</div>
<div class="field">
<label class="field-label" for="smtp-from-address">From address</label>
<input class="control" id="smtp-from-address" th:field="*{fromAddress}" type="email" required
th:attr="aria-invalid=${#fields.hasErrors('fromAddress')},aria-describedby=${#fields.hasErrors('fromAddress') ? 'smtp-from-address-error' : null}">
<p class="field-error" id="smtp-from-address-error" th:if="${#fields.hasErrors('fromAddress')}" th:errors="*{fromAddress}" role="alert"></p>
</div>
</div>
<div class="field">
<label class="field-label" for="smtp-from-name">From name</label>
<input class="control" id="smtp-from-name" th:field="*{fromName}" required
th:attr="aria-invalid=${#fields.hasErrors('fromName')},aria-describedby=${#fields.hasErrors('fromName') ? 'smtp-from-name-error' : null}">
<p class="field-error" id="smtp-from-name-error" th:if="${#fields.hasErrors('fromName')}" th:errors="*{fromName}" role="alert"></p>
</div>
<div class="form-actions"><button class="button button-primary" type="submit">Save draft</button></div>
</form>
<form th:if="${smtpStatus.draftId != null}" method="post" th:action="@{/admin/smtp/test}">
<input type="hidden" name="draftId" th:value="${smtpStatus.draftId}">
<button type="submit">Test connection</button>
</form>
<form th:if="${smtpStatus.draftId != null and smtpStatus.tested}" method="post" th:action="@{/admin/smtp/activate}">
<input type="hidden" name="draftId" th:value="${smtpStatus.draftId}">
<button type="submit">Activate SMTP</button>
</form>
<p th:if="${param.onboarding}">
<a th:href="@{/admin/smtp/defer}">Defer SMTP</a>
</p>
<div class="inline-actions">
<form th:if="${smtpStatus.draftId != null}" method="post" th:action="@{/admin/smtp/test}">
<input type="hidden" name="draftId" th:value="${smtpStatus.draftId}">
<button class="button" type="submit">Test connection</button>
</form>
<form th:if="${smtpStatus.draftId != null and smtpStatus.tested}" method="post" th:action="@{/admin/smtp/activate}">
<input type="hidden" name="draftId" th:value="${smtpStatus.draftId}">
<button class="button button-primary" type="submit">Activate SMTP</button>
</form>
<a class="button" th:if="${param.onboarding}" th:href="@{/admin/smtp/defer}">Defer SMTP</a>
</div>
</main>
</body>
</html>