mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 00:01:34 +00:00
Merge remote-tracking branch 'origin/main' into glm/select-users
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## [1.636.0](https://github.com/windmill-labs/windmill/compare/v1.635.1...v1.636.0) (2026-02-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow adding workspace scripts and flows as AI chat context ([#7882](https://github.com/windmill-labs/windmill/issues/7882)) ([5b8ec50](https://github.com/windmill-labs/windmill/commit/5b8ec502fef8fb439200e18b8c610d0f5998b6df))
|
||||
* google native triggers ([#7837](https://github.com/windmill-labs/windmill/issues/7837)) ([6f24f19](https://github.com/windmill-labs/windmill/commit/6f24f1939d75a597acc74c1589794d511e041baa))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* mark base_url as unsaved when using browser fallback ([#7964](https://github.com/windmill-labs/windmill/issues/7964)) ([e7b0b00](https://github.com/windmill-labs/windmill/commit/e7b0b00f5696828dec094155298d0c9dc033b355))
|
||||
|
||||
## [1.635.1](https://github.com/windmill-labs/windmill/compare/v1.635.0...v1.635.1) (2026-02-15)
|
||||
|
||||
|
||||
|
||||
Generated
+183
-183
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.635.1"
|
||||
version = "1.636.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -75,7 +75,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.635.1"
|
||||
version = "1.636.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
8c214ec5039be5353f5fea920e27b0c6af61e1fd
|
||||
9f6e1e533df7711600ec2b8d5f0c958448db1a20
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.635.1
|
||||
version: 1.636.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
|
||||
@@ -3,6 +3,8 @@ pub mod crd_ee;
|
||||
#[cfg(feature = "private")]
|
||||
pub mod db_sync_ee;
|
||||
#[cfg(feature = "private")]
|
||||
pub use db_sync_ee as db_sync;
|
||||
#[cfg(feature = "private")]
|
||||
pub mod reconciler_ee;
|
||||
#[cfg(feature = "private")]
|
||||
pub mod resolve_ee;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#[cfg(feature = "private")]
|
||||
pub use crate::oauth_refresh_ee::_refresh_token;
|
||||
#[cfg(feature = "private")]
|
||||
pub use crate::oauth_refresh_ee::_refresh_workspace_integration_token;
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
use sqlx::{Postgres, Transaction};
|
||||
@@ -36,3 +38,156 @@ pub async fn _refresh_token<'c>(
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
pub async fn _refresh_workspace_integration_token<'c>(
|
||||
mut tx: Transaction<'c, Postgres>,
|
||||
path: &str,
|
||||
w_id: &str,
|
||||
account_id: i32,
|
||||
db: &DB,
|
||||
client_name: &str,
|
||||
refresh_token: &str,
|
||||
) -> error::Result<String> {
|
||||
use windmill_common::global_settings::{
|
||||
get_instance_oauth_credentials, workspace_integration_auth_endpoint,
|
||||
workspace_integration_oauth_key, workspace_integration_token_endpoint,
|
||||
};
|
||||
use windmill_common::utils::now_from_db;
|
||||
use windmill_common::variables::{build_crypt, encrypt};
|
||||
use windmill_oauth::{OClient, RefreshToken, Url, OAUTH_HTTP_CLIENT};
|
||||
|
||||
tracing::info!(
|
||||
client = %client_name,
|
||||
workspace_id = %w_id,
|
||||
account_id = %account_id,
|
||||
"Refreshing workspace integration OAuth token"
|
||||
);
|
||||
|
||||
let oauth_data: serde_json::Value = sqlx::query_scalar(
|
||||
"SELECT oauth_data FROM workspace_integrations \
|
||||
WHERE workspace_id = $1 AND service_name::text = $2",
|
||||
)
|
||||
.bind(w_id)
|
||||
.bind(client_name)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
error::Error::NotFound(format!(
|
||||
"Workspace integration for {} not found or not configured",
|
||||
client_name
|
||||
))
|
||||
})?;
|
||||
|
||||
let is_instance_shared = oauth_data
|
||||
.get("instance_shared")
|
||||
.and_then(|v| v.as_bool())
|
||||
.unwrap_or(false);
|
||||
|
||||
let (client_id, client_secret, base_url);
|
||||
if is_instance_shared {
|
||||
let oauth_key = workspace_integration_oauth_key(client_name);
|
||||
let (id, secret) = get_instance_oauth_credentials(db, oauth_key).await?;
|
||||
client_id = id;
|
||||
client_secret = secret;
|
||||
base_url = String::new();
|
||||
} else {
|
||||
client_id = oauth_data["client_id"]
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
error::Error::InternalErr("Missing client_id in workspace integration".into())
|
||||
})?
|
||||
.to_string();
|
||||
client_secret = oauth_data["client_secret"]
|
||||
.as_str()
|
||||
.ok_or_else(|| {
|
||||
error::Error::InternalErr(
|
||||
"Missing client_secret in workspace integration".into(),
|
||||
)
|
||||
})?
|
||||
.to_string();
|
||||
base_url = oauth_data["base_url"].as_str().unwrap_or("").to_string();
|
||||
}
|
||||
|
||||
let token_endpoint = workspace_integration_token_endpoint(client_name, &base_url);
|
||||
let auth_endpoint = workspace_integration_auth_endpoint(client_name, &base_url);
|
||||
|
||||
let auth_url = Url::parse(&auth_endpoint)
|
||||
.map_err(|e| error::Error::InternalErr(format!("Invalid auth URL: {}", e)))?;
|
||||
let token_url = Url::parse(&token_endpoint)
|
||||
.map_err(|e| error::Error::InternalErr(format!("Invalid token URL: {}", e)))?;
|
||||
|
||||
let mut client = OClient::new(client_id, auth_url, token_url);
|
||||
client.set_client_secret(client_secret);
|
||||
|
||||
let token = client
|
||||
.exchange_refresh_token(&RefreshToken::from(refresh_token))
|
||||
.with_client(&*OAUTH_HTTP_CLIENT)
|
||||
.execute::<serde_json::Value>()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error::Error::InternalErr(format!(
|
||||
"Failed to refresh workspace integration token: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct WsTokenResponse {
|
||||
access_token: String,
|
||||
refresh_token: Option<String>,
|
||||
expires_in: Option<i64>,
|
||||
}
|
||||
|
||||
let token_result: WsTokenResponse = serde_json::from_value(token)
|
||||
.map_err(|e| error::Error::InternalErr(format!("Failed to parse token response: {}", e)))?;
|
||||
|
||||
let expires_at = now_from_db(&mut *tx).await?
|
||||
+ chrono::Duration::try_seconds(
|
||||
token_result
|
||||
.expires_in
|
||||
.ok_or_else(|| {
|
||||
error::Error::InternalErr("expires_in expected and not found".into())
|
||||
})?
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
sqlx::query(
|
||||
"UPDATE account SET refresh_token = $1, expires_at = $2, refresh_error = NULL \
|
||||
WHERE workspace_id = $3 AND id = $4",
|
||||
)
|
||||
.bind(
|
||||
token_result
|
||||
.refresh_token
|
||||
.as_deref()
|
||||
.unwrap_or(refresh_token),
|
||||
)
|
||||
.bind(expires_at)
|
||||
.bind(w_id)
|
||||
.bind(account_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
let token_str = &token_result.access_token;
|
||||
let mc = build_crypt(db, w_id).await?;
|
||||
let encrypted_token = encrypt(&mc, token_str);
|
||||
|
||||
sqlx::query("UPDATE variable SET value = $1 WHERE workspace_id = $2 AND path = $3")
|
||||
.bind(encrypted_token)
|
||||
.bind(w_id)
|
||||
.bind(path)
|
||||
.execute(db)
|
||||
.await?;
|
||||
|
||||
tracing::info!(
|
||||
client = %client_name,
|
||||
workspace_id = %w_id,
|
||||
account_id = %account_id,
|
||||
"Workspace integration OAuth token refreshed successfully"
|
||||
);
|
||||
|
||||
Ok(token_result.access_token)
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts";
|
||||
import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts";
|
||||
import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts";
|
||||
|
||||
export const VERSION = "v1.635.1";
|
||||
export const VERSION = "v1.636.0";
|
||||
|
||||
export async function login(email: string, password: string): Promise<string> {
|
||||
return await windmill.UserService.login({
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ export {
|
||||
// }
|
||||
// });
|
||||
|
||||
export const VERSION = "1.635.1";
|
||||
export const VERSION = "1.636.0";
|
||||
|
||||
// Re-exported from constants.ts to maintain backwards compatibility
|
||||
export { WM_FORK_PREFIX } from "./core/constants.ts";
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.635.1",
|
||||
"version": "1.636.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "windmill-components",
|
||||
"version": "1.635.1",
|
||||
"version": "1.636.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "windmill-components",
|
||||
"version": "1.635.1",
|
||||
"version": "1.636.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -35,9 +35,10 @@
|
||||
loading?: boolean
|
||||
openSmtpSettings?: () => void
|
||||
oauths?: Record<string, any>
|
||||
warning?: string
|
||||
}
|
||||
|
||||
let { setting, version, values, loading = true, openSmtpSettings, oauths }: Props = $props()
|
||||
let { setting, version, values, loading = true, openSmtpSettings, oauths, warning }: Props = $props()
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let latestKeyRenewalAttempt: {
|
||||
@@ -282,6 +283,11 @@
|
||||
bind:value={$values[setting.key]}
|
||||
class="max-w-lg"
|
||||
/>
|
||||
{#if warning}
|
||||
<span class="text-yellow-600 dark:text-yellow-500 text-2xs">
|
||||
{warning}
|
||||
</span>
|
||||
{/if}
|
||||
{#if setting.advancedToggle}
|
||||
<div class="mt-1">
|
||||
<Toggle
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
let requirePreexistingUserForOauth: boolean = $state(false)
|
||||
|
||||
let initialValues: Record<string, any> = $state({})
|
||||
let baseUrlIsFallback = $state(false)
|
||||
let snowflakeAccountIdentifier = $state('')
|
||||
let version: string = $state('')
|
||||
let loading = $state(true)
|
||||
@@ -96,16 +97,20 @@
|
||||
|
||||
let nvalues: Record<string, any> = { ...gs }
|
||||
|
||||
if (!nvalues['base_url']) {
|
||||
nvalues['base_url'] = window.location.origin
|
||||
}
|
||||
baseUrlIsFallback = !nvalues['base_url']
|
||||
if (nvalues['retention_period_secs'] == undefined) {
|
||||
nvalues['retention_period_secs'] = 60 * 60 * 24 * 30
|
||||
}
|
||||
applyFormDefaults(nvalues)
|
||||
|
||||
// Snapshot initialValues before applying the base_url fallback so that
|
||||
// the dirty-check detects the unsaved default and enables the Save button.
|
||||
initialValues = JSON.parse(JSON.stringify(nvalues))
|
||||
|
||||
if (baseUrlIsFallback) {
|
||||
nvalues['base_url'] = window.location.origin
|
||||
}
|
||||
$values = nvalues
|
||||
initialValues = JSON.parse(JSON.stringify($values))
|
||||
loading = false
|
||||
|
||||
// populate snowflake account identifier from db
|
||||
@@ -186,6 +191,7 @@
|
||||
initialValues = JSON.parse(JSON.stringify($values))
|
||||
initialOauths = JSON.parse(JSON.stringify(oauths))
|
||||
initialRequirePreexistingUserForOauth = requirePreexistingUserForOauth
|
||||
baseUrlIsFallback = false
|
||||
|
||||
if (licenseKeySet) {
|
||||
setLicense()
|
||||
@@ -502,6 +508,9 @@
|
||||
const v = $values[s.key]
|
||||
initialValues[s.key] = v !== undefined ? JSON.parse(JSON.stringify(v)) : undefined
|
||||
}
|
||||
if (categorySettings.some((s) => s.key === 'base_url')) {
|
||||
baseUrlIsFallback = false
|
||||
}
|
||||
|
||||
// Handle Auth/OAuth/SAML-specific saves
|
||||
if (category === 'Auth/OAuth/SAML') {
|
||||
@@ -998,6 +1007,7 @@
|
||||
{values}
|
||||
{version}
|
||||
{oauths}
|
||||
warning={setting.key === 'base_url' && baseUrlIsFallback ? 'Auto-detected from browser — not yet saved' : undefined}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
wmill = ">=1.635.1"
|
||||
wmill_pg = ">=1.635.1"
|
||||
wmill = ">=1.636.0"
|
||||
wmill_pg = ">=1.636.0"
|
||||
sendgrid = "*"
|
||||
mysql-connector-python = "*"
|
||||
pymongo = "*"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: '3.0.3'
|
||||
|
||||
info:
|
||||
version: 1.635.1
|
||||
version: 1.636.0
|
||||
title: OpenFlow Spec
|
||||
contact:
|
||||
name: Ruben Fiszel
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
RootModule = 'WindmillClient.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.635.1'
|
||||
ModuleVersion = '1.636.0'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill"
|
||||
version = "1.635.1"
|
||||
version = "1.636.0"
|
||||
description = "A client library for accessing Windmill server wrapping the Windmill client API"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "wmill-pg"
|
||||
version = "1.635.1"
|
||||
version = "1.636.0"
|
||||
description = "An extension client for the wmill client library focused on pg"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://windmill.dev"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@windmill/windmill",
|
||||
"version": "1.635.1",
|
||||
"version": "1.636.0",
|
||||
"exports": "./src/index.ts",
|
||||
"publish": {
|
||||
"exclude": ["!src", "./s3Types.ts", "./sqlUtils.ts", "./client.ts"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "windmill-client",
|
||||
"description": "Windmill SDK client for browsers and Node.js",
|
||||
"version": "1.635.1",
|
||||
"version": "1.636.0",
|
||||
"author": "Ruben Fiszel",
|
||||
"license": "Apache 2.0",
|
||||
"sideEffects": false,
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
1.635.1
|
||||
1.636.0
|
||||
|
||||
Reference in New Issue
Block a user