From 75d2001b37fa1011e6def1479844550e297dda38 Mon Sep 17 00:00:00 2001 From: Thomasr Date: Tue, 15 Apr 2025 19:26:51 -0400 Subject: [PATCH 1/4] email sending function --- .../service/EmailCommunicationService.java | 1 + .../EmailCommunicationServiceImpl.java | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java index d3775791b..ac99123a9 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java @@ -2,4 +2,5 @@ public interface EmailCommunicationService { boolean sendPasswordResetEmail(String to, String token, String message); + boolean sendInviteEmail(String[] to, String token, String message); } diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java index e83ed917b..c8cab0e39 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java @@ -49,4 +49,32 @@ public boolean sendPasswordResetEmail(String to, String token, String message) { } + @Override + public boolean sendInviteEmail(String[] to, String inviteLink, String message) { + try { + String subject = "You've been invited!"; + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true); + + mimeMessageHelper.setFrom(config.getNotificationsEmailSender()); + mimeMessageHelper.setTo(to); + mimeMessageHelper.setSubject(subject); + + // Construct the message with the invite link + String formattedMessage = String.format(message, String.join(", ", to), inviteLink); + mimeMessageHelper.setText(formattedMessage, true); // Set HTML to true to allow links + + javaMailSender.send(mimeMessage); + + return true; + + } catch (Exception e) { + log.error("Failed to send mail to: {}, Exception: ", to, e); + return false; + } + + + } + } From a81143198b1a022726968bfd1353edeedf47859c Mon Sep 17 00:00:00 2001 From: Thomasr Date: Tue, 15 Apr 2025 19:42:34 -0400 Subject: [PATCH 2/4] endpoint for invitation email --- .../service/EmailCommunicationService.java | 2 +- .../service/EmailCommunicationServiceImpl.java | 4 ++-- .../AuthenticationController.java | 18 +++++++++++++++--- .../AuthenticationEndpoints.java | 18 +++++++++++++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java index ac99123a9..20dfab94c 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationService.java @@ -2,5 +2,5 @@ public interface EmailCommunicationService { boolean sendPasswordResetEmail(String to, String token, String message); - boolean sendInviteEmail(String[] to, String token, String message); + boolean sendInvitationEmails(String[] to, String inviteLink, String message); } diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java index c8cab0e39..de09b45ac 100644 --- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java +++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/EmailCommunicationServiceImpl.java @@ -50,7 +50,7 @@ public boolean sendPasswordResetEmail(String to, String token, String message) { } @Override - public boolean sendInviteEmail(String[] to, String inviteLink, String message) { + public boolean sendInvitationEmails(String[] to, String inviteLink, String message) { try { String subject = "You've been invited!"; MimeMessage mimeMessage = javaMailSender.createMimeMessage(); @@ -62,7 +62,7 @@ public boolean sendInviteEmail(String[] to, String inviteLink, String message) { mimeMessageHelper.setSubject(subject); // Construct the message with the invite link - String formattedMessage = String.format(message, String.join(", ", to), inviteLink); + String formattedMessage = String.format(message, inviteLink); mimeMessageHelper.setText(formattedMessage, true); // Set HTML to true to allow links javaMailSender.send(mimeMessage); diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java index 36815b323..7fce49926 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java @@ -1,6 +1,7 @@ package org.lowcoder.api.authentication; -import lombok.RequiredArgsConstructor; +import java.util.List; + import org.lowcoder.api.authentication.dto.APIKeyRequest; import org.lowcoder.api.authentication.dto.AuthConfigRequest; import org.lowcoder.api.authentication.service.AuthenticationApiService; @@ -12,6 +13,7 @@ import org.lowcoder.api.util.BusinessEventPublisher; import org.lowcoder.domain.authentication.FindAuthConfig; import org.lowcoder.domain.user.model.APIKey; +import org.lowcoder.domain.user.service.EmailCommunicationService; import org.lowcoder.domain.user.service.UserService; import org.lowcoder.sdk.auth.AbstractAuthConfig; import org.lowcoder.sdk.util.CookieHelper; @@ -20,9 +22,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; -import java.util.List; +import lombok.RequiredArgsConstructor; +import reactor.core.publisher.Mono; @RequiredArgsConstructor @RestController @@ -34,6 +36,7 @@ public class AuthenticationController implements AuthenticationEndpoints private final CookieHelper cookieHelper; private final BusinessEventPublisher businessEventPublisher; private final UserService userService; + private final EmailCommunicationService emailCommunicationService; /** * login by email or phone with password; or register by email for now. @@ -134,4 +137,13 @@ public Mono> bindEmail(@RequestParam String email) { return sessionUserService.getVisitor().flatMap(user -> userService.bindEmail(user, email)) .map(ResponseView::success); } + + @Override + public Mono> sendInvitationEmails(InviteEmailRequest req) { + boolean isSuccess = emailCommunicationService.sendInvitationEmails(req.emails(), + req.inviteLink(), + "You have been invited to join our platform. Click here to accept the invitation: %s"); + + return Mono.just(ResponseView.success(isSuccess)); + } } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java index 27baf9674..df8c1de45 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java @@ -151,6 +151,15 @@ public Mono> linkAccountWithThirdParty( ) @PostMapping("/email/bind") public Mono> bindEmail(@RequestParam String email); + + @Operation( + tags = TAG_AUTHENTICATION, + operationId = "sendInvitationEmails", + summary = "Send invitation emails", + description = "Send invitation emails to the specified addresses" + ) + @PostMapping("/email/invite") + public Mono> sendInvitationEmails(@RequestBody InviteEmailRequest req); /** * @param loginId phone number or email for now. @@ -159,4 +168,11 @@ public Mono> linkAccountWithThirdParty( */ public record FormLoginRequest(String loginId, String password, boolean register, String source, String authId) { } -} + /** + * @param emails email addresses to send the invitation to + * @param inviteLink the link to be included in the email + */ + public record InviteEmailRequest(String[] emails, String inviteLink) { + } +} + From 54dcd9a68c835d18f15f3dd9ced91d65d307fcf6 Mon Sep 17 00:00:00 2001 From: Thomasr Date: Wed, 16 Apr 2025 14:12:44 -0400 Subject: [PATCH 3/4] remove invitationUrl field from request body --- .../AuthenticationController.java | 11 ------ .../AuthenticationEndpoints.java | 34 ++++++++----------- .../usermanagement/InvitationController.java | 17 ++++++++++ .../usermanagement/InvitationEndpoints.java | 16 +++++++++ 4 files changed, 47 insertions(+), 31 deletions(-) diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java index 7fce49926..3589aab52 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationController.java @@ -13,7 +13,6 @@ import org.lowcoder.api.util.BusinessEventPublisher; import org.lowcoder.domain.authentication.FindAuthConfig; import org.lowcoder.domain.user.model.APIKey; -import org.lowcoder.domain.user.service.EmailCommunicationService; import org.lowcoder.domain.user.service.UserService; import org.lowcoder.sdk.auth.AbstractAuthConfig; import org.lowcoder.sdk.util.CookieHelper; @@ -36,7 +35,6 @@ public class AuthenticationController implements AuthenticationEndpoints private final CookieHelper cookieHelper; private final BusinessEventPublisher businessEventPublisher; private final UserService userService; - private final EmailCommunicationService emailCommunicationService; /** * login by email or phone with password; or register by email for now. @@ -137,13 +135,4 @@ public Mono> bindEmail(@RequestParam String email) { return sessionUserService.getVisitor().flatMap(user -> userService.bindEmail(user, email)) .map(ResponseView::success); } - - @Override - public Mono> sendInvitationEmails(InviteEmailRequest req) { - boolean isSuccess = emailCommunicationService.sendInvitationEmails(req.emails(), - req.inviteLink(), - "You have been invited to join our platform. Click here to accept the invitation: %s"); - - return Mono.just(ResponseView.success(isSuccess)); - } } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java index df8c1de45..41a65d840 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/AuthenticationEndpoints.java @@ -1,7 +1,7 @@ package org.lowcoder.api.authentication; -import com.fasterxml.jackson.annotation.JsonView; -import io.swagger.v3.oas.annotations.Operation; +import java.util.List; + import org.lowcoder.api.authentication.dto.APIKeyRequest; import org.lowcoder.api.authentication.dto.AuthConfigRequest; import org.lowcoder.api.framework.view.ResponseView; @@ -13,11 +13,20 @@ import org.lowcoder.sdk.auth.AbstractAuthConfig; import org.lowcoder.sdk.config.JsonViews; import org.lowcoder.sdk.constants.AuthSourceConstants; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; -import reactor.core.publisher.Mono; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonView; + +import io.swagger.v3.oas.annotations.Operation; +import reactor.core.publisher.Mono; @RestController @RequestMapping(value = {NewUrl.CUSTOM_AUTH}) @@ -151,15 +160,6 @@ public Mono> linkAccountWithThirdParty( ) @PostMapping("/email/bind") public Mono> bindEmail(@RequestParam String email); - - @Operation( - tags = TAG_AUTHENTICATION, - operationId = "sendInvitationEmails", - summary = "Send invitation emails", - description = "Send invitation emails to the specified addresses" - ) - @PostMapping("/email/invite") - public Mono> sendInvitationEmails(@RequestBody InviteEmailRequest req); /** * @param loginId phone number or email for now. @@ -168,11 +168,5 @@ public Mono> linkAccountWithThirdParty( */ public record FormLoginRequest(String loginId, String password, boolean register, String source, String authId) { } - /** - * @param emails email addresses to send the invitation to - * @param inviteLink the link to be included in the email - */ - public record InviteEmailRequest(String[] emails, String inviteLink) { - } } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java index b83c6efc7..a7e13edd4 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java @@ -7,6 +7,8 @@ import org.lowcoder.api.framework.view.ResponseView; import org.lowcoder.api.home.SessionUserService; import org.lowcoder.api.usermanagement.view.InvitationVO; +import org.lowcoder.domain.user.service.EmailCommunicationService; +import org.lowcoder.sdk.config.CommonConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; @@ -24,6 +26,12 @@ public class InvitationController implements InvitationEndpoints @Autowired private SessionUserService sessionUserService; + @Autowired + private EmailCommunicationService emailCommunicationService; + + @Autowired + private CommonConfig config; + @Override public Mono> create(@RequestParam String orgId) { return invitationApiService.create(orgId) @@ -50,4 +58,13 @@ public Mono> inviteUser(@PathVariable String invitationId) { ); } + @Override + public Mono> sendInvitationEmails(InviteEmailRequest req) { + return invitationApiService.create(req.orgId()).map(invitation -> + emailCommunicationService.sendInvitationEmails(req.emails(), + config.getLowcoderPublicUrl() + "/" + invitation.getInviteCode(), + "You have been invited to join our platform. Click here to accept the invitation: %s")) + .map(ResponseView::success); + } + } diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationEndpoints.java index a1c3ba8db..906cbbf7f 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationEndpoints.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationEndpoints.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -46,5 +47,20 @@ public interface InvitationEndpoints ) @GetMapping("/{invitationId}/invite") public Mono> inviteUser(@PathVariable String invitationId); + + @Operation( + tags = TAG_INVITATION_MANAGEMENT, + operationId = "sendInvitationEmails", + summary = "Send invitation emails", + description = "Send invitation emails to the specified addresses" + ) + @PostMapping("/email/invite") + public Mono> sendInvitationEmails(@RequestBody InviteEmailRequest req); + /** + * @param emails email addresses to send the invitation to + * @param inviteLink the link to be included in the email + */ + public record InviteEmailRequest(String[] emails, String orgId) { + } } From 9cb040ca8fd3fe24e2864b393164226928215ffa Mon Sep 17 00:00:00 2001 From: Thomasr Date: Wed, 16 Apr 2025 14:20:14 -0400 Subject: [PATCH 4/4] fix invitation url --- .../org/lowcoder/api/usermanagement/InvitationController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java index a7e13edd4..eeda519e2 100644 --- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java +++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/InvitationController.java @@ -62,7 +62,7 @@ public Mono> inviteUser(@PathVariable String invitationId) { public Mono> sendInvitationEmails(InviteEmailRequest req) { return invitationApiService.create(req.orgId()).map(invitation -> emailCommunicationService.sendInvitationEmails(req.emails(), - config.getLowcoderPublicUrl() + "/" + invitation.getInviteCode(), + config.getLowcoderPublicUrl() + "/invite/" + invitation.getInviteCode(), "You have been invited to join our platform. Click here to accept the invitation: %s")) .map(ResponseView::success); }