mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-29 08:12:56 +00:00
feat: email triggers (#4163)
* feat: email triggers v0 * update docker compose to nginx with tcp reverse proxy + move smtp to private * fix: open source build * test: update ee ref for testing * feat: use caddy with layer4 * fix: nit * feat: configurable email domain * fix: nit * fix: nit * fix: get l4 from main * fix: default email domain to mail.domain * update ee ref
This commit is contained in:
@@ -1034,7 +1034,7 @@ pub struct ListableCompletedJob {
|
||||
pub labels: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
#[derive(Deserialize, Clone, Default)]
|
||||
pub struct RunJobQuery {
|
||||
scheduled_for: Option<chrono::DateTime<chrono::Utc>>,
|
||||
scheduled_in_secs: Option<i64>,
|
||||
@@ -2751,6 +2751,23 @@ pub async fn run_flow_by_path(
|
||||
Path((w_id, flow_path)): Path<(String, StripPath)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
args: PushArgsOwned,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
run_flow_by_path_inner(
|
||||
authed, db, user_db, rsmq, w_id, flow_path, run_query, args, None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn run_flow_by_path_inner(
|
||||
authed: ApiAuthed,
|
||||
db: DB,
|
||||
user_db: UserDB,
|
||||
rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
w_id: String,
|
||||
flow_path: StripPath,
|
||||
run_query: RunJobQuery,
|
||||
args: PushArgsOwned,
|
||||
label_prefix: Option<String>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
@@ -2784,7 +2801,9 @@ pub async fn run_flow_by_path(
|
||||
&w_id,
|
||||
JobPayload::Flow { path: flow_path.to_string(), dedicated_worker },
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
authed.display_username(),
|
||||
&label_prefix
|
||||
.map(|x| x + authed.display_username())
|
||||
.unwrap_or_else(|| authed.display_username().to_string()),
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
scheduled_for,
|
||||
@@ -2910,6 +2929,31 @@ pub async fn run_script_by_path(
|
||||
Path((w_id, script_path)): Path<(String, StripPath)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
args: PushArgsOwned,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
run_script_by_path_inner(
|
||||
authed,
|
||||
db,
|
||||
user_db,
|
||||
rsmq,
|
||||
w_id,
|
||||
script_path,
|
||||
run_query,
|
||||
args,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn run_script_by_path_inner(
|
||||
authed: ApiAuthed,
|
||||
db: DB,
|
||||
user_db: UserDB,
|
||||
rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
w_id: String,
|
||||
script_path: StripPath,
|
||||
run_query: RunJobQuery,
|
||||
args: PushArgsOwned,
|
||||
label_prefix: Option<String>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
@@ -2935,7 +2979,9 @@ pub async fn run_script_by_path(
|
||||
&w_id,
|
||||
job_payload,
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
authed.display_username(),
|
||||
&label_prefix
|
||||
.map(|x| x + authed.display_username())
|
||||
.unwrap_or_else(|| authed.display_username().to_string()),
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
scheduled_for,
|
||||
@@ -4352,6 +4398,31 @@ pub async fn run_job_by_hash(
|
||||
Path((w_id, script_hash)): Path<(String, ScriptHash)>,
|
||||
Query(run_query): Query<RunJobQuery>,
|
||||
args: PushArgsOwned,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
run_job_by_hash_inner(
|
||||
authed,
|
||||
db,
|
||||
user_db,
|
||||
rsmq,
|
||||
w_id,
|
||||
script_hash,
|
||||
run_query,
|
||||
args,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn run_job_by_hash_inner(
|
||||
authed: ApiAuthed,
|
||||
db: DB,
|
||||
user_db: UserDB,
|
||||
rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
w_id: String,
|
||||
script_hash: ScriptHash,
|
||||
run_query: RunJobQuery,
|
||||
args: PushArgsOwned,
|
||||
label_prefix: Option<String>,
|
||||
) -> error::Result<(StatusCode, String)> {
|
||||
#[cfg(feature = "enterprise")]
|
||||
check_license_key_valid().await?;
|
||||
@@ -4398,7 +4469,9 @@ pub async fn run_job_by_hash(
|
||||
priority,
|
||||
},
|
||||
PushArgs { args: &args.args, extra: args.extra },
|
||||
authed.display_username(),
|
||||
&label_prefix
|
||||
.map(|x| x + authed.display_username())
|
||||
.unwrap_or_else(|| authed.display_username().to_string()),
|
||||
&authed.email,
|
||||
username_to_permissioned_as(&authed.username),
|
||||
scheduled_for,
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::ee::ExternalJwks;
|
||||
#[cfg(feature = "embedding")]
|
||||
use crate::embeddings::load_embeddings_db;
|
||||
use crate::oauth2_ee::AllClients;
|
||||
use crate::smtp_server_ee::SmtpServer;
|
||||
use crate::tracing_init::MyOnFailure;
|
||||
use crate::{
|
||||
oauth2_ee::SlackVerifier,
|
||||
@@ -76,6 +77,7 @@ mod schedule;
|
||||
mod scim_ee;
|
||||
mod scripts;
|
||||
mod settings;
|
||||
pub mod smtp_server_ee;
|
||||
mod static_assets;
|
||||
mod stripe_ee;
|
||||
mod tracing_init;
|
||||
@@ -163,6 +165,7 @@ pub async fn run_server(
|
||||
mut rx: tokio::sync::broadcast::Receiver<()>,
|
||||
port_tx: tokio::sync::oneshot::Sender<String>,
|
||||
server_mode: bool,
|
||||
base_internal_url: String,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(mut rsmq) = rsmq.clone() {
|
||||
for tag in ALL_TAGS.read().await.iter() {
|
||||
@@ -194,8 +197,8 @@ pub async fn run_server(
|
||||
|
||||
let middleware_stack = ServiceBuilder::new()
|
||||
.layer(Extension(db.clone()))
|
||||
.layer(Extension(rsmq))
|
||||
.layer(Extension(user_db))
|
||||
.layer(Extension(rsmq.clone()))
|
||||
.layer(Extension(user_db.clone()))
|
||||
.layer(Extension(auth_cache.clone()))
|
||||
.layer(Extension(index_reader))
|
||||
.layer(Extension(index_writer))
|
||||
@@ -214,7 +217,18 @@ pub async fn run_server(
|
||||
|
||||
if server_mode {
|
||||
#[cfg(feature = "embedding")]
|
||||
load_embeddings_db(&db)
|
||||
load_embeddings_db(&db);
|
||||
|
||||
let smtp_server = Arc::new(SmtpServer {
|
||||
db: db.clone(),
|
||||
user_db: user_db,
|
||||
auth_cache: auth_cache.clone(),
|
||||
rsmq: rsmq,
|
||||
base_internal_url: base_internal_url.clone(),
|
||||
});
|
||||
if let Err(err) = smtp_server.start_listener_thread(addr).await {
|
||||
tracing::error!("Error starting SMTP server: {err:#}");
|
||||
}
|
||||
}
|
||||
|
||||
let job_helpers_service = {
|
||||
|
||||
@@ -24,7 +24,10 @@ use axum::{
|
||||
use serde::Deserialize;
|
||||
use windmill_common::{
|
||||
error::{self, JsonResult, Result},
|
||||
global_settings::{AUTOMATE_USERNAME_CREATION_SETTING, ENV_SETTINGS, HUB_BASE_URL_SETTING},
|
||||
global_settings::{
|
||||
AUTOMATE_USERNAME_CREATION_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS,
|
||||
HUB_BASE_URL_SETTING,
|
||||
},
|
||||
server::Smtp,
|
||||
utils::send_email,
|
||||
};
|
||||
@@ -256,6 +259,7 @@ pub async fn get_global_setting(
|
||||
&& !key.starts_with("default_recovery_handler_")
|
||||
&& key != AUTOMATE_USERNAME_CREATION_SETTING
|
||||
&& key != HUB_BASE_URL_SETTING
|
||||
&& key != EMAIL_DOMAIN_SETTING
|
||||
{
|
||||
require_super_admin(&db, &authed.email).await?;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
use crate::{db::DB, users::AuthCache};
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
use windmill_common::db::UserDB;
|
||||
|
||||
pub struct SmtpServer {
|
||||
pub auth_cache: Arc<AuthCache>,
|
||||
pub db: DB,
|
||||
pub user_db: UserDB,
|
||||
pub rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
pub base_internal_url: String,
|
||||
}
|
||||
|
||||
impl SmtpServer {
|
||||
pub async fn start_listener_thread(self: Arc<Self>, _addr: SocketAddr) -> anyhow::Result<()> {
|
||||
Err(anyhow::anyhow!("Implementation not open source"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user