mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-09 16:05:42 +00:00
feat: deployment UI filter deployable items (#4183)
* Add column to workspace settings table * Add endpoint to change the deploy UI settings * Make frontend page to edit UI settings * Prepare sqlx * Add deployment restrictions to frontend * Fix function name * Change dependency to minimatch, CE compatibility * Remove default include_path * Remove picomatch types (old dep) * Use empty list instead of globstar as default * All deployable if config is null
This commit is contained in:
+6
@@ -112,6 +112,11 @@
|
||||
"ordinal": 21,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "deploy_ui",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -141,6 +146,7 @@
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET deploy_ui = NULL WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "26b3e1f531909a2d841cfed49bb76be8aaa74ec3d32596c76a2041769478e61e"
|
||||
}
|
||||
+6
@@ -112,6 +112,11 @@
|
||||
"ordinal": 21,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "deploy_ui",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -141,6 +146,7 @@
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"name": "bool",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
false
|
||||
false,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET deploy_ui = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c3364a5cd3fb7d43a6a6f484009f47a3b108bbf61c9f4afd364ed869deac75a5"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add down migration script here
|
||||
ALTER TABLE workspace_settings DROP COLUMN deploy_ui;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add up migration script here
|
||||
ALTER TABLE workspace_settings ADD COLUMN IF NOT EXISTS deploy_ui JSONB;
|
||||
@@ -1507,6 +1507,8 @@ paths:
|
||||
$ref: "#/components/schemas/LargeFileStorage"
|
||||
git_sync:
|
||||
$ref: "#/components/schemas/WorkspaceGitSyncSettings"
|
||||
deploy_ui:
|
||||
$ref: "#/components/schemas/WorkspaceDeployUISettings"
|
||||
default_app:
|
||||
type: string
|
||||
default_scripts:
|
||||
@@ -1896,6 +1898,32 @@ paths:
|
||||
application/json:
|
||||
schema: {}
|
||||
|
||||
/w/{workspace}/workspaces/edit_deploy_ui_config:
|
||||
post:
|
||||
summary: edit workspace deploy ui settings
|
||||
operationId: editWorkspaceDeployUISettings
|
||||
tags:
|
||||
- workspace
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
description: Workspace deploy UI settings
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
deploy_ui_settings:
|
||||
$ref: "#/components/schemas/WorkspaceDeployUISettings"
|
||||
|
||||
responses:
|
||||
"200":
|
||||
description: status
|
||||
content:
|
||||
application/json:
|
||||
schema: {}
|
||||
|
||||
/w/{workspace}/workspaces/edit_default_app:
|
||||
post:
|
||||
summary: edit default app for workspace
|
||||
@@ -11078,6 +11106,25 @@ components:
|
||||
items:
|
||||
$ref: "#/components/schemas/GitRepositorySettings"
|
||||
|
||||
WorkspaceDeployUISettings:
|
||||
type: object
|
||||
properties:
|
||||
include_path:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
include_type:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum:
|
||||
- script
|
||||
- flow
|
||||
- app
|
||||
- resource
|
||||
- variable
|
||||
- secret
|
||||
|
||||
WorkspaceDefaultScripts:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -42,7 +42,7 @@ use windmill_common::schedule::Schedule;
|
||||
use windmill_common::users::username_to_permissioned_as;
|
||||
use windmill_common::variables::build_crypt;
|
||||
use windmill_common::worker::{to_raw_value, CLOUD_HOSTED};
|
||||
use windmill_common::workspaces::WorkspaceGitSyncSettings;
|
||||
use windmill_common::workspaces::{WorkspaceDeploymentUISettings, WorkspaceGitSyncSettings};
|
||||
use windmill_common::{
|
||||
error::{to_anyhow, Error, JsonResult, Result},
|
||||
flows::Flow,
|
||||
@@ -97,6 +97,7 @@ pub fn workspaced_service() -> Router {
|
||||
post(edit_large_file_storage_config),
|
||||
)
|
||||
.route("/edit_git_sync_config", post(edit_git_sync_config))
|
||||
.route("/edit_deploy_ui_config", post(edit_deploy_ui_config))
|
||||
.route("/edit_default_app", post(edit_default_app))
|
||||
.route("/default_app", get(get_default_app))
|
||||
.route(
|
||||
@@ -167,8 +168,9 @@ pub struct WorkspaceSettings {
|
||||
pub error_handler: Option<String>,
|
||||
pub error_handler_extra_args: Option<serde_json::Value>,
|
||||
pub error_handler_muted_on_cancel: Option<bool>,
|
||||
pub large_file_storage: Option<serde_json::Value>, // effectively: DatasetsStorage
|
||||
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
|
||||
pub large_file_storage: Option<serde_json::Value>, // effectively: DatasetsStorage
|
||||
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
|
||||
pub deploy_ui: Option<serde_json::Value>, // effectively: WorkspaceDeploymentUISettings
|
||||
pub default_app: Option<String>,
|
||||
pub automatic_billing: bool,
|
||||
pub default_scripts: Option<serde_json::Value>,
|
||||
@@ -1052,6 +1054,74 @@ async fn edit_git_sync_config(
|
||||
Ok(format!("Edit git sync config for workspace {}", &w_id))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct EditDeployUIConfig {
|
||||
deploy_ui_settings: Option<WorkspaceDeploymentUISettings>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
async fn edit_deploy_ui_config(
|
||||
_authed: ApiAuthed,
|
||||
Extension(_db): Extension<DB>,
|
||||
Path(_w_id): Path<String>,
|
||||
Json(_new_config): Json<EditDeployUIConfig>,
|
||||
) -> Result<String> {
|
||||
return Err(Error::BadRequest(
|
||||
"Deployment UI is only available on Windmill Enterprise Edition".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "enterprise")]
|
||||
async fn edit_deploy_ui_config(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
ApiAuthed { is_admin, username, .. }: ApiAuthed,
|
||||
Json(new_config): Json<EditDeployUIConfig>,
|
||||
) -> Result<String> {
|
||||
require_admin(is_admin, &username)?;
|
||||
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
let args_for_audit = format!("{:?}", new_config.deploy_ui_settings);
|
||||
audit_log(
|
||||
&mut *tx,
|
||||
&authed,
|
||||
"workspaces.edit_deploy_ui_config",
|
||||
ActionKind::Update,
|
||||
&w_id,
|
||||
Some(&authed.email),
|
||||
Some([("deployment_ui_settings", args_for_audit.as_str())].into()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(deploy_ui_settings) = new_config.deploy_ui_settings {
|
||||
let serialized_config = serde_json::to_value::<WorkspaceDeploymentUISettings>(deploy_ui_settings)
|
||||
.map_err(|err| Error::InternalErr(err.to_string()))?;
|
||||
|
||||
sqlx::query!(
|
||||
"UPDATE workspace_settings SET deploy_ui = $1 WHERE workspace_id = $2",
|
||||
serialized_config,
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"UPDATE workspace_settings SET deploy_ui = NULL WHERE workspace_id = $1",
|
||||
&w_id,
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(format!("Edit deployment UI config for workspace {}", &w_id))
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditDefaultApp {
|
||||
pub default_app_path: Option<String>,
|
||||
|
||||
@@ -7,6 +7,12 @@ pub struct WorkspaceGitSyncSettings {
|
||||
pub repositories: Vec<GitRepositorySettings>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
pub struct WorkspaceDeploymentUISettings {
|
||||
pub include_path: Vec<String>,
|
||||
pub include_type: Vec<ObjectType>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))]
|
||||
pub enum ObjectType {
|
||||
|
||||
Generated
+171
-32
@@ -34,6 +34,7 @@
|
||||
"hash-sum": "^2.0.0",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lucide-svelte": "^0.293.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
|
||||
"monaco-graphql": "^1.5.1",
|
||||
"monaco-languageclient": "~7.0.1",
|
||||
@@ -935,6 +936,28 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.54.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz",
|
||||
@@ -1026,6 +1049,28 @@
|
||||
"node": ">=10.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/module-importer": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
|
||||
@@ -1289,6 +1334,18 @@
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@rollup/pluginutils/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.10.0.tgz",
|
||||
@@ -2386,6 +2443,18 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/aproba": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||
@@ -2560,13 +2629,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"devOptional": true,
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
@@ -4061,6 +4128,16 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/eslint-scope": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
|
||||
@@ -4098,6 +4175,18 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/esm-env": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz",
|
||||
@@ -4607,6 +4696,28 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/global-modules": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
|
||||
@@ -6535,6 +6646,18 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -6597,15 +6720,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"devOptional": true,
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
||||
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
@@ -7349,18 +7474,6 @@
|
||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch-browser": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/picomatch-browser/-/picomatch-browser-2.2.6.tgz",
|
||||
@@ -8298,6 +8411,18 @@
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp/node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/redent": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz",
|
||||
@@ -9104,6 +9229,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/commander": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||
@@ -9133,6 +9268,18 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase/node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@@ -10159,14 +10306,6 @@
|
||||
"vscode": "^1.82.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageclient/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vscode-languageclient/node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"hash-sum": "^2.0.0",
|
||||
"highlight.js": "^11.8.0",
|
||||
"lucide-svelte": "^0.293.0",
|
||||
"minimatch": "^10.0.1",
|
||||
"monaco-editor": "npm:@codingame/monaco-editor-treemended@>=1.83.5 <1.84.0",
|
||||
"monaco-graphql": "^1.5.1",
|
||||
"monaco-languageclient": "~7.0.1",
|
||||
|
||||
@@ -1,13 +1,88 @@
|
||||
<script lang="ts">
|
||||
import { WorkspaceService } from '$lib/gen'
|
||||
import { usersWorkspaceStore, workspaceStore } from '$lib/stores'
|
||||
import { enterpriseLicense, usersWorkspaceStore, workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { fade } from 'svelte/transition'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { Plus, X } from 'lucide-svelte'
|
||||
import { Button } from './common'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { emptyString } from '$lib/utils'
|
||||
|
||||
export let workspaceToDeployTo: string | undefined
|
||||
|
||||
$: deployableWorkspaces = $usersWorkspaceStore?.workspaces
|
||||
.map((w) => w.id)
|
||||
.filter((w) => w != $workspaceStore)
|
||||
|
||||
type DeployUITypeMap = {
|
||||
scripts: boolean
|
||||
flows: boolean
|
||||
apps: boolean
|
||||
resources: boolean
|
||||
variables: boolean
|
||||
secrets: boolean
|
||||
}
|
||||
type DeployUIType = 'script' | 'flow' | 'app' | 'resource' | 'variable' | 'secret'
|
||||
|
||||
const all_ok: DeployUITypeMap = {
|
||||
scripts: true,
|
||||
flows: true,
|
||||
apps: true,
|
||||
resources: true,
|
||||
variables: true,
|
||||
secrets: true
|
||||
}
|
||||
|
||||
export let workspaceToDeployTo: string | undefined
|
||||
export let deployUiSettings: {
|
||||
include_path: string[]
|
||||
include_type: DeployUITypeMap
|
||||
} = {
|
||||
include_path: [],
|
||||
include_type: all_ok
|
||||
}
|
||||
|
||||
|
||||
function deployUITypeMapToArray(
|
||||
typesMap: DeployUITypeMap,
|
||||
expectedValue: boolean
|
||||
): DeployUIType[] {
|
||||
let result: DeployUIType[] = []
|
||||
if (typesMap.scripts == expectedValue) {
|
||||
result.push('script')
|
||||
}
|
||||
if (typesMap.flows == expectedValue) {
|
||||
result.push('flow')
|
||||
}
|
||||
if (typesMap.apps == expectedValue) {
|
||||
result.push('app')
|
||||
}
|
||||
if (typesMap.resources == expectedValue) {
|
||||
result.push('resource')
|
||||
}
|
||||
if (typesMap.variables == expectedValue) {
|
||||
result.push('variable')
|
||||
}
|
||||
if (typesMap.secrets == expectedValue) {
|
||||
result.push('secret')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function editWindmillDeploymentUISettings() {
|
||||
let include_path = deployUiSettings.include_path.filter((elmt) => !emptyString(elmt))
|
||||
let include_type = deployUITypeMapToArray(deployUiSettings.include_type, true)
|
||||
await WorkspaceService.editWorkspaceDeployUiSettings({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
deploy_ui_settings: {
|
||||
include_path: include_path,
|
||||
include_type: include_type
|
||||
}
|
||||
}
|
||||
})
|
||||
sendUserToast('Workspace Deployment UI settings updated')
|
||||
}
|
||||
</script>
|
||||
|
||||
<h3 class="mt-8">Workspace to link to</h3>
|
||||
@@ -36,3 +111,104 @@
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<h3 class="mt-6 mb-3">Deployable items</h3>
|
||||
<div class="flex flex-wrap gap-20">
|
||||
<div class="max-w-md w-full">
|
||||
{#if Array.isArray(deployUiSettings?.include_path)}
|
||||
<h4 class="flex gap-2 mb-4"
|
||||
>Filter on path<Tooltip>
|
||||
Only scripts, flows and apps with their path matching one of those filters will be allowed
|
||||
to be deployed in the deploy UI. The filters allow '*'' and '**' characters, with '*''
|
||||
matching any character allowed in paths until the next slash (/) and '**' matching
|
||||
anything including slashes.
|
||||
</Tooltip></h4
|
||||
>
|
||||
{#each deployUiSettings.include_path ?? [] as regexpPath, idx}
|
||||
<div class="flex mt-1 items-center">
|
||||
<input type="text" bind:value={regexpPath} id="arg-input-array" />
|
||||
<button
|
||||
transition:fade|local={{ duration: 100 }}
|
||||
class="rounded-full p-1 bg-surface-secondary duration-200 hover:bg-surface-hover ml-2"
|
||||
aria-label="Clear"
|
||||
on:click={() => {
|
||||
deployUiSettings.include_path.splice(idx, 1)
|
||||
deployUiSettings.include_path = [...deployUiSettings.include_path]
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
<div class="flex mt-2">
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="mt-1"
|
||||
on:click={() => {
|
||||
deployUiSettings.include_path = [...deployUiSettings.include_path, '']
|
||||
}}
|
||||
id="deploy-ui-add-path-filter"
|
||||
startIcon={{ icon: Plus }}
|
||||
>
|
||||
Add filter
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-w-md w-full">
|
||||
<h4 class="flex gap-2 mb-4"
|
||||
>Filter on type<Tooltip>
|
||||
You can filter which types of item can be deployed to the production workspace. By default
|
||||
everything is deployable.
|
||||
</Tooltip></h4
|
||||
>
|
||||
<div class="flex flex-col gap-2 mt-1">
|
||||
<Toggle
|
||||
bind:checked={deployUiSettings.include_type.scripts}
|
||||
options={{ right: 'Scripts' }}
|
||||
/>
|
||||
<Toggle
|
||||
bind:checked={deployUiSettings.include_type.flows}
|
||||
options={{ right: 'Flows' }}
|
||||
/>
|
||||
<Toggle
|
||||
bind:checked={deployUiSettings.include_type.apps}
|
||||
options={{ right: 'Apps' }}
|
||||
/>
|
||||
<Toggle
|
||||
bind:checked={deployUiSettings.include_type.resources}
|
||||
options={{ right: 'Resources' }}
|
||||
/>
|
||||
<div class="flex gap-3">
|
||||
<Toggle
|
||||
bind:checked={deployUiSettings.include_type.variables}
|
||||
on:change={(ev) => {
|
||||
if (!ev.detail) {
|
||||
deployUiSettings.include_type.secrets = false
|
||||
}
|
||||
}}
|
||||
options={{ right: 'Variables ' }}
|
||||
/>
|
||||
<span>-</span>
|
||||
<Toggle
|
||||
disabled={!deployUiSettings.include_type.variables}
|
||||
bind:checked={deployUiSettings.include_type.secrets}
|
||||
options={{ left: 'Include secrets' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if $enterpriseLicense}
|
||||
<div class="flex mt-5 mb-5 gap-1">
|
||||
<Button
|
||||
color="blue"
|
||||
disabled={workspaceToDeployTo == undefined}
|
||||
on:click={() => {
|
||||
editWindmillDeploymentUISettings()
|
||||
}}>Save Deployment UI settings</Button
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
import type MoveDrawer from '$lib/components/MoveDrawer.svelte'
|
||||
import SharedBadge from '$lib/components/SharedBadge.svelte'
|
||||
import type ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import { AppService, DraftService, type ListableApp } from '$lib/gen'
|
||||
import {
|
||||
AppService,
|
||||
DraftService,
|
||||
type ListableApp,
|
||||
type WorkspaceDeployUISettings
|
||||
} from '$lib/gen'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Button from '../button/Button.svelte'
|
||||
@@ -28,7 +33,7 @@
|
||||
import { goto as gotoUrl } from '$app/navigation'
|
||||
import { page } from '$app/stores'
|
||||
import type DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
|
||||
import { DELETE, copyToClipboard } from '$lib/utils'
|
||||
import { DELETE, copyToClipboard, isDeployable } from '$lib/utils'
|
||||
import AppDeploymentHistory from '$lib/components/apps/editor/AppDeploymentHistory.svelte'
|
||||
import AppJsonEditor from '$lib/components/apps/editor/AppJsonEditor.svelte'
|
||||
|
||||
@@ -41,6 +46,7 @@
|
||||
export let deleteConfirmedCallback: (() => void) | undefined
|
||||
export let depth: number = 0
|
||||
export let menuOpen: boolean = false
|
||||
export let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -163,14 +169,18 @@
|
||||
disabled: !canWrite,
|
||||
hide: $userStore?.operator
|
||||
},
|
||||
{
|
||||
displayName: 'Deploy to staging/prod',
|
||||
icon: Globe,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'app')
|
||||
},
|
||||
hide: $userStore?.operator
|
||||
},
|
||||
...(isDeployable('app', path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to staging/prod',
|
||||
icon: Globe,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'app')
|
||||
},
|
||||
hide: $userStore?.operator
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: $userStore?.operator ? 'View JSON' : 'View/Edit JSON',
|
||||
icon: FileJson,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import ScheduleEditor from '$lib/components/ScheduleEditor.svelte'
|
||||
import SharedBadge from '$lib/components/SharedBadge.svelte'
|
||||
import type ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import { FlowService, type Flow, DraftService } from '$lib/gen'
|
||||
import { FlowService, type Flow, DraftService, type WorkspaceDeployUISettings } from '$lib/gen'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Badge from '../badge/Badge.svelte'
|
||||
@@ -14,7 +14,7 @@
|
||||
import Row from './Row.svelte'
|
||||
import DraftBadge from '$lib/components/DraftBadge.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { DELETE, copyToClipboard, isOwner } from '$lib/utils'
|
||||
import { DELETE, copyToClipboard, isDeployable, isOwner } from '$lib/utils'
|
||||
import type DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
|
||||
import {
|
||||
Pen,
|
||||
@@ -42,6 +42,7 @@
|
||||
export let errorHandlerMuted: boolean
|
||||
export let depth: number = 0
|
||||
export let menuOpen: boolean = false
|
||||
export let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -190,15 +191,19 @@
|
||||
copyToClipboard(path)
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Deploy to staging/prod',
|
||||
icon: Globe,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'flow')
|
||||
},
|
||||
disabled: archived,
|
||||
hide: $userStore?.operator
|
||||
},
|
||||
...(isDeployable('flow', path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to staging/prod',
|
||||
icon: Globe,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'flow')
|
||||
},
|
||||
disabled: archived,
|
||||
hide: $userStore?.operator
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: 'Deployments',
|
||||
icon: HistoryIcon,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import type MoveDrawer from '$lib/components/MoveDrawer.svelte'
|
||||
import SharedBadge from '$lib/components/SharedBadge.svelte'
|
||||
import type ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import { RawAppService, type ListableRawApp } from '$lib/gen'
|
||||
import { RawAppService, type ListableRawApp, type WorkspaceDeployUISettings } from '$lib/gen'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Button from '../button/Button.svelte'
|
||||
@@ -15,6 +15,7 @@
|
||||
import { goto } from '$lib/navigation'
|
||||
import type DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
|
||||
import { FileUp, Globe, Pen, Share, Trash } from 'lucide-svelte'
|
||||
import { isDeployable } from '$lib/utils'
|
||||
|
||||
export let app: ListableRawApp & { canWrite: boolean }
|
||||
export let marked: string | undefined
|
||||
@@ -25,6 +26,7 @@
|
||||
export let deploymentDrawer: DeployWorkspaceDrawer
|
||||
export let depth: number = 0
|
||||
export let menuOpen: boolean = false
|
||||
export let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
let updateAppDrawer: Drawer
|
||||
|
||||
@@ -98,13 +100,17 @@
|
||||
},
|
||||
disabled: !canWrite
|
||||
},
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: Globe,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'raw_app')
|
||||
}
|
||||
},
|
||||
...(isDeployable('app', path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: Globe,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'raw_app')
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: canWrite ? 'Share' : 'See Permissions',
|
||||
icon: Share,
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
import SharedBadge from '$lib/components/SharedBadge.svelte'
|
||||
import type ShareModal from '$lib/components/ShareModal.svelte'
|
||||
|
||||
import { ScriptService, type Script, DraftService } from '$lib/gen'
|
||||
import {
|
||||
ScriptService,
|
||||
type Script,
|
||||
DraftService,
|
||||
type WorkspaceDeployUISettings
|
||||
} from '$lib/gen'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
@@ -16,7 +21,7 @@
|
||||
import Row from './Row.svelte'
|
||||
import DraftBadge from '$lib/components/DraftBadge.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { copyToClipboard, DELETE, isOwner } from '$lib/utils'
|
||||
import { copyToClipboard, DELETE, isDeployable, isOwner } from '$lib/utils'
|
||||
import type DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
|
||||
import { LanguageIcon } from '../languageIcons'
|
||||
import {
|
||||
@@ -49,6 +54,7 @@
|
||||
export let showCode: (path: string, summary: string) => void
|
||||
export let depth: number = 0
|
||||
export let menuOpen: boolean = false
|
||||
export let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -211,15 +217,19 @@
|
||||
disabled: !owner || script.archived,
|
||||
hide: $userStore?.operator
|
||||
},
|
||||
{
|
||||
displayName: 'Deploy to staging/prod',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(script.path, 'script')
|
||||
},
|
||||
disabled: script.archived,
|
||||
hide: $userStore?.operator
|
||||
},
|
||||
...(isDeployable('script', script.path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to staging/prod',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(script.path, 'script')
|
||||
},
|
||||
disabled: script.archived,
|
||||
hide: $userStore?.operator
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: 'View runs',
|
||||
icon: List,
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
import ShareModal from '../ShareModal.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { ArrowBigUp } from 'lucide-svelte'
|
||||
import { enterpriseLicense, workspaceStore } from '$lib/stores'
|
||||
import { WorkspaceService, type WorkspaceDeployUISettings } from '$lib/gen'
|
||||
import { ALL_DEPLOYABLE } from '$lib/utils'
|
||||
|
||||
export let item
|
||||
export let depth: number = 0
|
||||
@@ -23,6 +26,19 @@
|
||||
|
||||
let menuOpen: boolean = false
|
||||
export let showCode: (path: string, summary: string) => void
|
||||
|
||||
let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
async function getDeployUiSettings() {
|
||||
if (!$enterpriseLicense) {
|
||||
deployUiSettings = ALL_DEPLOYABLE
|
||||
return
|
||||
}
|
||||
let settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
deployUiSettings = settings.deploy_ui ?? ALL_DEPLOYABLE
|
||||
}
|
||||
getDeployUiSettings()
|
||||
|
||||
</script>
|
||||
|
||||
{#if item.type == 'script'}
|
||||
@@ -42,6 +58,7 @@
|
||||
{depth}
|
||||
bind:menuOpen
|
||||
{showCode}
|
||||
{deployUiSettings}
|
||||
/>
|
||||
{:else if item.type == 'flow'}
|
||||
<FlowRow
|
||||
@@ -59,6 +76,7 @@
|
||||
{deploymentDrawer}
|
||||
{depth}
|
||||
bind:menuOpen
|
||||
{deployUiSettings}
|
||||
/>
|
||||
{:else if item.type == 'app'}
|
||||
<AppRow
|
||||
@@ -72,6 +90,7 @@
|
||||
{deploymentDrawer}
|
||||
{depth}
|
||||
bind:menuOpen
|
||||
{deployUiSettings}
|
||||
/>
|
||||
{:else if item.type == 'raw_app'}
|
||||
<RawAppRow
|
||||
@@ -85,6 +104,7 @@
|
||||
{deploymentDrawer}
|
||||
{depth}
|
||||
bind:menuOpen
|
||||
{deployUiSettings}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -11,9 +11,10 @@ import { deepEqual } from 'fast-equals'
|
||||
import YAML from 'yaml'
|
||||
import type { UserExt } from './stores'
|
||||
import { sendUserToast } from './toast'
|
||||
import type { Script } from './gen'
|
||||
import type { Script, WorkspaceDeployUISettings } from './gen'
|
||||
import type { EnumType, SchemaProperty } from './common'
|
||||
import type { Schema } from './common'
|
||||
import { minimatch } from 'minimatch'
|
||||
export { sendUserToast }
|
||||
|
||||
export function validateUsername(username: string): string {
|
||||
@@ -494,7 +495,7 @@ export function isObject(obj: any) {
|
||||
|
||||
export function debounce(func: (...args: any[]) => any, wait: number) {
|
||||
let timeout: any
|
||||
return function (...args: any[]) {
|
||||
return function(...args: any[]) {
|
||||
// @ts-ignore
|
||||
const context = this
|
||||
clearTimeout(timeout)
|
||||
@@ -504,7 +505,7 @@ export function debounce(func: (...args: any[]) => any, wait: number) {
|
||||
|
||||
export function throttle<T>(func: (...args: any[]) => T, wait: number) {
|
||||
let timeout: any
|
||||
return function (...args: any[]) {
|
||||
return function(...args: any[]) {
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(() => {
|
||||
timeout = null
|
||||
@@ -720,7 +721,7 @@ export async function tryEvery({
|
||||
try {
|
||||
await tryCode()
|
||||
break
|
||||
} catch (err) {}
|
||||
} catch (err) { }
|
||||
i++
|
||||
}
|
||||
if (i >= times) {
|
||||
@@ -937,3 +938,38 @@ export function getSchemaFromProperties(properties: { [name: string]: SchemaProp
|
||||
order: Object.keys(properties).filter((k) => k !== 'label')
|
||||
}
|
||||
}
|
||||
|
||||
type DeployUIType = 'script' | 'flow' | 'app' | 'resource' | 'variable' | 'secret'
|
||||
|
||||
export function isDeployable(
|
||||
type: DeployUIType,
|
||||
path: string,
|
||||
deployUiSettings: WorkspaceDeployUISettings | undefined
|
||||
) {
|
||||
if (deployUiSettings == undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
deployUiSettings.include_type != undefined &&
|
||||
!deployUiSettings.include_type.includes(type)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
deployUiSettings.include_path != undefined &&
|
||||
deployUiSettings.include_path.length != 0 &&
|
||||
deployUiSettings.include_path.every((x) => !minimatch(path, x))
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export const ALL_DEPLOYABLE: WorkspaceDeployUISettings = {
|
||||
include_path: [],
|
||||
include_type: ['script', 'flow', 'app', 'resource', 'variable', 'secret']
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
import { FlowService, JobService, type Flow, type FlowModule } from '$lib/gen'
|
||||
import { canWrite, defaultIfEmptyString, emptyString } from '$lib/utils'
|
||||
import {
|
||||
FlowService,
|
||||
JobService,
|
||||
WorkspaceService,
|
||||
type Flow,
|
||||
type FlowModule,
|
||||
type WorkspaceDeployUISettings
|
||||
} from '$lib/gen'
|
||||
import { ALL_DEPLOYABLE, canWrite, defaultIfEmptyString, emptyString, isDeployable } from '$lib/utils'
|
||||
|
||||
import DetailPageLayout from '$lib/components/details/DetailPageLayout.svelte'
|
||||
import { goto } from '$lib/navigation'
|
||||
@@ -10,7 +17,7 @@
|
||||
import MoveDrawer from '$lib/components/MoveDrawer.svelte'
|
||||
import RunForm from '$lib/components/RunForm.svelte'
|
||||
import ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
|
||||
import SavedInputs from '$lib/components/SavedInputs.svelte'
|
||||
@@ -206,7 +213,22 @@
|
||||
|
||||
$: mainButtons = getMainButtons(flow, args)
|
||||
|
||||
function getMenuItems(flow: Flow | undefined) {
|
||||
let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
async function getDeployUiSettings() {
|
||||
if (!$enterpriseLicense) {
|
||||
deployUiSettings = ALL_DEPLOYABLE
|
||||
return
|
||||
}
|
||||
let settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
deployUiSettings = settings.deploy_ui ?? ALL_DEPLOYABLE
|
||||
}
|
||||
getDeployUiSettings()
|
||||
|
||||
function getMenuItems(
|
||||
flow: Flow | undefined,
|
||||
deployUiSettings: WorkspaceDeployUISettings | undefined
|
||||
) {
|
||||
if (!flow || $userStore?.operator) return []
|
||||
|
||||
const menuItems: any = []
|
||||
@@ -232,11 +254,13 @@
|
||||
}
|
||||
})
|
||||
|
||||
menuItems.push({
|
||||
label: 'Deploy to staging/prod',
|
||||
onclick: () => deploymentDrawer.openDrawer(flow?.path ?? '', 'flow'),
|
||||
Icon: Server
|
||||
})
|
||||
if (isDeployable('flow', flow?.path ?? '', deployUiSettings)) {
|
||||
menuItems.push({
|
||||
label: 'Deploy to staging/prod',
|
||||
onclick: () => deploymentDrawer.openDrawer(flow?.path ?? '', 'flow'),
|
||||
Icon: Server
|
||||
})
|
||||
}
|
||||
|
||||
if (can_write) {
|
||||
menuItems.push({
|
||||
@@ -318,7 +342,7 @@
|
||||
<svelte:fragment slot="header">
|
||||
<DetailPageHeader
|
||||
{mainButtons}
|
||||
menuItems={getMenuItems(flow)}
|
||||
menuItems={getMenuItems(flow, deployUiSettings)}
|
||||
title={defaultIfEmptyString(flow.summary, flow.path)}
|
||||
bind:errorHandlerMuted={flow.ws_error_handler_muted}
|
||||
scriptOrFlowPath={flow.path}
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
import Row from '$lib/components/table/Row.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import type { ResourceType } from '$lib/gen'
|
||||
import { OauthService, ResourceService, type ListableResource } from '$lib/gen'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import type { ResourceType, WorkspaceDeployUISettings } from '$lib/gen'
|
||||
import { OauthService, ResourceService, WorkspaceService, type ListableResource } from '$lib/gen'
|
||||
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { canWrite, classNames, emptySchema, removeMarkdown, truncate } from '$lib/utils'
|
||||
import { ALL_DEPLOYABLE, canWrite, classNames, emptySchema, isDeployable, removeMarkdown, truncate } from '$lib/utils'
|
||||
import { convert } from '@redocly/json-to-json-schema'
|
||||
import {
|
||||
Braces,
|
||||
@@ -324,6 +324,18 @@
|
||||
types: false
|
||||
}
|
||||
}
|
||||
|
||||
let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
async function getDeployUiSettings() {
|
||||
if (!$enterpriseLicense) {
|
||||
deployUiSettings = ALL_DEPLOYABLE
|
||||
return
|
||||
}
|
||||
let settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
deployUiSettings = settings.deploy_ui ?? ALL_DEPLOYABLE
|
||||
}
|
||||
getDeployUiSettings()
|
||||
</script>
|
||||
|
||||
<ConfirmationModal
|
||||
@@ -748,13 +760,17 @@
|
||||
resourceEditor?.initEdit?.(path)
|
||||
}
|
||||
},
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'resource')
|
||||
}
|
||||
},
|
||||
...(isDeployable('resource', path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'resource')
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: 'Delete',
|
||||
disabled: !canWrite,
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
import { base } from '$lib/base'
|
||||
import { JobService, ScriptService, type Script } from '$lib/gen'
|
||||
import { defaultIfEmptyString, emptyString, canWrite, truncateHash } from '$lib/utils'
|
||||
import { JobService, ScriptService, WorkspaceService, type Script, type WorkspaceDeployUISettings } from '$lib/gen'
|
||||
import {
|
||||
defaultIfEmptyString,
|
||||
emptyString,
|
||||
canWrite,
|
||||
truncateHash,
|
||||
isDeployable,
|
||||
|
||||
ALL_DEPLOYABLE
|
||||
|
||||
} from '$lib/utils'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import { hubBaseUrlStore, userStore, workspaceStore } from '$lib/stores'
|
||||
import { enterpriseLicense, hubBaseUrlStore, userStore, workspaceStore } from '$lib/stores'
|
||||
import SchemaViewer from '$lib/components/SchemaViewer.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
import HighlightCode from '$lib/components/HighlightCode.svelte'
|
||||
@@ -318,7 +327,22 @@
|
||||
}
|
||||
$: mainButtons = getMainButtons(script, args, topHash, can_write)
|
||||
|
||||
function getMenuItems(script: Script | undefined) {
|
||||
let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
async function getDeployUiSettings() {
|
||||
if (!$enterpriseLicense) {
|
||||
deployUiSettings = ALL_DEPLOYABLE
|
||||
return
|
||||
}
|
||||
let settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
deployUiSettings = settings.deploy_ui ?? ALL_DEPLOYABLE
|
||||
}
|
||||
getDeployUiSettings()
|
||||
|
||||
function getMenuItems(
|
||||
script: Script | undefined,
|
||||
deployUiSettings: WorkspaceDeployUISettings | undefined
|
||||
) {
|
||||
if (!script || $userStore?.operator) return []
|
||||
|
||||
const menuItems: any = []
|
||||
@@ -347,13 +371,15 @@
|
||||
}
|
||||
})
|
||||
|
||||
menuItems.push({
|
||||
label: 'Deploy to staging/prod',
|
||||
Icon: Server,
|
||||
onclick: () => {
|
||||
deploymentDrawer.openDrawer(script?.path ?? '', 'script')
|
||||
}
|
||||
})
|
||||
if (isDeployable('script', script?.path ?? '', deployUiSettings)) {
|
||||
menuItems.push({
|
||||
label: 'Deploy to staging/prod',
|
||||
Icon: Server,
|
||||
onclick: () => {
|
||||
deploymentDrawer.openDrawer(script?.path ?? '', 'script')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (SCRIPT_VIEW_SHOW_PUBLISH_TO_HUB) {
|
||||
menuItems.push({
|
||||
@@ -471,7 +497,7 @@
|
||||
<svelte:fragment slot="header">
|
||||
<DetailPageHeader
|
||||
{mainButtons}
|
||||
menuItems={getMenuItems(script)}
|
||||
menuItems={getMenuItems(script, deployUiSettings)}
|
||||
title={defaultIfEmptyString(script.summary, script.path)}
|
||||
bind:errorHandlerMuted={script.ws_error_handler_muted}
|
||||
errorHandlerKind="script"
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
import TableSimple from '$lib/components/TableSimple.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import VariableEditor from '$lib/components/VariableEditor.svelte'
|
||||
import type { ContextualVariable, ListableVariable } from '$lib/gen'
|
||||
import type { ContextualVariable, ListableVariable, WorkspaceDeployUISettings } from '$lib/gen'
|
||||
import { OauthService, VariableService, WorkspaceService } from '$lib/gen'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { canWrite, isOwner, truncate } from '$lib/utils'
|
||||
import { ALL_DEPLOYABLE, canWrite, isDeployable, isOwner, truncate } from '$lib/utils'
|
||||
import {
|
||||
Plus,
|
||||
FileUp,
|
||||
@@ -78,6 +78,18 @@
|
||||
})
|
||||
}
|
||||
|
||||
let deployUiSettings: WorkspaceDeployUISettings | undefined = undefined
|
||||
|
||||
async function getDeployUiSettings() {
|
||||
if (!$enterpriseLicense) {
|
||||
deployUiSettings = ALL_DEPLOYABLE
|
||||
return
|
||||
}
|
||||
let settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
deployUiSettings = settings.deploy_ui ?? ALL_DEPLOYABLE
|
||||
}
|
||||
getDeployUiSettings()
|
||||
|
||||
async function loadContextualVariables(): Promise<void> {
|
||||
contextualVariables = await VariableService.listContextualVariables({
|
||||
workspace: $workspaceStore!
|
||||
@@ -347,13 +359,17 @@
|
||||
},
|
||||
disabled: !owner
|
||||
},
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'variable')
|
||||
}
|
||||
},
|
||||
...(isDeployable(is_secret ? 'secret' : 'variable', path, deployUiSettings)
|
||||
? [
|
||||
{
|
||||
displayName: 'Deploy to prod/staging',
|
||||
icon: FileUp,
|
||||
action: () => {
|
||||
deploymentDrawer.openDrawer(path, 'variable')
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
displayName: owner ? 'Share' : 'See Permissions',
|
||||
action: () => {
|
||||
|
||||
@@ -482,6 +482,22 @@
|
||||
}
|
||||
gitSyncTestJobs = []
|
||||
}
|
||||
if (settings.deploy_ui != undefined && settings.deploy_ui != null) {
|
||||
deployUiSettings = {
|
||||
include_path:
|
||||
settings.deploy_ui.include_path?.length ?? 0 > 0
|
||||
? settings.deploy_ui.include_path ?? []
|
||||
: [],
|
||||
include_type: {
|
||||
scripts: (settings.deploy_ui.include_type?.indexOf('script') ?? -1) >= 0,
|
||||
flows: (settings.deploy_ui.include_type?.indexOf('flow') ?? -1) >= 0,
|
||||
apps: (settings.deploy_ui.include_type?.indexOf('app') ?? -1) >= 0,
|
||||
resources: (settings.deploy_ui.include_type?.indexOf('resource') ?? -1) >= 0,
|
||||
variables: (settings.deploy_ui.include_type?.indexOf('variable') ?? -1) >= 0,
|
||||
secrets: (settings.deploy_ui.include_type?.indexOf('secret') ?? -1) >= 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check openai_client_credentials_oauth
|
||||
usingOpenaiClientCredentialsOauth = await ResourceService.existsResourceType({
|
||||
@@ -490,6 +506,18 @@
|
||||
})
|
||||
}
|
||||
|
||||
let deployUiSettings: {
|
||||
include_path: string[]
|
||||
include_type: {
|
||||
scripts: boolean
|
||||
flows: boolean
|
||||
apps: boolean
|
||||
resources: boolean
|
||||
variables: boolean
|
||||
secrets: boolean
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if ($workspaceStore) {
|
||||
loadSettings()
|
||||
@@ -688,7 +716,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if $enterpriseLicense}
|
||||
<DeployToSetting bind:workspaceToDeployTo />
|
||||
<DeployToSetting bind:workspaceToDeployTo bind:deployUiSettings />
|
||||
{:else}
|
||||
<div class="my-2"
|
||||
><Alert type="error" title="Enterprise license required"
|
||||
|
||||
Reference in New Issue
Block a user