-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add verification function to the notification settings for the …
…mailbox (#5464) #### What type of PR is this? /kind feature /area ui /area core /milestone 2.14.x #### What this PR does / why we need it: 为邮件的 `通知设置` 添加验证的功能。 同时为 formkit 增加了一个新的组件 (verificationForm),用于支持验证,它的定义方式如下: ``` - $formkit: verificationForm action: "http://localhost:8090/verify/user" label: 用户验证 children: - $formkit: text label: "用户名" name: username validation: required - $formkit: password label: "密码" name: password validation: required ``` verificationForm 支持 `action` 属性,当前端数据验证通过时,会将其下所包含的子节点数据发送至 action 所代表的接口上。 按上述示例,则验证数据会提交至 `http://localhost:8090/verify/user` 进行验证。验证的数据为 `{name: xxx, password: xxx}` 需要注意的是,verificationForm 只用于包装需要验证的数据,不会破坏原始数据的格式。因此上述数据在提交保存后仍旧为 `{name: xxx, password: xxx}` 而不会变成 `{verificationForm1: {name: xxx, password: xxx}}` #### How to test it? 1. 测试邮箱中的 `通知设置` 新增的验证按钮是否可以正常验证邮箱。 2. 查看数据是否正常回显 #### Which issue(s) this PR fixes: Fixes #4714 #### Does this PR introduce a user-facing change? ```release-note 在邮件通知设置中增加了发送测试的功能。 ```
- Loading branch information
Showing
15 changed files
with
564 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
application/src/main/java/run/halo/app/notification/EmailSenderHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package run.halo.app.notification; | ||
|
||
import lombok.Data; | ||
import lombok.NonNull; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.springframework.mail.javamail.JavaMailSender; | ||
import org.springframework.mail.javamail.MimeMessagePreparator; | ||
|
||
public interface EmailSenderHelper { | ||
|
||
@NonNull | ||
JavaMailSender createJavaMailSender(EmailSenderConfig senderConfig); | ||
|
||
@NonNull | ||
MimeMessagePreparator createMimeMessagePreparator(EmailSenderConfig senderConfig, | ||
String toEmail, String subject, String raw, String html); | ||
|
||
@Data | ||
class EmailSenderConfig { | ||
private boolean enable; | ||
private String displayName; | ||
private String username; | ||
private String sender; | ||
private String password; | ||
private String host; | ||
private Integer port; | ||
private String encryption; | ||
|
||
/** | ||
* Gets email display name. | ||
* | ||
* @return display name if not blank, otherwise username. | ||
*/ | ||
public String getDisplayName() { | ||
return StringUtils.defaultIfBlank(displayName, username); | ||
} | ||
|
||
/** | ||
* Gets email sender address. | ||
* | ||
* @return sender if not blank, otherwise username | ||
*/ | ||
public String getSender() { | ||
return StringUtils.defaultIfBlank(sender, username); | ||
} | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
application/src/main/java/run/halo/app/notification/EmailSenderHelperImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package run.halo.app.notification; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.util.Properties; | ||
import lombok.NonNull; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.mail.javamail.JavaMailSender; | ||
import org.springframework.mail.javamail.JavaMailSenderImpl; | ||
import org.springframework.mail.javamail.MimeMessageHelper; | ||
import org.springframework.mail.javamail.MimeMessagePreparator; | ||
import org.springframework.stereotype.Component; | ||
|
||
/** | ||
* <p>A default implementation of {@link EmailSenderHelper}.</p> | ||
* | ||
* @author guqing | ||
* @since 2.14.0 | ||
*/ | ||
@Slf4j | ||
@Component | ||
public class EmailSenderHelperImpl implements EmailSenderHelper { | ||
|
||
@Override | ||
@NonNull | ||
public JavaMailSender createJavaMailSender(EmailSenderConfig senderConfig) { | ||
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); | ||
javaMailSender.setHost(senderConfig.getHost()); | ||
javaMailSender.setPort(senderConfig.getPort()); | ||
javaMailSender.setUsername(senderConfig.getUsername()); | ||
javaMailSender.setPassword(senderConfig.getPassword()); | ||
|
||
Properties props = javaMailSender.getJavaMailProperties(); | ||
props.put("mail.transport.protocol", "smtp"); | ||
props.put("mail.smtp.auth", "true"); | ||
if ("SSL".equals(senderConfig.getEncryption())) { | ||
props.put("mail.smtp.ssl.enable", "true"); | ||
} | ||
|
||
if ("TLS".equals(senderConfig.getEncryption())) { | ||
props.put("mail.smtp.starttls.enable", "true"); | ||
} | ||
|
||
if ("NONE".equals(senderConfig.getEncryption())) { | ||
props.put("mail.smtp.ssl.enable", "false"); | ||
props.put("mail.smtp.starttls.enable", "false"); | ||
} | ||
|
||
if (log.isDebugEnabled()) { | ||
props.put("mail.debug", "true"); | ||
} | ||
return javaMailSender; | ||
} | ||
|
||
@Override | ||
@NonNull | ||
public MimeMessagePreparator createMimeMessagePreparator(EmailSenderConfig senderConfig, | ||
String toEmail, String subject, String raw, String html) { | ||
return mimeMessage -> { | ||
MimeMessageHelper helper = | ||
new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.name()); | ||
helper.setFrom(senderConfig.getSender(), senderConfig.getDisplayName()); | ||
|
||
helper.setSubject(subject); | ||
helper.setText(raw, html); | ||
helper.setTo(toEmail); | ||
}; | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
...ation/src/main/java/run/halo/app/notification/endpoint/EmailConfigValidationEndpoint.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package run.halo.app.notification.endpoint; | ||
|
||
import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; | ||
import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder; | ||
|
||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import java.security.Principal; | ||
import lombok.Data; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; | ||
import org.springframework.mail.MailException; | ||
import org.springframework.security.core.context.ReactiveSecurityContextHolder; | ||
import org.springframework.security.core.context.SecurityContext; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.reactive.function.server.RouterFunction; | ||
import org.springframework.web.reactive.function.server.ServerRequest; | ||
import org.springframework.web.reactive.function.server.ServerResponse; | ||
import org.springframework.web.server.ServerWebInputException; | ||
import reactor.core.publisher.Mono; | ||
import run.halo.app.core.extension.User; | ||
import run.halo.app.core.extension.endpoint.CustomEndpoint; | ||
import run.halo.app.extension.GroupVersion; | ||
import run.halo.app.extension.ReactiveExtensionClient; | ||
import run.halo.app.notification.EmailSenderHelper; | ||
|
||
/** | ||
* Validation endpoint for email config. | ||
* | ||
* @author guqing | ||
* @since 2.14.0 | ||
*/ | ||
@Slf4j | ||
@Component | ||
@RequiredArgsConstructor | ||
public class EmailConfigValidationEndpoint implements CustomEndpoint { | ||
private static final String EMAIL_SUBJECT = "测试邮件 - 验证邮箱连通性"; | ||
private static final String EMAIL_BODY = """ | ||
你好!<br/> | ||
这是一封测试邮件,旨在验证您的邮箱发件配置是否正确。<br/> | ||
此邮件由系统自动发送,请勿回复。<br/> | ||
祝好 | ||
"""; | ||
|
||
private final EmailSenderHelper emailSenderHelper; | ||
private final ReactiveExtensionClient client; | ||
|
||
@Override | ||
public RouterFunction<ServerResponse> endpoint() { | ||
var tag = "console.api.notification.halo.run/v1alpha1/Notifier"; | ||
return SpringdocRouteBuilder.route() | ||
.POST("/notifiers/default-email-notifier/verify-connection", | ||
this::verifyEmailSenderConfig, | ||
builder -> builder.operationId("VerifyEmailSenderConfig") | ||
.description("Verify email sender config.") | ||
.tag(tag) | ||
.requestBody(requestBodyBuilder() | ||
.required(true) | ||
.implementation(ValidationRequest.class) | ||
) | ||
.response(responseBuilder().implementation(Void.class)) | ||
) | ||
.build(); | ||
} | ||
|
||
private Mono<ServerResponse> verifyEmailSenderConfig(ServerRequest request) { | ||
return request.bodyToMono(ValidationRequest.class) | ||
.switchIfEmpty( | ||
Mono.error(new ServerWebInputException("Required request body is missing.")) | ||
) | ||
.flatMap(validationRequest -> getCurrentUserEmail() | ||
.flatMap(recipient -> { | ||
var mailSender = emailSenderHelper.createJavaMailSender(validationRequest); | ||
var message = emailSenderHelper.createMimeMessagePreparator(validationRequest, | ||
recipient, EMAIL_SUBJECT, EMAIL_BODY, EMAIL_BODY); | ||
try { | ||
mailSender.send(message); | ||
} catch (MailException e) { | ||
String errorMsg = | ||
"Failed to send email, please check your email configuration."; | ||
log.error(errorMsg, e); | ||
throw new ServerWebInputException(errorMsg, null, e); | ||
} | ||
return ServerResponse.ok().build(); | ||
}) | ||
); | ||
} | ||
|
||
Mono<String> getCurrentUserEmail() { | ||
return ReactiveSecurityContextHolder.getContext() | ||
.map(SecurityContext::getAuthentication) | ||
.map(Principal::getName) | ||
.flatMap(username -> client.fetch(User.class, username)) | ||
.flatMap(user -> { | ||
var email = user.getSpec().getEmail(); | ||
if (StringUtils.isBlank(email)) { | ||
return Mono.error(new ServerWebInputException( | ||
"Your email is missing, please set it in your profile.")); | ||
} | ||
return Mono.just(email); | ||
}); | ||
} | ||
|
||
@Data | ||
@EqualsAndHashCode(callSuper = true) | ||
@Schema(name = "EmailConfigValidationRequest") | ||
static class ValidationRequest extends EmailSenderHelper.EmailSenderConfig { | ||
} | ||
|
||
@Override | ||
public GroupVersion groupVersion() { | ||
return GroupVersion.parseAPIVersion("console.api.notification.halo.run/v1alpha1"); | ||
} | ||
} |
Oops, something went wrong.