Compare commits
14 Commits
feat-go-re
...
inkeep-end
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
373864e586 | ||
|
|
25c8e6e090 | ||
|
|
9246512561 | ||
|
|
40712ecf7b | ||
|
|
138abad9e6 | ||
|
|
697f660532 | ||
|
|
8f29c9d333 | ||
|
|
106b180c80 | ||
|
|
68ebf667d5 | ||
|
|
0e316239dd | ||
|
|
64f35d050f | ||
|
|
4cbcbdb960 | ||
|
|
190d230a01 | ||
|
|
db873be43b |
2
.github/workflows/backend-check.yml
vendored
2
.github/workflows/backend-check.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/build-publish-rh-image.yml
vendored
4
.github/workflows/build-publish-rh-image.yml
vendored
@@ -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 }}
|
||||
|
||||
2
.github/workflows/build_windows_worker_.yml
vendored
2
.github/workflows/build_windows_worker_.yml
vendored
@@ -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"
|
||||
|
||||
4
.github/workflows/docker-image.yml
vendored
4
.github/workflows/docker-image.yml
vendored
@@ -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 }}
|
||||
|
||||
2
.github/workflows/publish_windows_worker.yml
vendored
2
.github/workflows/publish_windows_worker.yml
vendored
@@ -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"
|
||||
|
||||
13
CHANGELOG.md
13
CHANGELOG.md
@@ -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
3
backend/.gitignore
vendored
@@ -6,4 +6,5 @@ tracing.folded
|
||||
heaptrack*
|
||||
index/
|
||||
windmill-api/openapi-*.*
|
||||
.duckdb/*
|
||||
.duckdb/*
|
||||
*ee.rs
|
||||
1
backend/.ignore
Normal file
1
backend/.ignore
Normal file
@@ -0,0 +1 @@
|
||||
!*ee.rs
|
||||
24
backend/.sqlx/query-13444bbd5547e101c41206c5f97ac4dded0536faf52c370d704ed9a451041caf.json
generated
Normal file
24
backend/.sqlx/query-13444bbd5547e101c41206c5f97ac4dded0536faf52c370d704ed9a451041caf.json
generated
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
24
backend/.sqlx/query-6a19c440a7a8064f3969cf6f48adea0bfdb683de9555e374ce5731e0b3c379f9.json
generated
Normal file
24
backend/.sqlx/query-6a19c440a7a8064f3969cf6f48adea0bfdb683de9555e374ce5731e0b3c379f9.json
generated
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
24
backend/.sqlx/query-a8b470b463ca4b7c00c7ef6e9f36c23f8bbcefc288a56d61122bfd6fe5ca7e8d.json
generated
Normal file
24
backend/.sqlx/query-a8b470b463ca4b7c00c7ef6e9f36c23f8bbcefc288a56d61122bfd6fe5ca7e8d.json
generated
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
3
backend/.vscode/settings.json
vendored
3
backend/.vscode/settings.json
vendored
@@ -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
136
backend/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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
20
backend/all_features_oss.sh
Executable 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
|
||||
@@ -1 +1 @@
|
||||
8a2506e86b923c00522cb83b052586f705f7aa8e
|
||||
70895a4a8f8891032c5b478a37ab6fafd0d4a9d0
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {}
|
||||
@@ -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! {
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
use axum::Router;
|
||||
|
||||
pub fn global_unauthed_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
11
backend/windmill-api/src/apps_oss.rs
Normal file
11
backend/windmill-api/src/apps_oss.rs
Normal 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()
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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, _, _) =
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))])
|
||||
@@ -1,9 +0,0 @@
|
||||
use axum::routing::Router;
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
|
||||
pub fn global_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
16
backend/windmill-api/src/git_sync_oss.rs
Normal file
16
backend/windmill-api/src/git_sync_oss.rs
Normal 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()
|
||||
}
|
||||
@@ -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::{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
use axum::Router;
|
||||
|
||||
pub fn workspaced_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
|
||||
pub fn global_service() -> Router {
|
||||
Router::new()
|
||||
}
|
||||
16
backend/windmill-api/src/indexer_oss.rs
Normal file
16
backend/windmill-api/src/indexer_oss.rs
Normal 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()
|
||||
}
|
||||
11
backend/windmill-api/src/inkeep_oss.rs
Normal file
11
backend/windmill-api/src/inkeep_oss.rs
Normal 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()
|
||||
}
|
||||
@@ -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,
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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("ed_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("ed_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();
|
||||
|
||||
|
||||
@@ -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("e_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("e_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("ed_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(
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
@@ -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,
|
||||
|
||||
@@ -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!()
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
use axum::Router;
|
||||
|
||||
pub fn add_stripe_routes(router: Router) -> Router {
|
||||
return router;
|
||||
}
|
||||
11
backend/windmill-api/src/stripe_oss.rs
Normal file
11
backend/windmill-api/src/stripe_oss.rs
Normal 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;
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
@@ -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,
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
@@ -10,6 +10,7 @@ path = "./src/lib.rs"
|
||||
|
||||
[features]
|
||||
enterprise = ["windmill-common/enterprise"]
|
||||
private = []
|
||||
|
||||
[dependencies]
|
||||
serde.workspace = true
|
||||
|
||||
@@ -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?;
|
||||
@@ -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")]
|
||||
|
||||
@@ -10,6 +10,7 @@ path = "./src/lib.rs"
|
||||
|
||||
[features]
|
||||
enterprise = ["windmill-queue/enterprise", "windmill-common/enterprise"]
|
||||
private = []
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
use windmill_common::DB;
|
||||
|
||||
pub async fn apply_all_autoscaling(_db: &DB) -> anyhow::Result<()> {
|
||||
// Autoscaling is an ee feature
|
||||
Ok(())
|
||||
}
|
||||
12
backend/windmill-autoscaling/src/autoscaling_oss.rs
Normal file
12
backend/windmill-autoscaling/src/autoscaling_oss.rs
Normal 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(())
|
||||
}
|
||||
@@ -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::*;
|
||||
|
||||
@@ -7,6 +7,7 @@ edition.workspace = true
|
||||
[features]
|
||||
default = []
|
||||
enterprise = []
|
||||
private = []
|
||||
jemalloc = ["dep:tikv-jemalloc-ctl"]
|
||||
tantivy = []
|
||||
prometheus = ["dep:prometheus"]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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>,
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
92
backend/windmill-common/src/oidc_oss.rs
Normal file
92
backend/windmill-common/src/oidc_oss.rs
Normal 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
Reference in New Issue
Block a user