mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-30 00:16:53 +00:00
feat: move a live ai session into a workspace fork with its drafts
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
af1a29a3cb
commit
bdb111c236
@@ -8438,62 +8438,23 @@ async fn clone_raw_apps(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clone every per-user draft (and the legacy NULL-email workspace draft,
|
||||
/// if present) from the parent. The fork target is empty at create time so
|
||||
/// a plain INSERT is safe — no need to UPSERT against the partial unique
|
||||
/// indexes (`draft_pkey_with_user` / `draft_pkey_legacy`). `id` is the
|
||||
/// BIGSERIAL synthetic PK and is regenerated by the default; we don't list
|
||||
/// it in the column set. `created_at` is preserved so the per-tab
|
||||
/// `last_sync` baseline the editor reads (`?get_draft=true` → overlay's
|
||||
/// `draft_saved_at`) lines up with the parent's timeline — otherwise the
|
||||
/// fork's first POST from any open editor would race a stale `last_sync`
|
||||
/// and trip the conflict modal on every cloned draft.
|
||||
// Only `email = authed_email` and the legacy NULL row are cloned — see
|
||||
// `clone_workspace_data` for the rationale.
|
||||
// `clone_workspace_data` for the rationale. The fork is empty at create time, which is what
|
||||
// makes the plain INSERT of `copy_drafts_to_workspace` safe here.
|
||||
async fn clone_drafts(
|
||||
tx: &mut Transaction<'_, Postgres>,
|
||||
source_workspace_id: &str,
|
||||
target_workspace_id: &str,
|
||||
authed_email: &str,
|
||||
) -> Result<()> {
|
||||
// A script/flow draft carries the principal in its value, and deploying it in the clone
|
||||
// would send a pair naming somebody who is not a member there. Stripped rather than
|
||||
// filtered like `clone_scripts`: the address the draft still carries re-derives the
|
||||
// clone's own principal at deploy time, which is the more accurate answer of the two.
|
||||
sqlx::query!(
|
||||
// A script hash is content-addressed and copied as-is, so a script draft's base
|
||||
// still names a version the clone has. `clone_flows` / `clone_apps` mint new ids,
|
||||
// so those drafts arrive with no base (staleness falls back to the timestamps),
|
||||
// lineage field included, or the next autosave would re-derive the source id.
|
||||
//
|
||||
// `clean` is `strip_json_nul`'s parity rule in SQL, so a pre-sanitizer U+0000
|
||||
// escape cannot abort the clone on `to_jsonb` or arrive with its principal
|
||||
// unstripped: escaped backslashes park on chr(1) (lossless, a `json` value's text
|
||||
// cannot hold a raw control byte) so only a real NUL is removed, and chr(92)
|
||||
// spells the backslash so no escape sequence reaches this source file.
|
||||
r#"INSERT INTO draft (workspace_id, path, typ, value, created_at, email, base)
|
||||
SELECT $2, path, typ,
|
||||
to_json(
|
||||
CASE WHEN typ IN ('script', 'flow') THEN clean - 'on_behalf_of' ELSE clean END
|
||||
- CASE WHEN typ = 'flow' THEN 'version_id'
|
||||
WHEN typ IN ('app', 'raw_app') THEN 'parent_version'
|
||||
ELSE '' END
|
||||
),
|
||||
created_at, email,
|
||||
CASE WHEN typ = 'script' THEN base END
|
||||
FROM (
|
||||
SELECT d.path, d.typ, d.created_at, d.email, d.base,
|
||||
replace(replace(replace(d.value::text, chr(92) || chr(92), chr(1)), chr(92) || 'u0000', ''), chr(1), chr(92) || chr(92))::jsonb AS clean
|
||||
FROM draft d
|
||||
WHERE d.workspace_id = $1 AND (d.email = $3 OR d.email IS NULL)
|
||||
) s"#,
|
||||
windmill_common::user_drafts::copy_drafts_to_workspace(
|
||||
&mut **tx,
|
||||
source_workspace_id,
|
||||
target_workspace_id,
|
||||
authed_email,
|
||||
None,
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -13263,7 +13224,7 @@ async fn seed_full_diff_scan(
|
||||
/// If the user is not a member of `workspace_id`, returns an authed with no
|
||||
/// folders/groups/operator/admin (except for superadmins, who stay admin) —
|
||||
/// i.e. they will only see what RLS explicitly allows for unknown users.
|
||||
async fn load_workspace_authed(
|
||||
pub async fn load_workspace_authed(
|
||||
db: &DB,
|
||||
base_authed: &ApiAuthed,
|
||||
workspace_id: &str,
|
||||
|
||||
Reference in New Issue
Block a user