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_USERNAME=admin
|
||||||
export VANIKAM_email_PASSWORD=password
|
export VANIKAM_email_PASSWORD=password
|
||||||
export VANIKAM_email_SERVER_HOSTNAME=localhost:10025
|
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_FROM="Vanikam Info <info@vanikam.app>"
|
||||||
export VANIKAM_email_REPLY_TO="Vanikam Support <support@vanikam.app>"
|
export VANIKAM_email_REPLY_TO="Vanikam Support <support@vanikam.app>"
|
||||||
|
|
|
@ -6,9 +6,9 @@ steps:
|
||||||
environment:
|
environment:
|
||||||
- DATABASE_URL=postgres://postgres:password@database:5432/postgres
|
- DATABASE_URL=postgres://postgres:password@database:5432/postgres
|
||||||
- VANIKAM_email_URL=smtp://admin:password@email:10025
|
- VANIKAM_email_URL=smtp://admin:password@email:10025
|
||||||
- MAILDEV_URL=http://email:1080
|
|
||||||
- VANIKAM_meili_API_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
- VANIKAM_meili_API_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
||||||
- VANIKAM_meili_URL=http://meilisearch:7700
|
- VANIKAM_meili_URL=http://meilisearch:7700
|
||||||
|
- MAILPIT_URL=http://email:1080
|
||||||
commands:
|
commands:
|
||||||
# - curl -fsSL https://deb.nodesource.com/setup_16.x | bash - &&\
|
# - curl -fsSL https://deb.nodesource.com/setup_16.x | bash - &&\
|
||||||
# - apt update && apt-get -y --no-install-recommends install nodejs tar gpg curl wget
|
# - 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]
|
# secrets: [RELEASE_BOT_GPG_SIGNING_KEY, DUMBSERVE_PASSWORD, GPG_PASSWORD]
|
||||||
#
|
#
|
||||||
services:
|
services:
|
||||||
email:
|
# email:
|
||||||
image: axllent/mailpit
|
# image: axllent/mailpit
|
||||||
environment:
|
# environment:
|
||||||
- MP_SMTP_AUTH=admin:password
|
# - MP_SMTP_AUTH=admin:password
|
||||||
- MP_MAX_MESSAGES=5000
|
# - MP_MAX_MESSAGES=5000
|
||||||
- MP_SMTP_AUTH_ALLOW_INSECURE=1
|
# - MP_SMTP_AUTH_ALLOW_INSECURE=1
|
||||||
- MP_SMTP_BIND_ADDR=0.0.0.0:10025
|
# - MP_SMTP_BIND_ADDR=0.0.0.0:10025
|
||||||
- MP_SMTP_AUTH_ALLOW_INSECURE=true
|
# - MP_SMTP_AUTH_ALLOW_INSECURE=true
|
||||||
- MP_UI_BIND_ADDR=0.0.0.0:1080
|
# - MP_UI_BIND_ADDR=0.0.0.0:1080
|
||||||
|
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
@ -90,3 +90,11 @@ services:
|
||||||
environment:
|
environment:
|
||||||
- MEILI_ENV=development
|
- MEILI_ENV=development
|
||||||
- MEILI_MASTER_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
- 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]
|
[workspace]
|
||||||
exclude = ["utils/db-migrations"] #, "utils/cache-bust"]
|
exclude = ["utils/db-migrations"] #, "utils/cache-bust"]
|
||||||
memebers = ["."]
|
members = [".", "mailpit_client"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-identity = "0.7.1"
|
actix-identity = "0.7.1"
|
||||||
|
@ -40,3 +40,4 @@ validator = { version = "0.18.1", features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
reqwest = { version = "0.12.4", features = ["json"] }
|
reqwest = { version = "0.12.4", features = ["json"] }
|
||||||
|
mailpit_client = { path = "./mailpit_client" }
|
||||||
|
|
|
@ -3,16 +3,17 @@ version: "3"
|
||||||
services:
|
services:
|
||||||
email:
|
email:
|
||||||
image: axllent/mailpit
|
image: axllent/mailpit
|
||||||
ports:
|
restart: always
|
||||||
- 1080:1080
|
container_name: vanigam-dash-maildev
|
||||||
- 10025:10025
|
network_mode: host
|
||||||
environment:
|
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_BIND_ADDR=0.0.0.0:10025
|
||||||
- MP_SMTP_AUTH_ALLOW_INSECURE=true
|
|
||||||
- MP_UI_BIND_ADDR=0.0.0.0:1080
|
- 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:
|
postgres:
|
||||||
image: postgres:16.4
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use reqwest::Client;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use mailpit_client::*;
|
||||||
|
|
||||||
//#[derive(Deserialize, Clone)]
|
use serde::Deserialize;
|
||||||
//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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_mailer_account_validation_link() {
|
async fn test_mailer_account_validation_link() {
|
||||||
|
@ -113,38 +57,22 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let c = Client::default();
|
let mailpit_url =
|
||||||
|
std::env::var("MAILPIT_URL").expect("Please set mailpit instance URL in MAILPIT_URL");
|
||||||
let maildev_url =
|
let mc = MailPitHTTPClientBuilder::default()
|
||||||
std::env::var("MAILDEV_URL").expect("Please set maildev instance URL in MAILDEV_URL");
|
.url(Url::parse(&mailpit_url).unwrap())
|
||||||
|
.build()
|
||||||
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))
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
u.set_path(&format!("/api/v1/message/{}", mailpit_summary.id));
|
let mailpit_email = mc.get_email_addressed_to(email).await;
|
||||||
let mailpit_email: MailpitEmail =
|
|
||||||
c.get(u.clone()).send().await.unwrap().json().await.unwrap();
|
|
||||||
|
|
||||||
assert!(mailpit_email.text.contains(validation_secret));
|
assert!(mailpit_email.text().contains(validation_secret));
|
||||||
assert!(mailpit_email.text.contains(username));
|
assert!(mailpit_email.text().contains(username));
|
||||||
assert!(mailpit_email
|
assert!(mailpit_email
|
||||||
.to
|
.to()
|
||||||
.iter()
|
.iter()
|
||||||
.any(|t| t.address == email && t.name == username));
|
.any(|t| t.address() == email && t.name() == username));
|
||||||
|
|
||||||
u.set_path("/api/v1/messages");
|
mc.delete_email(mailpit_email).await;
|
||||||
let payload = MailpitDeleteEmail {
|
|
||||||
IDs: vec![mailpit_summary.id.clone()],
|
|
||||||
};
|
|
||||||
c.delete(u).json(&payload).send().await.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ name = "db-migrations"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
[workspace]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = "2.9.0"
|
actix-rt = "2.9.0"
|
||||||
|
|
Loading…
Reference in a new issue