mirror of https://github.com/halo-dev/halo
fix: incorrect unsubscribe link for email notification (#4695)
#### What type of PR is this? /kind bug /area core /milestone 2.10.x #### What this PR does / why we need it: 修复邮件通知中取消订阅链接不正确的问题 #### Does this PR introduce a user-facing change? ```release-note 修复邮件通知中取消订阅链接不正确的问题 ```pull/4703/head
parent
d97c1e6e3d
commit
1ff1b4f2a5
|
@ -5,7 +5,6 @@ import static org.apache.commons.lang3.StringUtils.defaultString;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
|
@ -135,6 +134,7 @@ public class DefaultNotificationCenter implements NotificationCenter {
|
|||
.subscription(subscription)
|
||||
.reasonType(notificationContent.reasonType())
|
||||
.notificationTitle(notificationContent.title())
|
||||
.reasonAttributes(notificationContent.reasonAttributes())
|
||||
.notificationRawBody(defaultString(notificationContent.rawBody()))
|
||||
.notificationHtmlBody(defaultString(notificationContent.htmlBody()))
|
||||
.build()
|
||||
|
@ -180,12 +180,13 @@ public class DefaultNotificationCenter implements NotificationCenter {
|
|||
});
|
||||
}
|
||||
|
||||
@Builder
|
||||
record NotificationElement(ReasonType reasonType, Reason reason,
|
||||
Subscription subscription, NotifierDescriptor descriptor,
|
||||
String notificationTitle,
|
||||
String notificationRawBody,
|
||||
String notificationHtmlBody) {
|
||||
private ReasonAttributes toReasonAttributes(Reason reason) {
|
||||
var model = new ReasonAttributes();
|
||||
var attributes = reason.getSpec().getAttributes();
|
||||
if (attributes != null) {
|
||||
model.putAll(attributes);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
Mono<NotificationContext> notificationContextFrom(NotificationElement element) {
|
||||
|
@ -198,7 +199,7 @@ public class DefaultNotificationCenter implements NotificationCenter {
|
|||
messagePayload.setTitle(element.notificationTitle());
|
||||
messagePayload.setRawBody(element.notificationRawBody());
|
||||
messagePayload.setHtmlBody(element.notificationHtmlBody());
|
||||
messagePayload.setAttributes(reason.getSpec().getAttributes());
|
||||
messagePayload.setAttributes(element.reasonAttributes());
|
||||
|
||||
var message = new NotificationContext.Message();
|
||||
message.setRecipient(subscription.getSpec().getSubscriber().getName());
|
||||
|
@ -239,11 +240,6 @@ public class DefaultNotificationCenter implements NotificationCenter {
|
|||
});
|
||||
}
|
||||
|
||||
@Builder
|
||||
record NotificationContent(String title, String rawBody, String htmlBody, ReasonType reasonType,
|
||||
Map<String, Object> reasonProperties) {
|
||||
}
|
||||
|
||||
Mono<NotificationContent> inferenceTemplate(Reason reason, Subscription subscription,
|
||||
Locale locale) {
|
||||
var reasonTypeName = reason.getSpec().getReasonType();
|
||||
|
@ -252,7 +248,7 @@ public class DefaultNotificationCenter implements NotificationCenter {
|
|||
.flatMap(reasonType -> notificationTemplateSelector.select(reasonTypeName, locale)
|
||||
.flatMap(template -> {
|
||||
final var templateContent = template.getSpec().getTemplate();
|
||||
Map<String, Object> model = toReasonAttributes(reason);
|
||||
var model = toReasonAttributes(reason);
|
||||
var identity = UserIdentity.of(subscriber.getName());
|
||||
var subscriberInfo = new HashMap<>();
|
||||
if (identity.isAnonymous()) {
|
||||
|
@ -266,7 +262,7 @@ public class DefaultNotificationCenter implements NotificationCenter {
|
|||
|
||||
var builder = NotificationContent.builder()
|
||||
.reasonType(reasonType)
|
||||
.reasonProperties(model);
|
||||
.reasonAttributes(model);
|
||||
|
||||
var titleMono = notificationTemplateRender
|
||||
.render(templateContent.getTitle(), model)
|
||||
|
@ -285,17 +281,22 @@ public class DefaultNotificationCenter implements NotificationCenter {
|
|||
);
|
||||
}
|
||||
|
||||
@Builder
|
||||
record NotificationContent(String title, String rawBody, String htmlBody, ReasonType reasonType,
|
||||
ReasonAttributes reasonAttributes) {
|
||||
}
|
||||
|
||||
String getUnsubscribeUrl(Subscription subscription) {
|
||||
return subscriptionRouter.getUnsubscribeUrl(subscription);
|
||||
}
|
||||
|
||||
private Map<String, Object> toReasonAttributes(Reason reason) {
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
var attributes = reason.getSpec().getAttributes();
|
||||
if (attributes != null) {
|
||||
model.putAll(attributes);
|
||||
}
|
||||
return model;
|
||||
@Builder
|
||||
record NotificationElement(ReasonType reasonType, Reason reason,
|
||||
Subscription subscription, NotifierDescriptor descriptor,
|
||||
String notificationTitle,
|
||||
String notificationRawBody,
|
||||
String notificationHtmlBody,
|
||||
ReasonAttributes reasonAttributes) {
|
||||
}
|
||||
|
||||
Mono<ReasonType> getReasonType(String reasonTypeName) {
|
||||
|
|
|
@ -145,12 +145,11 @@ public class EmailNotifier implements ReactiveNotifier {
|
|||
|
||||
Mono<String> appendHtmlBodyFooter(ReasonAttributes attributes) {
|
||||
return notificationTemplateRender.render("""
|
||||
---
|
||||
<div class="footer" style="font-size: 12px; color: #666">
|
||||
<div class="footer" style="font-size: 12px; color: #666;">
|
||||
<a th:href="${site.url}" th:text="${site.title}"></a>
|
||||
<p class="unsubscribe">
|
||||
—<br />请勿直接回复此回邮件,
|
||||
<a th:href="|${site.url}/console|">查看通知</a>
|
||||
<a th:href="|${site.url}/console/users/-/notifications|">查看通知</a>
|
||||
或
|
||||
<a th:href="${unsubscribeUrl}">取消订阅</a>。
|
||||
</p>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package run.halo.app.notification.endpoint;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder;
|
||||
import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
|
||||
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import java.net.URI;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
|
||||
|
@ -82,7 +84,8 @@ public class SubscriptionRouter {
|
|||
public String getUnsubscribeUrl(Subscription subscription) {
|
||||
var name = subscription.getMetadata().getName();
|
||||
var token = subscription.getSpec().getUnsubscribeToken();
|
||||
return UriComponentsBuilder.fromUri(externalUrlSupplier.get())
|
||||
var externalUrl = defaultIfNull(externalUrlSupplier.getRaw(), URI.create("/"));
|
||||
return UriComponentsBuilder.fromUriString(externalUrl.toString())
|
||||
.path(UNSUBSCRIBE_PATTERN)
|
||||
.queryParam("token", token)
|
||||
.build(name)
|
||||
|
|
|
@ -3,6 +3,7 @@ package run.halo.app.notification.endpoint;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
@ -11,7 +12,6 @@ import org.mockito.Mock;
|
|||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import run.halo.app.core.extension.notification.Subscription;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.infra.ExternalUrlSupplier;
|
||||
|
||||
/**
|
||||
|
@ -23,9 +23,6 @@ import run.halo.app.infra.ExternalUrlSupplier;
|
|||
@ExtendWith(MockitoExtension.class)
|
||||
class SubscriptionRouterTest {
|
||||
|
||||
@Mock
|
||||
private ReactiveExtensionClient client;
|
||||
|
||||
@Mock
|
||||
private ExternalUrlSupplier externalUrlSupplier;
|
||||
|
||||
|
@ -33,8 +30,8 @@ class SubscriptionRouterTest {
|
|||
SubscriptionRouter subscriptionRouter;
|
||||
|
||||
@Test
|
||||
void getUnsubscribeUrlTest() {
|
||||
when(externalUrlSupplier.get()).thenReturn(URI.create("https://halo.run"));
|
||||
void getUnsubscribeUrlTest() throws MalformedURLException {
|
||||
when(externalUrlSupplier.getRaw()).thenReturn(URI.create("https://halo.run").toURL());
|
||||
var subscription = new Subscription();
|
||||
subscription.setMetadata(new Metadata());
|
||||
subscription.getMetadata().setName("fake-subscription");
|
||||
|
@ -46,5 +43,4 @@ class SubscriptionRouterTest {
|
|||
+ "/subscriptions/fake-subscription/unsubscribe"
|
||||
+ "?token=fake-unsubscribe-token");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue