use super::{Edits, MODULES_MARKER, ROUTES_MARKER, insert_after_marker, mailer::DEV_ROUTES_EMPTY};
use crate::{CliResult, output::CliError, project::Project};
const LOCAL_ENDPOINT: &str = "http://localhost:8787/cdn-cgi/local/email";
const MAILBOX_RS: &str = r#"//! Incoming email. Generated by `ocre g mailbox`.
//!
//! Cloudflare Email Routing hands email sent to your domain to this Worker
//! (dashboard: Email Routing > Routing rules > action "Send to a Worker");
//! `email` in src/lib.rs calls `receive` once per message. Try it while
//! `ocre dev` runs with the form at http://localhost:8787/ocre/dev/mailbox,
//! or with curl (the message needs a Message-ID header):
//!
//! ```text
//! curl 'http://localhost:8787/cdn-cgi/local/email?from=ada@example.com&to=support@example.com' \
//! --data-binary $'From: ada@example.com\r\nTo: support@example.com\r\nSubject: Hi\r\nMessage-ID: <1@example.com>\r\n\r\nHello'
//! ```
use ocre::{Ctx, Result, mail::InboundEmail};
/// Handles one email; route it by recipient, like Rails' `routing`:
/// `match email.to() { "support@example.com" => ..., to if to.starts_with("reply+") => ..., _ => ... }`.
/// Read it with `email.from()`, `email.subject()`, `email.text()`,
/// `email.html()`, `email.header("Message-ID")`, `email.attachments()` and
/// `email.raw()`; store it with `ctx.db()?`; pass it on with
/// `email.forward("you@example.com").await?` (a verified destination
/// address); bounce it with `email.reject("Unknown address")`. Returning an
/// `Err` bounces it too.
pub async fn receive(_ctx: Ctx, _email: InboundEmail) -> Result<()> {
Ok(())
}
"#;
const EMAIL_EVENT: &str = r#"
/// Incoming email from Cloudflare Email Routing, handled in src/mailbox.rs.
#[worker::event(email)]
async fn email(message: worker::ForwardableEmailMessage, env: worker::Env, _ctx: worker::Context) -> worker::Result<()> {
ocre::mail::receive(message, env, mailbox::receive).await
}
"#;
pub fn mailbox(project: &Project) -> CliResult {
let mut edits = Edits::new(project);
edits.create("src/mailbox.rs", MAILBOX_RS.to_owned())?;
let lib = edits.read("src/lib.rs")?.unwrap_or_default();
if lib.contains("event(email)") {
return Err(CliError::new("src/lib.rs already handles the `email` event").hint(
"a Worker has one email entry point: call `ocre::mail::receive(message, env, mailbox::receive)` from it",
));
}
let lib = insert_after_marker(&lib, MODULES_MARKER, "mod mailbox;").ok_or_else(|| {
CliError::new("src/lib.rs is missing the `// ocre:modules` marker")
.hint("put `// ocre:modules` on its own line where `mod` declarations go")
})?;
let lib = if lib.contains("ocre::mail::dev_routes(") {
lib
} else {
insert_after_marker(&lib, ROUTES_MARKER, DEV_ROUTES_EMPTY).ok_or_else(|| {
CliError::new("src/lib.rs is missing the `// ocre:routes` marker")
.hint("put `// ocre:routes` on its own line at the end of the router in routes()")
})?
};
edits.update("src/lib.rs", lib + EMAIL_EVENT);
let mut report = edits.apply("generate mailbox")?;
report.next = vec![
"ocre dev".to_owned(),
"open http://localhost:8787/ocre/dev/mailbox to deliver a test email".to_owned(),
format!("or: curl '{LOCAL_ENDPOINT}?from=ada@example.com&to=support@example.com' --data-binary @message.eml"),
"route addresses to the Worker: Cloudflare dashboard > Email Routing > Routing rules > Send to a Worker"
.to_owned(),
];
Ok(report)
}