Compare commits

...

14 Commits

Author SHA1 Message Date
centdix
373864e586 Update backend/windmill-api/src/lib.rs
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-06-03 16:13:25 +02:00
centdix
25c8e6e090 update ref 2025-06-03 16:04:10 +02:00
centdix
9246512561 add route 2025-06-03 16:00:42 +02:00
centdix
40712ecf7b add inkeep file 2025-06-03 15:57:12 +02:00
Diego Imbert
138abad9e6 Move ee files from OSS to private repo script (#5858)
* .ignore file

* move ee files option

* trailing backslash

* missing quote
2025-06-03 13:50:08 +02:00
pyranota
697f660532 chore: fix python client build (#5856)
* chore: fix python client build

* nit
2025-06-03 00:05:12 +02:00
Ruben Fiszel
8f29c9d333 update all builds with private 2025-06-02 22:54:42 +02:00
Ruben Fiszel
106b180c80 update docker image with private 2025-06-02 22:51:41 +02:00
HugoCasa
68ebf667d5 feat: ai flow chat (#5842)
* feat: ai flow chat

* youpi

* feat: preprocessor and error handler support

* fix: reactivity

* feat: inline script editor cmd l

* nit

* fix: apply in script editor

* fixes

* prompt nits
2025-06-02 22:14:25 +02:00
Diego Imbert
0e316239dd EE Refactor (#5844)
* app compiles with every ee substituted

* Replace all oss files content

* Revert "Replace all oss files content"

This reverts commit ea4017d59f.

* delete all ee

* hide all _ee files under private flag

* hide every oss stuff when private flag set

* pub use *

* gitignore and substitute script

* pub mod for ee needed for ee repo

* small mistakes

* remove oidc_oss impl

* ee ref (temp)

* ee ref

* fix --all-features selecting private in OSS CI

* ee repo ref

* allow unused
2025-06-02 22:12:33 +02:00
claude[bot]
64f35d050f fix: replace crypto.randomUUID with generateRandomString for HTTP compatibility (#5849)
Replace crypto.randomUUID() with generateRandomString() in triggers.svelte.ts 
to fix schedule trigger creation on HTTP connections. The crypto.randomUUID() 
API requires a secure context (HTTPS), which breaks functionality for users 
connecting to Windmill over HTTP in internal networks.

Fixes #5847

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: rubenfiszel <rubenfiszel@users.noreply.github.com>
2025-06-01 19:44:41 +02:00
dieriba
4cbcbdb960 feat: fix backward compatibility pg 14 for postgres trigger (#5851)
* remove macro query when interacting with remote db

* fix compatibility and nits

* fix bug save for publication tracking all table

* fix infinite loop svelte 5

* nits on (gcp/postgres) trigger+ refactor on postgres trigger

* update .sqlx

* update repo ref

* uodate repo ref

* fix

* fix

* nits + extend postgres capture

* fix import gcp

* fix reactivity issue

* add postgres transaction for successive write operation

* fix isValid state edge cases

* fix reorder transaction

* reorder transaction

* remove unused import

* update test connection

* add missing props

* fix import/bugs and nits

* nits and fix small bugs

* add missing colon
2025-06-01 19:11:48 +02:00
Ruben Fiszel
190d230a01 chore(main): release 1.494.0 (#5837)
* chore(main): release 1.494.0

* Apply automatic changes

---------

Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
2025-05-31 05:20:37 +02:00
Ruben Fiszel
db873be43b nit editors reactivity on change 2025-05-31 05:15:24 +02:00
204 changed files with 4811 additions and 3956 deletions

View File

@@ -53,7 +53,7 @@ jobs:
timeout-minutes: 16
run: |
mkdir -p fake_frontend_build
FRONTEND_BUILD_DIR=$(pwd)/fake_frontend_build SQLX_OFFLINE=true cargo check --all-features
FRONTEND_BUILD_DIR=$(pwd)/fake_frontend_build SQLX_OFFLINE=true cargo check --features $(./all_features_oss.sh)
check_ee:
runs-on: ubicloud-standard-8

View File

@@ -64,7 +64,7 @@ jobs:
platforms: linux/amd64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp,private
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}
@@ -81,7 +81,7 @@ jobs:
platforms: linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,license,otel,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp,private
secrets: |
rh_username=${{ secrets.RH_USERNAME }}
rh_password=${{ secrets.RH_PASSWORD }}

View File

@@ -51,7 +51,7 @@ jobs:
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
mkdir frontend/build && cd backend
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,mcp
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,nats,sqs_trigger,postgres_trigger,gcp_trigger,mqtt_trigger,websocket,smtp,static_frontend,all_languages,mcp,private
- name: Rename binary with corresponding architecture
run: |
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"

View File

@@ -92,7 +92,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages,deno_core,mcp
features=embedding,parquet,openidconnect,jemalloc,license,http_trigger,zip,oauth2,dind,postgres_trigger,mqtt_trigger,websocket,smtp,static_frontend,agent_worker_server,all_languages,deno_core,mcp,private
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DEV_SHA }}
${{ steps.meta-public.outputs.tags }}
@@ -154,7 +154,7 @@ jobs:
platforms: linux/amd64,linux/arm64
push: true
build-args: |
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,deno_core,mcp
features=enterprise,enterprise_saml,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,agent_worker_server,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,otel,dind,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,private,deno_core,mcp
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-ee:${{ env.DEV_SHA }}
${{ steps.meta-ee-public.outputs.tags }}

View File

@@ -53,7 +53,7 @@ jobs:
$env:OPENSSL_DIR="${Env:VCPKG_INSTALLATION_ROOT}\installed\x64-windows-static"
mkdir frontend/build && cd backend
New-Item -Path . -Name "windmill-api/openapi-deref.yaml" -ItemType "File" -Force
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,mcp
cargo build --release --features=enterprise,stripe,embedding,parquet,prometheus,openidconnect,cloud,jemalloc,tantivy,license,http_trigger,zip,oauth2,kafka,sqs_trigger,nats,postgres_trigger,mqtt_trigger,gcp_trigger,websocket,smtp,static_frontend,all_languages,mcp,private
- name: Rename binary with corresponding architecture
run: |
Rename-Item -Path ".\backend\target\release\windmill.exe" -NewName "windmill-ee.exe"

View File

@@ -1,5 +1,18 @@
# Changelog
## [1.494.0](https://github.com/windmill-labs/windmill/compare/v1.493.4...v1.494.0) (2025-05-31)
### Features
* array of s3 objects in input maker ([806d669](https://github.com/windmill-labs/windmill/commit/806d66972568d21a1621acd1b30db5ae9b217341))
* **rust:** shared build directory ([#5610](https://github.com/windmill-labs/windmill/issues/5610)) ([ed61d97](https://github.com/windmill-labs/windmill/commit/ed61d9770031c1a04908880dbd3e5fb692df9946))
### Bug Fixes
* allow disable tabs for sidebar/accordion tabs ([#5838](https://github.com/windmill-labs/windmill/issues/5838)) ([80277d1](https://github.com/windmill-labs/windmill/commit/80277d14d02e8e596c7002326946142226d382a6))
## [1.493.4](https://github.com/windmill-labs/windmill/compare/v1.493.3...v1.493.4) (2025-05-29)

3
backend/.gitignore vendored
View File

@@ -6,4 +6,5 @@ tracing.folded
heaptrack*
index/
windmill-api/openapi-*.*
.duckdb/*
.duckdb/*
*ee.rs

1
backend/.ignore Normal file
View File

@@ -0,0 +1 @@
!*ee.rs

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM sqs_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool",
"Text"
]
},
"nullable": [
null
]
},
"hash": "13444bbd5547e101c41206c5f97ac4dded0536faf52c370d704ed9a451041caf"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SHOW WAL_LEVEL;",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "wal_level",
"type_info": "Text"
}
],
"parameters": {
"Left": []
},
"nullable": [
null
]
},
"hash": "2ef25599ea0c9ef946d6cc70ae048af970aed2638a3f767e152b654aebf68e48"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT pubname AS publication_name FROM pg_publication;",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "publication_name",
"type_info": "Name"
}
],
"parameters": {
"Left": []
},
"nullable": [
false
]
},
"hash": "4469ee6c206c46951980ea1bc73f126f339d2e3cf97f363be8921084b16dac45"
}

View File

@@ -1,26 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT \n slot_name,\n active\n FROM\n pg_replication_slots \n WHERE \n plugin = 'pgoutput' AND\n slot_type = 'logical';\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "slot_name",
"type_info": "Name"
},
{
"ordinal": 1,
"name": "active",
"type_info": "Bool"
}
],
"parameters": {
"Left": []
},
"nullable": [
true,
true
]
},
"hash": "4ee0017771f46f0272817d18edb821940cb5064e3f155b9630b131c09c9dba13"
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM gcp_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool",
"Text"
]
},
"nullable": [
null
]
},
"hash": "6a19c440a7a8064f3969cf6f48adea0bfdb683de9555e374ce5731e0b3c379f9"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT slot_name FROM pg_replication_slots where slot_name = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "slot_name",
"type_info": "Name"
}
],
"parameters": {
"Left": [
"Name"
]
},
"nullable": [
true
]
},
"hash": "6f56acb985aa7141ea1891d7ad58a32c35d1b02fe7070c92a2e62c1a5339c396"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT \n active_pid \n FROM \n pg_replication_slots \n WHERE \n slot_name = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "active_pid",
"type_info": "Int4"
}
],
"parameters": {
"Left": [
"Name"
]
},
"nullable": [
true
]
},
"hash": "7e64ba7e2362cc19d2aed9f34c9879983922e96a9baab7c1a2b09ed2b1c261e2"
}

View File

@@ -1,40 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n puballtables AS all_table,\n pubinsert AS insert,\n pubupdate AS update,\n pubdelete AS delete\n FROM\n pg_publication\n WHERE\n pubname = $1\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "all_table",
"type_info": "Bool"
},
{
"ordinal": 1,
"name": "insert",
"type_info": "Bool"
},
{
"ordinal": 2,
"name": "update",
"type_info": "Bool"
},
{
"ordinal": 3,
"name": "delete",
"type_info": "Bool"
}
],
"parameters": {
"Left": [
"Name"
]
},
"nullable": [
false,
false,
false,
false
]
},
"hash": "86ae16175ace0179e784aacfd381771f0137ecab6671d632febadede729e7783"
}

View File

@@ -0,0 +1,24 @@
{
"db_name": "PostgreSQL",
"query": "SELECT COUNT(*) FROM mqtt_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "count",
"type_info": "Int8"
}
],
"parameters": {
"Left": [
"Text",
"Bool",
"Text"
]
},
"nullable": [
null
]
},
"hash": "a8b470b463ca4b7c00c7ef6e9f36c23f8bbcefc288a56d61122bfd6fe5ca7e8d"
}

View File

@@ -1,22 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "SELECT pubname FROM pg_publication WHERE pubname = $1",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "pubname",
"type_info": "Name"
}
],
"parameters": {
"Left": [
"Name"
]
},
"nullable": [
false
]
},
"hash": "baa1dddc616419bf4b923715f0a863bc0ff69c98db0f0c8f55e4ac89fdde7a60"
}

View File

@@ -1,40 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT\n schemaname AS schema_name,\n tablename AS table_name,\n CASE\n WHEN array_length(attnames, 1) = (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = pg_publication_tables.schemaname AND table_name = pg_publication_tables.tablename)\n THEN NULL\n ELSE attnames\n END AS columns,\n rowfilter AS where_clause\n FROM\n pg_publication_tables\n WHERE\n pubname = $1;\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "schema_name",
"type_info": "Name"
},
{
"ordinal": 1,
"name": "table_name",
"type_info": "Name"
},
{
"ordinal": 2,
"name": "columns",
"type_info": "NameArray"
},
{
"ordinal": 3,
"name": "where_clause",
"type_info": "Text"
}
],
"parameters": {
"Left": [
"Name"
]
},
"nullable": [
true,
true,
null,
true
]
},
"hash": "fd5754fe3c6346ae28818a9d60d144a40f8884f47e5bbdd2824e939dafd8f154"
}

View File

@@ -11,5 +11,6 @@
"remote.autoForwardPorts": true,
"conventionalCommits.scopes": [
"restructring triggers, decoding trigger message on work"
]
],
"rust-analyzer.cargo.features": ["postgres_trigger"]
}

136
backend/Cargo.lock generated
View File

@@ -2013,9 +2013,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.24"
version = "1.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
dependencies = [
"jobserver",
"libc",
@@ -2728,7 +2728,7 @@ dependencies = [
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core 0.9.10",
"parking_lot_core 0.9.11",
]
[[package]]
@@ -2742,7 +2742,7 @@ dependencies = [
"hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core 0.9.10",
"parking_lot_core 0.9.11",
]
[[package]]
@@ -2799,7 +2799,7 @@ dependencies = [
"itertools 0.14.0",
"log",
"object_store",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"parquet",
"rand 0.8.5",
"regex",
@@ -2834,7 +2834,7 @@ dependencies = [
"itertools 0.14.0",
"log",
"object_store",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"tokio",
]
@@ -3007,7 +3007,7 @@ dependencies = [
"itertools 0.14.0",
"log",
"object_store",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"parquet",
"rand 0.8.5",
"tokio",
@@ -3032,7 +3032,7 @@ dependencies = [
"futures",
"log",
"object_store",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"rand 0.8.5",
"tempfile",
"url",
@@ -3168,7 +3168,7 @@ dependencies = [
"datafusion-common",
"datafusion-expr",
"datafusion-physical-plan",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"paste",
]
@@ -3309,7 +3309,7 @@ dependencies = [
"indexmap 2.9.0",
"itertools 0.14.0",
"log",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"pin-project-lite",
"tokio",
]
@@ -3334,7 +3334,7 @@ dependencies = [
"itertools 0.14.0",
"log",
"object_store",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"tokio",
]
@@ -3463,7 +3463,7 @@ dependencies = [
"indexmap 2.9.0",
"log",
"once_cell",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"serde",
"serde_json",
"sha2 0.10.9",
@@ -3545,7 +3545,7 @@ dependencies = [
"indexmap 2.9.0",
"libc",
"memoffset",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"percent-encoding",
"pin-project",
"serde",
@@ -3790,7 +3790,7 @@ dependencies = [
"log",
"once_cell",
"os_pipe",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"pin-project",
"rand 0.8.5",
"tokio",
@@ -4170,7 +4170,7 @@ dependencies = [
"log",
"node_resolver",
"once_cell",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"sys_traits",
"thiserror 2.0.12",
"url",
@@ -4327,7 +4327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c618b51088b3ac67f15c69b3ed7620ba3a7d495e5a090186df9424b5ab623e"
dependencies = [
"futures",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"tokio",
]
@@ -5483,7 +5483,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
dependencies = [
"futures-core",
"lock_api",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
]
[[package]]
@@ -6286,9 +6286,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.3.9"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
[[package]]
name = "hex"
@@ -6357,7 +6357,7 @@ dependencies = [
"ipconfig",
"moka",
"once_cell",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"rand 0.9.0",
"resolv-conf",
"serde",
@@ -7505,9 +7505,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
[[package]]
name = "lock_api"
version = "0.4.12"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
dependencies = [
"autocfg",
"scopeguard",
@@ -7975,7 +7975,7 @@ dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
"loom",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"portable-atomic",
"rustc_version 0.4.1",
"smallvec",
@@ -8593,9 +8593,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.16.0"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi",
"libc",
@@ -8684,7 +8684,7 @@ dependencies = [
"hyper 1.6.0",
"itertools 0.14.0",
"md-5 0.10.6",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"percent-encoding",
"quick-xml 0.37.5",
"rand 0.9.0",
@@ -9122,12 +9122,12 @@ dependencies = [
[[package]]
name = "parking_lot"
version = "0.12.3"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
dependencies = [
"lock_api",
"parking_lot_core 0.9.10",
"parking_lot_core 0.9.11",
]
[[package]]
@@ -9146,9 +9146,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.9.10"
version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
@@ -9594,9 +9594,9 @@ checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5"
[[package]]
name = "prettyplease"
version = "0.2.32"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d"
dependencies = [
"proc-macro2",
"syn 2.0.101",
@@ -9753,7 +9753,7 @@ dependencies = [
"fnv",
"lazy_static",
"memchr",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"thiserror 2.0.12",
]
@@ -9921,7 +9921,7 @@ dependencies = [
"ahash 0.8.12",
"equivalent",
"hashbrown 0.15.3",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
]
[[package]]
@@ -13195,7 +13195,7 @@ dependencies = [
"bytes",
"libc",
"mio 1.0.4",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"pin-project-lite",
"signal-hook-registry",
"socket2",
@@ -13259,7 +13259,7 @@ dependencies = [
"futures-channel",
"futures-util",
"log",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"percent-encoding",
"phf",
"pin-project-lite",
@@ -13285,7 +13285,7 @@ dependencies = [
"futures-channel",
"futures-util",
"log",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"percent-encoding",
"phf",
"pin-project-lite",
@@ -13556,7 +13556,7 @@ dependencies = [
"cookie 0.18.1",
"futures-util",
"http 1.3.1",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"pin-project-lite",
"tower-layer",
"tower-service",
@@ -14526,7 +14526,7 @@ dependencies = [
"log",
"naga",
"once_cell",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"profiling",
"raw-window-handle",
"ron",
@@ -14568,7 +14568,7 @@ dependencies = [
"ndk-sys",
"objc",
"once_cell",
"parking_lot 0.12.3",
"parking_lot 0.12.4",
"profiling",
"range-alloc",
"raw-window-handle",
@@ -14667,7 +14667,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"axum",
@@ -14718,7 +14718,7 @@ dependencies = [
[[package]]
name = "windmill-api"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"argon2",
@@ -14827,7 +14827,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -14842,7 +14842,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"chrono",
"serde",
@@ -14855,7 +14855,7 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"serde",
@@ -14869,7 +14869,7 @@ dependencies = [
[[package]]
name = "windmill-common"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"async-stream",
@@ -14945,7 +14945,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"regex",
"serde",
@@ -14959,7 +14959,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"bytes",
@@ -14982,7 +14982,7 @@ dependencies = [
[[package]]
name = "windmill-macros"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"itertools 0.14.0",
"lazy_static",
@@ -14994,7 +14994,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -15003,7 +15003,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15015,7 +15015,7 @@ dependencies = [
[[package]]
name = "windmill-parser-csharp"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15027,7 +15027,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"gosyn",
@@ -15039,7 +15039,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15051,7 +15051,7 @@ dependencies = [
[[package]]
name = "windmill-parser-java"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15063,7 +15063,7 @@ dependencies = [
[[package]]
name = "windmill-parser-nu"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"nu-parser",
@@ -15074,7 +15074,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15085,7 +15085,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15096,7 +15096,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15119,7 +15119,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -15136,7 +15136,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15148,7 +15148,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15166,7 +15166,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"getrandom 0.2.16",
@@ -15190,7 +15190,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15200,7 +15200,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15233,7 +15233,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -15243,7 +15243,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
version = "1.493.4"
version = "1.494.0"
dependencies = [
"anyhow",
"async-recursion",

View File

@@ -1,6 +1,6 @@
[package]
name = "windmill"
version = "1.493.4"
version = "1.494.0"
authors.workspace = true
edition.workspace = true
@@ -32,7 +32,7 @@ members = [
]
[workspace.package]
version = "1.493.4"
version = "1.494.0"
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
edition = "2021"
@@ -49,6 +49,7 @@ lto = "thin"
[features]
default = []
private = ["windmill-api/private", "windmill-autoscaling/private", "windmill-common/private", "windmill-git-sync/private", "windmill-indexer/private", "windmill-queue/private", "windmill-worker/private"]
agent_worker_server = ["windmill-api/agent_worker_server"]
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise", "dep:windmill-autoscaling", "windmill-autoscaling/enterprise", "windmill-git-sync/enterprise", "windmill-common/prometheus", "windmill-common/enterprise"]
enterprise_saml = ["windmill-api/enterprise_saml", "oauth2"]
@@ -94,7 +95,7 @@ php = ["windmill-worker/php"]
csharp = ["windmill-worker/csharp"]
nu = ["windmill-worker/nu"]
java = ["windmill-worker/java"]
all_languages = [ "python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "mssql", "bigquery", "csharp", "nu", "php", "java"]
all_languages = ["python", "deno_core", "rust", "mysql", "oracledb", "duckdb", "mssql", "bigquery", "csharp", "nu", "php", "java"]
[patch.crates-io]

20
backend/all_features_oss.sh Executable file
View File

@@ -0,0 +1,20 @@
# This script outputs all features except private. Usage :
# > cargo build --features $(./all_features_oss.sh)
#!/bin/bash
# Path to the Cargo.toml file
CARGO_TOML_PATH="./Cargo.toml"
# Extract features from Cargo.toml and output them separated by commas
if [[ -f "$CARGO_TOML_PATH" ]]; then
grep -A 100 '\[features\]' "$CARGO_TOML_PATH" | \
sed -n '/\[features\]/,/^\[/p' | \
grep -E '^[a-zA-Z0-9_-]+' | \
grep -v 'private' | \
cut -d' ' -f1 | \
paste -sd ',' -
else
echo "Cargo.toml not found at $CARGO_TOML_PATH"
exit 1
fi

View File

@@ -1 +1 @@
8a2506e86b923c00522cb83b052586f705f7aa8e
70895a4a8f8891032c5b478a37ab6fafd0d4a9d0

View File

@@ -1,8 +1,13 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::ee::*;
#[cfg(not(feature = "private"))]
pub async fn set_license_key(_license_key: String) -> () {
// Implementation is not open source
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn verify_license_key() -> () {
// Implementation is not open source
}

View File

@@ -28,7 +28,9 @@ use uuid::Uuid;
use windmill_api::HTTP_CLIENT;
#[cfg(feature = "enterprise")]
use windmill_common::ee::{maybe_renew_license_key_on_start, LICENSE_KEY_ID, LICENSE_KEY_VALID};
use windmill_common::ee_oss::{
maybe_renew_license_key_on_start, LICENSE_KEY_ID, LICENSE_KEY_VALID,
};
use windmill_common::{
agent_workers::build_agent_http_client,
@@ -49,7 +51,7 @@ use windmill_common::{
TIMEOUT_WAIT_RESULT_SETTING,
},
scripts::ScriptLang,
stats_ee::schedule_stats,
stats_oss::schedule_stats,
triggers::TriggerKind,
utils::{hostname, rd_string, Mode, GIT_VERSION, MODE_AND_ADDONS},
worker::{
@@ -98,7 +100,9 @@ const DEFAULT_NUM_WORKERS: usize = 1;
const DEFAULT_PORT: u16 = 8000;
const DEFAULT_SERVER_BIND_ADDR: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 0);
mod ee;
#[cfg(feature = "private")]
pub mod ee;
mod ee_oss;
mod monitor;
pub fn setup_deno_runtime() -> anyhow::Result<()> {
@@ -552,7 +556,7 @@ Windmill Community Edition {GIT_VERSION}
_ = indexer_rx.recv() => {
tracing::info!("Received killpill, aborting index initialization");
},
res = windmill_indexer::completed_runs_ee::init_index(&db) => {
res = windmill_indexer::completed_runs_oss::init_index(&db) => {
let res = res?;
reader = Some(res.0);
writer = Some(res.1);
@@ -574,7 +578,7 @@ Windmill Community Edition {GIT_VERSION}
async {
if let Some(db) = conn.as_sql() {
if let Some(index_writer) = index_writer2 {
windmill_indexer::completed_runs_ee::run_indexer(
windmill_indexer::completed_runs_oss::run_indexer(
db.clone(),
index_writer,
indexer_rx,
@@ -596,7 +600,7 @@ Windmill Community Edition {GIT_VERSION}
_ = indexer_rx.recv() => {
tracing::info!("Received killpill, aborting index initialization");
},
res = windmill_indexer::service_logs_ee::init_index(&db, killpill_tx.clone()) => {
res = windmill_indexer::service_logs_oss::init_index(&db, killpill_tx.clone()) => {
let res = res?;
reader = Some(res.0);
writer = Some(res.1);
@@ -618,7 +622,7 @@ Windmill Community Edition {GIT_VERSION}
async {
if let Some(db) = conn.as_sql() {
if let Some(log_index_writer) = log_index_writer2 {
windmill_indexer::service_logs_ee::run_indexer(
windmill_indexer::service_logs_oss::run_indexer(
db.clone(),
log_index_writer,
log_indexer_rx,
@@ -1086,7 +1090,7 @@ Windmill Community Edition {GIT_VERSION}
tracing::info!("Reloading config after 12 hours");
initial_load(&conn, tx.clone(), worker_mode, server_mode, #[cfg(feature = "parquet")] disable_s3_store).await;
#[cfg(feature = "enterprise")]
ee::verify_license_key().await;
ee_oss::verify_license_key().await;
}
}
},

View File

@@ -29,9 +29,9 @@ use windmill_api::{
};
#[cfg(feature = "enterprise")]
use windmill_common::ee::low_disk_alerts;
use windmill_common::ee_oss::low_disk_alerts;
#[cfg(feature = "enterprise")]
use windmill_common::ee::{jobs_waiting_alerts, worker_groups_alerts};
use windmill_common::ee_oss::{jobs_waiting_alerts, worker_groups_alerts};
use windmill_common::client::AuthedClient;
#[cfg(feature = "oauth2")]
@@ -41,7 +41,7 @@ use windmill_common::s3_helpers::reload_object_store_setting;
use windmill_common::{
agent_workers::DECODED_AGENT_TOKEN,
auth::create_token_for_owner,
ee::CriticalErrorChannel,
ee_oss::CriticalErrorChannel,
error,
flow_status::{FlowStatus, FlowStatusModule},
global_settings::{
@@ -87,9 +87,9 @@ use windmill_worker::{
use windmill_common::s3_helpers::ObjectStoreReload;
#[cfg(feature = "enterprise")]
use crate::ee::verify_license_key;
use crate::ee_oss::verify_license_key;
use crate::ee::set_license_key;
use crate::ee_oss::set_license_key;
#[cfg(feature = "prometheus")]
lazy_static::lazy_static! {
@@ -1609,7 +1609,7 @@ pub async fn reload_base_url_setting(conn: &Connection) -> error::Result<()> {
if let Some(q) = q_oauth {
if let Ok(v) = serde_json::from_value::<
Option<HashMap<String, windmill_api::oauth2_ee::OAuthClient>>,
Option<HashMap<String, windmill_api::oauth2_oss::OAuthClient>>,
>(q.clone())
{
v
@@ -1630,7 +1630,7 @@ pub async fn reload_base_url_setting(conn: &Connection) -> error::Result<()> {
{
if let Some(db) = conn.as_sql() {
let mut l = windmill_api::OAUTH_CLIENTS.write().await;
*l = windmill_api::oauth2_ee::build_oauth_clients(&base_url, oauths, db).await
*l = windmill_api::oauth2_oss::build_oauth_clients(&base_url, oauths, db).await
.map_err(|e| tracing::error!("Error building oauth clients (is the oauth.json mounted and in correct format? Use '{}' as minimal oauth.json): {}", "{}", e))
.unwrap();
}

View File

@@ -4,8 +4,8 @@ script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
root_dirpath="$(cd "${script_dirpath}/.." && pwd)"
REVERT="NO"
REVERT_PREVIOUS="NO"
COPY="NO"
MOVE_NEW_FILES="NO"
EE_CODE_DIR="../windmill-ee-private/"
while [[ $# -gt 0 ]]; do
@@ -16,13 +16,7 @@ while [[ $# -gt 0 ]]; do
# this to work (commit hooks should prevent this from happening, as well as the fact
# that we're using symlinks by default).
REVERT="YES"
shift
;;
--revert-previous)
# This is a special case of --revert that will revert to the previous commit.
REVERT="YES"
REVERT_PREVIOUS="YES"
echo "Reverting to previous commit"
MOVE_NEW_FILES="YES"
shift
;;
-c|--copy)
@@ -33,6 +27,11 @@ while [[ $# -gt 0 ]]; do
COPY="YES"
shift # past argument
;;
-m|--move-new-files)
# This moves all new EE files from the public repository to the private repository.
MOVE_NEW_FILES="YES"
shift # past argument
;;
-d|--dir)
# Path to the local directory of the windmill-ee-private repository. By defaults, it
# assumes it is cloned next to the Windmill OSS repo.
@@ -70,29 +69,34 @@ if [ "$REVERT" == "YES" ]; then
for ee_file in $(find ${EE_CODE_DIR} -name "*ee.rs"); do
ce_file="${ee_file/${EE_CODE_DIR}/}"
ce_file="${root_dirpath}/backend/${ce_file}"
if [ "$REVERT_PREVIOUS" == "YES" ]; then
git checkout HEAD@{3} ${ce_file} || true
else
git restore --staged ${ce_file} || true
git restore ${ce_file} || true
fi
rm ${ce_file}
done
else
elif [ "$MOVE_NEW_FILES" == "NO" ]; then
# This replaces all files in current repo with alternative EE files in windmill-ee-private
for ee_file in $(find "${EE_CODE_DIR}" -name "*ee.rs"); do
ce_file="${ee_file/${EE_CODE_DIR}/}"
ce_file="${root_dirpath}/backend/${ce_file}"
if [[ -f "${ce_file}" ]]; then
rm "${ce_file}"
if [ "$COPY" == "YES" ]; then
cp "${ee_file}" "${ce_file}"
echo "File copied '${ee_file}' -->> '${ce_file}'"
else
ln -s "${ee_file}" "${ce_file}"
echo "Symlink created '${ee_file}' -->> '${ce_file}'"
fi
ce_file="${ee_file/${EE_CODE_DIR}/}"
ce_file="${root_dirpath}/backend/${ce_file}"
if [ "$COPY" == "YES" ]; then
cp "${ee_file}" "${ce_file}"
echo "File copied '${ee_file}' -->> '${ce_file}'"
else
echo "File ${ce_file} is not a file, ignoring"
ln -s "${ee_file}" "${ce_file}"
echo "Symlink created '${ee_file}' -->> '${ce_file}'"
fi
done
fi
if [ "$MOVE_NEW_FILES" == "YES" ]; then
for ce_file in $(find "${root_dirpath}"/backend/windmill-*/src/ -name "*ee.rs"); do
backend_dirpath="${root_dirpath}/backend/"
ee_file="${ce_file/${backend_dirpath}/}"
ee_file="${EE_CODE_DIR}${ee_file}"
if [ ! -f "${ee_file}" ]; then
mv "${ce_file}" "${ee_file}"
if [ ! "$REVERT" == "YES" ]; then
ln -s "${ee_file}" "${ce_file}"
fi
echo "File moved '${ce_file}' -->> '${ee_file}'"
fi
done
fi

View File

@@ -9,7 +9,7 @@ if [[ "$(uname)" == "Darwin" ]]; then
sed -i '' 's/^# \(samael = { git="https:\/\/github.com\/njaremko\/samael", rev="464d015e3ae393e4b5dd00b4d6baa1b617de0dd6", features = \["xmlsec"\] }\)/\1/' Cargo.toml
fi
cargo sqlx prepare --workspace -- --all-targets --all-features
cargo sqlx prepare --workspace -- --all-targets --features $(./all_features_oss.sh)
./substitute_ee_code.sh -r --dir ../windmill-ee-private
# Undo the samael changes on macOS

View File

@@ -10,6 +10,7 @@ path = "src/lib.rs"
[features]
default = []
private = ["windmill-audit/private"]
enterprise = ["windmill-queue/enterprise", "windmill-audit/enterprise", "windmill-git-sync/enterprise", "windmill-common/enterprise", "windmill-worker/enterprise"]
stripe = []
agent_worker_server = []

View File

@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
version: 1.493.4
version: 1.494.0
title: Windmill API
contact:
@@ -9821,6 +9821,23 @@ paths:
items:
type: string
/w/{workspace}/postgres_triggers/postgres/version/{path}:
get:
summary: get postgres version
operationId: getPostgresVersion
tags:
- postgres_trigger
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- $ref: "#/components/parameters/Path"
responses:
"200":
description: postgres version
content:
application/json:
schema:
type: string
/w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}:
get:
summary: check if postgres configuration is set to logical
@@ -13168,6 +13185,8 @@ components:
# NOTE: Not so many generators and validators support this format:
# $ref: "../../openflow.openapi.yaml#/components/schemas"
# This is why it is better to inline each of schemas for better compat
# Do not change next line. It is used by python-client for pre-processing
# -- INLINE START --
OpenFlow:
$ref: "../../openflow.openapi.yaml#/components/schemas/OpenFlow"
FlowValue:
@@ -13206,6 +13225,8 @@ components:
$ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatus"
FlowStatusModule:
$ref: "../../openflow.openapi.yaml#/components/schemas/FlowStatusModule"
# -- INLINE END --
# Do not change line above
AIProvider:
type: string

View File

@@ -1,3 +1,7 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::agent_workers_ee::*;
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2042
@@ -6,16 +10,21 @@
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(not(feature = "private"))]
use crate::db::DB;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn workspaced_service(
db: DB,
_base_internal_url: String,
@@ -36,6 +45,7 @@ pub fn workspaced_service(
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg(not(feature = "private"))]
pub struct AgentAuth {
pub worker_group: String,
pub suffix: Option<String>,
@@ -43,8 +53,10 @@ pub struct AgentAuth {
pub exp: Option<usize>,
}
#[cfg(not(feature = "private"))]
pub struct AgentCache {}
#[cfg(not(feature = "private"))]
impl AgentCache {
pub fn new() -> Self {
AgentCache {}

View File

@@ -10,7 +10,7 @@ use reqwest::{Client, RequestBuilder};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use std::collections::HashMap;
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_audit::{audit_oss::audit_log, ActionKind};
use windmill_common::error::{to_anyhow, Error, Result};
lazy_static::lazy_static! {

View File

@@ -18,7 +18,7 @@ use crate::{
};
#[cfg(feature = "parquet")]
use crate::{
job_helpers_ee::{
job_helpers_oss::{
download_s3_file_internal, get_random_file_name, get_s3_resource,
get_workspace_s3_resource, upload_file_from_req, DownloadFileQuery,
},
@@ -48,7 +48,7 @@ use sha2::{Digest, Sha256};
use sql_builder::{bind::Bind, SqlBuilder};
use sqlx::{types::Uuid, FromRow};
use std::str;
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::{
apps::{AppScriptId, ListAppQuery},

View File

@@ -1,5 +0,0 @@
use axum::Router;
pub fn global_unauthed_service() -> Router {
Router::new()
}

View File

@@ -0,0 +1,11 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::apps_ee::*;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
pub fn global_unauthed_service() -> Router {
Router::new()
}

View File

@@ -85,7 +85,7 @@ impl RawWebhookArgs {
db: &DB,
w_id: &str,
) -> Result<HashMap<String, Box<RawValue>>, Error> {
use crate::job_helpers_ee::{
use crate::job_helpers_oss::{
get_random_file_name, get_workspace_s3_resource, upload_file_internal,
};
use futures::TryStreamExt;

View File

@@ -28,7 +28,7 @@ async fn get_audit(
Path((w_id, id)): Path<(String, i32)>,
) -> JsonResult<AuditLog> {
let tx = user_db.begin(&authed).await?;
let audit = windmill_audit::audit_ee::get_audit(tx, id, &w_id).await?;
let audit = windmill_audit::audit_oss::get_audit(tx, id, &w_id).await?;
Ok(Json(audit))
}
async fn list_audit(
@@ -39,6 +39,6 @@ async fn list_audit(
Query(lq): Query<ListAuditLogQuery>,
) -> JsonResult<Vec<AuditLog>> {
let tx = user_db.begin(&authed).await?;
let rows = windmill_audit::audit_ee::list_audit(tx, w_id, pagination, lq).await?;
let rows = windmill_audit::audit_oss::list_audit(tx, w_id, pagination, lq).await?;
Ok(Json(rows))
}

View File

@@ -1,5 +1,5 @@
#[cfg(feature = "enterprise")]
use crate::ee::ExternalJwks;
use crate::ee_oss::ExternalJwks;
use axum::{
async_trait,
extract::{FromRequestParts, OriginalUri, Query},
@@ -71,7 +71,7 @@ impl AuthCache {
}
#[cfg(feature = "enterprise")]
_ if token.starts_with("jwt_ext_") => {
let authed_and_exp = match crate::ee::jwt_ext_auth(
let authed_and_exp = match crate::ee_oss::jwt_ext_auth(
w_id.as_ref(),
token.trim_start_matches("jwt_ext_"),
self.ext_jwks.clone(),

View File

@@ -15,7 +15,7 @@ use {
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
use {
crate::gcp_triggers_ee::{
crate::gcp_triggers_oss::{
manage_google_subscription, process_google_push_request, validate_jwt_token,
CreateUpdateConfig, SubscriptionMode,
},
@@ -23,7 +23,10 @@ use {
http::HeaderMap,
};
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
#[cfg(any(
all(feature = "enterprise", feature = "gcp_trigger"),
feature = "postgres_trigger"
))]
use windmill_common::utils::empty_as_none;
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
@@ -33,25 +36,31 @@ use windmill_common::auth::aws::AwsAuthResourceType;
feature = "http_trigger",
all(feature = "enterprise", feature = "gcp_trigger")
))]
use {serde::de::DeserializeOwned, windmill_common::error::Error};
use serde::de::DeserializeOwned;
#[cfg(any(
feature = "http_trigger",
feature = "postgres_trigger",
all(feature = "enterprise", feature = "gcp_trigger")
))]
use windmill_common::error::Error;
#[cfg(all(feature = "enterprise", feature = "kafka"))]
use crate::kafka_triggers_ee::KafkaTriggerConfigConnection;
use crate::kafka_triggers_oss::KafkaTriggerConfigConnection;
#[cfg(feature = "mqtt_trigger")]
use crate::mqtt_triggers::{MqttClientVersion, MqttV3Config, MqttV5Config, SubscribeTopic};
#[cfg(all(feature = "enterprise", feature = "nats"))]
use crate::nats_triggers_ee::NatsTriggerConfigConnection;
use crate::nats_triggers_oss::NatsTriggerConfigConnection;
#[cfg(feature = "postgres_trigger")]
use {
crate::postgres_triggers::{
create_logical_replication_slot_query, create_publication_query, drop_publication_query,
generate_random_string, get_database_connection, PublicationData,
create_logical_replication_slot, create_pg_publication, generate_random_string,
get_pg_connection, PublicationData,
},
itertools::Itertools,
pg_escape::quote_literal,
sqlx::Connection,
};
use crate::{
@@ -200,9 +209,12 @@ pub struct MqttTriggerConfig {
#[derive(Serialize, Deserialize, Debug)]
pub struct PostgresTriggerConfig {
pub postgres_resource_path: String,
#[serde(default, deserialize_with = "empty_as_none")]
pub publication_name: Option<String>,
#[serde(default, deserialize_with = "empty_as_none")]
pub replication_slot_name: Option<String>,
pub publication: PublicationData,
pub basic_mode: Option<bool>,
}
#[cfg(feature = "websocket")]
@@ -292,57 +304,49 @@ async fn set_postgres_trigger_config(
user_db: UserDB,
mut capture_config: NewCaptureConfig,
) -> Result<NewCaptureConfig> {
let Some(TriggerConfig::Postgres(mut postgres_config)) = capture_config.trigger_config else {
return Err(windmill_common::error::Error::BadRequest(
"Invalid postgres config".to_string(),
));
let Some(TriggerConfig::Postgres(postgres_config)) = capture_config.trigger_config.as_mut()
else {
return Err(Error::BadRequest("Invalid postgres config".to_string()));
};
let mut connection = get_database_connection(
authed,
Some(user_db),
&db,
&postgres_config.postgres_resource_path,
&w_id,
)
.await?;
if postgres_config.basic_mode.unwrap_or(false) {
let mut pg_connection = get_pg_connection(
authed,
Some(user_db),
&db,
&postgres_config.postgres_resource_path,
&w_id,
)
.await?;
let publication_name = postgres_config
.publication_name
.get_or_insert(format!("windmill_capture_{}", generate_random_string()));
let replication_slot_name = postgres_config
.replication_slot_name
.get_or_insert(publication_name.clone());
let mut tx = pg_connection.begin().await?;
let query = drop_publication_query(&publication_name);
let publication_name = format!("windmill_capture_{}", generate_random_string());
let replication_slot_name = publication_name.clone();
sqlx::query(&query).execute(&mut connection).await?;
create_logical_replication_slot(&mut tx, &replication_slot_name).await?;
let query = create_publication_query(
&publication_name,
postgres_config.publication.table_to_track.as_deref(),
&postgres_config
.publication
.transaction_to_track
.iter()
.map(AsRef::as_ref)
.collect_vec(),
);
create_pg_publication(
&mut tx,
&publication_name,
postgres_config.publication.table_to_track.as_deref(),
&postgres_config.publication.transaction_to_track,
)
.await?;
sqlx::query(&query).execute(&mut connection).await?;
let query = format!(
"SELECT 1 from pg_replication_slots WHERE slot_name = {}",
quote_literal(replication_slot_name)
);
let row = sqlx::query(&query).fetch_optional(&mut connection).await?;
if row.is_none() {
let query = create_logical_replication_slot_query(&replication_slot_name);
sqlx::query(&query).execute(&mut connection).await?;
tx.commit().await?;
postgres_config.publication_name = Some(publication_name);
postgres_config.replication_slot_name = Some(replication_slot_name);
} else {
if postgres_config.publication_name.is_none()
|| postgres_config.replication_slot_name.is_none()
{
return Err(Error::BadRequest(
"Publication name and slot name required in advanced mode".to_string(),
));
}
}
capture_config.trigger_config = Some(TriggerConfig::Postgres(postgres_config));
Ok(capture_config)
}
@@ -366,9 +370,7 @@ async fn set_gcp_trigger_config(
mut capture_config: NewCaptureConfig,
) -> Result<NewCaptureConfig> {
let Some(TriggerConfig::Gcp(mut gcp_config)) = capture_config.trigger_config else {
return Err(windmill_common::error::Error::BadRequest(
"Invalid GCP Pub/Sub config".to_string(),
));
return Err(Error::BadRequest("Invalid GCP Pub/Sub config".to_string()));
};
let config = manage_google_subscription(
@@ -903,7 +905,7 @@ async fn gcp_payload(
headers: HeaderMap,
request: Request,
) -> Result<StatusCode> {
use crate::{gcp_triggers_ee::GcpTrigger, trigger_helpers::TriggerJobArgs};
use crate::{gcp_triggers_oss::GcpTrigger, trigger_helpers::TriggerJobArgs};
let is_flow = matches!(runnable_kind, RunnableKind::Flow);
let (gcp_trigger_config, owner, email): (GcpTriggerConfig, _, _) =

View File

@@ -14,7 +14,7 @@ use axum::{
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::{
error::{self},

View File

@@ -16,7 +16,7 @@ use sqlx::{
};
use tokio::task::JoinHandle;
use windmill_audit::audit_ee::{AuditAuthor, AuditAuthorable};
use windmill_audit::audit_oss::{AuditAuthor, AuditAuthorable};
use windmill_common::{
db::{Authable, Authed},
error::Error,

View File

@@ -1,15 +1,21 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::ee::*;
#[cfg(not(feature = "private"))]
use anyhow::anyhow;
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
use std::sync::Arc;
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
use tokio::sync::RwLock;
#[cfg(not(feature = "private"))]
pub async fn validate_license_key(_license_key: String) -> anyhow::Result<(String, bool)> {
// Implementation is not open source
Err(anyhow!("License can't be validated in Windmill CE"))
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn jwt_ext_auth(
_w_id: Option<&String>,
_token: &str,
@@ -20,10 +26,10 @@ pub async fn jwt_ext_auth(
Err(anyhow!("External JWT auth is not open source"))
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub struct ExternalJwks;
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
impl ExternalJwks {
pub async fn load() -> Option<Arc<RwLock<Self>>> {
// Implementation is not open source

View File

@@ -31,7 +31,7 @@ use hyper::StatusCode;
use serde::{Deserialize, Serialize};
use sql_builder::prelude::*;
use sqlx::{FromRow, Postgres, Transaction};
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::utils::query_elems_from_hub;
use windmill_common::worker::to_raw_value;

View File

@@ -23,7 +23,7 @@ use axum::{
};
use lazy_static::lazy_static;
use regex::Regex;
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::{
db::UserDB,

View File

@@ -1,29 +1,38 @@
use crate::db::{ApiAuthed, DB};
use crate::trigger_helpers::TriggerJobArgs;
use axum::{extract::Request, Router};
use http::HeaderMap;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
use sqlx::prelude::FromRow;
use sqlx::types::Json as SqlxJson;
use std::collections::HashMap;
use windmill_common::db::UserDB;
use windmill_common::worker::to_raw_value;
use windmill_common::{
error::{Error as WindmillError, Result as WindmillResult},
triggers::TriggerKind,
utils::empty_as_none,
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::gcp_triggers_ee::*;
#[cfg(not(feature = "private"))]
use {
crate::db::{ApiAuthed, DB},
crate::trigger_helpers::TriggerJobArgs,
axum::{extract::Request, Router},
http::HeaderMap,
serde::{Deserialize, Serialize},
serde_json::value::RawValue,
sqlx::prelude::FromRow,
sqlx::types::Json as SqlxJson,
std::collections::HashMap,
windmill_common::db::UserDB,
windmill_common::worker::to_raw_value,
windmill_common::{
error::{Error as WindmillError, Result as WindmillResult},
triggers::TriggerKind,
utils::empty_as_none,
},
};
#[derive(sqlx::Type, Debug, Deserialize, Serialize)]
#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))]
#[sqlx(type_name = "DELIVERY_MODE", rename_all = "lowercase")]
#[allow(unused)]
#[cfg(not(feature = "private"))]
pub enum DeliveryType {
Pull,
Push,
}
#[cfg(not(feature = "private"))]
impl Default for DeliveryType {
fn default() -> Self {
Self::Pull
@@ -32,6 +41,7 @@ impl Default for DeliveryType {
#[derive(FromRow, Deserialize, Serialize, Debug)]
#[allow(unused)]
#[cfg(not(feature = "private"))]
pub struct PushConfig {
#[serde(deserialize_with = "empty_as_none")]
route_path: Option<String>,
@@ -42,6 +52,7 @@ pub struct PushConfig {
}
#[derive(Default, Debug, Serialize, Deserialize)]
#[allow(unused)]
#[cfg(not(feature = "private"))]
pub struct CreateUpdateConfig {
pub delivery_type: DeliveryType,
#[serde(default, deserialize_with = "empty_as_none")]
@@ -50,6 +61,7 @@ pub struct CreateUpdateConfig {
}
#[derive(Debug, Deserialize, Serialize)]
#[cfg(not(feature = "private"))]
pub struct ExistingGcpSubscription {
pub subscription_id: String,
pub base_endpoint: String,
@@ -58,15 +70,18 @@ pub struct ExistingGcpSubscription {
#[derive(Debug, Deserialize, Serialize, sqlx::Type)]
#[serde(rename_all = "snake_case")]
#[sqlx(type_name = "GCP_SUBSCRIPTION_MODE", rename_all = "snake_case")]
#[cfg(not(feature = "private"))]
pub enum SubscriptionMode {
Existing,
CreateUpdate,
}
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn start_consuming_gcp_pubsub_event(
_db: DB,
mut _killpill_rx: tokio::sync::broadcast::Receiver<()>,
@@ -74,6 +89,7 @@ pub fn start_consuming_gcp_pubsub_event(
// implementation is not open source
}
#[cfg(not(feature = "private"))]
pub async fn manage_google_subscription(
_authed: ApiAuthed,
_db: &DB,
@@ -91,6 +107,7 @@ pub async fn manage_google_subscription(
Ok(CreateUpdateConfig::default())
}
#[cfg(not(feature = "private"))]
pub async fn process_google_push_request(
_headers: HeaderMap,
_request: Request,
@@ -98,6 +115,7 @@ pub async fn process_google_push_request(
Ok((String::new(), HashMap::new()))
}
#[cfg(not(feature = "private"))]
pub async fn validate_jwt_token(
_db: &DB,
_user_db: UserDB,
@@ -110,11 +128,13 @@ pub async fn validate_jwt_token(
Ok(())
}
#[cfg(not(feature = "private"))]
pub fn gcp_push_route_handler() -> Router {
Router::new()
}
#[derive(FromRow, Deserialize, Serialize, Debug)]
#[cfg(not(feature = "private"))]
pub struct GcpTrigger {
pub gcp_resource_path: String,
pub subscription_id: String,
@@ -135,7 +155,7 @@ pub struct GcpTrigger {
pub last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
pub enabled: bool,
}
#[cfg(not(feature = "private"))]
impl TriggerJobArgs<String> for GcpTrigger {
fn v1_payload_fn(payload: String) -> HashMap<String, Box<RawValue>> {
HashMap::from([("payload".to_string(), to_raw_value(&payload))])

View File

@@ -1,9 +0,0 @@
use axum::routing::Router;
pub fn workspaced_service() -> Router {
Router::new()
}
pub fn global_service() -> Router {
Router::new()
}

View File

@@ -0,0 +1,16 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::git_sync_ee::*;
#[cfg(not(feature = "private"))]
use axum::routing::Router;
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new()
}

View File

@@ -14,7 +14,7 @@ use axum::{
routing::{delete, get, post},
Json, Router,
};
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::worker::CLOUD_HOSTED;
use windmill_common::{

View File

@@ -1,7 +1,7 @@
#[cfg(feature = "http_trigger")]
use crate::http_trigger_args::{HttpMethod, RawHttpTriggerArgs};
#[cfg(feature = "parquet")]
use crate::job_helpers_ee::get_workspace_s3_resource;
use crate::job_helpers_oss::get_workspace_s3_resource;
use crate::resources::try_get_resource_from_db_as;
use crate::trigger_helpers::{get_runnable_format, RunnableId};
use crate::utils::{non_empty_str, ExpiringCacheEntry};
@@ -33,7 +33,7 @@ use std::borrow::Cow;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::{RwLock, RwLockReadGuard};
use tower_http::cors::CorsLayer;
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_audit::{audit_oss::audit_log, ActionKind};
use windmill_common::error::Error;
#[cfg(feature = "parquet")]
use windmill_common::s3_helpers::build_object_store_client;

View File

@@ -1,9 +0,0 @@
use axum::Router;
pub fn workspaced_service() -> Router {
Router::new()
}
pub fn global_service() -> Router {
Router::new()
}

View File

@@ -0,0 +1,16 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::indexer_ee::*;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new()
}

View File

@@ -0,0 +1,11 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::inkeep_ee::*;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new()
}

View File

@@ -1,34 +1,45 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::job_helpers_ee::*;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
use serde::Serialize;
#[cfg(not(feature = "private"))]
use uuid::Uuid;
#[cfg(not(feature = "private"))]
use windmill_common::s3_helpers::StorageResourceType;
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use crate::db::{ApiAuthed, DB};
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use object_store::{ObjectStore, PutMultipartOpts};
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use std::sync::Arc;
#[cfg(not(feature = "private"))]
use windmill_common::error;
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use windmill_common::{db::UserDB, s3_helpers::ObjectStoreResource};
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use bytes::Bytes;
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use futures::Stream;
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use axum::response::Response;
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
use serde::Deserialize;
#[derive(Serialize)]
#[cfg(not(feature = "private"))]
pub struct UploadFileResponse {
pub file_key: String,
}
#[derive(Deserialize)]
#[cfg(not(feature = "private"))]
pub struct LoadImagePreviewQuery {
#[allow(dead_code)]
pub file_key: String,
@@ -37,6 +48,7 @@ pub struct LoadImagePreviewQuery {
}
#[derive(Deserialize)]
#[cfg(not(feature = "private"))]
pub struct DownloadFileQuery {
#[allow(dead_code)]
pub file_key: String,
@@ -46,11 +58,12 @@ pub struct DownloadFileQuery {
pub s3_resource_path: Option<String>,
}
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
pub async fn get_workspace_s3_resource<'c>(
_authed: &ApiAuthed,
_db: &DB,
@@ -63,10 +76,12 @@ pub async fn get_workspace_s3_resource<'c>(
Ok((None, None))
}
#[cfg(not(feature = "private"))]
pub fn get_random_file_name(_file_extension: Option<String>) -> String {
unimplemented!("Not implemented in Windmill's Open Source repository")
}
#[cfg(not(feature = "private"))]
pub async fn get_s3_resource<'c>(
_authed: &ApiAuthed,
_db: &DB,
@@ -82,7 +97,7 @@ pub async fn get_s3_resource<'c>(
))
}
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
pub async fn upload_file_from_req(
_s3_client: Arc<dyn ObjectStore>,
_file_key: &str,
@@ -94,7 +109,7 @@ pub async fn upload_file_from_req(
))
}
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
pub async fn upload_file_internal(
_s3_client: Arc<dyn ObjectStore>,
_file_key: &str,
@@ -106,7 +121,7 @@ pub async fn upload_file_internal(
))
}
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
pub async fn download_s3_file_internal(
_authed: ApiAuthed,
_db: &DB,

View File

@@ -64,7 +64,7 @@ use sqlx::types::JsonRawValue;
use sqlx::{types::Uuid, FromRow, Postgres, Transaction};
use tower_http::cors::{Any, CorsLayer};
use urlencoding::encode;
use windmill_audit::audit_ee::{audit_log, AuditAuthor};
use windmill_audit::audit_oss::{audit_log, AuditAuthor};
use windmill_audit::ActionKind;
use windmill_common::worker::{to_raw_value, CUSTOM_TAGS_PER_WORKSPACE};
use windmill_common::{
@@ -3183,7 +3183,7 @@ async fn check_tag_available_for_workspace(
#[cfg(feature = "enterprise")]
pub async fn check_license_key_valid() -> error::Result<()> {
use windmill_common::ee::LICENSE_KEY_VALID;
use windmill_common::ee_oss::LICENSE_KEY_VALID;
let valid = *LICENSE_KEY_VALID.read().await;
if !valid {

View File

@@ -1,14 +1,24 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::kafka_triggers_ee::*;
#[cfg(not(feature = "private"))]
use crate::db::DB;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[cfg(not(feature = "private"))]
pub struct KafkaResourceSecurity {}
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn start_kafka_consumers(
_db: DB,
mut _killpill_rx: tokio::sync::broadcast::Receiver<()>,
@@ -17,9 +27,11 @@ pub fn start_kafka_consumers(
}
#[derive(Serialize, Deserialize)]
#[cfg(not(feature = "private"))]
pub enum KafkaTriggerConfigConnection {}
#[derive(Serialize, Clone)]
#[cfg(not(feature = "private"))]
pub struct KafkaTrigger {
pub workspace_id: String,
pub path: String,
@@ -39,4 +51,4 @@ pub struct KafkaTrigger {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub enabled: bool,
}
}

View File

@@ -8,15 +8,15 @@
use crate::db::ApiAuthed;
#[cfg(feature = "enterprise")]
use crate::ee::ExternalJwks;
use crate::ee_oss::ExternalJwks;
#[cfg(feature = "embedding")]
use crate::embeddings::load_embeddings_db;
#[cfg(feature = "oauth2")]
use crate::oauth2_ee::AllClients;
use crate::oauth2_oss::AllClients;
#[cfg(feature = "oauth2")]
use crate::oauth2_ee::SlackVerifier;
use crate::oauth2_oss::SlackVerifier;
#[cfg(feature = "smtp")]
use crate::smtp_server_ee::SmtpServer;
use crate::smtp_server_oss::SmtpServer;
#[cfg(feature = "mcp")]
use crate::mcp::{setup_mcp_server, Runner as McpRunner};
@@ -28,7 +28,7 @@ use crate::{
};
#[cfg(feature = "agent_worker_server")]
use agent_workers_ee::AgentCache;
use agent_workers_oss::AgentCache;
use anyhow::Context;
use argon2::Argon2;
@@ -58,11 +58,13 @@ use windmill_common::db::UserDB;
use windmill_common::worker::CLOUD_HOSTED;
use windmill_common::{utils::GIT_VERSION, BASE_URL, INSTANCE_NAME};
use crate::scim_ee::has_scim_token;
use crate::scim_oss::has_scim_token;
use windmill_common::error::AppError;
#[cfg(all(feature = "agent_worker_server", feature = "private"))]
pub mod agent_workers_ee;
#[cfg(feature = "agent_worker_server")]
mod agent_workers_ee;
mod agent_workers_oss;
mod ai;
mod apps;
pub mod args;
@@ -73,7 +75,9 @@ mod concurrency_groups;
mod configs;
mod db;
mod drafts;
#[cfg(feature = "private")]
pub mod ee;
pub mod ee_oss;
pub mod embeddings;
mod favorite;
mod flows;
@@ -86,56 +90,93 @@ mod http_trigger_args;
mod http_trigger_auth;
#[cfg(feature = "http_trigger")]
pub mod http_triggers;
mod indexer_ee;
#[cfg(feature = "private")]
pub mod indexer_ee;
mod indexer_oss;
#[cfg(feature = "private")]
mod inkeep_ee;
mod inkeep_oss;
mod inputs;
mod integration;
#[cfg(feature = "postgres_trigger")]
mod postgres_triggers;
mod approvals;
#[cfg(all(feature = "enterprise", feature = "private"))]
pub mod apps_ee;
#[cfg(feature = "enterprise")]
mod apps_ee;
mod apps_oss;
#[cfg(all(feature = "enterprise", feature = "gcp_trigger", feature = "private"))]
pub mod gcp_triggers_ee;
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
mod gcp_triggers_ee;
mod gcp_triggers_oss;
#[cfg(all(feature = "enterprise", feature = "private"))]
pub mod git_sync_ee;
#[cfg(feature = "enterprise")]
mod git_sync_ee;
mod git_sync_oss;
#[cfg(all(feature = "parquet", feature = "private"))]
pub mod job_helpers_ee;
#[cfg(feature = "parquet")]
mod job_helpers_ee;
mod job_helpers_oss;
pub mod job_metrics;
pub mod jobs;
#[cfg(all(feature = "enterprise", feature = "kafka", feature = "private"))]
pub mod kafka_triggers_ee;
#[cfg(all(feature = "enterprise", feature = "kafka"))]
mod kafka_triggers_ee;
mod kafka_triggers_oss;
#[cfg(feature = "mqtt_trigger")]
mod mqtt_triggers;
#[cfg(all(feature = "enterprise", feature = "nats", feature = "private"))]
pub mod nats_triggers_ee;
#[cfg(all(feature = "enterprise", feature = "nats"))]
mod nats_triggers_ee;
#[cfg(feature = "oauth2")]
mod nats_triggers_oss;
#[cfg(all(feature = "oauth2", feature = "private"))]
pub mod oauth2_ee;
mod oidc_ee;
#[cfg(feature = "oauth2")]
pub mod oauth2_oss;
#[cfg(feature = "private")]
pub mod oidc_ee;
mod oidc_oss;
mod raw_apps;
mod resources;
mod saml_ee;
#[cfg(feature = "private")]
pub mod saml_ee;
mod saml_oss;
mod schedule;
mod scim_ee;
#[cfg(feature = "private")]
pub mod scim_ee;
mod scim_oss;
mod scripts;
mod service_logs;
mod settings;
mod slack_approvals;
#[cfg(all(feature = "smtp", feature = "private"))]
pub mod smtp_server_ee;
#[cfg(feature = "smtp")]
mod smtp_server_ee;
mod smtp_server_oss;
#[cfg(all(feature = "enterprise", feature = "sqs_trigger", feature = "private"))]
pub mod sqs_triggers_ee;
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
mod sqs_triggers_ee;
mod teams_approvals_ee;
mod sqs_triggers_oss;
#[cfg(feature = "private")]
pub mod teams_approvals_ee;
mod teams_approvals_oss;
mod trigger_helpers;
mod static_assets;
#[cfg(all(feature = "stripe", feature = "enterprise", feature = "private"))]
pub mod stripe_ee;
#[cfg(all(feature = "stripe", feature = "enterprise"))]
mod stripe_ee;
mod teams_ee;
mod stripe_oss;
#[cfg(feature = "private")]
pub mod teams_ee;
mod teams_oss;
mod tracing_init;
mod triggers;
mod users;
mod users_ee;
#[cfg(feature = "private")]
pub mod users_ee;
mod users_oss;
mod utils;
mod variables;
pub mod webhook_util;
@@ -143,9 +184,11 @@ pub mod webhook_util;
mod websocket_triggers;
mod workers;
mod workspaces;
mod workspaces_ee;
#[cfg(feature = "private")]
pub mod workspaces_ee;
mod workspaces_export;
mod workspaces_extra;
mod workspaces_oss;
#[cfg(feature = "mcp")]
mod mcp;
@@ -218,9 +261,9 @@ type IndexReader = ();
type ServiceLogIndexReader = ();
#[cfg(feature = "tantivy")]
type IndexReader = windmill_indexer::completed_runs_ee::IndexReader;
type IndexReader = windmill_indexer::completed_runs_oss::IndexReader;
#[cfg(feature = "tantivy")]
type ServiceLogIndexReader = windmill_indexer::service_logs_ee::ServiceLogIndexReader;
type ServiceLogIndexReader = windmill_indexer::service_logs_oss::ServiceLogIndexReader;
pub async fn run_server(
db: DB,
@@ -278,7 +321,7 @@ pub async fn run_server(
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
.allow_origin(Any);
let sp_extension = Arc::new(saml_ee::build_sp_extension().await?);
let sp_extension = Arc::new(saml_oss::build_sp_extension().await?);
if server_mode {
#[cfg(feature = "embedding")]
@@ -317,7 +360,7 @@ pub async fn run_server(
let job_helpers_service = {
#[cfg(feature = "parquet")]
{
job_helpers_ee::workspaced_service()
job_helpers_oss::workspaced_service()
}
#[cfg(not(feature = "parquet"))]
@@ -329,7 +372,7 @@ pub async fn run_server(
let kafka_triggers_service = {
#[cfg(all(feature = "enterprise", feature = "kafka"))]
{
kafka_triggers_ee::workspaced_service()
kafka_triggers_oss::workspaced_service()
}
#[cfg(not(all(feature = "enterprise", feature = "kafka")))]
@@ -341,7 +384,7 @@ pub async fn run_server(
let nats_triggers_service = {
#[cfg(all(feature = "enterprise", feature = "nats"))]
{
nats_triggers_ee::workspaced_service()
nats_triggers_oss::workspaced_service()
}
#[cfg(not(all(feature = "enterprise", feature = "nats")))]
@@ -365,7 +408,7 @@ pub async fn run_server(
let gcp_triggers_service = {
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
{
gcp_triggers_ee::workspaced_service()
gcp_triggers_oss::workspaced_service()
}
#[cfg(not(all(feature = "enterprise", feature = "gcp_trigger")))]
@@ -377,7 +420,7 @@ pub async fn run_server(
let sqs_triggers_service = {
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
{
sqs_triggers_ee::workspaced_service()
sqs_triggers_oss::workspaced_service()
}
#[cfg(not(all(feature = "enterprise", feature = "sqs_trigger")))]
@@ -432,13 +475,13 @@ pub async fn run_server(
#[cfg(all(feature = "enterprise", feature = "kafka"))]
{
let kafka_killpill_rx = killpill_rx.resubscribe();
kafka_triggers_ee::start_kafka_consumers(db.clone(), kafka_killpill_rx);
kafka_triggers_oss::start_kafka_consumers(db.clone(), kafka_killpill_rx);
}
#[cfg(all(feature = "enterprise", feature = "nats"))]
{
let nats_killpill_rx = killpill_rx.resubscribe();
nats_triggers_ee::start_nats_consumers(db.clone(), nats_killpill_rx);
nats_triggers_oss::start_nats_consumers(db.clone(), nats_killpill_rx);
}
#[cfg(feature = "postgres_trigger")]
@@ -456,13 +499,13 @@ pub async fn run_server(
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
{
let sqs_killpill_rx = killpill_rx.resubscribe();
sqs_triggers_ee::start_sqs(db.clone(), sqs_killpill_rx);
sqs_triggers_oss::start_sqs(db.clone(), sqs_killpill_rx);
}
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
{
let gcp_killpill_rx = killpill_rx.resubscribe();
gcp_triggers_ee::start_consuming_gcp_pubsub_event(db.clone(), gcp_killpill_rx);
gcp_triggers_oss::start_consuming_gcp_pubsub_event(db.clone(), gcp_killpill_rx);
}
}
@@ -497,7 +540,7 @@ pub async fn run_server(
#[cfg(feature = "agent_worker_server")]
let (agent_workers_router, agent_workers_bg_processor, agent_workers_killpill_tx) =
if server_mode {
agent_workers_ee::workspaced_service(db.clone(), _base_internal_url.clone())
agent_workers_oss::workspaced_service(db.clone(), _base_internal_url.clone())
} else {
(Router::new(), vec![], None)
};
@@ -535,7 +578,7 @@ pub async fn run_server(
.nest("/oauth", {
#[cfg(feature = "oauth2")]
{
oauth2_ee::workspaced_service()
oauth2_oss::workspaced_service()
}
#[cfg(not(feature = "oauth2"))]
@@ -552,7 +595,7 @@ pub async fn run_server(
)
.nest("/variables", variables::workspaced_service())
.nest("/workspaces", workspaces::workspaced_service())
.nest("/oidc", oidc_ee::workspaced_service())
.nest("/oidc", oidc_oss::workspaced_service())
.nest("/http_triggers", http_triggers_service)
.nest("/websocket_triggers", websocket_triggers_service)
.nest("/kafka_triggers", kafka_triggers_service)
@@ -579,22 +622,23 @@ pub async fn run_server(
.nest("/schedules", schedule::global_service())
.nest("/embeddings", embeddings::global_service())
.nest("/ai", ai::global_service())
.nest("/inkeep", inkeep_oss::global_service())
.route_layer(from_extractor::<ApiAuthed>())
.route_layer(from_extractor::<users::Tokened>())
.nest("/jobs", jobs::global_root_service())
.nest(
"/srch/w/:workspace_id/index",
indexer_ee::workspaced_service(),
indexer_oss::workspaced_service(),
)
.nest("/srch/index", indexer_ee::global_service())
.nest("/oidc", oidc_ee::global_service())
.nest("/srch/index", indexer_oss::global_service())
.nest("/oidc", oidc_oss::global_service())
.nest(
"/saml",
saml_ee::global_service().layer(Extension(Arc::clone(&sp_extension))),
saml_oss::global_service().layer(Extension(Arc::clone(&sp_extension))),
)
.nest(
"/scim",
scim_ee::global_service()
scim_oss::global_service()
.route_layer(axum::middleware::from_fn(has_scim_token)),
)
.nest("/concurrency_groups", concurrency_groups::global_service())
@@ -602,7 +646,7 @@ pub async fn run_server(
.nest("/apps_u", {
#[cfg(feature = "enterprise")]
{
apps_ee::global_unauthed_service()
apps_oss::global_unauthed_service()
}
#[cfg(not(feature = "enterprise"))]
@@ -621,7 +665,7 @@ pub async fn run_server(
.nest("/agent_workers", {
#[cfg(feature = "agent_worker_server")]
{
agent_workers_ee::global_service().layer(Extension(agent_cache.clone()))
agent_workers_oss::global_service().layer(Extension(agent_cache.clone()))
}
#[cfg(not(feature = "agent_worker_server"))]
{
@@ -646,7 +690,7 @@ pub async fn run_server(
.nest("/teams", {
#[cfg(feature = "enterprise")]
{
teams_ee::teams_service()
teams_oss::teams_service()
}
#[cfg(not(feature = "enterprise"))]
@@ -660,12 +704,12 @@ pub async fn run_server(
)
.route(
"/w/:workspace_id/jobs/teams_approval/:job_id",
get(teams_approvals_ee::request_teams_approval),
get(teams_approvals_oss::request_teams_approval),
)
.nest("/w/:workspace_id/github_app", {
#[cfg(feature = "enterprise")]
{
git_sync_ee::workspaced_service()
git_sync_oss::workspaced_service()
}
#[cfg(not(feature = "enterprise"))]
@@ -674,7 +718,7 @@ pub async fn run_server(
.nest("/github_app", {
#[cfg(feature = "enterprise")]
{
git_sync_ee::global_service()
git_sync_oss::global_service()
}
#[cfg(not(feature = "enterprise"))]
@@ -695,7 +739,7 @@ pub async fn run_server(
.nest("/oauth", {
#[cfg(feature = "oauth2")]
{
oauth2_ee::global_service().layer(Extension(Arc::clone(&sp_extension)))
oauth2_oss::global_service().layer(Extension(Arc::clone(&sp_extension)))
}
#[cfg(not(feature = "oauth2"))]
@@ -721,7 +765,7 @@ pub async fn run_server(
{
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
{
gcp_triggers_ee::gcp_push_route_handler()
gcp_triggers_oss::gcp_push_route_handler()
}
#[cfg(not(all(feature = "enterprise", feature = "gcp_trigger")))]
{
@@ -842,7 +886,7 @@ async fn ee_license() -> &'static str {
#[cfg(feature = "enterprise")]
async fn ee_license() -> String {
use windmill_common::ee::{LICENSE_KEY_ID, LICENSE_KEY_VALID};
use windmill_common::ee_oss::{LICENSE_KEY_ID, LICENSE_KEY_VALID};
if *LICENSE_KEY_VALID.read().await {
LICENSE_KEY_ID.read().await.clone()

View File

@@ -39,7 +39,7 @@ use sql_builder::{bind::Bind, SqlBuilder};
use sqlx::{FromRow, Type};
use std::collections::HashMap;
use std::time::Duration;
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_audit::{audit_oss::audit_log, ActionKind};
use windmill_common::{
db::UserDB,
error::{self, JsonResult},

View File

@@ -1,22 +1,34 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::nats_triggers_ee::*;
#[cfg(not(feature = "private"))]
use crate::db::DB;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "private"))]
#[derive(Serialize, Deserialize)]
pub struct NatsResourceAuth {}
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn start_nats_consumers(_db: DB, mut _killpill_rx: tokio::sync::broadcast::Receiver<()>) -> () {
// implementation is not open source
}
#[derive(Serialize, Deserialize)]
#[cfg(not(feature = "private"))]
pub enum NatsTriggerConfigConnection {}
#[derive(Serialize, Clone)]
#[cfg(not(feature = "private"))]
pub struct NatsTrigger {
pub workspace_id: String,
pub path: String,
@@ -40,4 +52,4 @@ pub struct NatsTrigger {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub enabled: bool,
}
}

View File

@@ -1,3 +1,7 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::oauth2_ee::*;
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2022
@@ -6,39 +10,50 @@
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(not(feature = "private"))]
use std::{collections::HashMap, fmt::Debug};
#[cfg(not(feature = "private"))]
use axum::{routing::get, Json, Router};
#[cfg(not(feature = "private"))]
use hmac::Mac;
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
use itertools::Itertools;
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
use oauth2::{Client as OClient, *};
#[cfg(not(feature = "private"))]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "private"))]
use sqlx::{Postgres, Transaction};
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
use windmill_common::more_serde::maybe_number_opt;
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
use crate::OAUTH_CLIENTS;
#[cfg(not(feature = "private"))]
use windmill_common::error;
#[cfg(not(feature = "private"))]
use windmill_common::oauth2::*;
#[cfg(not(feature = "private"))]
use crate::db::DB;
#[cfg(not(feature = "private"))]
use std::str;
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new()
.route("/list_logins", get(list_logins))
.route("/list_connects", get(list_connects))
}
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
#[derive(Debug, Clone)]
pub struct ClientWithScopes {
_client: OClient,
@@ -48,9 +63,10 @@ pub struct ClientWithScopes {
_allowed_domains: Option<Vec<String>>,
_userinfo_url: Option<String>,
}
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
pub type BasicClientsMap = HashMap<String, ClientWithScopes>;
#[cfg(not(feature = "private"))]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OAuthConfig {
auth_url: String,
@@ -62,6 +78,7 @@ pub struct OAuthConfig {
req_body_auth: Option<bool>,
}
#[cfg(not(feature = "private"))]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OAuthClient {
id: String,
@@ -71,7 +88,7 @@ pub struct OAuthClient {
login_config: Option<OAuthConfig>,
}
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
#[derive(Debug)]
pub struct AllClients {
pub logins: BasicClientsMap,
@@ -79,7 +96,7 @@ pub struct AllClients {
pub slack: Option<OClient>,
}
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
pub async fn build_oauth_clients(
_base_url: &str,
_oauths_from_config: Option<HashMap<String, OAuthClient>>,
@@ -93,7 +110,7 @@ pub async fn build_oauth_clients(
});
}
#[cfg(feature = "oauth2")]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TokenResponse {
access_token: AccessToken,
@@ -107,17 +124,20 @@ pub struct TokenResponse {
scope: Option<Vec<Scope>>,
}
#[cfg(not(feature = "private"))]
#[derive(Serialize)]
struct Logins {
oauth: Vec<String>,
saml: Option<String>,
}
#[cfg(not(feature = "private"))]
async fn list_logins() -> error::JsonResult<Logins> {
// Implementation is not open source
return Ok(Json(Logins { oauth: vec![], saml: None }));
}
#[cfg(feature = "oauth2")]
#[allow(unused)]
#[cfg(all(feature = "oauth2", not(feature = "private")))]
async fn list_connects() -> error::JsonResult<Vec<String>> {
Ok(Json(
(&OAUTH_CLIENTS.read().await.connects)
@@ -127,12 +147,14 @@ async fn list_connects() -> error::JsonResult<Vec<String>> {
))
}
#[cfg(not(feature = "oauth2"))]
async fn list_connects() -> error::JsonResult<Vec<String>> {
#[allow(unused)]
#[cfg(not(all(feature = "oauth2", not(feature = "private"))))]
async fn list_connects() -> windmill_common::error::JsonResult<Vec<String>> {
// Implementation is not open source
return Ok(Json(vec![]));
return Ok(axum::Json(vec![]));
}
#[cfg(not(feature = "private"))]
pub async fn _refresh_token<'c>(
_tx: Transaction<'c, Postgres>,
_path: &str,
@@ -146,6 +168,7 @@ pub async fn _refresh_token<'c>(
))
}
#[cfg(not(feature = "private"))]
pub async fn check_nb_of_user(db: &DB) -> error::Result<()> {
let nb_users_sso =
sqlx::query_scalar!("SELECT COUNT(*) FROM password WHERE login_type != 'password'",)
@@ -171,10 +194,11 @@ pub async fn check_nb_of_user(db: &DB) -> error::Result<()> {
}
#[derive(Clone, Debug)]
#[cfg(not(feature = "private"))]
pub struct SlackVerifier {
_mac: HmacSha256,
}
#[cfg(not(feature = "private"))]
impl SlackVerifier {
pub fn new<S: AsRef<[u8]>>(secret: S) -> anyhow::Result<SlackVerifier> {
HmacSha256::new_from_slice(secret.as_ref())

View File

@@ -1,3 +1,7 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::oidc_ee::*;
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2023
@@ -6,12 +10,15 @@
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}

View File

@@ -6,7 +6,6 @@ use std::collections::{
use crate::{
db::{ApiAuthed, DB},
postgres_triggers::mapper::{Mapper, MappingInfo},
resources::try_get_resource_from_db_as,
};
use axum::{
extract::{Path, Query},
@@ -19,21 +18,20 @@ use quick_cache::sync::Cache;
use rust_postgres::types::Type;
use serde::{Deserialize, Deserializer, Serialize};
use sql_builder::{bind::Bind, SqlBuilder};
use sqlx::{postgres::types::Oid, FromRow, PgConnection};
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_common::error::Error;
use sqlx::{postgres::types::Oid, Connection, FromRow, PgConnection};
use windmill_audit::{audit_oss::audit_log, ActionKind};
use windmill_common::{
db::UserDB,
error::{self, JsonResult, Result},
utils::{not_found_if_none, paginate, Pagination, StripPath, empty_as_none},
error::{self, Error, JsonResult, Result},
utils::{empty_as_none, not_found_if_none, paginate, Pagination, StripPath},
worker::CLOUD_HOSTED,
};
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
use super::{
create_logical_replication_slot_query, create_publication_query, drop_publication_query,
generate_random_string, get_database_connection, get_raw_postgres_connection,
ERROR_PUBLICATION_NAME_NOT_EXISTS, ERROR_REPLICATION_SLOT_NOT_EXISTS,
check_if_valid_publication_for_postgres_version, create_logical_replication_slot,
create_pg_publication, drop_publication, generate_random_string, get_pg_connection,
ERROR_PUBLICATION_NAME_NOT_EXISTS,
};
use lazy_static::lazy_static;
@@ -53,15 +51,17 @@ pub struct Postgres {
#[derive(Debug, Clone, FromRow, Serialize, Deserialize)]
pub struct TableToTrack {
pub table_name: String,
#[serde(default, deserialize_with = "empty_as_none")]
pub where_clause: Option<String>,
pub columns_name: Vec<String>,
#[serde(default, deserialize_with = "empty_as_none")]
pub columns_name: Option<Vec<String>>,
}
impl TableToTrack {
fn new(
table_name: String,
where_clause: Option<String>,
columns_name: Vec<String>,
columns_name: Option<Vec<String>>,
) -> TableToTrack {
TableToTrack { table_name, where_clause, columns_name }
}
@@ -99,7 +99,6 @@ pub struct EditPostgresTrigger {
}
#[derive(Deserialize, Serialize, Debug)]
pub struct NewPostgresTrigger {
path: String,
script_path: String,
@@ -124,7 +123,7 @@ pub async fn test_postgres_connection(
Json(test_postgres): Json<TestPostgres>,
) -> Result<()> {
let connect_f = async {
get_database_connection(
get_pg_connection(
authed,
Some(user_db),
&db,
@@ -189,7 +188,7 @@ where
));
}
if !track_specific_columns_in_table && !table_to_track.columns_name.is_empty() {
if !track_specific_columns_in_table && table_to_track.columns_name.is_some() {
track_specific_columns_in_table = true;
}
}
@@ -267,39 +266,16 @@ impl PostgresPublicationReplication {
}
}
async fn check_if_publication_exist(
connection: &mut PgConnection,
publication_name: &str,
) -> Result<()> {
sqlx::query!(
"SELECT pubname FROM pg_publication WHERE pubname = $1",
publication_name
)
.fetch_one(connection)
.await
.map_err(|err| match err {
sqlx::Error::RowNotFound => {
Error::BadRequest(ERROR_PUBLICATION_NAME_NOT_EXISTS.to_string())
}
err => Error::SqlErr { error: err, location: "pg_trigger".to_string() },
})?;
Ok(())
}
async fn check_if_logical_replication_slot_exist(
connection: &mut PgConnection,
pg_connection: &mut PgConnection,
replication_slot_name: &str,
) -> Result<()> {
sqlx::query!(
"SELECT slot_name FROM pg_replication_slots where slot_name = $1",
&replication_slot_name
)
.fetch_one(connection)
.await
.map_err(|err| match err {
_ => Error::BadRequest(ERROR_REPLICATION_SLOT_NOT_EXISTS.to_string()),
})?;
Ok(())
) -> Result<bool> {
let exists = sqlx::query("SELECT slot_name FROM pg_replication_slots where slot_name = $1")
.bind(&replication_slot_name)
.fetch_optional(pg_connection)
.await?
.is_some();
Ok(exists)
}
async fn create_custom_slot_and_publication_inner(
@@ -310,33 +286,30 @@ async fn create_custom_slot_and_publication_inner(
w_id: &str,
publication: &PublicationData,
) -> Result<PostgresPublicationReplication> {
let publication_name = format!("windmill_trigger_{}", generate_random_string());
let replication_slot_name = publication_name.clone();
let query = create_publication_query(
&publication_name,
publication.table_to_track.as_deref(),
&publication
.transaction_to_track
.iter()
.map(AsRef::as_ref)
.collect_vec(),
);
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
Some(user_db),
&db,
&postgres_resource_path,
&w_id,
)
.await?;
sqlx::query(&query).execute(&mut connection).await?;
let mut tx = pg_connection.begin().await?;
let publication_name = format!("windmill_trigger_{}", generate_random_string());
let replication_slot_name = publication_name.clone();
let query = create_logical_replication_slot_query(&replication_slot_name);
create_logical_replication_slot(&mut tx, &replication_slot_name).await?;
sqlx::query(&query).execute(&mut connection).await?;
create_pg_publication(
&mut tx,
&publication_name,
publication.table_to_track.as_deref(),
&publication.transaction_to_track,
)
.await?;
tx.commit().await?;
Ok(PostgresPublicationReplication::new(
publication_name,
@@ -344,6 +317,38 @@ async fn create_custom_slot_and_publication_inner(
))
}
pub async fn get_postgres_version_internal(pg_connection: &mut PgConnection) -> Result<String> {
let postgres_version: String = sqlx::query_scalar("SHOW server_version;")
.fetch_one(&mut *pg_connection)
.await
.map_err(|e| Error::Anyhow {
error: anyhow::anyhow!("Failed to retrieve PostgreSQL version: {}", e),
location: "postgres_triggers/handler.rs@379".to_string(),
})?;
Ok(postgres_version)
}
pub async fn get_postgres_version(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Extension(user_db): Extension<UserDB>,
Path((w_id, postgres_resource_path)): Path<(String, String)>,
) -> Result<String> {
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db),
&db,
&postgres_resource_path,
&w_id,
)
.await?;
let postgres_version = get_postgres_version_internal(&mut pg_connection).await?;
Ok(postgres_version)
}
pub async fn create_postgres_trigger(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -377,6 +382,7 @@ pub async fn create_postgres_trigger(
if publication.is_none() {
return Err(Error::BadRequest("publication must be set".to_string()));
}
let PostgresPublicationReplication { publication_name, replication_slot_name } =
create_custom_slot_and_publication_inner(
authed.clone(),
@@ -577,7 +583,7 @@ impl PublicationData {
}
}
#[derive(Debug, Serialize)]
#[derive(FromRow, Debug, Serialize)]
pub struct SlotList {
slot_name: Option<String>,
active: Option<bool>,
@@ -589,7 +595,7 @@ pub async fn list_slot_name(
Extension(db): Extension<DB>,
Path((w_id, postgres_resource_path)): Path<(String, String)>,
) -> Result<Json<Vec<SlotList>>> {
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -598,8 +604,7 @@ pub async fn list_slot_name(
)
.await?;
let slots = sqlx::query_as!(
SlotList,
let slots: Vec<SlotList> = sqlx::query_as(
r#"
SELECT
slot_name,
@@ -609,9 +614,9 @@ pub async fn list_slot_name(
WHERE
plugin = 'pgoutput' AND
slot_type = 'logical';
"#
"#,
)
.fetch_all(&mut connection)
.fetch_all(&mut pg_connection)
.await?;
Ok(Json(slots))
@@ -629,7 +634,7 @@ pub async fn create_slot(
Path((w_id, postgres_resource_path)): Path<(String, String)>,
Json(Slot { name }): Json<Slot>,
) -> Result<String> {
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -638,13 +643,42 @@ pub async fn create_slot(
)
.await?;
let query = create_logical_replication_slot_query(&name);
sqlx::query(&query).execute(&mut connection).await?;
create_logical_replication_slot(&mut pg_connection, &name).await?;
Ok(format!("Replication slot {} created!", name))
}
pub async fn drop_logical_replication_slot(
pg_connection: &mut PgConnection,
slot_name: &str,
) -> Result<()> {
let active_pid: Option<i32> = sqlx::query_scalar(
r#"SELECT
active_pid
FROM
pg_replication_slots
WHERE
slot_name = $1
"#,
)
.bind(&slot_name)
.fetch_optional(&mut *pg_connection)
.await?
.flatten();
if let Some(pid) = active_pid {
sqlx::query("SELECT pg_terminate_backend($1)")
.bind(pid)
.execute(&mut *pg_connection)
.await?;
}
sqlx::query("SELECT pg_drop_replication_slot($1)")
.bind(&slot_name)
.execute(pg_connection)
.await?;
Ok(())
}
pub async fn drop_slot_name(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
@@ -652,45 +686,14 @@ pub async fn drop_slot_name(
Path((w_id, postgres_resource_path)): Path<(String, String)>,
Json(Slot { name }): Json<Slot>,
) -> Result<String> {
let database = try_get_resource_from_db_as::<Postgres>(
authed,
Some(user_db),
&db,
&postgres_resource_path,
&w_id,
)
.await?;
let mut pg_connection =
get_pg_connection(authed, Some(user_db), &db, &postgres_resource_path, &w_id).await?;
let mut connection = get_raw_postgres_connection(&database).await?;
let active_pid = sqlx::query_scalar!(
r#"SELECT
active_pid
FROM
pg_replication_slots
WHERE
slot_name = $1
"#,
&name
)
.fetch_optional(&mut connection)
.await?
.flatten();
if let Some(pid) = active_pid {
sqlx::query("SELECT pg_terminate_backend($1)")
.bind(pid)
.execute(&mut connection)
.await?;
}
sqlx::query("SELECT pg_drop_replication_slot($1)")
.bind(&name)
.execute(&mut connection)
.await?;
drop_logical_replication_slot(&mut pg_connection, &name).await?;
Ok(format!("Replication slot {} deleted!", name))
}
#[derive(Debug, Serialize)]
#[derive(FromRow, Debug, Serialize)]
struct PublicationName {
publication_name: String,
}
@@ -701,7 +704,7 @@ pub async fn list_database_publication(
Extension(db): Extension<DB>,
Path((w_id, postgres_resource_path)): Path<(String, String)>,
) -> Result<Json<Vec<String>>> {
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -710,12 +713,10 @@ pub async fn list_database_publication(
)
.await?;
let publication_names = sqlx::query_as!(
PublicationName,
"SELECT pubname AS publication_name FROM pg_publication;"
)
.fetch_all(&mut connection)
.await?;
let publication_names: Vec<PublicationName> =
sqlx::query_as("SELECT pubname AS publication_name FROM pg_publication;")
.fetch_all(&mut pg_connection)
.await?;
let publications = publication_names
.iter()
@@ -731,7 +732,7 @@ pub async fn get_publication_info(
Extension(db): Extension<DB>,
Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>,
) -> Result<Json<PublicationData>> {
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -741,11 +742,11 @@ pub async fn get_publication_info(
.await?;
let publication_data =
get_publication_scope_and_transaction(&mut connection, &publication_name).await;
get_publication_scope_and_transaction(&mut pg_connection, &publication_name).await;
let (all_table, transaction_to_track) = match publication_data {
Ok(pub_data) => pub_data,
Err(Error::SqlErr { error: sqlx::Error::RowNotFound, .. }) => {
Ok(Some(pub_data)) => pub_data,
Ok(None) => {
return Err(Error::NotFound(
ERROR_PUBLICATION_NAME_NOT_EXISTS.to_string(),
))
@@ -754,7 +755,7 @@ pub async fn get_publication_info(
};
let table_to_track = if !all_table {
Some(get_tracked_relations(&mut connection, &publication_name).await?)
Some(get_tracked_relations(&mut pg_connection, &publication_name).await?)
} else {
None
};
@@ -771,9 +772,7 @@ pub async fn create_publication(
Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>,
Json(publication_data): Json<PublicationData>,
) -> Result<String> {
let PublicationData { table_to_track, transaction_to_track } = publication_data;
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -782,13 +781,19 @@ pub async fn create_publication(
)
.await?;
let query = create_publication_query(
let PublicationData { table_to_track, transaction_to_track } = publication_data;
let mut tx = pg_connection.begin().await?;
create_pg_publication(
&mut tx,
&publication_name,
table_to_track.as_deref(),
&transaction_to_track.iter().map(AsRef::as_ref).collect_vec(),
);
&transaction_to_track,
)
.await?;
sqlx::query(&query).execute(&mut connection).await?;
tx.commit().await?;
Ok(format!(
"Publication {} successfully created!",
@@ -802,7 +807,7 @@ pub async fn delete_publication(
Extension(db): Extension<DB>,
Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>,
) -> Result<String> {
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -811,9 +816,7 @@ pub async fn delete_publication(
)
.await?;
let query = drop_publication_query(&publication_name);
sqlx::query(&query).execute(&mut connection).await?;
drop_publication(&mut pg_connection, &publication_name).await?;
Ok(format!(
"Publication {} successfully deleted!",
@@ -821,27 +824,37 @@ pub async fn delete_publication(
))
}
pub fn get_update_publication_query(
pub async fn update_pg_publication(
pg_connection: &mut PgConnection,
publication_name: &str,
PublicationData { table_to_track, transaction_to_track }: PublicationData,
all_table: bool,
) -> Vec<String> {
all_table: Option<bool>,
) -> Result<()> {
let quoted_publication_name = quote_identifier(&publication_name);
let transaction_to_track_as_str = transaction_to_track.iter().join(",");
let mut queries = Vec::with_capacity(2);
match table_to_track {
Some(ref relations) if !relations.is_empty() => {
if all_table {
queries.push(drop_publication_query(&publication_name));
queries.push(create_publication_query(
//if all table is none it means that the publication do not exist in the database
if all_table.unwrap_or(true) {
if all_table.is_some() {
drop_publication(pg_connection, &publication_name).await?;
}
create_pg_publication(
pg_connection,
&publication_name,
table_to_track.as_deref(),
&transaction_to_track.iter().map(AsRef::as_ref).collect_vec(),
));
&transaction_to_track,
)
.await?;
} else {
let mut query = String::from("");
let pg_14 = check_if_valid_publication_for_postgres_version(
pg_connection,
table_to_track.as_deref(),
)
.await?;
let mut query = String::from("");
let mut first = true;
query.push_str("ALTER PUBLICATION ");
query.push_str(&quoted_publication_name);
query.push_str(" SET");
@@ -851,16 +864,20 @@ pub fn get_update_publication_query(
let quoted_schema = quote_identifier(&schema.schema_name);
query.push_str(&quoted_schema);
} else {
query.push_str(" TABLE ONLY ");
if pg_14 && first {
query.push_str(" TABLE ONLY ");
first = false
} else if !pg_14 {
query.push_str(" TABLE ONLY ");
}
for (j, table) in schema.table_to_track.iter().enumerate() {
let table_name = quote_identifier(&table.table_name);
let schema_name = quote_identifier(&schema.schema_name);
let full_name = format!("{}.{}", &schema_name, &table_name);
query.push_str(&full_name);
if !table.columns_name.is_empty() {
if let Some(columns) = table.columns_name.as_ref() {
query.push_str(" (");
let columns = table
.columns_name
let columns = columns
.iter()
.map(|column| quote_identifier(column))
.join(", ");
@@ -883,9 +900,8 @@ pub fn get_update_publication_query(
query.push(',');
}
}
query.push(';');
queries.push(query);
sqlx::query(&query).execute(&mut *pg_connection).await?;
let mut query = String::new();
@@ -895,23 +911,25 @@ pub fn get_update_publication_query(
" SET (publish = '{}');",
transaction_to_track_as_str
));
queries.push(query);
sqlx::query(&query).execute(pg_connection).await?;
}
}
_ => {
queries.push(drop_publication_query(&publication_name));
let to_execute = format!(
drop_publication(pg_connection, &publication_name).await?;
let query_to_execute = format!(
r#"
CREATE
PUBLICATION {} FOR ALL TABLES WITH (publish = '{}');
"#,
quoted_publication_name, transaction_to_track_as_str
);
queries.push(to_execute);
sqlx::query(&query_to_execute)
.execute(pg_connection)
.await?;
}
};
queries
Ok(())
}
pub async fn alter_publication(
@@ -921,7 +939,7 @@ pub async fn alter_publication(
Path((w_id, publication_name, postgres_resource_path)): Path<(String, String, String)>,
Json(publication_data): Json<PublicationData>,
) -> Result<String> {
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -930,16 +948,19 @@ pub async fn alter_publication(
)
.await?;
check_if_publication_exist(&mut connection, &publication_name).await?;
let mut tx = pg_connection.begin().await?;
let (all_table, _) =
get_publication_scope_and_transaction(&mut connection, &publication_name).await?;
let publication = get_publication_scope_and_transaction(&mut tx, &publication_name).await?;
let queries = get_update_publication_query(&publication_name, publication_data, all_table);
update_pg_publication(
&mut tx,
&publication_name,
publication_data,
publication.map(|publication| publication.0),
)
.await?;
for query in queries {
sqlx::query(&query).execute(&mut connection).await?;
}
tx.commit().await?;
Ok(format!(
"Publication {} updated with success",
@@ -948,9 +969,9 @@ pub async fn alter_publication(
}
async fn get_publication_scope_and_transaction(
connection: &mut PgConnection,
pg_connection: &mut PgConnection,
publication_name: &str,
) -> std::result::Result<(bool, Vec<String>), Error> {
) -> Result<Option<(bool, Vec<String>)>> {
#[derive(Debug, Deserialize, FromRow)]
struct PublicationTransaction {
all_table: bool,
@@ -959,8 +980,7 @@ async fn get_publication_scope_and_transaction(
delete: bool,
}
let transaction = sqlx::query_as!(
PublicationTransaction,
let publication: Option<PublicationTransaction> = sqlx::query_as(
r#"
SELECT
puballtables AS all_table,
@@ -972,70 +992,100 @@ async fn get_publication_scope_and_transaction(
WHERE
pubname = $1
"#,
publication_name
)
.fetch_one(&mut *connection)
.bind(publication_name)
.fetch_optional(&mut *pg_connection)
.await?;
if publication.is_none() {
return Ok(None);
}
let mut transaction_to_track = Vec::with_capacity(3);
if transaction.insert {
let publication = publication.unwrap();
if publication.insert {
transaction_to_track.push("insert".to_string());
}
if transaction.update {
if publication.update {
transaction_to_track.push("update".to_string());
}
if transaction.delete {
if publication.delete {
transaction_to_track.push("delete".to_string());
}
Ok((transaction.all_table, transaction_to_track))
Ok(Some((publication.all_table, transaction_to_track)))
}
async fn get_tracked_relations(
connection: &mut PgConnection,
pg_connection: &mut PgConnection,
publication_name: &str,
) -> Result<Vec<Relations>> {
#[derive(Debug, Deserialize, FromRow)]
struct PublicationData {
schema_name: Option<String>,
table_name: Option<String>,
#[serde(default)]
columns: Option<Vec<String>>,
#[serde(default)]
where_clause: Option<String>,
}
let publications = sqlx::query_as!(
PublicationData,
let pg_version = get_postgres_version_internal(pg_connection).await?;
let query = if pg_version.starts_with("14") {
r#"
SELECT
schemaname AS schema_name,
tablename AS table_name,
CASE
WHEN array_length(attnames, 1) = (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = pg_publication_tables.schemaname AND table_name = pg_publication_tables.tablename)
THEN NULL
ELSE attnames
END AS columns,
NULL::text[] AS columns,
NULL::text AS where_clause
FROM
pg_publication_tables
WHERE
pubname = $1;
"#
} else {
r#"
SELECT
schemaname AS schema_name,
tablename AS table_name,
attnames AS columns,
rowfilter AS where_clause
FROM
pg_publication_tables
WHERE
pubname = $1;
"#,
publication_name
)
.fetch_all(&mut *connection)
.await?;
"#
};
let publications: Vec<PublicationData> = sqlx::query_as(query)
.bind(publication_name)
.fetch_all(&mut *pg_connection)
.await?;
let mut table_to_track: HashMap<String, Relations> = HashMap::new();
for publication in publications {
let schema_name = publication.schema_name.unwrap();
let schema_name = publication.schema_name.ok_or_else(|| Error::Anyhow {
error: anyhow::anyhow!(
"Unexpected NULL `schema_name` in publication entry (pubname: `{}`). This should never happen unless PostgreSQL internals are corrupted.",
publication_name,
),
location: "postgres_triggers/handler.rs@1093".to_string(),
})?;
let table_name = publication.table_name.ok_or_else(|| Error::Anyhow {
error: anyhow::anyhow!(
"Unexpected NULL `table_name` for schema `{}` in publication `{}`. This should never happen unless PostgreSQL internals are corrupted.",
schema_name,
publication_name,
),
location: "postgres_triggers/handler.rs@1102".to_string(),
})?;
let entry = table_to_track.entry(schema_name.clone());
let table_to_track = TableToTrack::new(
publication.table_name.unwrap(),
publication.where_clause,
publication.columns.unwrap_or_default(),
);
let table_to_track =
TableToTrack::new(table_name, publication.where_clause, publication.columns);
match entry {
Occupied(mut occuped) => {
occuped.get_mut().add_new_table(table_to_track);
@@ -1111,7 +1161,7 @@ pub async fn update_postgres_trigger(
publication,
} = postgres_trigger;
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -1120,18 +1170,33 @@ pub async fn update_postgres_trigger(
)
.await?;
check_if_logical_replication_slot_exist(&mut connection, &replication_slot_name).await?;
let exists =
check_if_logical_replication_slot_exist(&mut pg_connection, &replication_slot_name).await?;
let mut tx = pg_connection.begin().await?;
if !exists {
tracing::debug!(
"Logical replication slot named: {} does not exists creating it...",
&replication_slot_name
);
create_logical_replication_slot(&mut tx, &replication_slot_name).await?;
}
if let Some(publication) = publication {
check_if_publication_exist(&mut connection, &publication_name).await?;
let (all_table, _) =
get_publication_scope_and_transaction(&mut connection, &publication_name).await?;
let publication_data =
get_publication_scope_and_transaction(&mut tx, &publication_name).await?;
let queries = get_update_publication_query(&publication_name, publication, all_table);
for query in queries {
sqlx::query(&query).execute(&mut connection).await?;
}
update_pg_publication(
&mut tx,
&publication_name,
publication,
publication_data.map(|publication| publication.0),
)
.await?;
}
tx.commit().await?;
let mut tx = user_db.begin(&authed).await?;
sqlx::query!(
@@ -1356,7 +1421,7 @@ pub async fn create_template_script(
));
}
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -1384,11 +1449,12 @@ pub async fn create_template_script(
format!("{}.{}", &relation.schema_name, table_to_track.table_name);
schema_or_fully_qualified_name.push(quote_literal(&fully_qualified_name));
let columns = if !table_to_track.columns_name.is_empty() {
quote_literal(&table_to_track.columns_name.join(","))
} else {
"''".to_string()
};
let columns = table_to_track
.columns_name
.map(|columns| quote_literal(&columns.join(",")))
.or_else(|| Some("''".to_string()))
.unwrap();
columns_list.push(columns);
}
continue;
@@ -1448,11 +1514,7 @@ pub async fn create_template_script(
tables_name, columns_list
);
let rows: Vec<ColumnInfo> = sqlx::query_as(&query)
.fetch_all(&mut connection)
.await
.map_err(|e| error::Error::SqlErr { error: e, location: "pg_trigger".to_string() })?;
let rows: Vec<ColumnInfo> = sqlx::query_as(&query).fetch_all(&mut pg_connection).await?;
let mut mapper: HashMap<String, HashMap<String, Vec<MappingInfo>>> = HashMap::new();
for row in rows {
@@ -1509,7 +1571,7 @@ pub async fn is_database_in_logical_level(
Extension(db): Extension<DB>,
Path((w_id, postgres_resource_path)): Path<(String, String)>,
) -> error::JsonResult<bool> {
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -1518,8 +1580,8 @@ pub async fn is_database_in_logical_level(
)
.await?;
let wal_level = sqlx::query_scalar!("SHOW WAL_LEVEL;")
.fetch_optional(&mut connection)
let wal_level: Option<String> = sqlx::query_scalar("SHOW WAL_LEVEL;")
.fetch_optional(&mut pg_connection)
.await?
.flatten();

View File

@@ -25,12 +25,17 @@ pub use handler::PostgresTrigger;
use handler::{
alter_publication, create_postgres_trigger, create_publication, create_slot,
create_template_script, delete_postgres_trigger, delete_publication, drop_slot_name,
exists_postgres_trigger, get_postgres_trigger, get_publication_info, get_template_script,
exists_postgres_trigger, get_postgres_trigger, get_postgres_version,
get_postgres_version_internal, get_publication_info, get_template_script,
is_database_in_logical_level, list_database_publication, list_postgres_triggers,
list_slot_name, set_enabled, test_postgres_connection, update_postgres_trigger, Postgres,
Relations,
};
use windmill_common::{db::UserDB, error::Error, utils::StripPath};
use windmill_common::{
db::UserDB,
error::{Error, Result},
utils::StripPath,
};
mod bool;
mod converter;
mod handler;
@@ -47,13 +52,13 @@ const ERROR_REPLICATION_SLOT_NOT_EXISTS: &str = r#"The replication slot associat
const ERROR_PUBLICATION_NAME_NOT_EXISTS: &str = r#"The publication associated with this trigger no longer exists. Recreate a new publication or select an existing one in the advanced tab, or delete and recreate a new trigger"#;
pub async fn get_database_connection(
pub async fn get_pg_connection(
authed: ApiAuthed,
user_db: Option<UserDB>,
db: &DB,
postgres_resource_path: &str,
w_id: &str,
) -> std::result::Result<PgConnection, windmill_common::error::Error> {
) -> Result<PgConnection> {
let database =
try_get_resource_from_db_as::<Postgres>(authed, user_db, db, postgres_resource_path, w_id)
.await?;
@@ -61,9 +66,7 @@ pub async fn get_database_connection(
Ok(get_raw_postgres_connection(&database).await?)
}
pub async fn get_raw_postgres_connection(
db: &Postgres,
) -> std::result::Result<PgConnection, Error> {
pub async fn get_raw_postgres_connection(db: &Postgres) -> Result<PgConnection> {
let options = {
let sslmode = if !db.sslmode.is_empty() {
PgSslMode::from_str(&db.sslmode)?
@@ -99,7 +102,10 @@ pub async fn get_raw_postgres_connection(
Ok(PgConnection::connect_with(&options).await?)
}
pub fn create_logical_replication_slot_query(name: &str) -> String {
pub async fn create_logical_replication_slot(
pg_connection: &mut PgConnection,
name: &str,
) -> Result<()> {
let query = format!(
r#"
SELECT
@@ -109,14 +115,53 @@ pub fn create_logical_replication_slot_query(name: &str) -> String {
quote_literal(&name)
);
query
sqlx::query(&query).execute(pg_connection).await?;
Ok(())
}
pub fn create_publication_query(
async fn check_if_valid_publication_for_postgres_version(
pg_connection: &mut PgConnection,
table_to_track: Option<&[Relations]>,
) -> Result<bool> {
let postgres_version = get_postgres_version_internal(pg_connection).await?;
let pg_14 = postgres_version.starts_with("14");
if pg_14 {
let unsupported_publication = table_to_track
.and_then(|relations| {
relations.iter().find(|relation| {
let invalid_relation = relation.table_to_track.iter().find(|table_to_track| {
table_to_track.where_clause.is_some()
|| table_to_track.columns_name.is_some()
});
relation.table_to_track.is_empty() || invalid_relation.is_some()
})
})
.is_some();
if unsupported_publication {
return Err(Error::BadRequest(
"Your PostgreSQL database is running version 14, which does not support the following publication features: \
- WHERE clause filtering, \
- selective column tracking, and \
- tracking all tables within a schema.\n\
These features are only available in PostgreSQL 15 and above.".to_string(),
));
}
}
Ok(pg_14)
}
pub async fn create_pg_publication(
pg_connection: &mut PgConnection,
publication_name: &str,
table_to_track: Option<&[Relations]>,
transaction_to_track: &[&str],
) -> String {
transaction_to_track: &[String],
) -> Result<()> {
let pg_14 =
check_if_valid_publication_for_postgres_version(pg_connection, table_to_track).await?;
let mut query = String::from("CREATE PUBLICATION ");
query.push_str(&quote_identifier(publication_name));
@@ -124,21 +169,26 @@ pub fn create_publication_query(
match table_to_track {
Some(database_component) if !database_component.is_empty() => {
query.push_str(" FOR");
let mut first = true;
for (i, schema) in database_component.iter().enumerate() {
if schema.table_to_track.is_empty() {
query.push_str(" TABLES IN SCHEMA ");
query.push_str(&quote_identifier(&schema.schema_name));
} else {
query.push_str(" TABLE ONLY ");
if pg_14 && first {
query.push_str(" TABLE ONLY ");
first = false;
} else if !pg_14 {
query.push_str(" TABLE ONLY ");
}
for (j, table) in schema.table_to_track.iter().enumerate() {
let table_name = quote_identifier(&table.table_name);
let schema_name = quote_identifier(&schema.schema_name);
let full_name = format!("{}.{}", &schema_name, &table_name);
query.push_str(&full_name);
if !table.columns_name.is_empty() {
if let Some(columns) = table.columns_name.as_ref() {
query.push_str(" (");
let columns = table
.columns_name
let columns = columns
.iter()
.map(|column| quote_identifier(column))
.join(", ");
@@ -174,22 +224,22 @@ pub fn create_publication_query(
query.push_str("');");
}
query
sqlx::query(&query).execute(pg_connection).await?;
Ok(())
}
pub fn drop_publication_query(publication_name: &str) -> String {
pub async fn drop_publication(
pg_connection: &mut PgConnection,
publication_name: &str,
) -> Result<()> {
let mut query = String::from("DROP PUBLICATION IF EXISTS ");
let quoted_publication_name = quote_identifier(publication_name);
query.push_str(&quoted_publication_name);
query.push_str(";");
query
}
pub fn drop_logical_replication_slot_query(replication_slot_name: &str) -> String {
format!(
"SELECT pg_drop_replication_slot({});",
quote_literal(&replication_slot_name)
)
sqlx::query(&query).execute(pg_connection).await?;
Ok(())
}
pub fn generate_random_string() -> String {
@@ -228,6 +278,10 @@ fn slot_service() -> Router {
.route("/delete/*path", delete(drop_slot_name))
}
fn postgres_service() -> Router {
Router::new().route("/version/*path", get(get_postgres_version))
}
pub fn workspaced_service() -> Router {
Router::new()
.route("/test", post(test_postgres_connection))
@@ -246,6 +300,7 @@ pub fn workspaced_service() -> Router {
)
.nest("/publication", publication_service())
.nest("/slot", slot_service())
.nest("/postgres", postgres_service())
}
async fn run_job(

View File

@@ -34,8 +34,8 @@ use windmill_common::{
};
use super::{
drop_logical_replication_slot_query, drop_publication_query, get_database_connection,
handler::{Postgres, PostgresTrigger},
drop_publication, get_pg_connection,
handler::{drop_logical_replication_slot, Postgres, PostgresTrigger},
replication_message::PrimaryKeepAliveBody,
ERROR_PUBLICATION_NAME_NOT_EXISTS, ERROR_REPLICATION_SLOT_NOT_EXISTS,
};
@@ -585,7 +585,7 @@ impl PostgresConfig {
let user_db = UserDB::new(db.clone());
let mut connection = get_database_connection(
let mut pg_connection = get_pg_connection(
authed.clone(),
Some(user_db.clone()),
&db,
@@ -594,13 +594,12 @@ impl PostgresConfig {
)
.await?;
let query = drop_logical_replication_slot_query(replication_slot_name);
if capture.trigger_config.basic_mode.unwrap_or(false) {
drop_logical_replication_slot(&mut pg_connection, replication_slot_name)
.await?;
let _ = sqlx::query(&query).execute(&mut connection).await;
let query = drop_publication_query(publication_name);
let _ = sqlx::query(&query).execute(&mut connection).await;
drop_publication(&mut pg_connection, publication_name).await?;
}
Ok(())
}

View File

@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use sql_builder::{bind::Bind, SqlBuilder};
use sqlx::FromRow;
use std::str;
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::{
apps::ListAppQuery,

View File

@@ -26,7 +26,7 @@ use serde_json::{value::RawValue, Value};
use sql_builder::{bind::Bind, quote, SqlBuilder};
use sqlx::{FromRow, Postgres, Transaction};
use uuid::Uuid;
use windmill_audit::audit_ee::{audit_log, AuditAuthor};
use windmill_audit::audit_oss::{audit_log, AuditAuthor};
use windmill_audit::ActionKind;
use windmill_common::{
db::UserDB,

View File

@@ -7,18 +7,27 @@
*/
#![allow(non_snake_case)]
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::saml_ee::*;
#[cfg(not(feature = "private"))]
use axum::{routing::post, Router};
#[cfg(not(feature = "private"))]
pub struct ServiceProviderExt();
#[cfg(not(feature = "private"))]
pub async fn build_sp_extension() -> anyhow::Result<ServiceProviderExt> {
return Ok(ServiceProviderExt());
}
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new().route("/acs", post(acs))
}
#[cfg(not(feature = "private"))]
pub async fn acs() -> String {
// Implementation is not open source as it is a Windmill Enterprise Edition feature
"SAML available only in enterprise version".to_string()

View File

@@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize};
use sql_builder::{prelude::Bind, SqlBuilder};
use sqlx::{Postgres, Transaction};
use std::str::FromStr;
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::{
db::UserDB,

View File

@@ -1,3 +1,7 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::scim_ee::*;
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2023
@@ -6,17 +10,22 @@
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(not(feature = "private"))]
use axum::{middleware::Next, response::Response, routing::get, Router};
#[cfg(not(feature = "private"))]
use hyper::Request;
#[cfg(not(feature = "private"))]
pub fn global_service() -> Router {
Router::new().route("/ee", get(ee))
}
#[cfg(not(feature = "private"))]
pub async fn ee() -> String {
return "Enterprise Edition".to_string();
}
#[cfg(not(feature = "private"))]
pub async fn has_scim_token<B>(_request: Request<B>, _next: Next) -> Response {
//Not implemented in open-source version
todo!()

View File

@@ -38,7 +38,7 @@ use std::{
hash::{Hash, Hasher},
sync::Arc,
};
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_worker::process_relative_imports;

View File

@@ -10,7 +10,7 @@ use std::time::Duration;
use crate::{
db::{ApiAuthed, DB},
ee::validate_license_key,
ee_oss::validate_license_key,
utils::{generate_instance_username_for_all_users, require_super_admin},
HTTP_CLIENT,
};
@@ -29,9 +29,9 @@ use crate::utils::require_devops_role;
use serde::Deserialize;
#[cfg(feature = "enterprise")]
use windmill_common::ee::{send_critical_alert, CriticalAlertKind, CriticalErrorChannel};
use windmill_common::ee_oss::{send_critical_alert, CriticalAlertKind, CriticalErrorChannel};
use windmill_common::{
email_ee::send_email,
email_oss::send_email,
error::{self, JsonResult, Result},
global_settings::{
AUTOMATE_USERNAME_CREATION_SETTING, CRITICAL_ALERT_MUTE_UI_SETTING, EMAIL_DOMAIN_SETTING,
@@ -326,10 +326,10 @@ async fn list_global_settings() -> JsonResult<String> {
pub async fn send_stats(Extension(db): Extension<DB>, authed: ApiAuthed) -> Result<String> {
require_super_admin(&db, &authed.email).await?;
windmill_common::stats_ee::send_stats(
windmill_common::stats_oss::send_stats(
&HTTP_CLIENT,
&db,
windmill_common::stats_ee::SendStatsReason::Manual,
windmill_common::stats_oss::SendStatsReason::Manual,
)
.await?;
@@ -390,11 +390,11 @@ pub async fn renew_license_key(
authed: ApiAuthed,
) -> Result<String> {
require_super_admin(&db, &authed.email).await?;
let result = windmill_common::ee::renew_license_key(
let result = windmill_common::ee_oss::renew_license_key(
&HTTP_CLIENT,
&db,
license_key,
windmill_common::ee::RenewReason::Manual,
windmill_common::ee_oss::RenewReason::Manual,
)
.await;
@@ -424,7 +424,7 @@ pub async fn create_customer_portal_session(
Query(LicenseQuery { license_key }): Query<LicenseQuery>,
) -> Result<String> {
let url =
windmill_common::ee::create_customer_portal_session(&HTTP_CLIENT, license_key).await?;
windmill_common::ee_oss::create_customer_portal_session(&HTTP_CLIENT, license_key).await?;
return Ok(url);
}

View File

@@ -1,7 +1,15 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::smtp_server_ee::*;
#[cfg(not(feature = "private"))]
use crate::{auth::AuthCache, db::DB};
#[cfg(not(feature = "private"))]
use std::{net::SocketAddr, sync::Arc};
#[cfg(not(feature = "private"))]
use windmill_common::db::UserDB;
#[cfg(not(feature = "private"))]
pub struct SmtpServer {
pub auth_cache: Arc<AuthCache>,
pub db: DB,
@@ -9,6 +17,7 @@ pub struct SmtpServer {
pub base_internal_url: String,
}
#[cfg(not(feature = "private"))]
impl SmtpServer {
pub async fn start_listener_thread(self: Arc<Self>, _addr: SocketAddr) -> anyhow::Result<()> {
let _ = self.auth_cache;

View File

@@ -1,18 +1,28 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::sqs_triggers_ee::*;
#[cfg(not(feature = "private"))]
use crate::db::DB;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "private"))]
use windmill_common::auth::aws::AwsAuthResourceType;
#[cfg(not(feature = "private"))]
pub fn workspaced_service() -> Router {
Router::new()
}
#[cfg(not(feature = "private"))]
pub fn start_sqs(_db: DB, mut _killpill_rx: tokio::sync::broadcast::Receiver<()>) -> () {
// implementation is not open source
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg(not(feature = "private"))]
pub struct SqsTrigger {
pub queue_url: String,
pub aws_auth_resource_type: AwsAuthResourceType,
@@ -30,4 +40,4 @@ pub struct SqsTrigger {
pub server_id: Option<String>,
pub last_server_ping: Option<chrono::DateTime<chrono::Utc>>,
pub enabled: bool,
}
}

View File

@@ -1,5 +0,0 @@
use axum::Router;
pub fn add_stripe_routes(router: Router) -> Router {
return router;
}

View File

@@ -0,0 +1,11 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::stripe_ee::*;
#[cfg(not(feature = "private"))]
use axum::Router;
#[cfg(not(feature = "private"))]
pub fn add_stripe_routes(router: Router) -> Router {
return router;
}

View File

@@ -1,7 +1,14 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::teams_approvals_ee::*;
#[cfg(not(feature = "private"))]
use hyper::StatusCode;
#[cfg(not(feature = "private"))]
use windmill_common::error::Error;
#[cfg(not(feature = "private"))]
pub async fn request_teams_approval() -> Result<StatusCode, Error> {
Err(Error::InternalErr("enterprise feature only".to_string()))
}
}

View File

@@ -1,39 +1,50 @@
use http::status::StatusCode;
#[cfg(feature = "enterprise")]
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::teams_ee::*;
#[cfg(all(feature = "enterprise", not(feature = "private")))]
use axum::Router;
#[cfg(not(feature = "private"))]
use http::status::StatusCode;
#[cfg(not(feature = "private"))]
use windmill_common::error::Error;
#[cfg(not(feature = "private"))]
pub async fn edit_teams_command() -> Result<StatusCode, Error> {
return Err(Error::BadRequest(
"Teams only available on enterprise".to_string(),
));
}
#[cfg(not(feature = "private"))]
pub async fn workspaces_list_available_teams_ids() -> Result<StatusCode, Error> {
return Err(Error::BadRequest(
"Teams only available on enterprise".to_string(),
));
}
#[cfg(not(feature = "private"))]
pub async fn connect_teams() -> Result<StatusCode, Error> {
return Err(Error::BadRequest(
"Teams only available on enterprise".to_string(),
));
}
#[cfg(not(feature = "private"))]
pub async fn run_teams_message_test_job() -> Result<StatusCode, Error> {
return Err(Error::BadRequest(
"Teams only available on enterprise".to_string(),
));
}
#[cfg(not(feature = "private"))]
pub async fn workspaces_list_available_teams_channels() -> Result<StatusCode, Error> {
return Err(Error::BadRequest(
"Teams only available on enterprise".to_string(),
));
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub fn teams_service() -> Router {
Router::new()
}
}

View File

@@ -21,6 +21,9 @@ pub struct TriggersCount {
kafka_count: i64,
nats_count: i64,
postgres_count: i64,
mqtt_count: i64,
sqs_count: i64,
gcp_count: i64,
}
pub(crate) async fn get_triggers_count_internal(
db: &DB,
@@ -97,6 +100,36 @@ pub(crate) async fn get_triggers_count_internal(
.await?
.unwrap_or(0);
let mqtt_count = sqlx::query_scalar!(
"SELECT COUNT(*) FROM mqtt_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
path,
is_flow,
w_id
)
.fetch_one(db)
.await?
.unwrap_or(0);
let sqs_count = sqlx::query_scalar!(
"SELECT COUNT(*) FROM sqs_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
path,
is_flow,
w_id
)
.fetch_one(db)
.await?
.unwrap_or(0);
let gcp_count = sqlx::query_scalar!(
"SELECT COUNT(*) FROM gcp_trigger WHERE script_path = $1 AND is_flow = $2 AND workspace_id = $3",
path,
is_flow,
w_id
)
.fetch_one(db)
.await?
.unwrap_or(0);
let webhook_count = (if is_flow {
sqlx::query_scalar!(
"SELECT COUNT(*) FROM token WHERE label LIKE 'webhook-%' AND workspace_id = $1 AND scopes @> ARRAY['run:flow/' || $2]::text[]",
@@ -141,6 +174,9 @@ pub(crate) async fn get_triggers_count_internal(
kafka_count,
nats_count,
postgres_count,
mqtt_count,
gcp_count,
sqs_count,
}))
}

View File

@@ -42,7 +42,7 @@ use sqlx::FromRow;
use time::OffsetDateTime;
use tower_cookies::{Cookie, Cookies};
use tracing::Instrument;
use windmill_audit::audit_ee::{audit_log, AuditAuthor};
use windmill_audit::audit_oss::{audit_log, AuditAuthor};
use windmill_audit::ActionKind;
use windmill_common::auth::fetch_authed_from_permissioned_as;
use windmill_common::global_settings::AUTOMATE_USERNAME_CREATION_SETTING;
@@ -1508,7 +1508,7 @@ async fn create_user(
Extension(argon2): Extension<Arc<Argon2<'_>>>,
Json(nu): Json<NewUser>,
) -> Result<(StatusCode, String)> {
crate::users_ee::create_user(authed, db, webhook, argon2, nu).await
crate::users_oss::create_user(authed, db, webhook, argon2, nu).await
}
async fn delete_workspace_user(
@@ -1582,7 +1582,7 @@ async fn set_password(
Json(ep): Json<EditPassword>,
) -> Result<String> {
let email = authed.email.clone();
crate::users_ee::set_password(db, argon2, authed, &email, ep).await
crate::users_oss::set_password(db, argon2, authed, &email, ep).await
}
async fn set_password_of_user(
@@ -1593,7 +1593,7 @@ async fn set_password_of_user(
Json(ep): Json<EditPassword>,
) -> Result<String> {
require_super_admin(&db, &authed.email).await?;
crate::users_ee::set_password(db, argon2, authed, &email, ep).await
crate::users_oss::set_password(db, argon2, authed, &email, ep).await
}
async fn set_login_type(

View File

@@ -1,15 +1,27 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::users_ee::*;
#[cfg(not(feature = "private"))]
use std::sync::Arc;
#[cfg(not(feature = "private"))]
use crate::db::ApiAuthed;
#[cfg(not(feature = "private"))]
use crate::users::{EditPassword, NewUser};
#[cfg(not(feature = "private"))]
use crate::{db::DB, webhook_util::WebhookShared};
#[cfg(not(feature = "private"))]
use argon2::Argon2;
#[cfg(not(feature = "private"))]
use http::StatusCode;
#[cfg(not(feature = "private"))]
use windmill_common::error::{Error, Result};
#[cfg(not(feature = "private"))]
pub async fn create_user(
_authed: ApiAuthed,
_db: DB,
@@ -22,6 +34,7 @@ pub async fn create_user(
))
}
#[cfg(not(feature = "private"))]
pub async fn set_password(
_db: DB,
_argon2: Arc<Argon2<'_>>,
@@ -34,6 +47,7 @@ pub async fn set_password(
))
}
#[cfg(not(feature = "private"))]
pub fn send_email_if_possible(_subject: &str, _content: &str, _to: &str) {
tracing::warn!(
"send_email_if_possible is not implemented in Windmill's Open Source repository"

View File

@@ -20,7 +20,7 @@ use axum::{
use hyper::StatusCode;
use serde_json::Value;
use windmill_audit::audit_ee::{audit_log, AuditAuthorable};
use windmill_audit::audit_oss::{audit_log, AuditAuthorable};
use windmill_audit::ActionKind;
use windmill_common::{
db::UserDB,
@@ -186,7 +186,7 @@ async fn get_variable(
#[cfg(feature = "oauth2")]
{
Some(
crate::oauth2_ee::_refresh_token(
crate::oauth2_oss::_refresh_token(
tx,
&variable.path,
&w_id,
@@ -653,7 +653,7 @@ pub async fn get_value_internal<'c>(
if variable.is_expired.unwrap_or(false) && variable.account.is_some() {
#[cfg(feature = "oauth2")]
{
crate::oauth2_ee::_refresh_token(
crate::oauth2_oss::_refresh_token(
tx,
&variable.path,
&w_id,

View File

@@ -20,7 +20,7 @@ use std::{collections::HashMap, fmt};
use tokio::net::TcpStream;
use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream};
use uuid::Uuid;
use windmill_audit::{audit_ee::audit_log, ActionKind};
use windmill_audit::{audit_oss::audit_log, ActionKind};
use windmill_common::{
db::UserDB,
error::{self, to_anyhow, JsonResult},

View File

@@ -10,7 +10,7 @@ use std::collections::HashMap;
use crate::ai::{AIConfig, AI_REQUEST_CACHE};
use crate::db::ApiAuthed;
use crate::users_ee::send_email_if_possible;
use crate::users_oss::send_email_if_possible;
use crate::utils::get_instance_username_or_create_pending;
use crate::BASE_URL;
use crate::{
@@ -30,7 +30,7 @@ use chrono::Utc;
use regex::Regex;
use uuid::Uuid;
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::db::UserDB;
use windmill_common::s3_helpers::LargeFileStorage;
@@ -58,7 +58,7 @@ use sqlx::{FromRow, Postgres, Transaction};
use windmill_common::oauth2::InstanceEvent;
use windmill_common::utils::not_found_if_none;
use crate::teams_ee::{
use crate::teams_oss::{
connect_teams, edit_teams_command, run_teams_message_test_job,
workspaces_list_available_teams_channels, workspaces_list_available_teams_ids,
};
@@ -145,7 +145,7 @@ pub fn workspaced_service() -> Router {
#[cfg(all(feature = "stripe", feature = "enterprise"))]
{
crate::stripe_ee::add_stripe_routes(router)
crate::stripe_oss::add_stripe_routes(router)
}
#[cfg(not(feature = "stripe"))]
@@ -640,7 +640,7 @@ async fn edit_auto_invite(
Path(w_id): Path<String>,
Json(ea): Json<EditAutoInvite>,
) -> Result<String> {
crate::workspaces_ee::edit_auto_invite(authed, db, w_id, ea).await
crate::workspaces_oss::edit_auto_invite(authed, db, w_id, ea).await
}
async fn edit_webhook(

View File

@@ -622,7 +622,7 @@ pub(crate) async fn tarball_workspace(
#[cfg(all(feature = "enterprise", feature = "kafka"))]
{
let kafka_triggers = sqlx::query_as!(
crate::kafka_triggers_ee::KafkaTrigger,
crate::kafka_triggers_oss::KafkaTrigger,
"SELECT * FROM kafka_trigger
WHERE workspace_id = $1",
&w_id
@@ -644,7 +644,7 @@ pub(crate) async fn tarball_workspace(
#[cfg(all(feature = "enterprise", feature = "sqs_trigger"))]
{
let sqs_triggers = sqlx::query_as!(
crate::sqs_triggers_ee::SqsTrigger,
crate::sqs_triggers_oss::SqsTrigger,
r#"
SELECT
aws_auth_resource_type AS "aws_auth_resource_type: _",
@@ -684,7 +684,7 @@ pub(crate) async fn tarball_workspace(
#[cfg(all(feature = "enterprise", feature = "gcp_trigger"))]
{
let gcp_triggers = sqlx::query_as!(
crate::gcp_triggers_ee::GcpTrigger,
crate::gcp_triggers_oss::GcpTrigger,
r#"
SELECT
gcp_resource_path,
@@ -726,7 +726,7 @@ pub(crate) async fn tarball_workspace(
#[cfg(all(feature = "enterprise", feature = "nats"))]
{
let nats_triggers = sqlx::query_as!(
crate::nats_triggers_ee::NatsTrigger,
crate::nats_triggers_oss::NatsTrigger,
"SELECT * FROM nats_trigger
WHERE workspace_id = $1",
&w_id

View File

@@ -8,7 +8,7 @@ use axum::{
Json,
};
use windmill_audit::audit_ee::audit_log;
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
use windmill_common::worker::CLOUD_HOSTED;

View File

@@ -1,8 +1,14 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::workspaces_ee::*;
#[cfg(not(feature = "private"))]
use crate::{
db::{ApiAuthed, DB},
workspaces::EditAutoInvite,
};
#[cfg(not(feature = "private"))]
pub async fn edit_auto_invite(
_authed: ApiAuthed,
_db: DB,

View File

@@ -10,6 +10,7 @@ path = "./src/lib.rs"
[features]
enterprise = ["windmill-common/enterprise"]
private = []
[dependencies]
serde.workspace = true

View File

@@ -1,3 +1,7 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::audit_ee::*;
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2022
@@ -5,23 +9,26 @@
* Please see the included NOTICE for copyright information and
* LICENSE-AGPL for a copy of the license.
*/
use std::collections::HashMap;
use windmill_common::{
error::{Error, Result},
utils::Pagination,
#[cfg(not(feature = "private"))]
use {
crate::{ActionKind, AuditLog, ListAuditLogQuery},
sqlx::{Postgres, Transaction},
std::collections::HashMap,
windmill_common::{
error::{Error, Result},
utils::Pagination,
},
};
use crate::{ActionKind, AuditLog, ListAuditLogQuery};
use sqlx::{Postgres, Transaction};
#[derive(Clone)]
#[cfg(not(feature = "private"))]
pub struct AuditAuthor {
pub username: String,
pub email: String,
pub username_override: Option<String>,
}
#[cfg(not(feature = "private"))]
impl AuditAuthorable for AuditAuthor {
fn email(&self) -> &str {
&self.email
@@ -36,12 +43,14 @@ impl AuditAuthorable for AuditAuthor {
}
}
#[cfg(not(feature = "private"))]
pub trait AuditAuthorable {
fn username(&self) -> &str;
fn email(&self) -> &str;
fn username_override(&self) -> Option<&str>;
}
#[cfg(not(feature = "private"))]
#[tracing::instrument(level = "trace", skip_all)]
pub async fn audit_log<'c, E: sqlx::Executor<'c, Database = Postgres>>(
_db: E,
@@ -56,6 +65,7 @@ pub async fn audit_log<'c, E: sqlx::Executor<'c, Database = Postgres>>(
Ok(())
}
#[cfg(not(feature = "private"))]
pub async fn list_audit(
_tx: Transaction<'_, Postgres>,
_w_id: String,
@@ -66,6 +76,7 @@ pub async fn list_audit(
return Ok(vec![]);
}
#[cfg(not(feature = "private"))]
pub async fn get_audit(tx: Transaction<'_, Postgres>, _id: i32, _w_id: &str) -> Result<AuditLog> {
// Implementation is not open source as Audit logs is a Windmill Enterprise Edition feature
tx.commit().await?;

View File

@@ -1,7 +1,9 @@
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
#[cfg(feature = "private")]
pub mod audit_ee;
pub mod audit_oss;
#[derive(sqlx::Type, Serialize, Deserialize, Debug)]
#[sqlx(type_name = "ACTION_KIND", rename_all = "lowercase")]

View File

@@ -10,6 +10,7 @@ path = "./src/lib.rs"
[features]
enterprise = ["windmill-queue/enterprise", "windmill-common/enterprise"]
private = []
default = []
[dependencies]

View File

@@ -1,6 +0,0 @@
use windmill_common::DB;
pub async fn apply_all_autoscaling(_db: &DB) -> anyhow::Result<()> {
// Autoscaling is an ee feature
Ok(())
}

View File

@@ -0,0 +1,12 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::autoscaling_ee::*;
#[cfg(not(feature = "private"))]
use windmill_common::DB;
#[cfg(not(feature = "private"))]
pub async fn apply_all_autoscaling(_db: &DB) -> anyhow::Result<()> {
// Autoscaling is an ee feature
Ok(())
}

View File

@@ -1,2 +1,4 @@
mod autoscaling_ee;
pub use autoscaling_ee::*;
#[cfg(feature = "private")]
pub mod autoscaling_ee;
mod autoscaling_oss;
pub use autoscaling_oss::*;

View File

@@ -7,6 +7,7 @@ edition.workspace = true
[features]
default = []
enterprise = []
private = []
jemalloc = ["dep:tikv-jemalloc-ctl"]
tantivy = []
prometheus = ["dep:prometheus"]

View File

@@ -1,24 +1,35 @@
#[cfg(feature = "enterprise")]
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::ee::*;
#[cfg(all(feature = "enterprise", not(feature = "private")))]
use crate::db::DB;
use crate::ee::LicensePlan::Community;
#[cfg(feature = "enterprise")]
#[cfg(not(feature = "private"))]
use crate::ee_oss::LicensePlan::Community;
#[cfg(all(feature = "enterprise", not(feature = "private")))]
use crate::error;
#[cfg(not(feature = "private"))]
use serde::Deserialize;
#[cfg(not(feature = "private"))]
use std::sync::Arc;
#[cfg(not(feature = "private"))]
use tokio::sync::RwLock;
#[cfg(not(feature = "private"))]
lazy_static::lazy_static! {
pub static ref LICENSE_KEY_VALID: Arc<RwLock<bool>> = Arc::new(RwLock::new(true));
pub static ref LICENSE_KEY_ID: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
pub static ref LICENSE_KEY: Arc<RwLock<String>> = Arc::new(RwLock::new("".to_string()));
}
#[cfg(not(feature = "private"))]
pub enum LicensePlan {
Community,
Pro,
Enterprise,
}
#[cfg(not(feature = "private"))]
pub async fn get_license_plan() -> LicensePlan {
// Implementation is not open source
return Community;
@@ -26,6 +37,7 @@ pub async fn get_license_plan() -> LicensePlan {
#[derive(Deserialize)]
#[serde(untagged)]
#[cfg(not(feature = "private"))]
pub enum CriticalErrorChannel {
Email { email: String },
Slack { slack_channel: String },
@@ -33,6 +45,7 @@ pub enum CriticalErrorChannel {
}
#[derive(Deserialize)]
#[cfg(not(feature = "private"))]
pub struct TeamsChannel {
pub team_id: String,
pub team_name: String,
@@ -40,6 +53,7 @@ pub struct TeamsChannel {
pub channel_name: String,
}
#[cfg(not(feature = "private"))]
pub enum CriticalAlertKind {
#[cfg(feature = "enterprise")]
CriticalError,
@@ -47,7 +61,7 @@ pub enum CriticalAlertKind {
RecoveredCriticalError,
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn send_critical_alert(
_error_message: String,
_db: &DB,
@@ -56,7 +70,7 @@ pub async fn send_critical_alert(
) {
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn maybe_renew_license_key_on_start(
_http_client: &reqwest::Client,
_db: &crate::db::DB,
@@ -66,14 +80,14 @@ pub async fn maybe_renew_license_key_on_start(
force_renew_now
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub enum RenewReason {
Manual,
Schedule,
OnStart,
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn renew_license_key(
_http_client: &reqwest::Client,
_db: &crate::db::DB,
@@ -84,7 +98,7 @@ pub async fn renew_license_key(
"".to_string()
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn create_customer_portal_session(
_http_client: &reqwest::Client,
_key: Option<String>,
@@ -93,14 +107,18 @@ pub async fn create_customer_portal_session(
Ok("".to_string())
}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn worker_groups_alerts(_db: &DB) {}
#[cfg(feature = "enterprise")]
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn jobs_waiting_alerts(_db: &DB) {}
#[cfg(feature = "enterprise")]
pub async fn low_disk_alerts(_db: &DB, _server_mode: bool, _worker_mode: bool, _workers: Vec<String>) {
#[cfg(all(feature = "enterprise", not(feature = "private")))]
pub async fn low_disk_alerts(
_db: &DB,
_server_mode: bool,
_worker_mode: bool,
_workers: Vec<String>,
) {
// Implementation is not open source
}

View File

@@ -1,5 +1,11 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::email_ee::*;
#[cfg(not(feature = "private"))]
use crate::server::Smtp;
#[cfg(not(feature = "private"))]
pub async fn send_email(
_subject: &str,
_content: &str,

View File

@@ -1,5 +1,11 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::job_s3_helpers_ee::*;
#[cfg(not(feature = "private"))]
use crate::s3_helpers::{ObjectStoreResource, StorageResourceType};
#[cfg(not(feature = "private"))]
pub async fn get_s3_resource_internal<'c>(
_resource_type: StorageResourceType,
_s3_resource_value_raw: serde_json::Value,
@@ -9,11 +15,13 @@ pub async fn get_s3_resource_internal<'c>(
todo!()
}
#[cfg(not(feature = "private"))]
pub enum TokenGenerator<'c> {
AsClient(&'c crate::client::AuthedClient),
AsServerInstance(),
}
#[cfg(not(feature = "private"))]
impl<'c> TokenGenerator<'c> {
pub async fn gen_token(
&self,
@@ -24,7 +32,7 @@ impl<'c> TokenGenerator<'c> {
}
}
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", not(feature = "private")))]
pub(crate) async fn generate_s3_aws_oidc_resource<'c>(
_clone: crate::s3_helpers::S3AwsOidcResource,
_token_generator: TokenGenerator<'c>,

View File

@@ -19,7 +19,7 @@ use std::{
use tokio::sync::broadcast;
use ee::CriticalErrorChannel;
use ee_oss::CriticalErrorChannel;
use error::Error;
use scripts::ScriptLang;
use sqlx::{Pool, Postgres};
@@ -32,8 +32,12 @@ pub mod bench;
pub mod cache;
pub mod client;
pub mod db;
#[cfg(feature = "private")]
pub mod ee;
pub mod ee_oss;
#[cfg(feature = "private")]
pub mod email_ee;
pub mod email_oss;
pub mod error;
pub mod external_ip;
pub mod flow_status;
@@ -41,25 +45,35 @@ pub mod flows;
pub mod global_settings;
pub mod indexer;
pub mod job_metrics;
#[cfg(feature = "parquet")]
#[cfg(all(feature = "parquet", feature = "private"))]
pub mod job_s3_helpers_ee;
#[cfg(feature = "parquet")]
pub mod job_s3_helpers_oss;
#[cfg(all(feature = "enterprise", feature = "openidconnect"))]
#[cfg(all(feature = "enterprise", feature = "openidconnect", feature = "private"))]
pub mod oidc_ee;
#[cfg(all(feature = "enterprise", feature = "openidconnect"))]
pub mod oidc_oss;
pub mod jobs;
pub mod jwt;
pub mod more_serde;
pub mod oauth2;
#[cfg(feature = "private")]
pub mod otel_ee;
pub mod otel_oss;
pub mod queue;
pub mod s3_helpers;
pub mod schedule;
pub mod schema;
pub mod scripts;
pub mod server;
#[cfg(feature = "private")]
pub mod stats_ee;
pub mod stats_oss;
#[cfg(feature = "private")]
pub mod teams_ee;
pub mod teams_oss;
pub mod tracing_init;
pub mod users;
pub mod utils;

View File

@@ -1,198 +0,0 @@
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2023
* This file and its contents are licensed under the AGPLv3 License.
* Please see the included NOTICE for copyright information and
* LICENSE-AGPL for a copy of the license.
*/
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
#[cfg(all(feature = "enterprise", feature = "openidconnect"))]
use {
crate::db::DB,
crate::{auth::IdToken as WindmillIdToken, error::Result},
anyhow,
openidconnect::{
core::{CoreJwsSigningAlgorithm, CoreRsaPrivateSigningKey},
IssuerUrl, JsonWebKeyId,
},
std::process::Command,
};
#[cfg(feature = "openidconnect")]
use openidconnect::AdditionalClaims;
#[cfg(feature = "openidconnect")]
impl AdditionalClaims for JobClaim {}
#[cfg(feature = "openidconnect")]
impl AdditionalClaims for WorkspaceClaim {}
#[cfg(feature = "openidconnect")]
impl AdditionalClaims for InstanceClaim {}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct WorkspaceClaim {
pub workspace: String,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct InstanceClaim {}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
pub struct JobClaim {
pub job_id: String,
pub path: Option<String>,
pub flow_path: Option<String>,
pub groups: Vec<String>,
pub username: String,
pub email: String,
pub workspace: String,
}
lazy_static::lazy_static! {
static ref PRIVATE_KEY: RwLock<Option<String>> = RwLock::new(None);
}
pub async fn generate_id_token<T: AdditionalClaims>(
db: Option<&DB>,
claim: T,
audience: &str,
identifier: String,
email: Option<String>,
) -> Result<WindmillIdToken> {
use chrono::{Duration, Utc};
use openidconnect::{
core::{CoreGenderClaim, CoreJweContentEncryptionAlgorithm},
Audience, EndUserEmail, IdToken, IdTokenClaims, StandardClaims, SubjectIdentifier,
};
let private_key = get_private_key(db).await?;
let issue_url = format!("{}/api/oidc/", crate::BASE_URL.read().await.clone());
let issue_time = Utc::now();
let expiration = issue_time + Duration::try_hours(48).unwrap();
let id_token = IdToken::<
T,
CoreGenderClaim,
CoreJweContentEncryptionAlgorithm,
CoreJwsSigningAlgorithm,
>::new(
IdTokenClaims::<T, CoreGenderClaim>::new(
// Specify the issuer URL for the OpenID Connect Provider.
IssuerUrl::new(issue_url)
.map_err(|e| anyhow::anyhow!("Failed to generate IssueUrl: {}", e))?,
// The audience is usually a single entry with the client ID of the client for whom
// the ID token is intended. This is a required claim.
vec![Audience::new(audience.to_string())],
// The ID token expiration is usually much shorter than that of the access or refresh
// tokens issued to clients.
expiration,
// The issue time is usually the current time.
issue_time,
// Set the standard claims defined by the OpenID Connect Core spec.
StandardClaims::new(
// Stable subject identifiers are recommended in place of e-mail addresses or other
// potentially unstable identifiers. This is the only required claim.
SubjectIdentifier::new(identifier),
)
// Optional: specify the user's e-mail address. This should only be provided if the
// client has been granted the 'profile' or 'email' scopes.
.set_email(email.map(|x| EndUserEmail::new(x)))
// Optional: specify whether the provider has verified the user's e-mail address.
.set_email_verified(Some(true)),
// OpenID Connect Providers may supply custom claims by providing a struct that
// implements the AdditionalClaims trait. This requires manually using the
// generic IdTokenClaims struct rather than the CoreIdTokenClaims type alias,
// however.
claim,
),
// The private key used for signing the ID token. For confidential clients (those able
// to maintain a client secret), a CoreHmacKey can also be used, in conjunction
// with one of the CoreJwsSigningAlgorithm::HmacSha* signing algorithms. When using an
// HMAC-based signing algorithm, the UTF-8 representation of the client secret should
// be used as the HMAC key.
&CoreRsaPrivateSigningKey::from_pem(
&private_key,
Some(JsonWebKeyId::new("windmill".to_string())),
)
.map_err(|e| anyhow::anyhow!("Invalid private key: {}", e))?,
// Uses the RS256 signature algorithm. This crate supports any RS*, PS*, or HS*
// signature algorithm.
CoreJwsSigningAlgorithm::RsaSsaPkcs1V15Sha256,
// When returning the ID token alongside an access token (e.g., in the Authorization Code
// flow), it is recommended to pass the access token here to set the `at_hash` claim
// automatically.
None,
// When returning the ID token alongside an authorization code (e.g., in the implicit
// flow), it is recommended to pass the authorization code here to set the `c_hash` claim
// automatically.
None,
)
.map_err(|e| anyhow::anyhow!("Failed to generate token: {}", e))?;
Ok(WindmillIdToken::new(id_token.to_string(), expiration))
}
#[cfg(all(feature = "enterprise", feature = "openidconnect"))]
pub async fn get_private_key(db: Option<&DB>) -> anyhow::Result<String> {
if let Some(key) = PRIVATE_KEY.read().await.clone() {
return Ok(key);
} else if let Some(db) = db {
let key = sqlx::query_scalar!(
"SELECT value->>'private_key' FROM global_settings WHERE name = 'rsa_keys'",
)
.fetch_optional(db)
.await?
.flatten();
let key = key.filter(|s| !s.is_empty());
if let Some(key) = key {
return Ok(key);
} else {
let keys = gen_pems(db).await?;
return Ok(keys.private_key);
}
} else {
return Err(anyhow::anyhow!("Private key not found and no db provided"));
}
}
#[cfg(all(feature = "enterprise", feature = "openidconnect"))]
#[derive(Debug, Clone, serde::Serialize)]
struct Keys {
private_key: String,
}
#[cfg(all(feature = "enterprise", feature = "openidconnect"))]
async fn gen_pems(db: &DB) -> anyhow::Result<Keys> {
use anyhow::anyhow;
let private_key_cmd = Command::new("openssl")
.arg("genrsa")
.arg("--traditional")
.arg("2048")
.output()
.expect("failed to execute process");
let private_key = String::from_utf8(private_key_cmd.stdout)?;
tracing::debug!("Generated private key: {}", private_key);
if private_key.is_empty() {
return Err(anyhow!("Failed to generate RSA key: key is empty"));
}
let keys = Keys { private_key };
sqlx::query!(
r#"INSERT INTO global_settings (name, value) VALUES ('rsa_keys', $1)"#,
serde_json::to_value(&keys).unwrap()
)
.execute(db)
.await?;
Ok(keys)
}

View File

@@ -0,0 +1,92 @@
#[cfg(feature = "private")]
#[allow(unused)]
pub use crate::oidc_ee::*;
/*
* Author: Ruben Fiszel
* Copyright: Windmill Labs, Inc 2023
* This file and its contents are licensed under the AGPLv3 License.
* Please see the included NOTICE for copyright information and
* LICENSE-AGPL for a copy of the license.
*/
#[cfg(not(feature = "private"))]
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "private"))]
use tokio::sync::RwLock;
#[cfg(all(
feature = "enterprise",
feature = "openidconnect",
not(feature = "private")
))]
use {
crate::db::DB,
crate::{
auth::IdToken as WindmillIdToken,
error::{Error, Result},
},
anyhow,
};
#[cfg(all(feature = "openidconnect", not(feature = "private")))]
use openidconnect::AdditionalClaims;
#[cfg(all(feature = "openidconnect", not(feature = "private")))]
impl AdditionalClaims for JobClaim {}
#[cfg(all(feature = "openidconnect", not(feature = "private")))]
impl AdditionalClaims for WorkspaceClaim {}
#[cfg(all(feature = "openidconnect", not(feature = "private")))]
impl AdditionalClaims for InstanceClaim {}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[cfg(not(feature = "private"))]
pub struct WorkspaceClaim {
pub workspace: String,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[cfg(not(feature = "private"))]
pub struct InstanceClaim {}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[cfg(not(feature = "private"))]
pub struct JobClaim {
pub job_id: String,
pub path: Option<String>,
pub flow_path: Option<String>,
pub groups: Vec<String>,
pub username: String,
pub email: String,
pub workspace: String,
}
#[cfg(not(feature = "private"))]
lazy_static::lazy_static! {
static ref PRIVATE_KEY: RwLock<Option<String>> = RwLock::new(None);
}
#[cfg(not(feature = "private"))]
pub async fn generate_id_token<T: AdditionalClaims>(
_db: Option<&DB>,
_claim: T,
_audience: &str,
_identifier: String,
_email: Option<String>,
) -> Result<WindmillIdToken> {
Err(Error::internal_err(
"Not implemented in Windmill's Open Source repository".to_string(),
))
}
#[cfg(all(
feature = "enterprise",
feature = "openidconnect",
not(feature = "private")
))]
pub async fn get_private_key(_db: Option<&DB>) -> anyhow::Result<String> {
Err(anyhow::anyhow!(
"Not implemented in Windmill's Open Source repository"
))
}

Some files were not shown because too many files have changed in this diff Show More