mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-11 00:06:06 +00:00
feat: private hub (#3491)
* feat: private hub v0 * feat: add UI setting * Update ee.rs * Update ee.rs * fix: remove ee symlink * fix: add back CE ee.rs * feat: disable s3 embedding loading when private hub * feat: reload embeddings on hub url change * fix: only reload embeddings db on server mode * patch: set default hub url const * fix: nit
This commit is contained in:
+11
-7
@@ -19,8 +19,8 @@ use windmill_api::HTTP_CLIENT;
|
||||
use windmill_common::{
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, CUSTOM_TAGS_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, DISABLE_STATS_SETTING, ENV_SETTINGS,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
DEFAULT_TAGS_PER_WORKSPACE_SETTING, ENV_SETTINGS, EXPOSE_DEBUG_METRICS_SETTING,
|
||||
EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING, HUB_BASE_URL_SETTING,
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING,
|
||||
NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING, PIP_INDEX_URL_SETTING,
|
||||
REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING,
|
||||
@@ -47,10 +47,10 @@ use windmill_worker::{
|
||||
use crate::monitor::{
|
||||
initial_load, load_keep_job_dir, load_require_preexisting_user, load_tag_per_workspace_enabled,
|
||||
monitor_db, monitor_pool, reload_base_url_setting, reload_bunfig_install_scopes_setting,
|
||||
reload_extra_pip_index_url_setting, reload_job_default_timeout_setting, reload_license_key,
|
||||
reload_npm_config_registry_setting, reload_pip_index_url_setting,
|
||||
reload_retention_period_setting, reload_scim_token_setting, reload_server_config,
|
||||
reload_worker_config,
|
||||
reload_extra_pip_index_url_setting, reload_hub_base_url_setting,
|
||||
reload_job_default_timeout_setting, reload_license_key, reload_npm_config_registry_setting,
|
||||
reload_pip_index_url_setting, reload_retention_period_setting, reload_scim_token_setting,
|
||||
reload_server_config, reload_worker_config,
|
||||
};
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -498,7 +498,11 @@ Windmill Community Edition {GIT_VERSION}
|
||||
tracing::error!(error = %e, "Could not send killpill to server");
|
||||
}
|
||||
},
|
||||
DISABLE_STATS_SETTING => {},
|
||||
HUB_BASE_URL_SETTING => {
|
||||
if let Err(e) = reload_hub_base_url_setting(&db, server_mode).await {
|
||||
tracing::error!(error = %e, "Could not reload hub base url setting");
|
||||
}
|
||||
},
|
||||
a @_ => {
|
||||
tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a);
|
||||
}
|
||||
|
||||
+52
-3
@@ -14,6 +14,9 @@ use tokio::{
|
||||
join,
|
||||
sync::{mpsc, RwLock},
|
||||
};
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
use windmill_api::embeddings::update_embeddings_db;
|
||||
use windmill_api::{
|
||||
oauth2_ee::{build_oauth_clients, OAuthClient},
|
||||
DEFAULT_BODY_LIMIT, IS_SECURE, OAUTH_CLIENTS, REQUEST_SIZE_LIMIT, SAML_METADATA, SCIM_TOKEN,
|
||||
@@ -24,8 +27,8 @@ use windmill_common::{
|
||||
global_settings::{
|
||||
BASE_URL_SETTING, BUNFIG_INSTALL_SCOPES_SETTING, DEFAULT_TAGS_PER_WORKSPACE_SETTING,
|
||||
EXPOSE_DEBUG_METRICS_SETTING, EXPOSE_METRICS_SETTING, EXTRA_PIP_INDEX_URL_SETTING,
|
||||
JOB_DEFAULT_TIMEOUT_SECS_SETTING, KEEP_JOB_DIR_SETTING, LICENSE_KEY_SETTING,
|
||||
NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING, PIP_INDEX_URL_SETTING,
|
||||
HUB_BASE_URL_SETTING, JOB_DEFAULT_TIMEOUT_SECS_SETTING, KEEP_JOB_DIR_SETTING,
|
||||
LICENSE_KEY_SETTING, NPM_CONFIG_REGISTRY_SETTING, OAUTH_SETTING, PIP_INDEX_URL_SETTING,
|
||||
REQUEST_SIZE_LIMIT_SETTING, REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING,
|
||||
RETENTION_PERIOD_SECS_SETTING, SAML_METADATA_SETTING, SCIM_TOKEN_SETTING,
|
||||
},
|
||||
@@ -37,7 +40,7 @@ use windmill_common::{
|
||||
load_worker_config, reload_custom_tags_setting, DEFAULT_TAGS_PER_WORKSPACE, SERVER_CONFIG,
|
||||
WORKER_CONFIG,
|
||||
},
|
||||
BASE_URL, DB, METRICS_DEBUG_ENABLED, METRICS_ENABLED,
|
||||
BASE_URL, DB, DEFAULT_HUB_BASE_URL, HUB_BASE_URL, METRICS_DEBUG_ENABLED, METRICS_ENABLED,
|
||||
};
|
||||
use windmill_queue::cancel_job;
|
||||
use windmill_worker::{
|
||||
@@ -136,6 +139,10 @@ pub async fn initial_load(
|
||||
tracing::error!("Error reloading base url: {:?}", e)
|
||||
}
|
||||
|
||||
if let Err(e) = reload_hub_base_url_setting(db, server_mode).await {
|
||||
tracing::error!("Error reloading hub base url: {:?}", e)
|
||||
}
|
||||
|
||||
#[cfg(feature = "parquet")]
|
||||
if !_is_agent {
|
||||
reload_s3_cache_setting(&db).await;
|
||||
@@ -1025,3 +1032,45 @@ async fn cancel_zombie_flow_job(
|
||||
ntx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn reload_hub_base_url_setting(db: &DB, server_mode: bool) -> error::Result<()> {
|
||||
let hub_base_url = load_value_from_global_settings(db, HUB_BASE_URL_SETTING).await?;
|
||||
|
||||
let base_url = if let Some(q) = hub_base_url {
|
||||
if let Ok(v) = serde_json::from_value::<String>(q.clone()) {
|
||||
if v != "" {
|
||||
v
|
||||
} else {
|
||||
DEFAULT_HUB_BASE_URL.to_string()
|
||||
}
|
||||
} else {
|
||||
tracing::error!(
|
||||
"Could not parse hub_base_url setting as a string, found: {:#?}",
|
||||
&q
|
||||
);
|
||||
DEFAULT_HUB_BASE_URL.to_string()
|
||||
}
|
||||
} else {
|
||||
DEFAULT_HUB_BASE_URL.to_string()
|
||||
};
|
||||
|
||||
let mut l = HUB_BASE_URL.write().await;
|
||||
if server_mode {
|
||||
#[cfg(feature = "embedding")]
|
||||
if *l != base_url {
|
||||
let disable_embedding = std::env::var("DISABLE_EMBEDDING")
|
||||
.ok()
|
||||
.map(|x| x.parse::<bool>().unwrap_or(false))
|
||||
.unwrap_or(false);
|
||||
if !disable_embedding {
|
||||
let db_clone = db.clone();
|
||||
tokio::spawn(async move {
|
||||
update_embeddings_db(&db_clone).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
*l = base_url;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ use windmill_common::{
|
||||
http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, Pagination, StripPath,
|
||||
},
|
||||
variables::build_crypt,
|
||||
HUB_BASE_URL,
|
||||
};
|
||||
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
@@ -633,16 +634,12 @@ async fn create_app(
|
||||
async fn list_hub_apps(Extension(db): Extension<DB>) -> impl IntoResponse {
|
||||
let (status_code, headers, body) = query_elems_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
"https://hub.windmill.dev/searchUiData?approved=true",
|
||||
&format!("{}/searchUiData?approved=true", *HUB_BASE_URL.read().await),
|
||||
None,
|
||||
&db,
|
||||
)
|
||||
.await?;
|
||||
Ok::<_, Error>((
|
||||
status_code,
|
||||
headers,
|
||||
body
|
||||
))
|
||||
Ok::<_, Error>((status_code, headers, body))
|
||||
}
|
||||
|
||||
pub async fn get_hub_app_by_id(
|
||||
@@ -651,7 +648,7 @@ pub async fn get_hub_app_by_id(
|
||||
) -> JsonResult<serde_json::Value> {
|
||||
let value = http_get_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
&format!("https://hub.windmill.dev/apps/{id}/json"),
|
||||
&format!("{}/apps/{}/json", *HUB_BASE_URL.read().await, id),
|
||||
false,
|
||||
None,
|
||||
&db,
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
#[cfg(feature = "embedding")]
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
#[cfg(feature = "embedding")]
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
#[cfg(feature = "embedding")]
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
use windmill_common::DEFAULT_HUB_BASE_URL;
|
||||
#[cfg(feature = "embedding")]
|
||||
use windmill_common::HUB_BASE_URL;
|
||||
|
||||
use axum::Router;
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
Json,
|
||||
Json,
|
||||
};
|
||||
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
use axum::{
|
||||
routing::get, Extension
|
||||
};
|
||||
use axum::routing::get;
|
||||
#[cfg(feature = "embedding")]
|
||||
use candle_core::{Device, Tensor};
|
||||
#[cfg(feature = "embedding")]
|
||||
@@ -45,9 +46,14 @@ use windmill_common::utils::http_get_from_hub;
|
||||
use windmill_common::error::JsonResult;
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
|
||||
use crate::{resources::ResourceType, HTTP_CLIENT};
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref EMBEDDINGS_DB: Arc<RwLock<Option<EmbeddingsDb>>> = Arc::new(RwLock::new(None));
|
||||
pub static ref MODEL_INSTANCE: Arc<RwLock<Option<Arc<ModelInstance>>>> = Arc::new(RwLock::new(None));
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
#[derive(Deserialize)]
|
||||
struct HubScriptsQuery {
|
||||
@@ -71,9 +77,8 @@ pub struct HubScriptResult {
|
||||
#[cfg(feature = "embedding")]
|
||||
async fn query_hub_scripts(
|
||||
Query(query): Query<HubScriptsQuery>,
|
||||
Extension(embeddings_db): Extension<Arc<RwLock<Option<EmbeddingsDb>>>>,
|
||||
) -> JsonResult<Vec<HubScriptResult>> {
|
||||
let embeddings_db = embeddings_db.read().await;
|
||||
let embeddings_db = EMBEDDINGS_DB.read().await;
|
||||
|
||||
if let Some(embeddings_db) = embeddings_db.as_ref() {
|
||||
let results = embeddings_db
|
||||
@@ -88,7 +93,6 @@ async fn query_hub_scripts(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
#[derive(Deserialize)]
|
||||
struct ResourceTypesQuery {
|
||||
@@ -106,9 +110,8 @@ pub struct ResourceTypeResult {
|
||||
async fn query_resource_types(
|
||||
Query(query): Query<ResourceTypesQuery>,
|
||||
Path(w_id): Path<String>,
|
||||
Extension(embeddings_db): Extension<Arc<RwLock<Option<EmbeddingsDb>>>>,
|
||||
) -> JsonResult<Vec<ResourceTypeResult>> {
|
||||
let embeddings_db = embeddings_db.read().await;
|
||||
let embeddings_db = EMBEDDINGS_DB.read().await;
|
||||
|
||||
if let Some(embeddings_db) = embeddings_db.as_ref() {
|
||||
let results = embeddings_db
|
||||
@@ -123,7 +126,6 @@ async fn query_resource_types(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
struct HubScript {
|
||||
@@ -292,24 +294,41 @@ impl EmbeddingsDb {
|
||||
self.db
|
||||
.create_collection("resource_types".to_string(), 384, Distance::Cosine)?;
|
||||
|
||||
let response = HTTP_CLIENT
|
||||
.get("https://bucket.windmillhub.com/embeddings/scripts_embeddings.json")
|
||||
.send()
|
||||
.await;
|
||||
let response =
|
||||
if response.is_err() || response.as_ref().unwrap().error_for_status_ref().is_err() {
|
||||
tracing::warn!("Failed to get scripts embeddings from bucket, trying hub...");
|
||||
let hub_base_url = HUB_BASE_URL.read().await.clone();
|
||||
|
||||
let response = match hub_base_url.as_str() {
|
||||
DEFAULT_HUB_BASE_URL => {
|
||||
let response = HTTP_CLIENT
|
||||
.get("https://bucket.windmillhub.com/embeddings/scripts_embeddings.json")
|
||||
.send()
|
||||
.await;
|
||||
|
||||
if response.is_err() || response.as_ref().unwrap().error_for_status_ref().is_err() {
|
||||
tracing::warn!("Failed to get scripts embeddings from bucket, trying hub...");
|
||||
http_get_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
&format!("{}/scripts/embeddings", hub_base_url),
|
||||
false,
|
||||
None,
|
||||
pg_db,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
response.unwrap()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
http_get_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
"https://hub.windmill.dev/scripts/embeddings",
|
||||
&format!("{}/scripts/embeddings", hub_base_url),
|
||||
false,
|
||||
None,
|
||||
pg_db,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
response.unwrap()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if response.error_for_status_ref().is_err() {
|
||||
return Err(anyhow!(
|
||||
"Failed to get scripts embeddings from hub with error code: {}",
|
||||
@@ -338,25 +357,40 @@ impl EmbeddingsDb {
|
||||
self.db.insert_into_collection("scripts", embedding)?;
|
||||
}
|
||||
|
||||
let response = HTTP_CLIENT
|
||||
.get("https://bucket.windmillhub.com/embeddings/resource_types_embeddings.json")
|
||||
.send()
|
||||
.await;
|
||||
let response = if response.is_err()
|
||||
|| response.as_ref().unwrap().error_for_status_ref().is_err()
|
||||
{
|
||||
tracing::warn!("Failed to get resource types embeddings from bucket, trying hub...");
|
||||
http_get_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
"https://hub.windmill.dev/resource_types/embeddings",
|
||||
false,
|
||||
None,
|
||||
pg_db,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
response.unwrap()
|
||||
let response = match hub_base_url.as_str() {
|
||||
DEFAULT_HUB_BASE_URL => {
|
||||
let response = HTTP_CLIENT
|
||||
.get("https://bucket.windmillhub.com/embeddings/resource_types_embeddings.json")
|
||||
.send()
|
||||
.await;
|
||||
if response.is_err() || response.as_ref().unwrap().error_for_status_ref().is_err() {
|
||||
tracing::warn!(
|
||||
"Failed to get resource types embeddings from bucket, trying hub..."
|
||||
);
|
||||
http_get_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
&format!("{}/resource_types/embeddings", hub_base_url),
|
||||
false,
|
||||
None,
|
||||
pg_db,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
response.unwrap()
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
http_get_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
&format!("{}/resource_types/embeddings", hub_base_url),
|
||||
false,
|
||||
None,
|
||||
pg_db,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
|
||||
if response.error_for_status_ref().is_err() {
|
||||
return Err(anyhow!(
|
||||
"Failed to get resource types embeddings from hub with error code: {}",
|
||||
@@ -554,9 +588,7 @@ fn normalize_l2(v: &Tensor) -> Result<Tensor> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
pub fn load_embeddings_db(db: &Pool<Postgres>) -> Arc<RwLock<Option<EmbeddingsDb>>> {
|
||||
let embeddings_db: Arc<RwLock<Option<EmbeddingsDb>>> = Arc::new(RwLock::new(None));
|
||||
|
||||
pub fn load_embeddings_db(db: &Pool<Postgres>) -> () {
|
||||
let disable_embedding = std::env::var("DISABLE_EMBEDDING")
|
||||
.ok()
|
||||
.map(|x| x.parse::<bool>().unwrap_or(false))
|
||||
@@ -564,24 +596,14 @@ pub fn load_embeddings_db(db: &Pool<Postgres>) -> Arc<RwLock<Option<EmbeddingsDb
|
||||
|
||||
if !disable_embedding {
|
||||
let db_clone = db.clone();
|
||||
let embeddings_clone: Arc<RwLock<Option<EmbeddingsDb>>> = embeddings_db.clone();
|
||||
tokio::spawn(async move {
|
||||
let model_instance = ModelInstance::new().await;
|
||||
|
||||
if let Ok(model_instance) = model_instance {
|
||||
let model_instance = Arc::new(model_instance);
|
||||
let mut model_instance_lock = MODEL_INSTANCE.write().await;
|
||||
*model_instance_lock = Some(Arc::new(model_instance));
|
||||
drop(model_instance_lock);
|
||||
loop {
|
||||
tracing::info!("Creating embeddings DB...");
|
||||
let new_embeddings_db =
|
||||
EmbeddingsDb::new(&db_clone, model_instance.clone()).await;
|
||||
if let Err(e) = new_embeddings_db.as_ref() {
|
||||
tracing::error!("Failed to create embeddings db: {}", e);
|
||||
} else {
|
||||
let mut embeddings_db = embeddings_clone.write().await;
|
||||
*embeddings_db = new_embeddings_db.ok();
|
||||
tracing::info!("Created embeddings DB");
|
||||
}
|
||||
|
||||
update_embeddings_db(&db_clone).await;
|
||||
tokio::time::sleep(std::time::Duration::from_secs(3600 * 24)).await;
|
||||
}
|
||||
} else {
|
||||
@@ -592,39 +614,41 @@ pub fn load_embeddings_db(db: &Pool<Postgres>) -> Arc<RwLock<Option<EmbeddingsDb
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
embeddings_db
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
pub fn workspaced_service(embeddings_db: Option<Arc<RwLock<Option<EmbeddingsDb>>>>) -> Router {
|
||||
if let Some(embeddings_db) = embeddings_db {
|
||||
Router::new()
|
||||
.route("/query_resource_types", get(query_resource_types))
|
||||
.layer(Extension(embeddings_db))
|
||||
pub async fn update_embeddings_db(db: &Pool<Postgres>) -> () {
|
||||
if let Some(model_instance) = MODEL_INSTANCE.read().await.as_ref() {
|
||||
tracing::info!("Creating embeddings DB...");
|
||||
let new_embeddings_db = EmbeddingsDb::new(&db, model_instance.clone()).await;
|
||||
if let Err(e) = new_embeddings_db.as_ref() {
|
||||
tracing::error!("Failed to create embeddings db: {}", e);
|
||||
} else {
|
||||
let mut embeddings_db = EMBEDDINGS_DB.write().await;
|
||||
*embeddings_db = new_embeddings_db.ok();
|
||||
tracing::info!("Created embeddings DB");
|
||||
}
|
||||
} else {
|
||||
Router::new()
|
||||
tracing::error!("Could not update embeddings DB, model instance not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedding")]
|
||||
pub fn global_service(embeddings_db: Option<Arc<RwLock<Option<EmbeddingsDb>>>>) -> Router {
|
||||
if let Some(embeddings_db) = embeddings_db {
|
||||
Router::new()
|
||||
.route("/query_hub_scripts", get(query_hub_scripts))
|
||||
.layer(Extension(embeddings_db))
|
||||
} else {
|
||||
Router::new()
|
||||
}
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new().route("/query_resource_types", get(query_resource_types))
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "embedding"))]
|
||||
pub fn workspaced_service(_embeddings_db: Option<()>) -> Router {
|
||||
Router::new()
|
||||
#[cfg(feature = "embedding")]
|
||||
pub fn global_service() -> Router {
|
||||
Router::new().route("/query_hub_scripts", get(query_hub_scripts))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "embedding"))]
|
||||
pub fn global_service(_embeddings_db: Option<()>) -> Router {
|
||||
Router::new()
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "embedding"))]
|
||||
pub fn global_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ use sqlx::{FromRow, Postgres, Transaction};
|
||||
use windmill_audit::audit_ee::audit_log;
|
||||
use windmill_audit::ActionKind;
|
||||
use windmill_common::utils::query_elems_from_hub;
|
||||
use windmill_common::HUB_BASE_URL;
|
||||
use windmill_common::{
|
||||
db::UserDB,
|
||||
error::{self, to_anyhow, Error, JsonResult, Result},
|
||||
@@ -167,7 +168,10 @@ async fn list_flows(
|
||||
async fn list_hub_flows(Extension(db): Extension<DB>) -> impl IntoResponse {
|
||||
let (status_code, headers, response) = query_elems_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
"https://hub.windmill.dev/searchFlowData?approved=true",
|
||||
&format!(
|
||||
"{}/searchFlowData?approved=true",
|
||||
*HUB_BASE_URL.read().await
|
||||
),
|
||||
None,
|
||||
&db,
|
||||
)
|
||||
@@ -199,7 +203,7 @@ pub async fn get_hub_flow_by_id(
|
||||
) -> JsonResult<serde_json::Value> {
|
||||
let value = http_get_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
&format!("https://hub.windmill.dev/flows/{id}/json"),
|
||||
&format!("{}/flows/{}/json", *HUB_BASE_URL.read().await, id),
|
||||
false,
|
||||
None,
|
||||
&db,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use crate::{db::DB, HTTP_CLIENT};
|
||||
use axum::{
|
||||
extract::Query, response::IntoResponse, routing::get, Extension, Router,
|
||||
};
|
||||
use windmill_common::{error::Error, utils::query_elems_from_hub};
|
||||
use axum::{extract::Query, response::IntoResponse, routing::get, Extension, Router};
|
||||
use windmill_common::{error::Error, utils::query_elems_from_hub, HUB_BASE_URL};
|
||||
|
||||
pub fn global_service() -> Router {
|
||||
Router::new().route("/hub/list", get(list_hub_integrations))
|
||||
@@ -24,14 +22,10 @@ async fn list_hub_integrations(
|
||||
|
||||
let (status_code, headers, response) = query_elems_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
"https://hub.windmill.dev/integrations/list",
|
||||
&format!("{}/integrations/list", *HUB_BASE_URL.read().await),
|
||||
Some(query_params),
|
||||
&db,
|
||||
)
|
||||
.await?;
|
||||
Ok::<_, Error>((
|
||||
status_code,
|
||||
headers,
|
||||
response
|
||||
))
|
||||
Ok::<_, Error>((status_code, headers, response))
|
||||
}
|
||||
|
||||
@@ -171,18 +171,10 @@ pub async fn run_server(
|
||||
|
||||
let sp_extension = Arc::new(saml_ee::build_sp_extension().await?);
|
||||
|
||||
let embeddings_db = if server_mode {
|
||||
if server_mode {
|
||||
#[cfg(feature = "embedding")]
|
||||
{
|
||||
Some(load_embeddings_db(&db))
|
||||
}
|
||||
#[cfg(not(feature = "embedding"))]
|
||||
{
|
||||
Some(())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
load_embeddings_db(&db)
|
||||
}
|
||||
|
||||
let job_helpers_service = {
|
||||
#[cfg(feature = "parquet")]
|
||||
@@ -209,10 +201,7 @@ pub async fn run_server(
|
||||
.nest("/apps", apps::workspaced_service())
|
||||
.nest("/audit", audit::workspaced_service())
|
||||
.nest("/capture", capture::workspaced_service())
|
||||
.nest(
|
||||
"/embeddings",
|
||||
embeddings::workspaced_service(embeddings_db.clone()),
|
||||
)
|
||||
.nest("/embeddings", embeddings::workspaced_service())
|
||||
.nest("/drafts", drafts::workspaced_service())
|
||||
.nest("/favorites", favorite::workspaced_service())
|
||||
.nest("/flows", flows::workspaced_service())
|
||||
@@ -250,10 +239,7 @@ pub async fn run_server(
|
||||
.nest("/flows", flows::global_service())
|
||||
.nest("/apps", apps::global_service().layer(cors.clone()))
|
||||
.nest("/schedules", schedule::global_service())
|
||||
.nest(
|
||||
"/embeddings",
|
||||
embeddings::global_service(embeddings_db.clone()),
|
||||
)
|
||||
.nest("/embeddings", embeddings::global_service())
|
||||
.route_layer(from_extractor::<ApiAuthed>())
|
||||
.route_layer(from_extractor::<users::Tokened>())
|
||||
.nest("/jobs", jobs::global_root_service())
|
||||
|
||||
@@ -44,6 +44,7 @@ use windmill_common::{
|
||||
utils::{
|
||||
not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath,
|
||||
},
|
||||
HUB_BASE_URL,
|
||||
};
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
use windmill_parser_ts::remove_pinned_imports;
|
||||
@@ -290,7 +291,7 @@ async fn get_top_hub_scripts(
|
||||
|
||||
let (status_code, headers, response) = query_elems_from_hub(
|
||||
&HTTP_CLIENT,
|
||||
"https://hub.windmill.dev/scripts/top",
|
||||
&format!("{}/scripts/top", *HUB_BASE_URL.read().await),
|
||||
Some(query_params),
|
||||
&db,
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ use serde::Deserialize;
|
||||
use tokio::time::timeout;
|
||||
use windmill_common::{
|
||||
error::{self, to_anyhow, JsonResult, Result},
|
||||
global_settings::{AUTOMATE_USERNAME_CREATION_SETTING, ENV_SETTINGS},
|
||||
global_settings::{AUTOMATE_USERNAME_CREATION_SETTING, ENV_SETTINGS, HUB_BASE_URL_SETTING},
|
||||
server::Smtp,
|
||||
};
|
||||
|
||||
@@ -147,7 +147,6 @@ pub async fn test_s3_bucket(
|
||||
Ok("Tested blob storage successfully".to_string())
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct TestKey {
|
||||
pub license_key: String,
|
||||
@@ -251,6 +250,7 @@ pub async fn get_global_setting(
|
||||
if !key.starts_with("default_error_handler_")
|
||||
&& !key.starts_with("default_recovery_handler_")
|
||||
&& key != AUTOMATE_USERNAME_CREATION_SETTING
|
||||
&& key != HUB_BASE_URL_SETTING
|
||||
{
|
||||
require_super_admin(&db, &authed.email).await?;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ pub const REQUIRE_PREEXISTING_USER_FOR_OAUTH_SETTING: &str = "require_preexistin
|
||||
pub const OBJECT_STORE_CACHE_CONFIG_SETTING: &str = "object_store_cache_config";
|
||||
|
||||
pub const AUTOMATE_USERNAME_CREATION_SETTING: &str = "automate_username_creation";
|
||||
pub const HUB_BASE_URL_SETTING: &str = "hub_base_url";
|
||||
|
||||
pub const ENV_SETTINGS: [&str; 50] = [
|
||||
"DISABLE_NSJAIL",
|
||||
|
||||
@@ -46,6 +46,8 @@ pub mod tracing_init;
|
||||
pub const DEFAULT_MAX_CONNECTIONS_SERVER: u32 = 50;
|
||||
pub const DEFAULT_MAX_CONNECTIONS_WORKER: u32 = 4;
|
||||
|
||||
pub const DEFAULT_HUB_BASE_URL: &str = "https://hub.windmill.dev";
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref METRICS_PORT: u16 = std::env::var("METRICS_PORT")
|
||||
.ok()
|
||||
@@ -69,6 +71,8 @@ lazy_static::lazy_static! {
|
||||
|
||||
pub static ref BASE_URL: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
|
||||
pub static ref IS_READY: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
|
||||
pub static ref HUB_BASE_URL: Arc<RwLock<String>> = Arc::new(RwLock::new(DEFAULT_HUB_BASE_URL.to_string()));
|
||||
}
|
||||
|
||||
pub async fn shutdown_signal(
|
||||
|
||||
@@ -14,7 +14,7 @@ use std::{
|
||||
use crate::{
|
||||
error::{to_anyhow, Error},
|
||||
utils::http_get_from_hub,
|
||||
DB,
|
||||
DB, HUB_BASE_URL,
|
||||
};
|
||||
use serde::de::Error as _;
|
||||
use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize};
|
||||
@@ -353,7 +353,7 @@ pub async fn get_hub_script_by_path(
|
||||
|
||||
let content = http_get_from_hub(
|
||||
http_client,
|
||||
&format!("https://hub.windmill.dev/raw/{path}.ts"),
|
||||
&format!("{}/raw/{}.ts", *HUB_BASE_URL.read().await, path),
|
||||
true,
|
||||
None,
|
||||
db,
|
||||
@@ -377,7 +377,7 @@ pub async fn get_full_hub_script_by_path(
|
||||
|
||||
let value = http_get_from_hub(
|
||||
http_client,
|
||||
&format!("https://hub.windmill.dev/raw2/{path}"),
|
||||
&format!("{}/raw2/{}", *HUB_BASE_URL.read().await, path),
|
||||
true,
|
||||
None,
|
||||
db,
|
||||
|
||||
+11
-9
@@ -21,6 +21,11 @@ async function pull(opts: GlobalOptions) {
|
||||
key: "uid",
|
||||
});
|
||||
|
||||
const hubBaseUrl =
|
||||
(await SettingService.getGlobal({
|
||||
key: "hubBaseUrl",
|
||||
})) ?? "https://hub.windmill.dev";
|
||||
|
||||
const headers = {
|
||||
Accept: "application/json",
|
||||
"X-email": userInfo.email,
|
||||
@@ -40,20 +45,17 @@ async function pull(opts: GlobalOptions) {
|
||||
created_by: string;
|
||||
created_at: Date;
|
||||
comments: never[];
|
||||
}[] = await fetch("https://hub.windmill.dev/resource_types/list", {
|
||||
}[] = await fetch(hubBaseUrl + "/resource_types/list", {
|
||||
headers,
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then((list: { id: number; name: string }[]) =>
|
||||
list.map((x) =>
|
||||
fetch(
|
||||
"https://hub.windmill.dev/resource_types/" + x.id + "/" + x.name,
|
||||
{
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
fetch(hubBaseUrl + "/resource_types/" + x.id + "/" + x.name, {
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
.then((x) => Promise.all(x))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
type Flow
|
||||
} from '$lib/gen'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { hubBaseUrlStore } from '$lib/stores'
|
||||
|
||||
import { CheckCircle2, Loader2, RotateCw, XCircle } from 'lucide-svelte'
|
||||
|
||||
@@ -221,7 +222,7 @@
|
||||
<div class="text-xs">
|
||||
Example of error handler scripts can be found on <a
|
||||
target="_blank"
|
||||
href="https://hub.windmill.dev/failures"
|
||||
href="{$hubBaseUrlStore}/failures"
|
||||
>
|
||||
Windmill Hub</a
|
||||
>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import SchemaViewer from './SchemaViewer.svelte'
|
||||
import { scriptPathToHref } from '$lib/scripts'
|
||||
import { cleanExpr } from '$lib/utils'
|
||||
import { hubBaseUrlStore } from '$lib/stores'
|
||||
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import FlowModuleScript from './flows/content/FlowModuleScript.svelte'
|
||||
@@ -34,7 +35,7 @@
|
||||
<a
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
href={scriptPathToHref(stepDetail?.value?.path ?? '')}
|
||||
href={scriptPathToHref(stepDetail?.value?.path ?? '', $hubBaseUrlStore)}
|
||||
class=""
|
||||
>
|
||||
<IconedPath path={stepDetail?.value?.path ?? ''} />
|
||||
@@ -52,7 +53,7 @@
|
||||
class="w-full h-full text-sm"
|
||||
title="embedded script from hub"
|
||||
frameborder="0"
|
||||
src="https://hub.windmill.dev/embed/script/{stepDetail.value?.path?.substring(4)}"
|
||||
src="{$hubBaseUrlStore}/embed/script/{stepDetail.value?.path?.substring(4)}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -116,7 +117,7 @@
|
||||
<a
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
href={scriptPathToHref(stepDetail?.value?.path ?? '')}
|
||||
href={scriptPathToHref(stepDetail?.value?.path ?? '', $hubBaseUrlStore)}
|
||||
class=""
|
||||
>
|
||||
<IconedPath path={stepDetail?.value?.path ?? ''} />
|
||||
@@ -166,7 +167,7 @@
|
||||
class="w-full grow text-sm"
|
||||
title="embedded script from hub"
|
||||
frameborder="0"
|
||||
src="https://hub.windmill.dev/embed/script/{stepDetail.value?.path?.substring(4)}"
|
||||
src="{$hubBaseUrlStore}/embed/script/{stepDetail.value?.path?.substring(4)}"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
@@ -628,7 +628,7 @@
|
||||
// displayName: 'Publish to Hub',
|
||||
// icon: faGlobe,
|
||||
// action: () => {
|
||||
// const url = appToHubUrl(toStatic($app, $staticExporter, $summary))
|
||||
// const url = appToHubUrl(toStatic($app, $staticExporter, $summary, $hubBaseUrlStore))
|
||||
// window.open(url.toString(), '_blank')
|
||||
// }
|
||||
// },
|
||||
|
||||
@@ -110,6 +110,14 @@ export const settings: Record<string, Setting[]> = {
|
||||
fieldType: 'text',
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
},
|
||||
{
|
||||
label: 'Private hub base url',
|
||||
description: 'Base url of your private hub instance',
|
||||
key: 'hub_base_url',
|
||||
fieldType: 'text',
|
||||
storage: 'setting',
|
||||
ee_only: ''
|
||||
}
|
||||
],
|
||||
'SSO/OAuth': [],
|
||||
|
||||
@@ -9,9 +9,10 @@ export function scriptToHubUrl(
|
||||
kind: Script.kind,
|
||||
language: Script.language,
|
||||
schema: Schema | any,
|
||||
lock: string | undefined
|
||||
lock: string | undefined,
|
||||
hubBaseUrl: string
|
||||
): URL {
|
||||
const url = new URL('https://hub.windmill.dev/scripts/add')
|
||||
const url = new URL(hubBaseUrl + '/scripts/add')
|
||||
url.hash = encodeState({ content, summary, description, kind, language, schema, lock })
|
||||
|
||||
return url
|
||||
@@ -37,8 +38,8 @@ export async function loadHubApps() {
|
||||
}
|
||||
}
|
||||
|
||||
export function flowToHubUrl(flow: Flow): URL {
|
||||
const url = new URL('https://hub.windmill.dev/flows/add')
|
||||
export function flowToHubUrl(flow: Flow, hubBaseUrl: string): URL {
|
||||
const url = new URL(hubBaseUrl + '/flows/add')
|
||||
const openFlow = {
|
||||
value: flow.value,
|
||||
summary: flow.summary,
|
||||
@@ -49,8 +50,8 @@ export function flowToHubUrl(flow: Flow): URL {
|
||||
return url
|
||||
}
|
||||
|
||||
export function appToHubUrl(staticApp: any): URL {
|
||||
const url = new URL('https://hub.windmill.dev/apps/add')
|
||||
export function appToHubUrl(staticApp: any, hubBaseUrl: string): URL {
|
||||
const url = new URL(hubBaseUrl + '/apps/add')
|
||||
url.searchParams.append('app', encodeState(staticApp))
|
||||
return url
|
||||
}
|
||||
|
||||
@@ -79,9 +79,9 @@ export async function loadSchemaFlow(path: string): Promise<Schema> {
|
||||
return flow.schema as any
|
||||
}
|
||||
|
||||
export function scriptPathToHref(path: string): string {
|
||||
export function scriptPathToHref(path: string, hubBaseUrl: string): string {
|
||||
if (path.startsWith('hub/')) {
|
||||
return 'https://hub.windmill.dev/from_version/' + path.substring(4)
|
||||
return hubBaseUrl + '/from_version/' + path.substring(4)
|
||||
} else {
|
||||
return `/scripts/get/${path}?workspace=${get(workspaceStore)}`
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ export const starStore = writable(1)
|
||||
export const usersWorkspaceStore = writable<UserWorkspaceList | undefined>(undefined)
|
||||
export const superadmin = writable<string | false | undefined>(undefined)
|
||||
export const lspTokenStore = writable<string | undefined>(undefined)
|
||||
export const hubBaseUrlStore = writable<string>('https://hub.windmill.dev')
|
||||
export const userWorkspaces: Readable<
|
||||
Array<{
|
||||
id: string
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
OpenAPI,
|
||||
RawAppService,
|
||||
ScriptService,
|
||||
SettingService,
|
||||
UserService,
|
||||
WorkspaceService
|
||||
} from '$lib/gen'
|
||||
@@ -23,7 +24,8 @@
|
||||
userStore,
|
||||
workspaceStore,
|
||||
type UserExt,
|
||||
defaultScripts
|
||||
defaultScripts,
|
||||
hubBaseUrlStore
|
||||
} from '$lib/stores'
|
||||
import CenteredModal from '$lib/components/CenteredModal.svelte'
|
||||
import { afterNavigate, beforeNavigate, goto } from '$app/navigation'
|
||||
@@ -113,6 +115,7 @@
|
||||
loadFavorites()
|
||||
loadUsage()
|
||||
syncTutorialsTodos()
|
||||
loadHubBaseUrl()
|
||||
}
|
||||
|
||||
async function loadUsage() {
|
||||
@@ -124,6 +127,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadHubBaseUrl() {
|
||||
$hubBaseUrlStore =
|
||||
(await SettingService.getGlobal({ key: 'hub_base_url' })) ?? 'https://hub.windmill.dev'
|
||||
}
|
||||
|
||||
async function loadFavorites() {
|
||||
const scripts = await ScriptService.listScripts({
|
||||
workspace: $workspaceStore ?? '',
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import FlowViewer from '$lib/components/FlowViewer.svelte'
|
||||
import HighlightCode from '$lib/components/HighlightCode.svelte'
|
||||
import { Building, ExternalLink, GitFork, Globe2, Loader2 } from 'lucide-svelte'
|
||||
import { hubBaseUrlStore } from '$lib/stores'
|
||||
|
||||
import ItemsList from '$lib/components/home/ItemsList.svelte'
|
||||
import CreateActionsApp from '$lib/components/flows/CreateActionsApp.svelte'
|
||||
@@ -82,8 +83,7 @@
|
||||
<DrawerContent title={codeViewerObj?.summary ?? ''} on:close={codeViewer.closeDrawer}>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
href="https://hub.windmill.dev/scripts/{codeViewerObj?.app ?? ''}/{codeViewerObj?.ask_id ??
|
||||
0}"
|
||||
href="{$hubBaseUrlStore}/scripts/{codeViewerObj?.app ?? ''}/{codeViewerObj?.ask_id ?? 0}"
|
||||
variant="contained"
|
||||
color="light"
|
||||
size="xs"
|
||||
@@ -119,7 +119,7 @@
|
||||
<DrawerContent title="Hub flow" on:close={flowViewer.closeDrawer}>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
href="https://hub.windmill.dev/flows/{flowViewerFlow?.flow?.id}"
|
||||
href="{$hubBaseUrlStore}/flows/{flowViewerFlow?.flow?.id}"
|
||||
variant="contained"
|
||||
color="light"
|
||||
size="xs"
|
||||
@@ -157,7 +157,7 @@
|
||||
<DrawerContent title="Hub app" on:close={appViewer.closeDrawer}>
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
href="https://hub.windmill.dev/apps/{appViewerApp?.app?.id}"
|
||||
href="{$hubBaseUrlStore}/apps/{appViewerApp?.app?.id}"
|
||||
variant="contained"
|
||||
color="light"
|
||||
size="xs"
|
||||
@@ -271,7 +271,7 @@
|
||||
<Button
|
||||
startIcon={{ icon: ExternalLink }}
|
||||
target="_blank"
|
||||
href="https://hub.windmill.dev"
|
||||
href={$hubBaseUrlStore}
|
||||
variant="border"
|
||||
color="light"
|
||||
>
|
||||
@@ -283,7 +283,7 @@
|
||||
<Button
|
||||
startIcon={{ icon: ExternalLink }}
|
||||
target="_blank"
|
||||
href="https://hub.windmill.dev"
|
||||
href={$hubBaseUrlStore}
|
||||
variant="border"
|
||||
color="light"
|
||||
>Hub
|
||||
@@ -294,7 +294,7 @@
|
||||
<Button
|
||||
startIcon={{ icon: ExternalLink }}
|
||||
target="_blank"
|
||||
href="https://hub.windmill.dev"
|
||||
href={$hubBaseUrlStore}
|
||||
variant="border"
|
||||
color="light">Hub</Button
|
||||
>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
} from '$lib/utils'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import { runFormStore, userStore, workspaceStore } from '$lib/stores'
|
||||
import { hubBaseUrlStore, runFormStore, userStore, workspaceStore } from '$lib/stores'
|
||||
import SchemaViewer from '$lib/components/SchemaViewer.svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
import HighlightCode from '$lib/components/HighlightCode.svelte'
|
||||
@@ -374,7 +374,8 @@
|
||||
script.kind,
|
||||
script.language,
|
||||
script.schema,
|
||||
script.lock ?? ''
|
||||
script.lock ?? '',
|
||||
$hubBaseUrlStore
|
||||
).toString(),
|
||||
'_blank'
|
||||
)
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
superadmin,
|
||||
userStore,
|
||||
usersWorkspaceStore,
|
||||
workspaceStore
|
||||
workspaceStore,
|
||||
hubBaseUrlStore
|
||||
} from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { setQueryWithoutLoad, emptyString, tryEvery } from '$lib/utils'
|
||||
@@ -799,10 +800,8 @@
|
||||
|
||||
<div class="prose text-2xs text-tertiary">
|
||||
Pick a script or flow meant to be triggered when the `/windmill` command is invoked. Upon
|
||||
connection, templates for a <a href="https://hub.windmill.dev/scripts/slack/1405/"
|
||||
>script</a
|
||||
>
|
||||
and <a href="https://hub.windmill.dev/flows/28/">flow</a> are available.
|
||||
connection, templates for a <a href="{$hubBaseUrlStore}/scripts/slack/1405/">script</a>
|
||||
and <a href="{$hubBaseUrlStore}/flows/28/">flow</a> are available.
|
||||
|
||||
<br /><br />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user