-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for custom SMTP Server (#2)
- Loading branch information
1 parent
d66d673
commit 5dba535
Showing
10 changed files
with
223 additions
and
61 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,23 +29,24 @@ | |
|
||
To run this project, you will need to add the following environment variables | ||
|
||
- `CONTACT_FORM_CONFIGS_FOLDER` | ||
- `MAIL_CONFIGS_FOLDER` | ||
- `TEMPLATES_FOLDER` | ||
- `GOOGLE_RECAPTCHA_SECRET` | ||
- `SENDGRID_API_KEY` | ||
- `CONTACT_FORM_CONFIGS_FOLDER`: An existing folder in your file system where the contact form configs will be stored. | ||
- `MAIL_CONFIGS_FOLDER`: An existing folder in your file system where the email configs will be stored. | ||
- `TEMPLATES_FOLDER`: An existing folder in your file system where the email templates will be stored. | ||
- `GOOGLE_RECAPTCHA_SECRET`: A Google ReCaptcha secret (required only when using forms). | ||
|
||
## Documentation | ||
|
||
### Supported Mail Providers | ||
|
||
- ~~[Sendgrid](https://sendgrid.com/)~~ Removed due to lack of support for batch emails requests | ||
- [Resend](https://resend.io/) | ||
- Custom SMTP server, that can be configured in the email & contact form configs | ||
|
||
### Contact Form | ||
|
||
#### Example of `CONTACT_FORM_CONFIGS_FOLDER` configuration files | ||
|
||
**Resend-based config** | ||
```json | ||
{ | ||
"id": "UUID", | ||
|
@@ -56,7 +57,25 @@ To run this project, you will need to add the following environment variables | |
"lang": "fr", // ISO 639-1 | ||
"subjectTemplate": "New mail from {{form.firstName}}", | ||
"provider": "RESEND", | ||
"apiKey": "" | ||
"apiKey": "<YOUR_RESEND_API_KEY>" | ||
} | ||
``` | ||
|
||
**SMTP-based config** | ||
```json | ||
{ | ||
"id": "UUID", | ||
"dailyLimit": 10, | ||
"destination": "[email protected]", | ||
"sender": "[email protected]", | ||
"threshold": 0.5, // Recapthca score thresold | ||
"lang": "fr", // ISO 639-1 | ||
"subjectTemplate": "New mail from {{form.firstName}}", | ||
"provider": "SMTP", | ||
"username": "<SMTP_USERNAME>", | ||
"password": "<SMTP_PASSWORD>", | ||
"smtpHost": "<SMTP_SERVER_IP>", | ||
"smtpPort": "<SMTP_SERVER_PORT>" | ||
} | ||
``` | ||
|
||
|
@@ -66,13 +85,28 @@ Filename does not have to respect any convention. | |
|
||
#### Example of `MAIL_CONFIGS_FOLDER` configuration files | ||
|
||
**Resend-based config** | ||
```json | ||
{ | ||
"id": "UUID", | ||
"sender": "[email protected]", | ||
"subjectTemplate": "New mail from {{form.firstName}}", | ||
"provider": "RESEND", | ||
"apiKey": "" | ||
"apiKey": "<YOUR_RESEND_API_KEY>" | ||
} | ||
``` | ||
|
||
**SMTP-based config** | ||
```json | ||
{ | ||
"id": "UUID", | ||
"sender": "[email protected]", | ||
"subjectTemplate": "New mail from {{form.firstName}}", | ||
"provider": "SMTP", | ||
"username": "<SMTP_USERNAME>", | ||
"password": "<SMTP_PASSWORD>", | ||
"smtpHost": "<SMTP_SERVER_IP>", | ||
"smtpPort": "<SMTP_SERVER_PORT>" | ||
} | ||
``` | ||
|
||
|
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
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 |
---|---|---|
@@ -1,6 +1,50 @@ | ||
package com.vandeas.dto.configs | ||
|
||
import com.vandeas.service.Mailer | ||
import com.vandeas.service.impl.mailer.ResendMailer | ||
import com.vandeas.service.impl.mailer.SMTPMailer | ||
import kotlinx.serialization.Serializable | ||
|
||
const val RESEND_SERIAL_NAME = "RESEND" | ||
const val SMTP_SERIAL_NAME = "SMTP" | ||
|
||
interface Config { | ||
val id: String | ||
val apiKey: String | ||
|
||
/** | ||
* Instantiates a new [Mailer] using this config. | ||
*/ | ||
fun toMailer(): Mailer | ||
|
||
/** | ||
* @return a string that uniquely identifies this config based on the credentials. | ||
*/ | ||
fun identifierFromCredentials(): String | ||
} | ||
|
||
@Serializable | ||
abstract class ResendProvider: Config { | ||
protected abstract val apiKey: String | ||
|
||
override fun toMailer() = ResendMailer(apiKey = apiKey) | ||
|
||
override fun identifierFromCredentials() = apiKey | ||
|
||
} | ||
|
||
@Serializable | ||
abstract class SMTPProvider: Config { | ||
protected abstract val username: String | ||
protected abstract val password: String | ||
protected abstract val smtpHost: String | ||
protected abstract val smtpPort: Int | ||
|
||
override fun toMailer() = SMTPMailer( | ||
username = username, | ||
password = password, | ||
host = smtpHost, | ||
port = smtpPort | ||
) | ||
|
||
override fun identifierFromCredentials() = "smtp://${username}:${password}@$smtpHost:$smtpPort" | ||
} |
56 changes: 40 additions & 16 deletions
56
src/main/kotlin/com/vandeas/dto/configs/ContactFormConfig.kt
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 |
---|---|---|
@@ -1,23 +1,47 @@ | ||
package com.vandeas.dto.configs | ||
|
||
import com.vandeas.dto.enums.Providers | ||
import com.vandeas.dto.enums.toMailer | ||
import com.vandeas.service.Mailer | ||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.json.JsonClassDiscriminator | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
@Serializable | ||
data class ContactFormConfig( | ||
@JsonClassDiscriminator("provider") | ||
sealed interface ContactFormConfig : Config { | ||
val dailyLimit: Int | ||
val destination: String | ||
val sender: String | ||
val threshold: Double | ||
val lang: String | ||
val subjectTemplate: String | ||
} | ||
|
||
@Serializable | ||
@SerialName(RESEND_SERIAL_NAME) | ||
data class ResendContactFormConfig( | ||
override val id: String, | ||
val dailyLimit: Int, | ||
val destination: String, | ||
val sender: String, | ||
val threshold: Double, | ||
val lang: String, | ||
val subjectTemplate: String, | ||
val provider: Providers, | ||
override val apiKey: String, | ||
): Config | ||
override val dailyLimit: Int, | ||
override val destination: String, | ||
override val sender: String, | ||
override val threshold: Double, | ||
override val lang: String, | ||
override val subjectTemplate: String, | ||
override val apiKey: String | ||
) : ContactFormConfig, ResendProvider() | ||
|
||
fun ContactFormConfig.toMailer(): Mailer { | ||
return provider.toMailer(apiKey) | ||
} | ||
@Serializable | ||
@SerialName(SMTP_SERIAL_NAME) | ||
data class SMTPContactFormConfig( | ||
override val id: String, | ||
override val dailyLimit: Int, | ||
override val destination: String, | ||
override val sender: String, | ||
override val threshold: Double, | ||
override val lang: String, | ||
override val subjectTemplate: String, | ||
override val username: String, | ||
override val password: String, | ||
override val smtpHost: String, | ||
override val smtpPort: Int = 587 | ||
) : ContactFormConfig, SMTPProvider() |
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 |
---|---|---|
@@ -1,19 +1,37 @@ | ||
package com.vandeas.dto.configs | ||
|
||
import com.vandeas.dto.enums.Providers | ||
import com.vandeas.dto.enums.toMailer | ||
import com.vandeas.service.Mailer | ||
import com.vandeas.service.impl.mailer.ResendMailer | ||
import com.vandeas.service.impl.mailer.SMTPMailer | ||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.SerialName | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.json.JsonClassDiscriminator | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
@Serializable | ||
data class MailConfig( | ||
@JsonClassDiscriminator("provider") | ||
sealed interface MailConfig : Config { | ||
val sender: String | ||
val subjectTemplate: String | ||
} | ||
|
||
@Serializable | ||
@SerialName(RESEND_SERIAL_NAME) | ||
data class ResendMailConfig( | ||
override val id: String, | ||
val sender: String, | ||
val subjectTemplate: String, | ||
val provider: Providers, | ||
override val apiKey: String, | ||
): Config | ||
override val sender: String, | ||
override val subjectTemplate: String, | ||
override val apiKey: String | ||
) : MailConfig, ResendProvider() | ||
|
||
fun MailConfig.toMailer(): Mailer { | ||
return provider.toMailer(apiKey) | ||
} | ||
@Serializable | ||
@SerialName(SMTP_SERIAL_NAME) | ||
data class SMTPMailConfig( | ||
override val id: String, | ||
override val sender: String, | ||
override val subjectTemplate: String, | ||
override val username: String, | ||
override val password: String, | ||
override val smtpHost: String, | ||
override val smtpPort: Int = 587 | ||
) : MailConfig, SMTPProvider() |
This file was deleted.
Oops, something went wrong.
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
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
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
61 changes: 61 additions & 0 deletions
61
src/main/kotlin/com/vandeas/service/impl/mailer/SMTPMailer.kt
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,61 @@ | ||
package com.vandeas.service.impl.mailer | ||
|
||
import com.vandeas.entities.Mail | ||
import com.vandeas.service.BatchResponse | ||
import com.vandeas.service.Mailer | ||
import com.vandeas.service.Response | ||
import java.util.* | ||
import javax.mail.Authenticator | ||
import javax.mail.Message | ||
import javax.mail.PasswordAuthentication | ||
import javax.mail.SendFailedException | ||
import javax.mail.Session | ||
import javax.mail.Transport | ||
import javax.mail.internet.InternetAddress | ||
import javax.mail.internet.MimeMessage | ||
|
||
class SMTPMailer( | ||
username: String, | ||
password: String, | ||
host: String, | ||
port: Int = 587 | ||
): Mailer { | ||
|
||
private val session: Session = Session.getInstance( | ||
Properties().apply { | ||
put("mail.smtp.host", host) | ||
put("mail.smtp.port", "$port") | ||
put("mail.smtp.auth", "true") | ||
put("mail.smtp.starttls.enable", "true") | ||
}, | ||
object : Authenticator() { | ||
override fun getPasswordAuthentication(): PasswordAuthentication { | ||
return PasswordAuthentication(username, password) | ||
} | ||
}, | ||
) | ||
|
||
override fun sendEmail(to: String, from: String, subject: String, content: String): Response { | ||
val message = MimeMessage(session).apply { | ||
setFrom(InternetAddress(from)) | ||
addRecipient(Message.RecipientType.TO, InternetAddress(to)) | ||
this.subject = subject | ||
setText(content) | ||
} | ||
return try { | ||
Transport.send(message) | ||
Response(200, "[$to] ok") | ||
} catch (e: SendFailedException) { | ||
Response(500, "[$to] ${e.message ?: "Unknown error"}") | ||
} | ||
} | ||
|
||
override suspend fun sendEmails(mails: List<Mail>) = mails.map { | ||
sendEmail(it.to, it.from, it.subject, it.content) | ||
}.let { responses -> | ||
BatchResponse( | ||
200.takeIf { responses.all { it.isSuccessful } } ?: 500, | ||
responses.map { it.body ?: "Unknown error"} | ||
) | ||
} | ||
} |