Merge pull request 'feat&fix: replace maildev with mailpit and use util lib to interact with its HTTP API' (#99) from maildev-client into master
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Reviewed-on: #99
This commit is contained in:
commit
750849b287
9 changed files with 543 additions and 506 deletions
|
@ -4,6 +4,6 @@ export POSTGRES_DATABASE_URL="postgres://postgres:password@localhost:5432/postgr
|
|||
export VANIKAM_email_USERNAME=admin
|
||||
export VANIKAM_email_PASSWORD=password
|
||||
export VANIKAM_email_SERVER_HOSTNAME=localhost:10025
|
||||
export MAILDEV_URL=http://localhost:1080
|
||||
export MAILPIT_URL=http://localhost:1080
|
||||
export VANIKAM_email_FROM="Vanikam Info <info@vanikam.app>"
|
||||
export VANIKAM_email_REPLY_TO="Vanikam Support <support@vanikam.app>"
|
||||
|
|
|
@ -6,9 +6,9 @@ steps:
|
|||
environment:
|
||||
- DATABASE_URL=postgres://postgres:password@database:5432/postgres
|
||||
- VANIKAM_email_URL=smtp://admin:password@email:10025
|
||||
- MAILDEV_URL=http://email:1080
|
||||
- VANIKAM_meili_API_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
||||
- VANIKAM_meili_URL=http://meilisearch:7700
|
||||
- MAILPIT_URL=http://email:1080
|
||||
commands:
|
||||
# - curl -fsSL https://deb.nodesource.com/setup_16.x | bash - &&\
|
||||
# - apt update && apt-get -y --no-install-recommends install nodejs tar gpg curl wget
|
||||
|
@ -69,15 +69,15 @@ steps:
|
|||
# secrets: [RELEASE_BOT_GPG_SIGNING_KEY, DUMBSERVE_PASSWORD, GPG_PASSWORD]
|
||||
#
|
||||
services:
|
||||
email:
|
||||
image: axllent/mailpit
|
||||
environment:
|
||||
- MP_SMTP_AUTH=admin:password
|
||||
- MP_MAX_MESSAGES=5000
|
||||
- MP_SMTP_AUTH_ALLOW_INSECURE=1
|
||||
- MP_SMTP_BIND_ADDR=0.0.0.0:10025
|
||||
- MP_SMTP_AUTH_ALLOW_INSECURE=true
|
||||
- MP_UI_BIND_ADDR=0.0.0.0:1080
|
||||
# email:
|
||||
# image: axllent/mailpit
|
||||
# environment:
|
||||
# - MP_SMTP_AUTH=admin:password
|
||||
# - MP_MAX_MESSAGES=5000
|
||||
# - MP_SMTP_AUTH_ALLOW_INSECURE=1
|
||||
# - MP_SMTP_BIND_ADDR=0.0.0.0:10025
|
||||
# - MP_SMTP_AUTH_ALLOW_INSECURE=true
|
||||
# - MP_UI_BIND_ADDR=0.0.0.0:1080
|
||||
|
||||
|
||||
database:
|
||||
|
@ -90,3 +90,11 @@ services:
|
|||
environment:
|
||||
- MEILI_ENV=development
|
||||
- MEILI_MASTER_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
||||
|
||||
email:
|
||||
image: axllent/mailpit
|
||||
environment:
|
||||
- MP_SMTP_BIND_ADDR=0.0.0.0:10025
|
||||
- MP_UI_BIND_ADDR=0.0.0.0:1080
|
||||
- MP_SMTP_AUTH_ACCEPT_ANY=true
|
||||
- MP_SMTP_AUTH_ALLOW_INSECURE=true
|
||||
|
|
796
Cargo.lock
generated
796
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
|
||||
[workspace]
|
||||
exclude = ["utils/db-migrations"] #, "utils/cache-bust"]
|
||||
memebers = ["."]
|
||||
members = [".", "mailpit_client"]
|
||||
|
||||
[dependencies]
|
||||
actix-identity = "0.7.1"
|
||||
|
@ -40,3 +40,4 @@ validator = { version = "0.18.1", features = ["derive"] }
|
|||
|
||||
[dev-dependencies]
|
||||
reqwest = { version = "0.12.4", features = ["json"] }
|
||||
mailpit_client = { path = "./mailpit_client" }
|
||||
|
|
|
@ -3,16 +3,17 @@ version: "3"
|
|||
services:
|
||||
email:
|
||||
image: axllent/mailpit
|
||||
ports:
|
||||
- 1080:1080
|
||||
- 10025:10025
|
||||
restart: always
|
||||
container_name: vanigam-dash-maildev
|
||||
network_mode: host
|
||||
environment:
|
||||
- MP_SMTP_AUTH=admin:password
|
||||
- MP_MAX_MESSAGES=5000
|
||||
- MP_SMTP_AUTH_ALLOW_INSECURE=1
|
||||
- MP_SMTP_BIND_ADDR=0.0.0.0:10025
|
||||
- MP_SMTP_AUTH_ALLOW_INSECURE=true
|
||||
- MP_UI_BIND_ADDR=0.0.0.0:1080
|
||||
- MP_SMTP_AUTH_ACCEPT_ANY=true
|
||||
- MP_SMTP_AUTH_ALLOW_INSECURE=true
|
||||
# - MAILDEV_SMTP_PORT=10025
|
||||
# - MAILDEV_INCOMING_USER=admin
|
||||
# - MAILDEV_INCOMING_PASS=password
|
||||
|
||||
postgres:
|
||||
image: postgres:16.4
|
||||
|
|
15
mailpit_client/Cargo.toml
Normal file
15
mailpit_client/Cargo.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "mailpit_client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.12.4", features = ["json"] }
|
||||
serde = { version = "1.0.201", features = ["derive"] }
|
||||
serde_json = "1.0.117"
|
||||
actix-rt = "2.9.0"
|
||||
derive-getters = "0.4.0"
|
||||
derive_more = "0.99.17"
|
||||
log = "0.4.21"
|
||||
derive_builder = "0.20.0"
|
||||
url = { version = "2.5.0", features = ["serde"] }
|
89
mailpit_client/src/lib.rs
Normal file
89
mailpit_client/src/lib.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
#[allow(unused_imports)]
|
||||
use log::*;
|
||||
#[allow(unused_imports)]
|
||||
#[cfg(test)]
|
||||
use println as info;
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Getters, Builder)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct MailPitAddress {
|
||||
address: String,
|
||||
name: String,
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Getters, Builder)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct MailPitEmail {
|
||||
#[serde(rename = "ID")]
|
||||
id: String,
|
||||
from: MailPitAddress,
|
||||
to: Vec<MailPitAddress>,
|
||||
subject: String,
|
||||
#[serde(rename = "Snippet")]
|
||||
text: String,
|
||||
html: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Getters, Builder)]
|
||||
pub struct MailPitHTTPClient {
|
||||
#[builder(default = "Client::default()")]
|
||||
client: Client,
|
||||
url: Url,
|
||||
}
|
||||
|
||||
impl MailPitHTTPClient {
|
||||
pub async fn list_emails(&self) -> Vec<MailPitEmail> {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
struct List {
|
||||
messages: Vec<MailPitEmail>,
|
||||
}
|
||||
|
||||
let mut u = self.url.clone();
|
||||
u.set_path("/api/v1/messages");
|
||||
|
||||
info!("trying to fetch emails: {}", u.as_str());
|
||||
|
||||
let list: List = self
|
||||
.client
|
||||
.get(u.clone())
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
list.messages
|
||||
}
|
||||
|
||||
pub async fn get_email_addressed_to(&self, email_address: &str) -> MailPitEmail {
|
||||
self.list_emails()
|
||||
.await
|
||||
.drain(0..)
|
||||
.find(|e| e.to.iter().any(|f| f.address == email_address))
|
||||
.unwrap()
|
||||
}
|
||||
pub async fn delete_email(&self, email: MailPitEmail) {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
|
||||
struct DeletePayload {
|
||||
#[serde(rename = "IDs")]
|
||||
id: Vec<String>,
|
||||
}
|
||||
|
||||
let mut u = self.url.clone();
|
||||
u.set_path("/api/v1/messages/");
|
||||
|
||||
info!("Deleting email from: {:?}", email.from);
|
||||
|
||||
let payload = DeletePayload { id: vec![email.id] };
|
||||
|
||||
self.client.delete(u).json(&payload).send().await.unwrap();
|
||||
}
|
||||
}
|
|
@ -38,67 +38,11 @@ impl AccountValidationLinkOutMailerPort for LettreMailer {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use reqwest::Client;
|
||||
use url::Url;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use mailpit_client::*;
|
||||
|
||||
//#[derive(Deserialize, Clone)]
|
||||
//struct MaildevAddress {
|
||||
// address: String,
|
||||
// name: String,
|
||||
//}
|
||||
//#[derive(Deserialize, Clone)]
|
||||
//struct MaildevEmail {
|
||||
// id: String,
|
||||
// from: Vec<MaildevAddress>,
|
||||
// to: Vec<MaildevAddress>,
|
||||
// subject: String,
|
||||
// text: String,
|
||||
// html: Option<String>,
|
||||
//}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct MailPitAddress {
|
||||
#[serde(rename = "Address")]
|
||||
address: String,
|
||||
#[serde(rename = "Name")]
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct MailpitSummary {
|
||||
#[serde(rename = "ID")]
|
||||
id: String,
|
||||
#[serde(rename = "To")]
|
||||
to: Vec<MailPitAddress>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct MailpitEmail {
|
||||
#[serde(rename = "From")]
|
||||
from: MailPitAddress,
|
||||
#[serde(rename = "To")]
|
||||
to: Vec<MailPitAddress>,
|
||||
#[serde(rename = "ReplyTo")]
|
||||
reply_to: Vec<MailPitAddress>,
|
||||
#[serde(rename = "Subject")]
|
||||
subject: String,
|
||||
#[serde(rename = "Text")]
|
||||
text: String,
|
||||
#[serde(rename = "HTML")]
|
||||
html: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct MailpitListEmails {
|
||||
messages: Vec<MailpitSummary>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct MailpitDeleteEmail {
|
||||
IDs: Vec<String>,
|
||||
}
|
||||
use serde::Deserialize;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_mailer_account_validation_link() {
|
||||
|
@ -113,38 +57,22 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let c = Client::default();
|
||||
|
||||
let maildev_url =
|
||||
std::env::var("MAILDEV_URL").expect("Please set maildev instance URL in MAILDEV_URL");
|
||||
|
||||
let mut u = Url::parse(&maildev_url).unwrap();
|
||||
|
||||
u.set_path("/api/v1/messages");
|
||||
let mailpit_emails: MailpitListEmails =
|
||||
c.get(u.clone()).send().await.unwrap().json().await.unwrap();
|
||||
|
||||
let mailpit_summary = mailpit_emails
|
||||
.messages
|
||||
.iter()
|
||||
.find(|e| e.to.iter().any(|f| f.address == email))
|
||||
let mailpit_url =
|
||||
std::env::var("MAILPIT_URL").expect("Please set mailpit instance URL in MAILPIT_URL");
|
||||
let mc = MailPitHTTPClientBuilder::default()
|
||||
.url(Url::parse(&mailpit_url).unwrap())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
u.set_path(&format!("/api/v1/message/{}", mailpit_summary.id));
|
||||
let mailpit_email: MailpitEmail =
|
||||
c.get(u.clone()).send().await.unwrap().json().await.unwrap();
|
||||
let mailpit_email = mc.get_email_addressed_to(email).await;
|
||||
|
||||
assert!(mailpit_email.text.contains(validation_secret));
|
||||
assert!(mailpit_email.text.contains(username));
|
||||
assert!(mailpit_email.text().contains(validation_secret));
|
||||
assert!(mailpit_email.text().contains(username));
|
||||
assert!(mailpit_email
|
||||
.to
|
||||
.to()
|
||||
.iter()
|
||||
.any(|t| t.address == email && t.name == username));
|
||||
.any(|t| t.address() == email && t.name() == username));
|
||||
|
||||
u.set_path("/api/v1/messages");
|
||||
let payload = MailpitDeleteEmail {
|
||||
IDs: vec![mailpit_summary.id.clone()],
|
||||
};
|
||||
c.delete(u).json(&payload).send().await.unwrap();
|
||||
mc.delete_email(mailpit_email).await;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ name = "db-migrations"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[workspace]
|
||||
|
||||
|
||||
[dependencies]
|
||||
actix-rt = "2.9.0"
|
||||
|
|
Loading…
Reference in a new issue