From ea70c9c9ad266c6da5ee17d3ac74e306fb6766fc Mon Sep 17 00:00:00 2001 From: Aaron Leopold <36278431+aaronleopold@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:04:17 -0700 Subject: [PATCH] :bug: Fix `TemplateRegistrationFailed` error when sending email Should resolve #549, and added supporting documentation --- crates/email/src/lib.rs | 4 +- crates/email/src/template.rs | 35 +++++-- docs/pages/guides/basics/_meta.ts | 1 + docs/pages/guides/basics/webapp.md | 11 +++ .../guides/configuration/server-options.md | 8 ++ docs/pages/guides/desktop/index.mdx | 6 +- docs/pages/guides/features/_meta.ts | 1 + docs/pages/guides/features/email.mdx | 95 +++++++++++++++++++ 8 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 docs/pages/guides/basics/webapp.md create mode 100644 docs/pages/guides/features/email.mdx diff --git a/crates/email/src/lib.rs b/crates/email/src/lib.rs index 0d1c4891d..0f9fbf5fb 100644 --- a/crates/email/src/lib.rs +++ b/crates/email/src/lib.rs @@ -12,6 +12,8 @@ mod template; pub use emailer::{AttachmentPayload, EmailerClient, EmailerClientConfig}; pub use error::{EmailError, EmailResult}; -pub use template::{render_template, EmailTemplate}; +pub use template::{ + render_template, EmailTemplate, ATTACHMENT_TEMPLATE, BASE_TEMPLATE, TEMPLATES, +}; pub use lettre::message::header::ContentType as EmailContentType; diff --git a/crates/email/src/template.rs b/crates/email/src/template.rs index 3ae747f00..65d4c3b07 100644 --- a/crates/email/src/template.rs +++ b/crates/email/src/template.rs @@ -3,6 +3,12 @@ use std::path::PathBuf; use crate::EmailResult; use handlebars::Handlebars; +pub static BASE_TEMPLATE: &str = include_str!("../templates/base.hbs"); +pub static ATTACHMENT_TEMPLATE: &str = include_str!("../templates/attachment.hbs"); + +pub static TEMPLATES: &[(&str, &str)] = + &[("base", BASE_TEMPLATE), ("attachment", ATTACHMENT_TEMPLATE)]; + // TODO: expose this enumeration to the public API somehow, so that users can define their own template overrides pub enum EmailTemplate { @@ -19,6 +25,7 @@ impl AsRef for EmailTemplate { } /// Render a template to a string using the given data and templates directory. +/// If the template does not exist on disk, the default template will be used. /// /// # Example /// ```no_run @@ -40,15 +47,19 @@ pub fn render_template( ) -> EmailResult { let mut handlebars = Handlebars::new(); handlebars.register_partial("base_partial", "{{> base}}")?; - handlebars.register_template_file("base", templates_dir.join("base.hbs"))?; - handlebars - .register_template_file("attachment", templates_dir.join("attachment.hbs"))?; + + for (name, template) in TEMPLATES { + let override_template = templates_dir.join(format!("{}.hbs", name)); + if override_template.exists() { + handlebars.register_template_file(name, override_template)?; + } else { + handlebars.register_template_string(name, template)?; + } + } Ok(handlebars.render(template.as_ref(), data)?) } -// TODO: Write meaningful tests - #[cfg(test)] mod tests { use super::*; @@ -58,7 +69,7 @@ mod tests { } #[test] - fn render_template_attachment() { + fn render_template_attachment_from_disk() { let data = serde_json::json!({ "title": "Stump Attachment", }); @@ -69,4 +80,16 @@ mod tests { assert!(rendered.contains("Stump Attachment")); } + + #[test] + fn render_default_template_attachment() { + let data = serde_json::json!({ + "title": "Stump Attachment", + }); + + let rendered = + render_template(EmailTemplate::Attachment, &data, PathBuf::new()).unwrap(); + + assert!(rendered.contains("Stump Attachment")); + } } diff --git a/docs/pages/guides/basics/_meta.ts b/docs/pages/guides/basics/_meta.ts index 3fc2179e7..cfe828712 100644 --- a/docs/pages/guides/basics/_meta.ts +++ b/docs/pages/guides/basics/_meta.ts @@ -1,6 +1,7 @@ import { Meta } from 'nextra' export default { + webapp: 'App', books: 'Books', progress: 'Book Progress', readers: 'Book Readers', diff --git a/docs/pages/guides/basics/webapp.md b/docs/pages/guides/basics/webapp.md new file mode 100644 index 000000000..6a3b563e5 --- /dev/null +++ b/docs/pages/guides/basics/webapp.md @@ -0,0 +1,11 @@ +# Web App + +The web app is the default, browser-based interface for Stump. It is a single-page application (SPA) built with [React](https://react.dev/). + +## Access + +The web app is accessible by navigating the host machine's IP address and the configured port in a web browser. For example, if your Stump server is running on your local machine and the default port is used, you would navigate to `http://localhost:10801` in your browser. If you are attempting to access the web app from a different machine on the same network, you would navigate to `http://{machine_ip}:10801`. + +Accessing the web app from a different machine on a different network is a bit more complicated, and a bit beyond the scope of this guide. In the future, I'd love to have some curated tutorials for common setups (e.g., using a reverse proxy, setting up a VPN, etc.). I personally use Tailscale and Caddy, but there are many ways to accomplish this. + +If you have a specific setup you'd like to see a guide for, or are willing to share your setup with a proper how-to write-up, please reach out! diff --git a/docs/pages/guides/configuration/server-options.md b/docs/pages/guides/configuration/server-options.md index 3ede86878..c2cc86f97 100644 --- a/docs/pages/guides/configuration/server-options.md +++ b/docs/pages/guides/configuration/server-options.md @@ -22,6 +22,14 @@ The version of the Stump API to use. This should really be left alone and **not* This corresponds to the `api_version` configuration option in the `Stump.toml` file. +### EMAIL_TEMPLATES_DIR + +The directory where Stump will look for email templates. This is only required if you want to use custom email templates. By default, Stump will look for email templates in the `templates` directory in the root of the configuration directory. E.g., if your configuration directory is `~/.stump`, Stump will look for email templates in `~/.stump/templates`. + +| Type | Default Value | +| ------ | ------------------------ | +| String | `{config_dir}/templates` | + #### PDFIUM_PATH The path to the PDFium binary. This is only required if you want PDF support and you're running Stump outside of Docker, since the PDFium binary is included in the Docker image. You'll want to find and download the PDFium binary for your platform from [here](https://github.com/bblanchon/pdfium-binaries/releases), and then set this environment variable to the path of the binary. diff --git a/docs/pages/guides/desktop/index.mdx b/docs/pages/guides/desktop/index.mdx index 174bdd2f5..f98a501c8 100644 --- a/docs/pages/guides/desktop/index.mdx +++ b/docs/pages/guides/desktop/index.mdx @@ -3,8 +3,10 @@ import { Callout } from 'nextra/components' # Desktop App - This app is not yet available for download. I expect an initial **unstable** release to be - available towards the end of 2024. Expect bugs and missing features, but stay tuned for updates! + At the time of writing, the desktop app has a **nightly** release available on + [GitHub](https://github.com/stumpapp/stump/releases/tag/untagged-dcf8ed03bb6a19b142e0). It is not + stable, will contain bugs, and not feature complete. However if you decide to try it, please + report any issues you encounter! The very same app you use in your browser, but with added features: diff --git a/docs/pages/guides/features/_meta.ts b/docs/pages/guides/features/_meta.ts index bad49baad..f4b682cf8 100644 --- a/docs/pages/guides/features/_meta.ts +++ b/docs/pages/guides/features/_meta.ts @@ -3,6 +3,7 @@ import { Meta } from 'nextra' export default { 'api-keys': 'API Keys', 'book-clubs': 'Book Clubs', + email: 'Email', 'file-explorer': 'File Explorer', upload: 'File Uploads', 'smart-list': 'Smart Lists', diff --git a/docs/pages/guides/features/email.mdx b/docs/pages/guides/features/email.mdx new file mode 100644 index 000000000..070fff3ee --- /dev/null +++ b/docs/pages/guides/features/email.mdx @@ -0,0 +1,95 @@ +import { Callout } from 'nextra/components' + +# Emailing + + + Sending emails is gated behind the `email:send` user permission at a minumum. To learn more about + permissions, see the [permissions](/guides/access-control/permissions) guide. + + +Stump has basic email functionality built-in, allowing you to send books via email. This feature was primarily developed to support email-to-ereader integrations, however there are loose plans for expanding it in the future. + +## Emailers + +An emailer is essentially just a named configuration object that tells Stump how to send emails from a specific email account. Stump doesn't have its own email server or client, so it relies on SMTP to send emails. This means that you'll need to provide the necessary connection and auth details to send emails. + +### Configuration + +The following fields are required to configure an emailer: + +- `name` - The name of the emailer, used for display purposes throughout the app +- SMTP-specifics: + - `host` - The SMTP server hostname, e.g., `smtp.gmail.com` + - `port` - The SMTP server port, e.g., `587` + - `username` - The username for the SMTP server, e.g., an email address + - `password` - The password for the SMTP server, encrypted at rest + - `TLS enabled` - Whether to use a secure connection (e.g., `true` for SSL, `false` for no security) +- Sender information: + - `Display name` - The name that will be displayed to recipients + - `Email address` - The email address that will be displayed to recipients + + + The password field is encrypted at rest and not visible after creation. Stump will decrypt the + password when sending emails. This is a security measure to prevent unauthorized access to your + email server. You should still take care to secure your Stump instance. + + +You may also optionally configure the following fields: + +- Max attachment size - The maximum size of attachments that can be sent via this emailer + +## Device Aliases + +A device alias refers to a named email address that can be quickly selected when sending books via email. The name is perhaps misleading, since your email doesn't necessarily have to be associated with a device. This is subject to change. + +### Forbidden Aliases + +A device alias may also be "inverted" to create a forbidden email address. This is useful for blocking certain email addresses from receiving books via your server. This really only comes into play when considering arbitrary send permissions, since without that permission a user would have to select from a list of device aliases which _aren't_ forbidden. + +## Templates + +Stump uses [handlebars](https://handlebarsjs.com/) for email templating. The default templates are very basic, but you can override them with your own custom templates. The only requirement is that you ensure the template fields align with the fields Stump expects. + +The default templates can be found [on GitHub](https://github.com/stumpapp/stump/tree/main/crates/email/templates). + +### Template Overrides + +To override a template, simply create a new template with the same name in the template directory of your Stump instance. Stump will automatically use your custom template in place of the default. + +The template directory defaults to a `templates` directory in the root of your Stump configuration directory. You can change this by setting the `EMAIL_TEMPLATES_DIR` environment variable. For more information on configuring Stump, see the [configuration](/guides/configuration/server-options) guide. + +## Permissions + +The following table lists perissions which can be assigned to users to control access to email functionality: + +| Permission | Description | Notes / Inherited Permissions | +| ---------------- | ---------------------------------------------- | ----------------------------- | +| `emailer:read` | Allows the user to view registered emailers | | +| `emailer:create` | Allows the user to create new emailers | `emailer:read` | +| `emailer:manage` | Allows the user to manage existing emailers | `emailer:read` | +| `email:send` | Allows the user to send emails from the server | `emailer:read` | + +## Audit Logs + +Stump will log each email sent, however the logs are not currently exposed in the UI. This is a planned feature, but for now you can view the logs directly in the database with your preferred SQL client. + +The following fields are logged for each record: + +- The ID of the emailer used +- The email address the email was sent to +- A JSON blob containing metadata for the attachment +- The time the email was sent +- The user ID of the user who sent the email + +## Future Plans + +The email feature is not fully fleshed out yet, and there are plans to expand it in the future. Some of the planned features revolve around: + +- Server invitations via email, e.g., inviting users to join your server +- Email notifications for various server events +- Book club email notifications + + + If you have any ideas for expanding the email functionalities, feel free to open an issue on + [GitHub](https://github.com/stumpapp/stump/issues) +