mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-19 00:03:19 +00:00
Merge remote-tracking branch 'origin/main' into change-68b704f7
# Conflicts: # backend/ee-repo-ref.txt # cli/src/commands/datatable/datatable.ts # cli/src/guidance/skills.gen.ts # system_prompts/auto-generated/cli/cli-commands.md # system_prompts/auto-generated/prompts.ts # system_prompts/auto-generated/skills/cli-commands/SKILL.md
This commit is contained in:
@@ -48,7 +48,9 @@ use windmill_common::workspaces::{Ducklake, DucklakeCatalogResourceType};
|
||||
use windmill_common::PgDatabase;
|
||||
use windmill_common::{
|
||||
error::{Error, JsonResult, Result},
|
||||
global_settings::AUTOMATE_USERNAME_CREATION_SETTING,
|
||||
global_settings::{
|
||||
AUTOMATE_USERNAME_CREATION_SETTING, DISABLE_WORKSPACE_INVITE_EMAILS_SETTING,
|
||||
},
|
||||
oauth2::WORKSPACE_SLACK_BOT_TOKEN_PATH,
|
||||
utils::{paginate, rd_string, require_admin, Pagination},
|
||||
};
|
||||
@@ -4774,6 +4776,30 @@ async fn check_fork_w_id_conflict(db: &DB, w_id: &str) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A fork id is reusable: it is freed when a fork is deleted and can be claimed
|
||||
/// again under the same name. `workspace_diff` and `skip_workspace_diff_tally`
|
||||
/// are keyed by workspace id with no FK cascade, so a freshly created fork could
|
||||
/// inherit cached diff state from a previous occupant of its id — a stale skip
|
||||
/// row suppresses comparison entirely, and stale workspace_diff rows produce a
|
||||
/// spurious "changes not visible" warning that hides the deploy button. Clear
|
||||
/// both so a new fork always starts with clean diff state, regardless of how the
|
||||
/// id was freed.
|
||||
async fn purge_stale_fork_diff_state(db: &DB, fork_id: &str) -> Result<()> {
|
||||
sqlx::query!(
|
||||
"DELETE FROM workspace_diff WHERE source_workspace_id = $1 OR fork_workspace_id = $1",
|
||||
fork_id
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"DELETE FROM skip_workspace_diff_tally WHERE workspace_id = $1",
|
||||
fork_id
|
||||
)
|
||||
.execute(db)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
pub static ref CREATE_WORKSPACE_REQUIRE_SUPERADMIN: bool = {
|
||||
@@ -5977,6 +6003,7 @@ async fn create_workspace_fork_branch(
|
||||
// Fail before creating any git branch so a name conflict doesn't leave a
|
||||
// dangling branch on the synced repos.
|
||||
check_fork_w_id_conflict(&db, &nw.id).await?;
|
||||
purge_stale_fork_diff_state(&db, &nw.id).await?;
|
||||
|
||||
Ok(Json(
|
||||
handle_fork_branch_creation(&authed.email, &authed.username, &db, &w_id, &nw.id).await?,
|
||||
@@ -6121,6 +6148,7 @@ async fn create_workspace_fork(
|
||||
// re-using a taken (possibly archived) fork id reports the actual
|
||||
// conflict instead of a misleading "maximum number of workspaces" error.
|
||||
check_fork_w_id_conflict(&db, &nw.id).await?;
|
||||
purge_stale_fork_diff_state(&db, &nw.id).await?;
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
_check_nb_of_workspaces(&db).await?;
|
||||
@@ -6441,6 +6469,20 @@ async fn unarchive_workspace(
|
||||
Ok(format!("Unarchived workspace {}", &w_id))
|
||||
}
|
||||
|
||||
/// Whether the instance is configured to suppress the email notifications sent
|
||||
/// when a user is invited or added to a workspace. Defaults to false (emails on).
|
||||
async fn workspace_invite_emails_disabled(db: &DB) -> Result<bool> {
|
||||
Ok(
|
||||
windmill_common::global_settings::load_value_from_global_settings(
|
||||
db,
|
||||
DISABLE_WORKSPACE_INVITE_EMAILS_SETTING,
|
||||
)
|
||||
.await?
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false),
|
||||
)
|
||||
}
|
||||
|
||||
async fn invite_user(
|
||||
ApiAuthed { username, is_admin, .. }: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
@@ -6499,16 +6541,18 @@ async fn invite_user(
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
send_email_if_possible(
|
||||
&format!("Invited to Windmill's workspace: {w_id}"),
|
||||
&format!(
|
||||
"You have been granted access to Windmill's workspace {w_id}
|
||||
if !workspace_invite_emails_disabled(&db).await? {
|
||||
send_email_if_possible(
|
||||
&format!("Invited to Windmill's workspace: {w_id}"),
|
||||
&format!(
|
||||
"You have been granted access to Windmill's workspace {w_id}
|
||||
|
||||
If you do not have an account on {}, login with SSO or ask an admin to create an account for you.",
|
||||
(**BASE_URL.load()).clone()
|
||||
),
|
||||
&nu.email,
|
||||
);
|
||||
(**BASE_URL.load()).clone()
|
||||
),
|
||||
&nu.email,
|
||||
);
|
||||
}
|
||||
|
||||
webhook.send_instance_event(InstanceEvent::UserInvitedWorkspace {
|
||||
email: nu.email.clone(),
|
||||
@@ -6651,17 +6695,19 @@ async fn add_user(
|
||||
)
|
||||
.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 {}
|
||||
if !workspace_invite_emails_disabled(&db).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 {}
|
||||
|
||||
If you do not have an account on {}, login with SSO or ask an admin to create an account for you.",
|
||||
authed.email,
|
||||
(**BASE_URL.load()).clone()
|
||||
),
|
||||
&nu.email,
|
||||
);
|
||||
authed.email,
|
||||
(**BASE_URL.load()).clone()
|
||||
),
|
||||
&nu.email,
|
||||
);
|
||||
}
|
||||
|
||||
webhook.send_instance_event(InstanceEvent::UserAddedWorkspace {
|
||||
workspace: w_id.clone(),
|
||||
|
||||
@@ -868,6 +868,25 @@ pub(crate) async fn delete_workspace(
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
// workspace_diff and skip_workspace_diff_tally are keyed by workspace id with no
|
||||
// FK cascade. A fork id is reused when a fork is deleted and recreated under the
|
||||
// same name, so leaving these rows behind leaks the previous fork's cached diff
|
||||
// verdicts onto the new fork — causing a spurious "changes not visible" warning
|
||||
// that hides the deploy button.
|
||||
sqlx::query!(
|
||||
"DELETE FROM workspace_diff WHERE source_workspace_id = $1 OR fork_workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"DELETE FROM skip_workspace_diff_tally WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
sqlx::query!("DELETE FROM workspace WHERE id = $1", &w_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
Reference in New Issue
Block a user