Mail.java
package jasper.component.delta;
import jakarta.annotation.PostConstruct;
import jasper.component.ConfigCache;
import jasper.domain.Ref;
import jasper.domain.proj.RefUrl;
import jasper.repository.ExtRepository;
import jasper.repository.RefRepository;
import jasper.repository.filter.TagQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static jasper.domain.proj.Tag.localTag;
import static jasper.domain.proj.Tag.tagOrigin;
import static java.util.Arrays.stream;
import static java.util.stream.Stream.concat;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Profile("smtp-relay")
@Component
public class Mail implements Async.AsyncRunner {
private static final Logger logger = LoggerFactory.getLogger(Mail.class);
@Autowired
Async async;
@Autowired
RefRepository refRepository;
@Autowired
ExtRepository extRepository;
@Autowired
JavaMailSender emailSender;
@Autowired
ConfigCache configs;
record QT(String tag, String origin) {}
@PostConstruct
void init() {
async.addAsyncTag("plugin/email", this);
}
@Override
public String signature() {
return "+plugin/email";
}
@Override
public void run(Ref ref) throws Exception {
var mb = ref.getTags().stream()
.filter(t -> t.startsWith("plugin/inbox/") || t.startsWith("plugin/outbox/"))
.toArray(String[]::new);
String[] emails = new String[]{};
String[] outboxUserTags = new String[]{};
if (mb.length != 0) {
var inboxUserTags = stream(mb)
.filter(t -> t.startsWith("plugin/inbox/"))
.map(t -> "+" + t.substring("plugin/inbox/".length()) + ref.getOrigin())
.toArray(String[]::new);
outboxUserTags = stream(mb)
.filter(t -> t.startsWith("plugin/outbox/"))
.map(t -> t.substring("plugin/outbox/".length()))
.map(t -> new QT(t.substring(t.indexOf("/") + 1), t.substring(0, t.indexOf("/"))))
.map(to -> to.tag + (isNotBlank(to.origin) ? ("@" + to.origin) : ""))
.toArray(String[]::new);
var query = String.join("|", concat(stream(inboxUserTags), stream(outboxUserTags)).toArray(String[]::new));
emails = extRepository.findAll(new TagQuery(query).spec())
.stream()
.filter(ext -> ext.getConfig().has("email"))
.map(ext -> ext.getConfig().get("email").asText())
.filter(StringUtils::isNotBlank)
.toArray(String[]::new);
}
if (!ref.hasTag("+user") && !ref.hasTag("_user") && emails.length == 0) {
// Mail from webhook with no recipient
return;
}
var tos = stream(outboxUserTags)
.map(t -> localTag(t) + refRepository.originUrl(ref.getOrigin(), tagOrigin(t))
.map(RefUrl::get)
.map(o -> "@" + o)
.orElse(tagOrigin(t)))
.toArray(String[]::new);
if (tos.length == 0 && emails.length == 0) {
logger.error("No recipients for email.");
return;
}
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(concat(stream(tos), stream(emails))
.distinct()
.toArray(String[]::new));
var host = refRepository.originUrl(ref.getOrigin(), ref.getOrigin()).map(RefUrl::get).map(str -> {
try {
return new URI(str);
} catch (URISyntaxException e) {
return null;
}
}).map(URI::getHost).orElse(Stream.of(ref.getOrigin(), configs.root().getEmailHost()).filter(StringUtils::isNotBlank).collect(Collectors.joining(".")));
message.setFrom(ref.getExpandedTags().stream()
.filter(t -> t.startsWith("+user/") || t.startsWith("+user") || t.startsWith("_user/") || t.startsWith("_user"))
.findFirst()
.map(t -> t + "@" + host)
.orElse("no-reply@" + host)
);
// TODO: Add notifications as reply-to ?
message.setSubject(ref.getTitle());
message.setText(ref.getComment());
emailSender.send(message);
}
}