mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
split better oss and ee
This commit is contained in:
+3
-3
@@ -160,7 +160,7 @@ hex = "^0"
|
||||
sql-builder = "^3"
|
||||
argon2 = "^0"
|
||||
quick_cache = "^0"
|
||||
rand = "0.8.5"
|
||||
rand = "^0"
|
||||
rand_core = { version = "^0", features = ["std"] }
|
||||
magic-crypt = "^3"
|
||||
git-version = "^0"
|
||||
@@ -172,7 +172,7 @@ urlencoding = "^2"
|
||||
url = "^2"
|
||||
async-oauth2 = "^0"
|
||||
reqwest = { version = "^0.12", features = ["json", "stream", "gzip"] }
|
||||
time = "0.3.16"
|
||||
time = "^0"
|
||||
serde_urlencoded = "^0"
|
||||
tokio-tar = "^0"
|
||||
tempfile = "^3"
|
||||
@@ -198,7 +198,7 @@ swc_ecma_visit = "=0.104.8"
|
||||
|
||||
async-recursion = "^1"
|
||||
|
||||
base64 = "0.21.0"
|
||||
base64 = "^0"
|
||||
base32 = "^0"
|
||||
hmac = "0.12.1"
|
||||
sha2 = "0.10.6"
|
||||
|
||||
@@ -1 +1 @@
|
||||
150c3176f2c192366f2e8298ef6ece43780b529e
|
||||
f136a2f499e0fe7c10c54c79488851980d796eb2
|
||||
@@ -84,12 +84,14 @@ mod stripe_ee;
|
||||
mod tracing_init;
|
||||
mod triggers;
|
||||
mod users;
|
||||
mod users_ee;
|
||||
mod utils;
|
||||
mod variables;
|
||||
mod webhook_util;
|
||||
mod websocket_triggers;
|
||||
mod workers;
|
||||
mod workspaces;
|
||||
mod workspaces_ee;
|
||||
|
||||
pub const GIT_VERSION: &str =
|
||||
git_version!(args = ["--tag", "--always"], fallback = "unknown-version");
|
||||
|
||||
@@ -28,13 +28,13 @@ use serde::Deserialize;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use windmill_common::ee::{send_critical_alert, CriticalAlertKind, CriticalErrorChannel};
|
||||
use windmill_common::{
|
||||
email_ee::send_email,
|
||||
error::{self, JsonResult, Result},
|
||||
global_settings::{
|
||||
AUTOMATE_USERNAME_CREATION_SETTING, EMAIL_DOMAIN_SETTING, ENV_SETTINGS,
|
||||
HUB_ACCESSIBLE_URL_SETTING, HUB_BASE_URL_SETTING,
|
||||
},
|
||||
server::Smtp,
|
||||
utils::send_email,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
|
||||
use crate::users::{EditPassword, NewUser};
|
||||
use crate::{db::DB, webhook_util::WebhookShared};
|
||||
use argon2::Argon2;
|
||||
|
||||
use http::StatusCode;
|
||||
|
||||
use windmill_common::error::{Error, Result};
|
||||
|
||||
pub async fn create_user(
|
||||
_authed: ApiAuthed,
|
||||
_db: DB,
|
||||
_webhook: WebhookShared,
|
||||
_argon2: Arc<Argon2<'_>>,
|
||||
_rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
mut _nu: NewUser,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
Err(Error::InternalErr(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn set_password(
|
||||
_db: DB,
|
||||
_argon2: Arc<Argon2<'_>>,
|
||||
_authed: ApiAuthed,
|
||||
_ep: EditPassword,
|
||||
) -> Result<String> {
|
||||
Err(Error::InternalErr(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn send_email_if_possible(_subject: &str, _content: &str, _to: &str) {
|
||||
tracing::warn!(
|
||||
"send_email_if_possible is not implemented in Windmill's Open Source repository"
|
||||
);
|
||||
}
|
||||
@@ -155,25 +155,6 @@ pub async fn get_instance_username_or_create_pending<'c>(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_and_delete_pending_username_or_generate<'c>(
|
||||
tx: &mut Transaction<'c, Postgres>,
|
||||
email: &str,
|
||||
) -> error::Result<String> {
|
||||
let username = sqlx::query_scalar!("SELECT username FROM pending_user WHERE email = $1", email)
|
||||
.fetch_optional(&mut **tx)
|
||||
.await?;
|
||||
|
||||
if let Some(username) = username {
|
||||
sqlx::query!("DELETE FROM pending_user WHERE email = $1", email)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
Ok(username)
|
||||
} else {
|
||||
let username = generate_instance_wide_unique_username(&mut *tx, email).await?;
|
||||
Ok(username)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn content_plain(body: Body) -> Response {
|
||||
use axum::http::header;
|
||||
Response::builder()
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::db::ApiAuthed;
|
||||
use crate::utils::{get_instance_username_or_create_pending, INVALID_USERNAME_CHARS};
|
||||
use crate::users_ee::send_email_if_possible;
|
||||
use crate::utils::get_instance_username_or_create_pending;
|
||||
use crate::BASE_URL;
|
||||
use crate::{
|
||||
apps::AppWithLastVersion,
|
||||
db::DB,
|
||||
folders::Folder,
|
||||
resources::{Resource, ResourceType},
|
||||
users::{send_email_if_possible, WorkspaceInvite, VALID_USERNAME},
|
||||
users::{WorkspaceInvite, VALID_USERNAME},
|
||||
utils::require_super_admin,
|
||||
webhook_util::WebhookShared,
|
||||
};
|
||||
@@ -34,7 +35,7 @@ use itertools::Itertools;
|
||||
use regex::Regex;
|
||||
|
||||
use uuid::Uuid;
|
||||
use windmill_audit::audit_ee::{audit_log, AuditAuthor, AuditAuthorable};
|
||||
use windmill_audit::audit_ee::audit_log;
|
||||
use windmill_audit::ActionKind;
|
||||
use windmill_common::db::UserDB;
|
||||
use windmill_common::s3_helpers::LargeFileStorage;
|
||||
@@ -218,11 +219,12 @@ struct EditDeployTo {
|
||||
deploy_to: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
struct EditAutoInvite {
|
||||
operator: Option<bool>,
|
||||
invite_all: Option<bool>,
|
||||
auto_add: Option<bool>,
|
||||
pub struct EditAutoInvite {
|
||||
pub operator: Option<bool>,
|
||||
pub invite_all: Option<bool>,
|
||||
pub auto_add: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -573,250 +575,21 @@ async fn edit_deploy_to() -> Result<String> {
|
||||
));
|
||||
}
|
||||
|
||||
const BANNED_DOMAINS: &str = include_str!("../banned_domains.txt");
|
||||
pub const BANNED_DOMAINS: &str = include_str!("../banned_domains.txt");
|
||||
|
||||
async fn is_allowed_auto_domain(ApiAuthed { email, .. }: ApiAuthed) -> JsonResult<bool> {
|
||||
let domain = email.split('@').last().unwrap();
|
||||
return Ok(Json(!BANNED_DOMAINS.contains(domain)));
|
||||
}
|
||||
|
||||
async fn auto_add_user(
|
||||
email: &str,
|
||||
w_id: &str,
|
||||
operator: &bool,
|
||||
tx: &mut Transaction<'_, Postgres>,
|
||||
authorable: &impl AuditAuthorable,
|
||||
) -> Result<String> {
|
||||
let automate_username_creation = sqlx::query_scalar!(
|
||||
"SELECT value FROM global_settings WHERE name = $1",
|
||||
AUTOMATE_USERNAME_CREATION_SETTING,
|
||||
)
|
||||
.fetch_optional(&mut **tx)
|
||||
.await?
|
||||
.map(|v| v.as_bool())
|
||||
.flatten()
|
||||
.unwrap_or(false);
|
||||
|
||||
let username = if automate_username_creation {
|
||||
get_instance_username_or_create_pending(&mut *tx, &email).await?
|
||||
} else {
|
||||
let mut username = email
|
||||
.split('@')
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.replace(".", "");
|
||||
|
||||
username = INVALID_USERNAME_CHARS
|
||||
.replace_all(&mut username, "")
|
||||
.to_string();
|
||||
|
||||
if username.is_empty() {
|
||||
username = "user".to_string()
|
||||
}
|
||||
|
||||
let base_username = username.clone();
|
||||
let mut username_conflict = true;
|
||||
let mut i = 1;
|
||||
while username_conflict {
|
||||
if i > 1000 {
|
||||
return Err(Error::InternalErr(format!(
|
||||
"too many username conflicts for {}",
|
||||
email
|
||||
)));
|
||||
}
|
||||
if i > 1 {
|
||||
username = format!("{}{}", base_username, i)
|
||||
}
|
||||
username_conflict = sqlx::query_scalar!(
|
||||
"SELECT EXISTS(SELECT 1 FROM usr WHERE username = $1 AND workspace_id = $2)",
|
||||
&username,
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&mut **tx)
|
||||
.await?
|
||||
.unwrap_or(false);
|
||||
i += 1;
|
||||
}
|
||||
username
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO usr (workspace_id, username, email, is_admin, operator) VALUES ($1, $2, $3, false, $4) ON CONFLICT DO NOTHING",
|
||||
&w_id,
|
||||
&username,
|
||||
&email,
|
||||
&operator
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query_as!(
|
||||
Group,
|
||||
"INSERT INTO usr_to_group (workspace_id, usr, group_) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING",
|
||||
&w_id,
|
||||
username,
|
||||
"all",
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
let audit_author = if authorable.username() == authorable.email() && authorable.email() == email
|
||||
{
|
||||
// if the user is auto adding themselves (e.g. by joining the instance), we use their newly created workspace username for audit logs
|
||||
AuditAuthor {
|
||||
username: username.clone(),
|
||||
email: email.to_string(),
|
||||
username_override: None,
|
||||
}
|
||||
} else {
|
||||
AuditAuthor {
|
||||
username: authorable.username().to_string(),
|
||||
email: authorable.email().to_string(),
|
||||
username_override: authorable.username_override().map(|x| x.to_string()),
|
||||
}
|
||||
};
|
||||
audit_log(
|
||||
&mut **tx,
|
||||
&audit_author,
|
||||
"users.auto_invite_add",
|
||||
ActionKind::Create,
|
||||
&w_id,
|
||||
Some(email),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
Ok(username)
|
||||
}
|
||||
|
||||
async fn edit_auto_invite(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(rsmq): Extension<Option<rsmq_async::MultiplexedRsmq>>,
|
||||
Path(w_id): Path<String>,
|
||||
ApiAuthed { is_admin, email, username, .. }: ApiAuthed,
|
||||
Json(ea): Json<EditAutoInvite>,
|
||||
) -> Result<String> {
|
||||
require_admin(is_admin, &username)?;
|
||||
|
||||
// #[cfg(not(feature = "enterprise"))]
|
||||
// {
|
||||
// return Err(Error::BadRequest(
|
||||
// "Auto-invite is only available on enterprise".to_string(),
|
||||
// ));
|
||||
// }
|
||||
|
||||
let domain = if ea.invite_all.is_some_and(|x| x) {
|
||||
if *CLOUD_HOSTED {
|
||||
return Err(Error::BadRequest(
|
||||
"invite_all is only available locally".to_string(),
|
||||
));
|
||||
} else {
|
||||
"*"
|
||||
}
|
||||
} else {
|
||||
email.split('@').last().unwrap()
|
||||
};
|
||||
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
let mut users_to_auto_add = Option::None;
|
||||
|
||||
if let (Some(operator), Some(auto_add)) = (ea.operator, ea.auto_add) {
|
||||
if BANNED_DOMAINS.contains(domain) {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"Domain {} is not allowed",
|
||||
domain
|
||||
)));
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE workspace_settings SET auto_invite_domain = $1, auto_invite_operator = $2, auto_add = $4 WHERE workspace_id = $3",
|
||||
domain,
|
||||
operator,
|
||||
&w_id,
|
||||
auto_add,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
if auto_add {
|
||||
users_to_auto_add = Some(sqlx::query!(
|
||||
"SELECT email FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%', $2::text)) AND NOT EXISTS (
|
||||
SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email
|
||||
)",
|
||||
&w_id,
|
||||
domain
|
||||
)
|
||||
.fetch_all(&mut *tx).await?);
|
||||
|
||||
for user in users_to_auto_add.as_ref().unwrap() {
|
||||
auto_add_user(&user.email, &w_id, &operator, &mut tx, &authed).await?;
|
||||
send_email_if_possible(
|
||||
&format!("Added to Windmill's workspace: {w_id}"),
|
||||
&format!(
|
||||
"You have been granted access to Windmill's workspace {w_id} by {email}.
|
||||
|
||||
Access the workspace at {}/?workspace={w_id}",
|
||||
BASE_URL.read().await.clone()
|
||||
),
|
||||
&user.email,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"INSERT INTO workspace_invite
|
||||
(workspace_id, email, is_admin, operator)
|
||||
SELECT $1::text, email, false, $3 FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%', $2::text)) AND NOT EXISTS (
|
||||
SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email
|
||||
)
|
||||
ON CONFLICT DO NOTHING",
|
||||
&w_id,
|
||||
domain,
|
||||
operator
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"UPDATE workspace_settings SET auto_invite_domain = NULL, auto_invite_operator = NULL, auto_add = NULL WHERE workspace_id = $1",
|
||||
&w_id,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"workspaces.edit_auto_invite_domain",
|
||||
ActionKind::Update,
|
||||
&w_id,
|
||||
Some(&authed.email),
|
||||
Some([("operator", &format!("{:?}", ea.operator)[..])].into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
if let Some(users) = users_to_auto_add {
|
||||
for user in users {
|
||||
handle_deployment_metadata(
|
||||
&email,
|
||||
&username,
|
||||
&db,
|
||||
&w_id,
|
||||
windmill_git_sync::DeployedObject::User { email: user.email.clone() },
|
||||
Some(format!("Auto-added user '{}' to workspace", &user.email)),
|
||||
rsmq.clone(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(format!(
|
||||
"Edit auto-invite for workspace {} to {}",
|
||||
&w_id, domain
|
||||
))
|
||||
crate::workspaces_ee::edit_auto_invite(authed, db, rsmq, w_id, ea).await
|
||||
}
|
||||
|
||||
async fn edit_webhook(
|
||||
@@ -2001,61 +1774,6 @@ async fn delete_workspace(
|
||||
Ok(format!("Deleted workspace {}", &w_id))
|
||||
}
|
||||
|
||||
pub async fn invite_user_to_all_auto_invite_worspaces(
|
||||
db: &DB,
|
||||
email: &str,
|
||||
rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
authorable: &impl AuditAuthorable,
|
||||
) -> Result<()> {
|
||||
let mut tx = db.begin().await?;
|
||||
let domain = email.split('@').last().unwrap();
|
||||
let workspaces = sqlx::query!(
|
||||
"SELECT workspace_id, auto_invite_operator, auto_add FROM workspace_settings ws WHERE (auto_invite_domain = $1 OR auto_invite_domain = '*') AND NOT EXISTS (SELECT 1 FROM usr WHERE workspace_id = ws.workspace_id AND email = $2)",
|
||||
domain,
|
||||
email
|
||||
)
|
||||
.fetch_all(&mut *tx)
|
||||
.await?;
|
||||
let mut auto_added_workspace_usernames: Vec<(String, String)> = vec![];
|
||||
for r in workspaces {
|
||||
if r.auto_add.is_some() && r.auto_add.unwrap() {
|
||||
let operator = r.auto_invite_operator.unwrap_or(false);
|
||||
let username =
|
||||
auto_add_user(email, &r.workspace_id, &operator, &mut tx, authorable).await?;
|
||||
auto_added_workspace_usernames.push((r.workspace_id, username));
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"INSERT INTO workspace_invite
|
||||
(workspace_id, email, is_admin, operator)
|
||||
VALUES ($1, $2, false, $3)
|
||||
ON CONFLICT DO NOTHING",
|
||||
r.workspace_id,
|
||||
email,
|
||||
r.auto_invite_operator
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
for workspace_username_tuple in auto_added_workspace_usernames {
|
||||
let (w_id, username) = workspace_username_tuple;
|
||||
handle_deployment_metadata(
|
||||
&email,
|
||||
&username,
|
||||
db,
|
||||
&w_id,
|
||||
windmill_git_sync::DeployedObject::User { email: email.to_string() },
|
||||
Some(format!("Auto-added user '{}' to workspace", email)),
|
||||
rsmq.clone(),
|
||||
true,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn invite_user(
|
||||
ApiAuthed { username, is_admin, .. }: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::{
|
||||
db::{ApiAuthed, DB},
|
||||
workspaces::EditAutoInvite,
|
||||
};
|
||||
|
||||
pub async fn edit_auto_invite(
|
||||
_authed: ApiAuthed,
|
||||
_db: DB,
|
||||
_rsmq: Option<rsmq_async::MultiplexedRsmq>,
|
||||
_w_id: String,
|
||||
_ea: EditAutoInvite,
|
||||
) -> windmill_common::error::Result<String> {
|
||||
Err(windmill_common::error::Error::InternalErr(
|
||||
"Not implemented on OSS".to_string(),
|
||||
))
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::db::DB;
|
||||
use crate::ee::LicensePlan::Community;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::error;
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref LICENSE_KEY_VALID: Arc<RwLock<bool>> = Arc::new(RwLock::new(true));
|
||||
pub static ref LICENSE_KEY_ID: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
pub static ref LICENSE_KEY: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
}
|
||||
|
||||
pub enum LicensePlan {
|
||||
Community,
|
||||
Pro,
|
||||
Enterprise,
|
||||
}
|
||||
|
||||
pub async fn get_license_plan() -> LicensePlan {
|
||||
// Implementation is not open source
|
||||
return Community;
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CriticalErrorChannel {}
|
||||
|
||||
pub enum CriticalAlertKind {
|
||||
#[cfg(feature = "enterprise")]
|
||||
CriticalError,
|
||||
#[cfg(feature = "enterprise")]
|
||||
RecoveredCriticalError,
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn send_critical_alert(
|
||||
_error_message: String,
|
||||
_db: &DB,
|
||||
_kind: CriticalAlertKind,
|
||||
_channels: Option<Vec<CriticalErrorChannel>>,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::db::DB) -> () {
|
||||
// Implementation is not open source
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn renew_license_key(
|
||||
_http_client: &reqwest::Client,
|
||||
_db: &crate::db::DB,
|
||||
_key: Option<String>,
|
||||
_manual: bool,
|
||||
) -> String {
|
||||
// Implementation is not open source
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn create_customer_portal_session(
|
||||
_http_client: &reqwest::Client,
|
||||
_key: Option<String>,
|
||||
) -> error::Result<String> {
|
||||
// Implementation is not open source
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn worker_groups_alerts(_db: &DB) {}
|
||||
@@ -1,76 +0,0 @@
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::db::DB;
|
||||
use crate::ee::LicensePlan::Community;
|
||||
#[cfg(feature = "enterprise")]
|
||||
use crate::error;
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref LICENSE_KEY_VALID: Arc<RwLock<bool>> = Arc::new(RwLock::new(true));
|
||||
pub static ref LICENSE_KEY_ID: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
pub static ref LICENSE_KEY: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
}
|
||||
|
||||
pub enum LicensePlan {
|
||||
Community,
|
||||
Pro,
|
||||
Enterprise,
|
||||
}
|
||||
|
||||
pub async fn get_license_plan() -> LicensePlan {
|
||||
// Implementation is not open source
|
||||
return Community;
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum CriticalErrorChannel {
|
||||
Email { email: String },
|
||||
Slack { slack_channel: String },
|
||||
}
|
||||
|
||||
pub enum CriticalAlertKind {
|
||||
#[cfg(feature = "enterprise")]
|
||||
CriticalError,
|
||||
#[cfg(feature = "enterprise")]
|
||||
RecoveredCriticalError,
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn send_critical_alert(
|
||||
_error_message: String,
|
||||
_db: &DB,
|
||||
_kind: CriticalAlertKind,
|
||||
_channels: Option<Vec<CriticalErrorChannel>>,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn schedule_key_renewal(_http_client: &reqwest::Client, _db: &crate::db::DB) -> () {
|
||||
// Implementation is not open source
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn renew_license_key(
|
||||
_http_client: &reqwest::Client,
|
||||
_db: &crate::db::DB,
|
||||
_key: Option<String>,
|
||||
_manual: bool,
|
||||
) -> String {
|
||||
// Implementation is not open source
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn create_customer_portal_session(
|
||||
_http_client: &reqwest::Client,
|
||||
_key: Option<String>,
|
||||
) -> error::Result<String> {
|
||||
// Implementation is not open source
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
pub async fn worker_groups_alerts(_db: &DB) {}
|
||||
@@ -0,0 +1,11 @@
|
||||
use crate::server::Smtp;
|
||||
|
||||
pub async fn send_email(
|
||||
_subject: &str,
|
||||
_content: &str,
|
||||
_to: Vec<String>,
|
||||
_smtp: Smtp,
|
||||
_client_timeout: Option<tokio::time::Duration>,
|
||||
) -> crate::error::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
@@ -22,6 +22,7 @@ pub mod auth;
|
||||
pub mod bench;
|
||||
pub mod db;
|
||||
pub mod ee;
|
||||
pub mod email_ee;
|
||||
pub mod error;
|
||||
pub mod external_ip;
|
||||
pub mod flow_status;
|
||||
|
||||
@@ -11,13 +11,11 @@ use crate::ee::LICENSE_KEY_ID;
|
||||
use crate::ee::{send_critical_alert, CriticalAlertKind};
|
||||
use crate::error::{to_anyhow, Error, Result};
|
||||
use crate::global_settings::UNIQUE_ID_SETTING;
|
||||
use crate::server::Smtp;
|
||||
use crate::DB;
|
||||
use anyhow::Context;
|
||||
use gethostname::gethostname;
|
||||
use git_version::git_version;
|
||||
use mail_send::mail_builder::MessageBuilder;
|
||||
use mail_send::SmtpClientBuilder;
|
||||
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -243,58 +241,6 @@ pub fn generate_lock_id(database_name: &str) -> i64 {
|
||||
0x3d32ad9e * (CRC_IEEE.checksum(database_name.as_bytes()) as i64)
|
||||
}
|
||||
|
||||
pub async fn send_email(
|
||||
subject: &str,
|
||||
content: &str,
|
||||
to: Vec<String>,
|
||||
smtp: Smtp,
|
||||
client_timeout: Option<tokio::time::Duration>,
|
||||
) -> Result<()> {
|
||||
let mut client = SmtpClientBuilder::new(smtp.host, smtp.port)
|
||||
.implicit_tls(smtp.tls_implicit.unwrap_or(false));
|
||||
if std::env::var("ACCEPT_INVALID_CERTS").is_ok() {
|
||||
client = client.allow_invalid_certs();
|
||||
}
|
||||
let client = if let (Some(username), Some(password)) = (smtp.username, smtp.password) {
|
||||
if !username.is_empty() {
|
||||
client.credentials((username, password))
|
||||
} else {
|
||||
client
|
||||
}
|
||||
} else {
|
||||
client
|
||||
};
|
||||
let message = MessageBuilder::new()
|
||||
.from(("Windmill", smtp.from.as_str()))
|
||||
.to(to.clone())
|
||||
.subject(subject)
|
||||
.text_body(content);
|
||||
|
||||
match client_timeout {
|
||||
Some(timeout) => {
|
||||
tokio::time::timeout(timeout, client.connect())
|
||||
.await
|
||||
.map_err(to_anyhow)?
|
||||
.map_err(to_anyhow)?
|
||||
.send(message)
|
||||
.await
|
||||
.map_err(to_anyhow)?;
|
||||
}
|
||||
None => {
|
||||
client
|
||||
.connect()
|
||||
.await
|
||||
.map_err(to_anyhow)?
|
||||
.send(message)
|
||||
.await
|
||||
.map_err(to_anyhow)?;
|
||||
}
|
||||
}
|
||||
tracing::info!("Sent email to {:#?}: {subject}", to);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub async fn report_critical_error(error_message: String, _db: DB) -> () {
|
||||
tracing::error!("CRITICAL ERROR: {error_message}");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user