diff --git a/.github/change-versions-mac.sh b/.github/change-versions-mac.sh index 2257d348ae..50ec3b17ed 100755 --- a/.github/change-versions-mac.sh +++ b/.github/change-versions-mac.sh @@ -16,7 +16,7 @@ sed -i '' -e "/\"version\": /s/: .*,/: \"$VERSION\",/" ${root_dirpath}/frontend/ sed -i '' -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml sed -i '' -e "/^windmill-api =/s/= .*/= \"\\^$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml sed -i '' -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill_pg/pyproject.toml -sed -i '' -e "/^ModuleVersion =/s/= .*/= '$VERSION'/" ${root_dirpath}/powershell-client/WindmillClient/WindmillClient.psd1 +sed -i '' -e "/^[[:space:]]*ModuleVersion[[:space:]]*=/s/= .*/= '$VERSION'/" ${root_dirpath}/powershell-client/WindmillClient/WindmillClient.psd1 # sed -i '' -e "/^wmill =/s/= .*/= \"\\^$VERSION\"/" python-client/wmill_pg/pyproject.toml sed -i '' -e "/^wmill =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile sed -i '' -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile diff --git a/.github/change-versions.sh b/.github/change-versions.sh index 77ecbc6985..699b87b0d7 100755 --- a/.github/change-versions.sh +++ b/.github/change-versions.sh @@ -17,7 +17,7 @@ sed -i -e "/\"version\": /s/: .*,/: \"$VERSION\",/" ${root_dirpath}/frontend/pac sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml sed -i -e "/^windmill-api =/s/= .*/= \"\\^$VERSION\"/" ${root_dirpath}/python-client/wmill/pyproject.toml sed -i -e "/^version =/s/= .*/= \"$VERSION\"/" ${root_dirpath}/python-client/wmill_pg/pyproject.toml -sed -i -e "/^ModuleVersion =/s/= .*/= '$VERSION'/" ${root_dirpath}/powershell-client/WindmillClient/WindmillClient.psd1 +sed -i -e "/^[[:space:]]*ModuleVersion[[:space:]]*=/s/= .*/= '$VERSION'/" ${root_dirpath}/powershell-client/WindmillClient/WindmillClient.psd1 # sed -i -e "/^wmill =/s/= .*/= \"\\^$VERSION\"/" ${root_dirpath}/python-client/wmill_pg/pyproject.toml sed -i -e "/^wmill =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile sed -i -e "/^wmill_pg =/s/= .*/= \">=$VERSION\"/" ${root_dirpath}/lsp/Pipfile diff --git a/CHANGELOG.md b/CHANGELOG.md index 656fb77d8a..94c2ef5cf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## [1.435.2](https://github.com/windmill-labs/windmill/compare/v1.435.1...v1.435.2) (2024-12-05) + + +### Bug Fixes + +* job search toast on error ([#4851](https://github.com/windmill-labs/windmill/issues/4851)) ([a99e63f](https://github.com/windmill-labs/windmill/commit/a99e63f5435725c42e38b46933ff3caa345002b5)) + +## [1.435.1](https://github.com/windmill-labs/windmill/compare/v1.435.0...v1.435.1) (2024-12-05) + + +### Bug Fixes + +* improve critical alerts filters ([548cfcf](https://github.com/windmill-labs/windmill/commit/548cfcfbde23ac7ae129e10f6e92d57516e61f20)) + +## [1.435.0](https://github.com/windmill-labs/windmill/compare/v1.434.2...v1.435.0) (2024-12-05) + + +### Features + +* app custom paths ([#4828](https://github.com/windmill-labs/windmill/issues/4828)) ([1ec6c6f](https://github.com/windmill-labs/windmill/commit/1ec6c6f765904361e641d89495890bc87e8544aa)) + + +### Bug Fixes + +* pass USERPROFILE on windows ([5404ec9](https://github.com/windmill-labs/windmill/commit/5404ec9b48e8d7a0cb27b2319d54f04c94ec6fd0)) + +## [1.434.2](https://github.com/windmill-labs/windmill/compare/v1.434.1...v1.434.2) (2024-12-04) + + +### Bug Fixes + +* custom http routes auth ([#4835](https://github.com/windmill-labs/windmill/issues/4835)) ([02611e4](https://github.com/windmill-labs/windmill/commit/02611e42e1b5c81d9bc57e3b46795e4c8cad67f7)) + ## [1.434.1](https://github.com/windmill-labs/windmill/compare/v1.434.0...v1.434.1) (2024-11-29) diff --git a/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json b/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json index 043d236a6b..c8b5e3086f 100644 --- a/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json +++ b/backend/.sqlx/query-02424907504848e983bfa89eec343061932dc5b4b17cf13d5cf8d833aedbe6d5.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "bool", + "name": "?column?", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-062859f1d0e5cfba3115f4241115753b86a4ad239708851c998ff5620ebca5b8.json b/backend/.sqlx/query-062859f1d0e5cfba3115f4241115753b86a4ad239708851c998ff5620ebca5b8.json deleted file mode 100644 index 9803b76cea..0000000000 --- a/backend/.sqlx/query-062859f1d0e5cfba3115f4241115753b86a4ad239708851c998ff5620ebca5b8.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE queue SET last_ping = now() WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "062859f1d0e5cfba3115f4241115753b86a4ad239708851c998ff5620ebca5b8" -} diff --git a/backend/.sqlx/query-0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe.json b/backend/.sqlx/query-0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe.json new file mode 100644 index 0000000000..5bb7767b0e --- /dev/null +++ b/backend/.sqlx/query-0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts\n WHERE COALESCE(acknowledged, false) = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "0a46f1f3047d15227f82ae24ad2113eb91d65b98927eaaba427cbde27dd79bfe" +} diff --git a/backend/.sqlx/query-113b120ae10ea4469ec3575dc3506aaa6d6a8940017a1172403ca9851d0f13a7.json b/backend/.sqlx/query-113b120ae10ea4469ec3575dc3506aaa6d6a8940017a1172403ca9851d0f13a7.json deleted file mode 100644 index 64ec588ffa..0000000000 --- a/backend/.sqlx/query-113b120ae10ea4469ec3575dc3506aaa6d6a8940017a1172403ca9851d0f13a7.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT flow as \"flow!: sqlx::types::Json>\" FROM flow_node WHERE id = $1 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "flow!: sqlx::types::Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - true - ] - }, - "hash": "113b120ae10ea4469ec3575dc3506aaa6d6a8940017a1172403ca9851d0f13a7" -} diff --git a/backend/.sqlx/query-15a5db0202c61ed187d9594311cb32cbde4240e143df1c980843dcdfeef3499e.json b/backend/.sqlx/query-15a5db0202c61ed187d9594311cb32cbde4240e143df1c980843dcdfeef3499e.json deleted file mode 100644 index d06eadbccf..0000000000 --- a/backend/.sqlx/query-15a5db0202c61ed187d9594311cb32cbde4240e143df1c980843dcdfeef3499e.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT lock, code AS \"code!: String\" FROM flow_node WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "lock", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "code!: String", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "15a5db0202c61ed187d9594311cb32cbde4240e143df1c980843dcdfeef3499e" -} diff --git a/backend/.sqlx/query-1ec97e1bf7c6edfa82b7e64585171ca897dcfa9e82618ce8f11afb08a39e3b20.json b/backend/.sqlx/query-1ec97e1bf7c6edfa82b7e64585171ca897dcfa9e82618ce8f11afb08a39e3b20.json new file mode 100644 index 0000000000..51343088a6 --- /dev/null +++ b/backend/.sqlx/query-1ec97e1bf7c6edfa82b7e64585171ca897dcfa9e82618ce8f11afb08a39e3b20.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2))", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "1ec97e1bf7c6edfa82b7e64585171ca897dcfa9e82618ce8f11afb08a39e3b20" +} diff --git a/backend/.sqlx/query-372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4.json b/backend/.sqlx/query-372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4.json new file mode 100644 index 0000000000..68fe6dba85 --- /dev/null +++ b/backend/.sqlx/query-372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts\n WHERE workspace_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "372eb162ace15a4f07162bb46706eaca91a3616c9bfbd78a408b7079ca9706d4" +} diff --git a/backend/.sqlx/query-3d77e5b691dab38b3e39477ed980560bacd55c6ae9fafcbb0239163a3d7f3c0c.json b/backend/.sqlx/query-3d77e5b691dab38b3e39477ed980560bacd55c6ae9fafcbb0239163a3d7f3c0c.json deleted file mode 100644 index edaec50386..0000000000 --- a/backend/.sqlx/query-3d77e5b691dab38b3e39477ed980560bacd55c6ae9fafcbb0239163a3d7f3c0c.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT flow AS \"flow!: Json>\" FROM flow_node WHERE id = $1 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "flow!: Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - true - ] - }, - "hash": "3d77e5b691dab38b3e39477ed980560bacd55c6ae9fafcbb0239163a3d7f3c0c" -} diff --git a/backend/.sqlx/query-5ac7c9eb3ce21e46f90e23435562b5f818a4274ba223f905e68280b86b24fa25.json b/backend/.sqlx/query-5ac7c9eb3ce21e46f90e23435562b5f818a4274ba223f905e68280b86b24fa25.json deleted file mode 100644 index 11c522e70d..0000000000 --- a/backend/.sqlx/query-5ac7c9eb3ce21e46f90e23435562b5f818a4274ba223f905e68280b86b24fa25.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH existing AS (\n SELECT id FROM flow_node\n WHERE hash = $1 AND path = $2 AND workspace_id = $3 AND code = $4 AND lock = $5 AND (flow IS NOT DISTINCT FROM $6)\n LIMIT 1\n ),\n inserted AS (\n INSERT INTO flow_node (hash, path, workspace_id, code, lock, flow)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT DO NOTHING\n RETURNING id\n )\n SELECT id FROM existing\n UNION ALL\n SELECT id FROM inserted\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8", - "Text", - "Text", - "Text", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "5ac7c9eb3ce21e46f90e23435562b5f818a4274ba223f905e68280b86b24fa25" -} diff --git a/backend/.sqlx/query-65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a.json b/backend/.sqlx/query-65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a.json deleted file mode 100644 index e183d3b95c..0000000000 --- a/backend/.sqlx/query-65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE alerts\n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true\n ELSE acknowledged_workspace\n END\n WHERE id = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int4", - "Text" - ] - }, - "nullable": [] - }, - "hash": "65da41c7ded54cdee8d33211561c068b72294cc99ff44ed0a13179df508ebc6a" -} diff --git a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json index 1fa370e682..75b8108281 100644 --- a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json +++ b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "bool", + "name": "?column?", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-75e880f9d9fbda36c2314706923cef36e4667d930fb8ee1876dd9ce1c92396b2.json b/backend/.sqlx/query-6b53f7c4bb73177316d6134698f3979f51b53dcd4d8ec50d312c9e7fe31ad5f5.json similarity index 63% rename from backend/.sqlx/query-75e880f9d9fbda36c2314706923cef36e4667d930fb8ee1876dd9ce1c92396b2.json rename to backend/.sqlx/query-6b53f7c4bb73177316d6134698f3979f51b53dcd4d8ec50d312c9e7fe31ad5f5.json index efaac63d39..0c82c9dd74 100644 --- a/backend/.sqlx/query-75e880f9d9fbda36c2314706923cef36e4667d930fb8ee1876dd9ce1c92396b2.json +++ b/backend/.sqlx/query-6b53f7c4bb73177316d6134698f3979f51b53dcd4d8ec50d312c9e7fe31ad5f5.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO app\n (workspace_id, path, summary, policy, versions, draft_only)\n VALUES ($1, $2, $3, $4, '{}', $5) RETURNING id", + "query": "INSERT INTO app\n (workspace_id, path, summary, policy, versions, draft_only, custom_path)\n VALUES ($1, $2, $3, $4, '{}', $5, $6) RETURNING id", "describe": { "columns": [ { @@ -15,12 +15,13 @@ "Varchar", "Varchar", "Jsonb", - "Bool" + "Bool", + "Text" ] }, "nullable": [ false ] }, - "hash": "75e880f9d9fbda36c2314706923cef36e4667d930fb8ee1876dd9ce1c92396b2" + "hash": "6b53f7c4bb73177316d6134698f3979f51b53dcd4d8ec50d312c9e7fe31ad5f5" } diff --git a/backend/.sqlx/query-7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3.json b/backend/.sqlx/query-7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3.json deleted file mode 100644 index cf976de33f..0000000000 --- a/backend/.sqlx/query-7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE alerts \n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $1::text IS NOT NULL THEN true\n ELSE acknowledged_workspace\n END\n WHERE ($1::text IS NOT NULL AND workspace_id = $1)\n OR ($1::text IS NULL)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "7c32176755c6ea2b6ae531860d436caae3fa256fc0803749ec5107632669adb3" -} diff --git a/backend/.sqlx/query-80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36.json b/backend/.sqlx/query-80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36.json new file mode 100644 index 0000000000..5be45ac0fb --- /dev/null +++ b/backend/.sqlx/query-80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts \n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $2 THEN\n CASE\n WHEN $1::text IS NOT NULL THEN true\n ELSE acknowledged_workspace\n END\n ELSE true\n END\n WHERE ($1::text IS NOT NULL AND workspace_id = $1)\n OR ($1::text IS NULL)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "80864158f61adaad8df934acc54ba523c9f17d106298d8781885134d28553d36" +} diff --git a/backend/.sqlx/query-83cc9e432aea1450f79e9fce04eca0e75f30faa8c39ad4754bd568ae6f210806.json b/backend/.sqlx/query-83cc9e432aea1450f79e9fce04eca0e75f30faa8c39ad4754bd568ae6f210806.json new file mode 100644 index 0000000000..4e1242428a --- /dev/null +++ b/backend/.sqlx/query-83cc9e432aea1450f79e9fce04eca0e75f30faa8c39ad4754bd568ae6f210806.json @@ -0,0 +1,27 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO flow_node (path, workspace_id, hash_v2, lock, code, flow)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT (hash_v2) DO UPDATE SET path = EXCLUDED.path -- trivial update to return the id\n RETURNING id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Bpchar", + "Text", + "Text", + "Jsonb" + ] + }, + "nullable": [ + false + ] + }, + "hash": "83cc9e432aea1450f79e9fce04eca0e75f30faa8c39ad4754bd568ae6f210806" +} diff --git a/backend/.sqlx/query-8653373c2c8301c902e21e8bf5a11480ff040bb987a48b6f66522bc9b7e2c1d8.json b/backend/.sqlx/query-8653373c2c8301c902e21e8bf5a11480ff040bb987a48b6f66522bc9b7e2c1d8.json deleted file mode 100644 index f57226bfa9..0000000000 --- a/backend/.sqlx/query-8653373c2c8301c902e21e8bf5a11480ff040bb987a48b6f66522bc9b7e2c1d8.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT lock, code AS \"code!: String\" FROM flow_node WHERE id = $1 LIMIT 1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "lock", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "code!: String", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "8653373c2c8301c902e21e8bf5a11480ff040bb987a48b6f66522bc9b7e2c1d8" -} diff --git a/backend/.sqlx/query-951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f.json b/backend/.sqlx/query-951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f.json new file mode 100644 index 0000000000..7c51668501 --- /dev/null +++ b/backend/.sqlx/query-951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "951c053cf756f00e1da26b06edb3d0193a0ee707e6482a892db58915e0c8a27f" +} diff --git a/backend/.sqlx/query-aa98ab0e4b9a0eb41a804ab047a120444af65b1dc43a234821cde5bf6bf8b74f.json b/backend/.sqlx/query-aa98ab0e4b9a0eb41a804ab047a120444af65b1dc43a234821cde5bf6bf8b74f.json deleted file mode 100644 index d70e81e786..0000000000 --- a/backend/.sqlx/query-aa98ab0e4b9a0eb41a804ab047a120444af65b1dc43a234821cde5bf6bf8b74f.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH existing AS (\n SELECT id FROM flow_node\n WHERE hash = $1 AND path = $2 AND workspace_id = $3 AND code = $4 AND lock = $5 AND flow = $6\n LIMIT 1\n ),\n inserted AS (\n INSERT INTO flow_node (hash, path, workspace_id, code, lock, flow)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT DO NOTHING\n RETURNING id\n )\n SELECT id FROM existing\n UNION ALL\n SELECT id FROM inserted\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8", - "Text", - "Text", - "Text", - "Text", - "Jsonb" - ] - }, - "nullable": [ - null - ] - }, - "hash": "aa98ab0e4b9a0eb41a804ab047a120444af65b1dc43a234821cde5bf6bf8b74f" -} diff --git a/backend/.sqlx/query-b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413.json b/backend/.sqlx/query-b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413.json new file mode 100644 index 0000000000..40f0418eef --- /dev/null +++ b/backend/.sqlx/query-b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*)\n FROM alerts\n WHERE workspace_id = $1 AND COALESCE(acknowledged_workspace, false) = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text", + "Bool" + ] + }, + "nullable": [ + null + ] + }, + "hash": "b9ed42d4b795942251baafd016b4361e75257c0e5faf8795274ec86236a27413" +} diff --git a/backend/.sqlx/query-cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8.json b/backend/.sqlx/query-cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8.json new file mode 100644 index 0000000000..1d1b577f7a --- /dev/null +++ b/backend/.sqlx/query-cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE alerts\n SET\n acknowledged = true,\n acknowledged_workspace = CASE\n WHEN $3 THEN\n CASE\n WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true\n ELSE acknowledged_workspace\n END\n ELSE true\n END\n WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Text", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "cd4067e68b375461a495f402d05976da6c6e331d5748bf6f8d59f9f75c027fe8" +} diff --git a/backend/.sqlx/query-d6615719bf8db4b333ed55c9a3c160ad1962668fc3305d8e80ae91ef73614a80.json b/backend/.sqlx/query-d6615719bf8db4b333ed55c9a3c160ad1962668fc3305d8e80ae91ef73614a80.json index 103219fe05..271c395f9a 100644 --- a/backend/.sqlx/query-d6615719bf8db4b333ed55c9a3c160ad1962668fc3305d8e80ae91ef73614a80.json +++ b/backend/.sqlx/query-d6615719bf8db4b333ed55c9a3c160ad1962668fc3305d8e80ae91ef73614a80.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "bool", + "name": "?column?", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-fb1399c1dc171ec6bb24fee3477a1d606d9725e20e9c2d76a0887fadfd87f8df.json b/backend/.sqlx/query-fb1399c1dc171ec6bb24fee3477a1d606d9725e20e9c2d76a0887fadfd87f8df.json new file mode 100644 index 0000000000..a2362be620 --- /dev/null +++ b/backend/.sqlx/query-fb1399c1dc171ec6bb24fee3477a1d606d9725e20e9c2d76a0887fadfd87f8df.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2) AND NOT (path = $3 AND workspace_id = $4))", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "exists", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "fb1399c1dc171ec6bb24fee3477a1d606d9725e20e9c2d76a0887fadfd87f8df" +} diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 2e176de462..dac85d4f09 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -166,9 +166,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" +checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" [[package]] name = "arc-swap" @@ -359,7 +359,7 @@ dependencies = [ "arrow-schema", "chrono", "half", - "indexmap 2.6.0", + "indexmap 2.7.0", "lexical-core", "num", "serde", @@ -441,7 +441,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -499,7 +499,7 @@ checksum = "021cf450e9574793e45e1044a5d3d94bba7dbaa0802e6122e9c10eb8c4dd12dc" dependencies = [ "base64 0.22.1", "bytes", - "http 1.1.0", + "http 1.2.0", "rand 0.8.5", "reqwest 0.12.9", "serde", @@ -518,7 +518,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -540,7 +540,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -576,7 +576,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -641,7 +641,7 @@ dependencies = [ "aws-sdk-sts", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.60.7", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -672,9 +672,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10d5c055aa540164d9561a0e2e74ad30f0dcf7393c3a92f6733ddf9c5762468" +checksum = "b5ac934720fbb46206292d2c75b57e67acfc56fe7dfd34fb9a02334af08409ea" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -697,15 +697,15 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.49.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09677244a9da92172c8dc60109b4a9658597d4d298b188dd0018b6a66b410ca4" +checksum = "05ca43a4ef210894f93096039ef1d6fa4ad3edfabb3be92b80908b9f2e4b4eab" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -719,15 +719,15 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.50.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fea2f3a8bb3bd10932ae7ad59cc59f65f270fc9183a7e91f501dc5efbef7ee" +checksum = "abaf490c2e48eed0bb8e2da2fb08405647bd7f253996e0f93b981958ea0f73b0" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -741,15 +741,15 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.50.0" +version = "1.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ada54e5f26ac246dc79727def52f7f8ed38915cb47781e2a72213957dc3a7d5" +checksum = "b68fde0d69c8bfdc1060ea7da21df3e39f6014da316783336deff0a9ec28f4bf" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", - "aws-smithy-json", + "aws-smithy-json 0.61.1", "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", @@ -764,9 +764,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5619742a0d8f253be760bfbb8e8e8368c69e3587e4637af5754e488a611499b1" +checksum = "7d3820e0c08d0737872ff3c7c1f21ebbb6693d832312d6152bf18ef50a5471c2" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -777,7 +777,7 @@ dependencies = [ "hex", "hmac", "http 0.2.12", - "http 1.1.0", + "http 1.2.0", "once_cell", "percent-encoding", "sha2 0.10.8", @@ -825,6 +825,15 @@ dependencies = [ "aws-smithy-types", ] +[[package]] +name = "aws-smithy-json" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e69cc50921eb913c6b662f8d909131bb3e6ad6cb6090d3a39b66fc5c52095" +dependencies = [ + "aws-smithy-types", +] + [[package]] name = "aws-smithy-query" version = "0.60.7" @@ -837,9 +846,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.3" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be28bd063fa91fd871d131fc8b68d7cd4c5fa0869bea68daca50dcb1cbd76be2" +checksum = "9f20685047ca9d6f17b994a07f629c813f08b5bce65523e47124879e60103d45" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -872,7 +881,7 @@ dependencies = [ "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.1.0", + "http 1.2.0", "pin-project-lite", "tokio", "tracing", @@ -890,7 +899,7 @@ dependencies = [ "bytes-utils", "futures-core", "http 0.2.12", - "http 1.1.0", + "http 1.2.0", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -938,7 +947,7 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "hyper 1.5.1", @@ -972,7 +981,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1115,7 +1124,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.89", + "syn 2.0.90", "which 4.4.2", ] @@ -1134,7 +1143,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1265,7 +1274,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1367,7 +1376,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1598,9 +1607,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.21" +version = "4.5.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +checksum = "69371e34337c4c984bbe322360c2547210bf632eb2814bbe78a6e87a2935bd2b" dependencies = [ "clap_builder", "clap_derive", @@ -1608,9 +1617,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.21" +version = "4.5.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +checksum = "6e24c1b4099818523236a8ca881d2b45db98dadfb4625cf6608c12069fcbbde1" dependencies = [ "anstream", "anstyle", @@ -1627,7 +1636,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2012,7 +2021,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2060,7 +2069,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2082,7 +2091,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2144,7 +2153,7 @@ dependencies = [ "glob", "half", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.0", "itertools 0.12.1", "log", "num_cpus", @@ -2313,7 +2322,7 @@ dependencies = [ "datafusion-expr", "datafusion-physical-expr", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.0", "itertools 0.12.1", "log", "regex-syntax 0.8.5", @@ -2342,7 +2351,7 @@ dependencies = [ "half", "hashbrown 0.14.5", "hex", - "indexmap 2.6.0", + "indexmap 2.7.0", "itertools 0.12.1", "log", "paste", @@ -2386,7 +2395,7 @@ dependencies = [ "futures", "half", "hashbrown 0.14.5", - "indexmap 2.6.0", + "indexmap 2.7.0", "itertools 0.12.1", "log", "once_cell", @@ -2531,7 +2540,7 @@ dependencies = [ "deno_tls", "dyn-clone", "error_reporter", - "http 1.1.0", + "http 1.2.0", "http-body-util", "hyper 1.5.1", "hyper-rustls 0.27.3", @@ -2603,7 +2612,7 @@ dependencies = [ "quote", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.89", + "syn 2.0.90", "thiserror 1.0.69", ] @@ -2676,10 +2685,11 @@ dependencies = [ [[package]] name = "deno_unsync" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f36b4ef61a04ce201b925a5dffa90f88437d37fee4836c758470dd15ba7f05e" +checksum = "d774fd83f26b24f0805a6ab8b26834a0d06ceac0db517b769b1e4633c96a2057" dependencies = [ + "futures", "parking_lot", "tokio", ] @@ -2795,7 +2805,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2900,7 +2910,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2923,7 +2933,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3079,7 +3089,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3099,7 +3109,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3304,7 +3314,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3336,7 +3346,7 @@ checksum = "e99b8b3c28ae0e84b604c75f721c21dc77afb3706076af5e8216d15fd1deaae3" dependencies = [ "frunk_proc_macro_helpers", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3348,7 +3358,7 @@ dependencies = [ "frunk_core", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3360,7 +3370,7 @@ dependencies = [ "frunk_core", "frunk_proc_macro_helpers", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3471,7 +3481,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3724,7 +3734,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3802,7 +3812,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.6.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -3820,8 +3830,8 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.6.0", + "http 1.2.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -4002,9 +4012,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -4029,7 +4039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -4040,7 +4050,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] @@ -4118,7 +4128,7 @@ dependencies = [ "futures-channel", "futures-util", "h2 0.4.7", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "httparse", "httpdate", @@ -4152,7 +4162,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http 1.1.0", + "http 1.2.0", "hyper 1.5.1", "hyper-util", "rustls 0.23.19", @@ -4201,7 +4211,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "hyper 1.5.1", "pin-project-lite", @@ -4350,7 +4360,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4409,9 +4419,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -4482,7 +4492,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4571,7 +4581,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee7893dab2e44ae5f9d0173f26ff4aa327c10b01b06a72b52dd9405b628640d" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", ] [[package]] @@ -5126,7 +5136,7 @@ checksum = "a7ce64b975ed4f123575d11afd9491f2e37bbd5813fbfbc0f09ae1fbddea74e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -5138,7 +5148,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.1.0", + "http 1.2.0", "httparse", "memchr", "mime", @@ -5165,7 +5175,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "termcolor", "thiserror 1.0.69", ] @@ -5422,7 +5432,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -5596,7 +5606,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -5840,7 +5850,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.6.0", + "indexmap 2.7.0", ] [[package]] @@ -5883,7 +5893,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -5924,7 +5934,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -6080,7 +6090,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -6120,7 +6130,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -6131,7 +6141,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -6143,7 +6153,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -6204,7 +6214,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "syn 2.0.89", + "syn 2.0.90", "thiserror 1.0.69", "typify", "unicode-ident", @@ -6224,7 +6234,7 @@ dependencies = [ "serde_json", "serde_tokenstream", "serde_yaml", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -6267,7 +6277,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -6380,10 +6390,10 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls 0.23.19", "socket2", - "thiserror 2.0.3", + "thiserror 2.0.4", "tokio", "tracing", ] @@ -6398,11 +6408,11 @@ dependencies = [ "getrandom 0.2.15", "rand 0.8.5", "ring 0.17.8", - "rustc-hash 2.0.0", + "rustc-hash 2.1.0", "rustls 0.23.19", "rustls-pki-types", "slab", - "thiserror 2.0.3", + "thiserror 2.0.4", "tinyvec", "tracing", "web-time", @@ -6759,7 +6769,7 @@ dependencies = [ "futures-core", "futures-util", "h2 0.4.7", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "hyper 1.5.1", @@ -6944,7 +6954,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.89", + "syn 2.0.90", "walkdir", ] @@ -6999,9 +7009,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_version" @@ -7326,7 +7336,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -7486,7 +7496,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -7497,7 +7507,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -7506,7 +7516,7 @@ version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "itoa", "memchr", "ryu", @@ -7572,7 +7582,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -7610,7 +7620,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -7627,7 +7637,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -7636,7 +7646,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "itoa", "ryu", "serde", @@ -7989,7 +7999,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8028,7 +8038,7 @@ dependencies = [ "hashbrown 0.14.5", "hashlink", "hex", - "indexmap 2.6.0", + "indexmap 2.7.0", "log", "memchr", "once_cell", @@ -8060,7 +8070,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8083,7 +8093,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.89", + "syn 2.0.90", "tempfile", "tokio", "url", @@ -8235,7 +8245,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8289,7 +8299,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8302,7 +8312,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8394,7 +8404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" dependencies = [ "anyhow", - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_json", "swc_cached", @@ -8410,7 +8420,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8459,7 +8469,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8506,7 +8516,7 @@ checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" dependencies = [ "better_scoped_tls", "bitflags 2.6.0", - "indexmap 2.6.0", + "indexmap 2.7.0", "once_cell", "phf", "rustc-hash 1.1.0", @@ -8544,7 +8554,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8575,7 +8585,7 @@ checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" dependencies = [ "base64 0.21.7", "dashmap", - "indexmap 2.6.0", + "indexmap 2.7.0", "once_cell", "serde", "sha1", @@ -8615,7 +8625,7 @@ version = "0.134.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "num_cpus", "once_cell", "rustc-hash 1.1.0", @@ -8651,7 +8661,7 @@ checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8662,7 +8672,7 @@ checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8685,7 +8695,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8701,9 +8711,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.89" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -8733,7 +8743,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -8992,11 +9002,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" dependencies = [ - "thiserror-impl 2.0.3", + "thiserror-impl 2.0.4", ] [[package]] @@ -9007,18 +9017,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "thiserror-impl" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -9114,9 +9124,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -9135,9 +9145,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -9253,7 +9263,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -9367,9 +9377,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -9418,7 +9428,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -9431,7 +9441,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.6.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -9492,7 +9502,7 @@ dependencies = [ "axum-core", "cookie 0.18.1", "futures-util", - "http 1.1.0", + "http 1.2.0", "parking_lot", "pin-project-lite", "tower-layer", @@ -9509,7 +9519,7 @@ dependencies = [ "bitflags 2.6.0", "bytes", "futures-core", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", "pin-project-lite", @@ -9564,7 +9574,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -9755,7 +9765,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http 1.2.0", "httparse", "log", "native-tls", @@ -9811,7 +9821,7 @@ dependencies = [ "regress", "schemars", "serde_json", - "syn 2.0.89", + "syn 2.0.90", "thiserror 1.0.69", "unicode-ident", ] @@ -9828,7 +9838,7 @@ dependencies = [ "serde", "serde_json", "serde_tokenstream", - "syn 2.0.89", + "syn 2.0.90", "typify-impl", ] @@ -10034,9 +10044,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.10.1" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ "base64 0.22.1", "flate2", @@ -10235,7 +10245,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -10269,7 +10279,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10302,7 +10312,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -10436,7 +10446,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "axum", @@ -10477,7 +10487,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "argon2", @@ -10504,7 +10514,7 @@ dependencies = [ "hex", "hf-hub", "hmac", - "http 1.1.0", + "http 1.2.0", "hyper 1.5.1", "itertools 0.13.0", "jsonwebtoken", @@ -10563,7 +10573,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.434.1" +version = "1.435.2" dependencies = [ "base64 0.22.1", "chrono", @@ -10581,7 +10591,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.434.1" +version = "1.435.2" dependencies = [ "chrono", "serde", @@ -10594,7 +10604,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "serde", @@ -10608,7 +10618,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "async-stream", @@ -10628,12 +10638,13 @@ dependencies = [ "hex", "hmac", "hyper 1.5.1", - "indexmap 2.6.0", + "indexmap 2.7.0", "itertools 0.13.0", "lazy_static", "magic-crypt", "mail-send", "object_store", + "pin-project-lite", "prometheus", "quick_cache", "rand 0.8.5", @@ -10644,7 +10655,7 @@ dependencies = [ "serde_json", "sha2 0.10.8", "sqlx", - "thiserror 2.0.3", + "thiserror 2.0.4", "tikv-jemalloc-ctl", "tokio", "tracing", @@ -10658,7 +10669,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.434.1" +version = "1.435.2" dependencies = [ "regex", "serde", @@ -10672,7 +10683,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "bytes", @@ -10695,19 +10706,19 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.434.1" +version = "1.435.2" dependencies = [ "itertools 0.13.0", "lazy_static", "proc-macro2", "quote", "regex", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "windmill-parser" -version = "1.434.1" +version = "1.435.2" dependencies = [ "convert_case 0.6.0", "serde", @@ -10716,7 +10727,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "lazy_static", @@ -10728,7 +10739,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "gosyn", @@ -10740,7 +10751,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "lazy_static", @@ -10752,7 +10763,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "itertools 0.13.0", @@ -10763,7 +10774,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "itertools 0.13.0", @@ -10774,7 +10785,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "async-recursion", @@ -10792,7 +10803,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -10802,14 +10813,14 @@ dependencies = [ "quote", "regex", "serde_json", - "syn 2.0.89", + "syn 2.0.90", "toml 0.7.8", "windmill-parser", ] [[package]] name = "windmill-parser-sql" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "lazy_static", @@ -10821,7 +10832,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "lazy_static", @@ -10839,7 +10850,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "getrandom 0.2.15", @@ -10860,7 +10871,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "serde_json", @@ -10870,7 +10881,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "async-recursion", @@ -10904,7 +10915,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.434.1" +version = "1.435.2" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -10914,7 +10925,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.434.1" +version = "1.435.2" dependencies = [ "anyhow", "async-recursion", @@ -11292,7 +11303,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -11314,7 +11325,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -11334,7 +11345,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -11363,7 +11374,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 8802e10403..3f591dfd84 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.434.1" +version = "1.435.2" authors.workspace = true edition.workspace = true @@ -29,7 +29,7 @@ members = [ ] [workspace.package] -version = "1.434.1" +version = "1.435.2" authors = ["Ruben Fiszel "] edition = "2021" @@ -284,6 +284,7 @@ tikv-jemalloc-sys = { version = "^0.5" } tikv-jemalloc-ctl = { version = "^0.5" } triomphe = "^0" +pin-project-lite = "^0" tantivy = "0.22.0" diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 496432b7e0..c7e750d0f4 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -aefbc1e2188fea312996fcfc30a29d8fb5315316 \ No newline at end of file +8606d98a692d11b09a387c5efbd6b4335c533fd3 \ No newline at end of file diff --git a/backend/migrations/20241202134622_app_custom_path.down.sql b/backend/migrations/20241202134622_app_custom_path.down.sql new file mode 100644 index 0000000000..1dadbefff1 --- /dev/null +++ b/backend/migrations/20241202134622_app_custom_path.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE app DROP COLUMN custom_path; diff --git a/backend/migrations/20241202134622_app_custom_path.up.sql b/backend/migrations/20241202134622_app_custom_path.up.sql new file mode 100644 index 0000000000..832efd0cb2 --- /dev/null +++ b/backend/migrations/20241202134622_app_custom_path.up.sql @@ -0,0 +1,2 @@ +-- Add up migration script here +ALTER TABLE app ADD COLUMN custom_path TEXT CHECK (custom_path ~ '^[\w-]+(\/[\w-]+)*$'); diff --git a/backend/migrations/20241204154025_grant_all_concurrency_key.down.sql b/backend/migrations/20241204154025_grant_all_concurrency_key.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20241204154025_grant_all_concurrency_key.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20241204154025_grant_all_concurrency_key.up.sql b/backend/migrations/20241204154025_grant_all_concurrency_key.up.sql new file mode 100644 index 0000000000..da9b9704d7 --- /dev/null +++ b/backend/migrations/20241204154025_grant_all_concurrency_key.up.sql @@ -0,0 +1,3 @@ +-- Add up migration script here +GRANT ALL ON concurrency_key TO windmill_admin; +GRANT ALL ON concurrency_key TO windmill_user; \ No newline at end of file diff --git a/backend/migrations/20241205135747_fix_flow_node_uniqueness.down.sql b/backend/migrations/20241205135747_fix_flow_node_uniqueness.down.sql new file mode 100644 index 0000000000..7a5d08f3f8 --- /dev/null +++ b/backend/migrations/20241205135747_fix_flow_node_uniqueness.down.sql @@ -0,0 +1,2 @@ +-- Add down migration script here +ALTER TABLE flow_node DROP COLUMN hash_v2; diff --git a/backend/migrations/20241205135747_fix_flow_node_uniqueness.up.sql b/backend/migrations/20241205135747_fix_flow_node_uniqueness.up.sql new file mode 100644 index 0000000000..799f33d341 --- /dev/null +++ b/backend/migrations/20241205135747_fix_flow_node_uniqueness.up.sql @@ -0,0 +1,4 @@ +-- Add up migration script here +CREATE SEQUENCE IF NOT EXISTS flow_node_hash_seq; +ALTER TABLE flow_node ALTER COLUMN hash DROP NOT NULL; +ALTER TABLE flow_node ADD COLUMN hash_v2 CHAR(64) NOT NULL UNIQUE DEFAULT to_hex(nextval('flow_node_hash_seq')); diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 733a841283..df3b695168 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -85,12 +85,12 @@ lazy_static::lazy_static! { static ref ZOMBIE_JOB_TIMEOUT: String = std::env::var("ZOMBIE_JOB_TIMEOUT") .ok() .and_then(|x| x.parse::().ok()) - .unwrap_or_else(|| "30".to_string()); + .unwrap_or_else(|| "60".to_string()); static ref FLOW_ZOMBIE_TRANSITION_TIMEOUT: String = std::env::var("FLOW_ZOMBIE_TRANSITION_TIMEOUT") .ok() .and_then(|x| x.parse::().ok()) - .unwrap_or_else(|| "30".to_string()); + .unwrap_or_else(|| "60".to_string()); pub static ref RESTART_ZOMBIE_JOBS: bool = std::env::var("RESTART_ZOMBIE_JOBS") diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 7a7525bf04..95c06d5aae 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.434.1 + version: 1.435.2 title: Windmill API contact: @@ -872,9 +872,20 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/CriticalAlert' + type: object + properties: + alerts: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + total_rows: + type: integer + description: Total number of rows matching the query. + example: 100 + total_pages: + type: integer + description: Total number of pages based on the page size. + example: 10 /settings/critical_alerts/{id}/acknowledge: post: @@ -2757,9 +2768,20 @@ paths: content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/CriticalAlert' + type: object + properties: + alerts: + type: array + items: + $ref: '#/components/schemas/CriticalAlert' + total_rows: + type: integer + description: Total number of rows matching the query. + example: 100 + total_pages: + type: integer + description: Total number of pages based on the page size. + example: 10 /w/{workspace}/workspaces/critical_alerts/{id}/acknowledge: post: @@ -3733,6 +3755,27 @@ paths: required: - app + /apps_u/public_app_by_custom_path/{custom_path}: + get: + summary: get public app by custom path + operationId: getPublicAppByCustomPath + tags: + - app + parameters: + - $ref: "#/components/parameters/CustomPath" + responses: + "200": + description: app details + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/AppWithLastVersion" + - type: object + properties: + workspace_id: + type: string + /scripts/hub/get/{path}: get: summary: get hub script content by path @@ -5371,6 +5414,8 @@ paths: type: boolean deployment_message: type: string + custom_path: + type: string required: - path - value @@ -5696,6 +5741,8 @@ paths: $ref: "#/components/schemas/Policy" deployment_message: type: string + custom_path: + type: string responses: "200": description: app updated @@ -5704,6 +5751,23 @@ paths: schema: type: string + /w/{workspace}/apps/custom_path_exists/{custom_path}: + get: + summary: check if custom path exists + operationId: customPathExists + tags: + - app + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/CustomPath" + responses: + "200": + description: custom path exists + content: + application/json: + schema: + type: boolean + /w/{workspace}/apps_u/execute_component/{path}: post: summary: executeComponent @@ -10222,6 +10286,12 @@ components: required: true schema: type: string + CustomPath: + name: custom_path + in: path + required: true + schema: + type: string PathId: name: id in: path @@ -12860,6 +12930,8 @@ components: draft_only: type: boolean draft: {} + custom_path: + type: string AppHistory: type: object diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 5e236baeb6..e1a9421252 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -56,10 +56,10 @@ use windmill_common::{ jobs::{get_payload_tag_from_prefixed_path, JobPayload, RawCode}, users::username_to_permissioned_as, utils::{ - http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, Pagination, StripPath, + http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath }, variables::{build_crypt, build_crypt_with_key_suffix}, - worker::to_raw_value, + worker::{to_raw_value, CLOUD_HOSTED}, HUB_BASE_URL, }; @@ -81,6 +81,7 @@ pub fn workspaced_service() -> Router { .route("/history/p/*path", get(get_app_history)) .route("/get_latest_version/*path", get(get_latest_version)) .route("/history_update/a/:id/v/:version", post(update_app_history)) + .route("/custom_path_exists/*custom_path", get(custom_path_exists)) } pub fn unauthed_service() -> Router { @@ -90,13 +91,17 @@ pub fn unauthed_service() -> Router { .route("/public_app/:secret", get(get_public_app_by_secret)) .route("/public_resource/*path", get(get_public_resource)) } - pub fn global_service() -> Router { Router::new() .route("/hub/list", get(list_hub_apps)) .route("/hub/get/:id", get(get_hub_app_by_id)) } +#[cfg(not(feature = "enterprise"))] +pub fn global_unauthed_service() -> Router { + Router::new() +} + #[derive(FromRow, Deserialize, Serialize)] pub struct ListableApp { pub id: i64, @@ -147,21 +152,26 @@ pub struct AppWithLastVersionAndStarred { pub starred: Option, } +#[cfg(feature = "enterprise")] +#[derive(Serialize, FromRow)] +pub struct AppWithLastVersionAndWorkspace { + #[sqlx(flatten)] + #[serde(flatten)] + pub app: AppWithLastVersion, + pub workspace_id: String, +} + #[derive(Serialize, Deserialize, FromRow)] pub struct AppWithLastVersionAndDraft { - pub id: i64, - pub path: String, - pub summary: String, - pub policy: sqlx::types::Json>, - pub versions: Vec, - pub value: sqlx::types::Json>, - pub created_by: String, - pub created_at: chrono::DateTime, - pub extra_perms: serde_json::Value, + #[sqlx(flatten)] + #[serde(flatten)] + pub app: AppWithLastVersion, #[serde(skip_serializing_if = "Option::is_none")] pub draft: Option>>, #[serde(skip_serializing_if = "Option::is_none")] pub draft_only: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub custom_path: Option, } #[derive(Serialize)] @@ -229,6 +239,7 @@ pub struct CreateApp { pub policy: Policy, pub draft_only: Option, pub deployment_message: Option, + pub custom_path: Option, } #[derive(Deserialize)] @@ -238,6 +249,7 @@ pub struct EditApp { pub value: Option>>, pub policy: Option, pub deployment_message: Option, + pub custom_path: Option, } #[derive(Serialize, FromRow)] @@ -408,7 +420,7 @@ async fn get_app_w_draft( let app_o = sqlx::query_as::<_, AppWithLastVersionAndDraft>( r#"SELECT app.id, app.path, app.summary, app.versions, app.policy, - app.extra_perms, app_version.value, + app.extra_perms, app_version.value, app.custom_path, app_version.created_at, app_version.created_by, app.draft_only, draft.value as "draft" from app @@ -515,6 +527,22 @@ async fn update_app_history( return Ok(()); } + +async fn custom_path_exists( + Extension(db): Extension, + Path((w_id, custom_path)): Path<(String, String)>, +) -> JsonResult { + let exists = + sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2))", + custom_path, + if *CLOUD_HOSTED { Some(&w_id) } else { None } + ) + .fetch_one(&db) + .await?.unwrap_or(false); + Ok(Json(exists)) +} + async fn get_app_by_id( authed: ApiAuthed, Extension(user_db): Extension, @@ -598,6 +626,7 @@ async fn get_public_app_by_secret( Ok(Json(app)) } + async fn get_public_resource( Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, @@ -680,6 +709,26 @@ async fn create_app( ))); } + if let Some(custom_path) = &app.custom_path { + + require_admin(authed.is_admin, &authed.username)?; + + let exists = sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2))", + custom_path, + if *CLOUD_HOSTED { Some(&w_id) } else { None } + ) + .fetch_one(&mut *tx) + .await?.unwrap_or(false); + + if exists { + return Err(Error::BadRequest(format!( + "App with custom path {} already exists", + custom_path + ))); + } + } + sqlx::query!( "DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'app'", &app.path, @@ -690,13 +739,14 @@ async fn create_app( let id = sqlx::query_scalar!( "INSERT INTO app - (workspace_id, path, summary, policy, versions, draft_only) - VALUES ($1, $2, $3, $4, '{}', $5) RETURNING id", + (workspace_id, path, summary, policy, versions, draft_only, custom_path) + VALUES ($1, $2, $3, $4, '{}', $5, $6) RETURNING id", w_id, app.path, app.summary, json!(app.policy), app.draft_only, + app.custom_path, ) .fetch_one(&mut *tx) .await?; @@ -899,7 +949,11 @@ async fn update_app( let mut tx = user_db.clone().begin(&authed).await?; - let npath = if ns.policy.is_some() || ns.path.is_some() || ns.summary.is_some() { + let npath = if ns.policy.is_some() + || ns.path.is_some() + || ns.summary.is_some() + || ns.custom_path.is_some() + { let mut sqlb = SqlBuilder::update_table("app"); sqlb.and_where_eq("path", "?".bind(&path)); sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); @@ -932,6 +986,29 @@ async fn update_app( sqlb.set_str("summary", nsummary); } + if let Some(ncustom_path) = &ns.custom_path { + + require_admin(authed.is_admin, &authed.username)?; + + let exists = sqlx::query_scalar!( + "SELECT EXISTS(SELECT 1 FROM app WHERE custom_path = $1 AND ($2::TEXT IS NULL OR workspace_id = $2) AND NOT (path = $3 AND workspace_id = $4))", + ncustom_path, + if *CLOUD_HOSTED { Some(&w_id) } else { None }, + path, + w_id + ) + .fetch_one(&mut *tx) + .await?.unwrap_or(false); + + if exists { + return Err(Error::BadRequest(format!( + "App with custom path {} already exists", + ncustom_path + ))); + } + sqlb.set_str("custom_path", ncustom_path); + } + if let Some(mut npolicy) = ns.policy { npolicy.on_behalf_of = Some(username_to_permissioned_as(&authed.username)); npolicy.on_behalf_of_email = Some(authed.email.clone()); diff --git a/backend/windmill-api/src/apps_ee.rs b/backend/windmill-api/src/apps_ee.rs new file mode 100644 index 0000000000..a7737664b9 --- /dev/null +++ b/backend/windmill-api/src/apps_ee.rs @@ -0,0 +1,5 @@ +use axum::Router; + +pub fn global_unauthed_service() -> Router { + Router::new() +} diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 94f1e00c94..9d25860f21 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -64,6 +64,8 @@ mod indexer_ee; mod inputs; mod integration; +#[cfg(feature = "enterprise")] +mod apps_ee; #[cfg(feature = "parquet")] mod job_helpers_ee; pub mod job_metrics; @@ -343,6 +345,17 @@ pub async fn run_server( ) .nest("/concurrency_groups", concurrency_groups::global_service()) .nest("/scripts_u", scripts::global_unauthed_service()) + .nest("/apps_u", { + #[cfg(feature = "enterprise")] + { + apps_ee::global_unauthed_service() + } + + #[cfg(not(feature = "enterprise"))] + { + Router::new() + } + }) .nest( "/w/:workspace_id/apps_u", apps::unauthed_service() diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 2f8c54232b..ba21c688e4 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -1439,14 +1439,27 @@ async fn delete_script_by_path( require_admin(authed.is_admin, &authed.username)?; } - let script = sqlx::query_scalar!( - "DELETE FROM script WHERE path = $1 AND workspace_id = $2 RETURNING path", - path, - w_id - ) - .fetch_one(&db) - .await - .map_err(|e| Error::InternalErr(format!("deleting script by path {w_id}: {e:#}")))?; + let script = if !draft_only { + require_admin(authed.is_admin, &authed.username)?; + sqlx::query_scalar!( + "DELETE FROM script WHERE path = $1 AND workspace_id = $2 RETURNING path", + path, + w_id + ) + .fetch_one(&db) + .await + .map_err(|e| Error::InternalErr(format!("deleting script by path {w_id}: {e:#}")))? + } else { + // If the script is draft only, we can delete it without admin permissions but we still need write permissions + sqlx::query_scalar!( + "DELETE FROM script WHERE path = $1 AND workspace_id = $2 RETURNING path", + path, + w_id + ) + .fetch_one(&mut *tx) + .await + .map_err(|e| Error::InternalErr(format!("deleting script by path {w_id}: {e:#}")))? + }; sqlx::query!( "DELETE FROM draft WHERE path = $1 AND workspace_id = $2 AND typ = 'script'", diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index 61a7e46fef..c527eb481a 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -448,7 +448,7 @@ pub async fn get_critical_alerts( Extension(db): Extension, authed: ApiAuthed, Query(params): Query, -) -> JsonResult> { +) -> JsonResult { require_devops_role(&db, &authed.email).await?; crate::utils::get_critical_alerts(db, params, None).await diff --git a/backend/windmill-api/src/utils.rs b/backend/windmill-api/src/utils.rs index cc6360b994..a003481c9a 100644 --- a/backend/windmill-api/src/utils.rs +++ b/backend/windmill-api/src/utils.rs @@ -10,6 +10,7 @@ use axum::{body::Body, response::Response}; use regex::Regex; use serde::Deserialize; use sqlx::{Postgres, Transaction}; +use windmill_common::worker::CLOUD_HOSTED; use windmill_common::{ auth::{is_devops_email, is_super_admin_email}, error::{self, Error}, @@ -206,11 +207,55 @@ pub async fn get_critical_alerts( db: DB, params: AlertQueryParams, workspace_id: Option, -) -> JsonResult> { +) -> JsonResult { + // Returning total rows and total pages let page = params.page.unwrap_or(1).max(1); let page_size = params.page_size.unwrap_or(10).min(100) as i64; let offset = ((page - 1) * page_size as i32) as i64; + // Count total rows + let total_rows = if let Some(workspace_id) = &workspace_id { + if params.acknowledged.is_none() { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts + WHERE workspace_id = $1", + workspace_id + ) + .fetch_one(&db) + .await? + } else { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts + WHERE workspace_id = $1 AND COALESCE(acknowledged_workspace, false) = $2", + workspace_id, + params.acknowledged + ) + .fetch_one(&db) + .await? + } + } else { + if params.acknowledged.is_none() { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts" + ) + .fetch_one(&db) + .await? + } else { + sqlx::query_scalar!( + "SELECT COUNT(*) + FROM alerts + WHERE COALESCE(acknowledged, false) = $1", + params.acknowledged + ) + .fetch_one(&db) + .await? + } + }; + + // Fetch paginated rows let alerts = if let Some(workspace_id) = workspace_id { // `workspace_id` is provided => workspace admin if params.acknowledged.is_none() { @@ -278,7 +323,14 @@ pub async fn get_critical_alerts( } }; - Ok(Json(alerts)) + let total_rows = total_rows.unwrap_or(0); + let total_pages = ((total_rows as f64) / (page_size as f64)).ceil() as i64; + + Ok(Json(serde_json::json!({ + "alerts": alerts, + "total_rows": total_rows, + "total_pages": total_pages + }))) } #[cfg(feature = "enterprise")] @@ -292,12 +344,17 @@ pub async fn acknowledge_critical_alert( SET acknowledged = true, acknowledged_workspace = CASE - WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true - ELSE acknowledged_workspace + WHEN $3 THEN + CASE + WHEN $2::text IS NOT NULL AND workspace_id = $2 THEN true + ELSE acknowledged_workspace + END + ELSE true END WHERE id = $1", id, - workspace_id + workspace_id, + *CLOUD_HOSTED ) .execute(&db) .await?; @@ -320,12 +377,17 @@ pub async fn acknowledge_all_critical_alerts( SET acknowledged = true, acknowledged_workspace = CASE - WHEN $1::text IS NOT NULL THEN true - ELSE acknowledged_workspace + WHEN $2 THEN + CASE + WHEN $1::text IS NOT NULL THEN true + ELSE acknowledged_workspace + END + ELSE true END WHERE ($1::text IS NOT NULL AND workspace_id = $1) OR ($1::text IS NULL)", - workspace_id + workspace_id, + *CLOUD_HOSTED ) .execute(&db) .await?; diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index c6d6e5301c..bba06b238b 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -3111,7 +3111,7 @@ pub async fn get_critical_alerts( Path(w_id): Path, authed: ApiAuthed, Query(params): Query, -) -> JsonResult> { +) -> JsonResult { require_admin_or_devops(authed.is_admin, &authed.username, &authed.email, &db).await?; crate::utils::get_critical_alerts(db, params, Some(w_id)).await diff --git a/backend/windmill-common/Cargo.toml b/backend/windmill-common/Cargo.toml index 068668930d..b56b9a39cf 100644 --- a/backend/windmill-common/Cargo.toml +++ b/backend/windmill-common/Cargo.toml @@ -62,6 +62,7 @@ windmill-macros.workspace = true semver.workspace = true croner = "2.0.6" quick_cache.workspace = true +pin-project-lite.workspace = true [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemalloc-ctl = { optional = true, workspace = true } diff --git a/backend/windmill-common/src/cache.rs b/backend/windmill-common/src/cache.rs index bb738bddf1..9810e83be5 100644 --- a/backend/windmill-common/src/cache.rs +++ b/backend/windmill-common/src/cache.rs @@ -34,24 +34,36 @@ pub mod flow { /// If not present, import from the file-system cache or fetch it from the database and write /// it to the file system and cache. /// This should be preferred over fetching the database directly. - pub async fn fetch_script(e: impl PgExecutor<'_>, node: FlowNodeId) - -> error::Result<(Option, String)> - { - fetch(e, node).await.and_then(|Val { lock, code, .. }| Ok((lock, code.ok_or_else(|| { - error::Error::InternalErr(format!("Flow node ({:x}) isn't a script node.", node.0)) - })?))) + pub async fn fetch_script( + e: impl PgExecutor<'_>, + node: FlowNodeId, + ) -> error::Result<(Option, String)> { + fetch(e, node).await.and_then(|Val { lock, code, .. }| { + Ok(( + lock, + code.ok_or_else(|| { + error::Error::InternalErr(format!( + "Flow node ({:x}) isn't a script node.", + node.0 + )) + })?, + )) + }) } /// Fetch the flow node flow value referenced by `node` from the cache. /// If not present, import from the file-system cache or fetch it from the database and write /// it to the file system and cache. /// This should be preferred over fetching the database directly. - pub async fn fetch_flow(e: impl PgExecutor<'_>, node: FlowNodeId) - -> error::Result - { - fetch(e, node).await.and_then(|Val { flow, .. }| flow.ok_or_else(|| { - error::Error::InternalErr(format!("Flow node ({:x}) isn't a flow value node.", node.0)) - })) + pub async fn fetch_flow(e: impl PgExecutor<'_>, node: FlowNodeId) -> error::Result { + fetch(e, node).await.and_then(|Val { flow, .. }| { + flow.ok_or_else(|| { + error::Error::InternalErr(format!( + "Flow node ({:x}) isn't a flow value node.", + node.0 + )) + }) + }) } /// Fetch the flow node referenced by `node` from the cache. @@ -62,32 +74,40 @@ pub mod flow { // If not present, `get_or_insert_async` will lock the key until the future completes, // so only one thread will be able to fetch the data from the database and write it to // the file system and cache, hence no race on the file system. - CACHE.get_or_insert_async( - &node, - fs::import_or_insert_with(CACHE_DIR, node.0 as u64, async { - sqlx::query!( - "SELECT \ + CACHE + .get_or_insert_async( + &node, + fs::import_or_insert_with(CACHE_DIR, node.0 as u64, async { + sqlx::query!( + "SELECT \ lock AS \"lock: String\", \ code AS \"code: String\", \ flow::text AS \"flow: Box\" \ FROM flow_node WHERE id = $1 LIMIT 1", - node.0, - ) - .fetch_one(e) - .await - .map_err(Into::into) - .and_then(|r| Ok(Val { - lock: r.lock.and_then(|x| if x.is_empty() { None } else { Some(x) }), - code: r.code, - flow: match r.flow { - None => None, - Some(flow) => serde_json::from_str(&flow).map_err(|err| { - error::Error::InternalErr(format!("Unable to parse flow value: {err:?}")) - })?, - } - })) - }) - ).await + node.0, + ) + .fetch_one(e) + .await + .map_err(Into::into) + .and_then(|r| { + Ok(Val { + lock: r + .lock + .and_then(|x| if x.is_empty() { None } else { Some(x) }), + code: r.code, + flow: match r.flow { + None => None, + Some(flow) => serde_json::from_str(&flow).map_err(|err| { + error::Error::InternalErr(format!( + "Unable to parse flow value: {err:?}" + )) + })?, + }, + }) + }) + }), + ) + .await } // ---------------------------------------------------------------------------------------------- @@ -130,7 +150,11 @@ pub mod flow { match item { Item::Lock => Ok(self.lock.as_ref().map(|s| s.as_bytes().to_vec())), Item::Code => Ok(self.code.as_ref().map(|s| s.as_bytes().to_vec())), - Item::Flow => Ok(self.flow.as_ref().map(|f| serde_json::to_vec(f)).transpose()?), + Item::Flow => Ok(self + .flow + .as_ref() + .map(|f| serde_json::to_vec(f)) + .transpose()?), } } } @@ -164,39 +188,44 @@ pub mod script { /// If not present, import from the file-system cache or fetch it from the database and write /// it to the file system and cache. /// This should be preferred over fetching the database directly. - pub async fn fetch(e: impl PgExecutor<'_>, hash: ScriptHash, workspace_id: &str) - -> error::Result - { + pub async fn fetch( + e: impl PgExecutor<'_>, + hash: ScriptHash, + workspace_id: &str, + ) -> error::Result { // If not present, `get_or_insert_async` will lock the key until the future completes, // so only one thread will be able to fetch the data from the database and write it to // the file system and cache, hence no race on the file system. - CACHE.get_or_insert_async( - &hash, - fs::import_or_insert_with(CACHE_DIR, hash.0 as u64, async { - sqlx::query!( - "SELECT \ + CACHE + .get_or_insert_async( + &hash, + fs::import_or_insert_with(CACHE_DIR, hash.0 as u64, async { + sqlx::query!( + "SELECT \ lock AS \"lock: String\", \ content AS \"code!: String\", language AS \"language: Option\", \ envs AS \"envs: Vec\", \ codebase AS \"codebase: String\" \ FROM script WHERE hash = $1 AND workspace_id = $2 LIMIT 1", - hash.0, - workspace_id, - ) - .fetch_one(e) - .await - .map_err(Into::into) - .map(|r| Val { - lock: r.lock.and_then(|x| if x.is_empty() { None } else { Some(x) }), - code: r.code, - language: r.language, - envs: r.envs, - codebase: r.codebase, - }) - }) - ) - .await + hash.0, + workspace_id, + ) + .fetch_one(e) + .await + .map_err(Into::into) + .map(|r| Val { + lock: r + .lock + .and_then(|x| if x.is_empty() { None } else { Some(x) }), + code: r.code, + language: r.language, + envs: r.envs, + codebase: r.codebase, + }) + }), + ) + .await } // ---------------------------------------------------------------------------------------------- @@ -230,7 +259,9 @@ pub mod script { match item { Item::Lock => self.lock = Some(String::from_utf8(data)?), Item::Code => self.code = String::from_utf8(data)?, - Item::Info => (self.language, self.envs, self.codebase) = serde_json::from_slice(&data)?, + Item::Info => { + (self.language, self.envs, self.codebase) = serde_json::from_slice(&data)? + } } Ok(()) } @@ -239,7 +270,11 @@ pub mod script { match item { Item::Lock => Ok(self.lock.as_ref().map(|s| s.as_bytes().to_vec())), Item::Code => Ok(Some(self.code.as_bytes().to_vec())), - Item::Info => Ok(Some(serde_json::to_vec(&(&self.language, &self.envs, &self.codebase))?)), + Item::Info => Ok(Some(serde_json::to_vec(&( + &self.language, + &self.envs, + &self.codebase, + ))?)), } } } @@ -272,8 +307,7 @@ mod fs { } /// Import or insert a bundle within the given combination of `{root}/{key}/`. - pub async fn import_or_insert_with(root: &str, key: u64, f: F) - -> error::Result + pub async fn import_or_insert_with(root: &str, key: u64, f: F) -> error::Result where T: Bundle, F: Future>, @@ -287,8 +321,9 @@ mod fs { let mut data = T::default(); for item in T::items() { let mut buf = vec![]; - let Ok(mut file) = OpenOptions::new().read(true).open(item.path(&path)) - else { continue }; + let Ok(mut file) = OpenOptions::new().read(true).open(item.path(&path)) else { + continue; + }; file.read_to_end(&mut buf)?; data.import(*item, buf)?; } @@ -299,7 +334,7 @@ mod fs { Ok(data) => return Ok(data), Err(err) => tracing::warn!( "Failed to import from file-system, fetch source..: {path:?}: {err:?}" - ) + ), } } // Cache path doesn't exist or import failed, generate the content. @@ -308,9 +343,13 @@ mod fs { fs::create_dir_all(&path)?; // Write the generated data to the file. for item in T::items() { - let Some(buf) = data.export(*item)? - else { continue }; - let mut file = OpenOptions::new().write(true).create(true).open(item.path(&path))?; + let Some(buf) = data.export(*item)? else { + continue; + }; + let mut file = OpenOptions::new() + .write(true) + .create(true) + .open(item.path(&path))?; file.write_all(&buf)?; } tracing::debug!("Exported to file-system: {:?}", path); diff --git a/backend/windmill-common/src/job_metrics.rs b/backend/windmill-common/src/job_metrics.rs index c0c2300fed..6577e34d6e 100644 --- a/backend/windmill-common/src/job_metrics.rs +++ b/backend/windmill-common/src/job_metrics.rs @@ -1,4 +1,4 @@ -use crate::{db::DB, error}; +use crate::{db::DB, error, utils::WarnAfterExt}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -90,6 +90,7 @@ pub async fn register_metric_for_job( .bind(timeseries_int) .bind(timeseries_float) .execute(db) + .warn_after_seconds(1) .await?; Ok(metric_id) diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 08a0c32e3b..c13f96e2de 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -34,8 +34,8 @@ pub const GIT_VERSION: &str = git_version!(args = ["--tag", "--always"], fallback = "unknown-version"); use crate::CRITICAL_ALERT_MUTE_UI_ENABLED; +use std::panic::{self, AssertUnwindSafe, Location}; use std::sync::atomic::Ordering; -use std::panic::{self, AssertUnwindSafe}; use crate::worker::CLOUD_HOSTED; @@ -78,14 +78,18 @@ pub fn require_admin(is_admin: bool, username: &str) -> Result<()> { } } -pub async fn require_admin_or_devops(is_admin: bool, username: &str, email: &str, db: &DB) -> Result<()> { +pub async fn require_admin_or_devops( + is_admin: bool, + username: &str, + email: &str, + db: &DB, +) -> Result<()> { if !is_admin { if !is_devops_email(db, email).await? { return Err(Error::RequireAdmin(username.to_string())); } } Ok(()) - } pub fn hostname() -> String { @@ -94,7 +98,7 @@ pub fn hostname() -> String { .to_str() .map(|x| x.to_string()) .unwrap_or_else(|| rd_string(5)) - }) + }) } pub fn paginate(pagination: Pagination) -> (usize, usize) { @@ -440,9 +444,7 @@ impl ScheduleType { Some("v2") | Some(_) => { // Use Croner for v2 let schedule_type_result = panic::catch_unwind(AssertUnwindSafe(|| { - Cron::new(schedule_str) - .with_seconds_optional() - .parse() + Cron::new(schedule_str).with_seconds_optional().parse() })) .map_err(|_| { tracing::error!( @@ -478,8 +480,14 @@ impl ScheduleType { } if let Err(e) = result { - tracing::error!("An error occurred while finding the next occurrence: {:?}", e); - return Err(Error::BadRequest(format!("cron: error during find_next_occurrence: {:?}", e))); + tracing::error!( + "An error occurred while finding the next occurrence: {:?}", + e + ); + return Err(Error::BadRequest(format!( + "cron: error during find_next_occurrence: {:?}", + e + ))); } } @@ -526,3 +534,78 @@ impl ScheduleType { Ok(events) } } + +use std::future::Future; +use std::pin::Pin; +use std::task::{Context as TContext, Poll}; +use tokio::time::{self, Duration, Sleep}; + +use pin_project_lite::pin_project; + +pub trait WarnAfterExt: Future + Sized { + /// Warns if the future takes longer than the specified number of seconds to complete. + #[track_caller] + fn warn_after_seconds(self, seconds: u8) -> WarnAfterFuture { + let caller = Location::caller(); + let location = format!("{}:{}", caller.file(), caller.line()); + WarnAfterFuture { + future: self, + timeout: time::sleep(Duration::from_secs(seconds as u64)), + warned: false, + start_time: std::time::Instant::now(), + location: location, + seconds, + } + } +} + +// Blanket implementation for all futures. +impl WarnAfterExt for F {} + +pin_project! { + /// A future that wraps another future and prints a warning if it takes too long. + pub struct WarnAfterFuture { + #[pin] + future: F, + #[pin] + timeout: Sleep, + warned: bool, + location: String, + start_time: std::time::Instant, + seconds: u8, + } +} + +impl Future for WarnAfterFuture { + type Output = F::Output; + + fn poll(self: Pin<&mut Self>, cx: &mut TContext<'_>) -> Poll { + let this = self.project(); + + // Poll the timeout future to check if it has elapsed. + if !*this.warned { + if this.timeout.poll(cx).is_ready() { + tracing::warn!(location = this.location, "SLOW_QUERY: query to db taking longer than expected (> {} seconds). This is a sign the database is under heavy load, query is too heavy or database is undersized", + this.seconds, + ); + *this.warned = true; + } + } + + // Poll the wrapped future. + match this.future.poll(cx) { + Poll::Ready(output) => { + if *this.warned { + let elapsed = this.start_time.elapsed(); + tracing::warn!( + location = this.location, + "SLOW_QUERY: completed with total duration: {:.2?}", + elapsed + ); + } + Poll::Ready(output) + } + Poll::Pending => Poll::Pending, + } + } +} diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 2390ca47bb..1e516f4821 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -39,8 +39,8 @@ use windmill_audit::audit_ee::{audit_log, AuditAuthor}; use windmill_audit::ActionKind; use windmill_common::{ - cache, auth::{fetch_authed_from_permissioned_as, permissioned_as_to_username}, + cache, db::{Authed, UserDB}, error::{self, to_anyhow, Error}, flow_status::{ @@ -57,7 +57,7 @@ use windmill_common::{ schedule::Schedule, scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang}, users::{SUPERADMIN_NOTIFICATION_EMAIL, SUPERADMIN_SECRET_EMAIL}, - utils::{not_found_if_none, report_critical_error, StripPath}, + utils::{not_found_if_none, report_critical_error, StripPath, WarnAfterExt}, worker::{ to_raw_value, CLOUD_HOSTED, DEFAULT_TAGS_PER_WORKSPACE, DEFAULT_TAGS_WORKSPACES, DISABLE_FLOW_SCRIPT, MIN_VERSION_IS_AT_LEAST_1_427, MIN_VERSION_IS_AT_LEAST_1_432, NO_LOGS, @@ -318,6 +318,7 @@ pub async fn append_logs( workspace.as_ref(), ) .execute(db.borrow()) + .warn_after_seconds(1) .await { tracing::error!(%job_id, %err, "error updating logs for large_log job {job_id}: {err}"); @@ -3227,7 +3228,7 @@ pub async fn push<'c, 'd>( None, None, ) - }, + } JobPayload::ScriptHub { path } => { if path == "hub/7771/slack" || path == "hub/7836/slack" { permissioned_as = SUPERADMIN_NOTIFICATION_EMAIL.to_string(); @@ -3851,6 +3852,7 @@ pub async fn push<'c, 'd>( tag, ) .execute(&mut *tx) + .warn_after_seconds(1) .await?; let (raw_code, raw_lock, raw_flow) = if !*MIN_VERSION_IS_AT_LEAST_1_427.read().await { @@ -3901,6 +3903,7 @@ pub async fn push<'c, 'd>( final_priority, ) .fetch_one(&mut *tx) + .warn_after_seconds(1) .await .map_err(|e| Error::InternalErr(format!("Could not insert into queue {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}: {e:#}")))?; diff --git a/backend/windmill-worker/src/ansible_executor.rs b/backend/windmill-worker/src/ansible_executor.rs index fe8531d0ac..dcc288f108 100644 --- a/backend/windmill-worker/src/ansible_executor.rs +++ b/backend/windmill-worker/src/ansible_executor.rs @@ -433,6 +433,10 @@ fi .args(cmd_args) .stdout(Stdio::piped()) .stderr(Stdio::piped()); + + #[cfg(windows)] + ansible_cmd.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); + start_child_process(ansible_cmd, ANSIBLE_PLAYBOOK_PATH.as_str()).await? }; @@ -565,13 +569,9 @@ async fn create_file_resources( get_resource_or_variable_content(client, &file_res.resource_path, job_id.to_string()) .await?; let path = file_res.target_path.clone(); - let validated_path = write_file_at_user_defined_location( - job_dir, - path.as_str(), - &r, - file_res.mode, - ) - .map_err(|e| anyhow!("Couldn't write text file at {}: {}", path, e))?; + let validated_path = + write_file_at_user_defined_location(job_dir, path.as_str(), &r, file_res.mode) + .map_err(|e| anyhow!("Couldn't write text file at {}: {}", path, e))?; nsjail_mounts.push( define_nsjail_mount(job_dir, &validated_path) diff --git a/backend/windmill-worker/src/bash_executor.rs b/backend/windmill-worker/src/bash_executor.rs index 2582f98b58..680831e990 100644 --- a/backend/windmill-worker/src/bash_executor.rs +++ b/backend/windmill-worker/src/bash_executor.rs @@ -522,7 +522,8 @@ $env:PSModulePath = \"{};$PSModulePathBackup\"", std::env::var("PATHEXT").unwrap_or_else(|_| { String::from(".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL") }), - ); + ) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); } cmd.spawn()? diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index a7d92874e1..707a668b84 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -1522,6 +1522,15 @@ pub async fn get_common_bun_proc_envs(base_internal_url: Option<&str>) -> HashMa bun_envs.insert(String::from("NODE_PATH"), node_path.to_string()); } + #[cfg(windows)] + { + bun_envs.insert("SystemRoot".to_string(), crate::SYSTEM_ROOT.to_string()); + bun_envs.insert( + "USERPROFILE".to_string(), + crate::USERPROFILE_ENV.to_string(), + ); + } + return bun_envs; } diff --git a/backend/windmill-worker/src/deno_executor.rs b/backend/windmill-worker/src/deno_executor.rs index 88f6733d1a..54a2631ccd 100644 --- a/backend/windmill-worker/src/deno_executor.rs +++ b/backend/windmill-worker/src/deno_executor.rs @@ -84,6 +84,15 @@ async fn get_common_deno_proc_envs( if DENO_TLS_CA_STORE.len() > 0 { deno_envs.insert(String::from("DENO_TLS_CA_STORE"), DENO_TLS_CA_STORE.clone()); } + + #[cfg(windows)] + { + deno_envs.insert("SystemRoot".to_string(), crate::SYSTEM_ROOT.to_string()); + deno_envs.insert( + "USERPROFILE".to_string(), + crate::USERPROFILE_ENV.to_string(), + ); + } return deno_envs; } diff --git a/backend/windmill-worker/src/go_executor.rs b/backend/windmill-worker/src/go_executor.rs index 470e647383..ea06f82c5a 100644 --- a/backend/windmill-worker/src/go_executor.rs +++ b/backend/windmill-worker/src/go_executor.rs @@ -194,6 +194,10 @@ func Run(req Req) (interface{{}}, error){{ .args(vec!["build", "main.go"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); + + #[cfg(windows)] + build_go_cmd.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); + let build_go_process = start_child_process(build_go_cmd, GO_PATH.as_str()).await?; handle_child( &job.id, @@ -293,6 +297,9 @@ func Run(req Req) (interface{{}}, error){{ run_go.env("GOPROXY", goproxy); } + #[cfg(windows)] + run_go.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); + run_go.stdout(Stdio::piped()).stderr(Stdio::piped()); start_child_process(run_go, compiled_executable_name).await? }; diff --git a/backend/windmill-worker/src/job_logger_ee.rs b/backend/windmill-worker/src/job_logger_ee.rs index e0758b31f3..5414ccd496 100644 --- a/backend/windmill-worker/src/job_logger_ee.rs +++ b/backend/windmill-worker/src/job_logger_ee.rs @@ -8,7 +8,14 @@ use windmill_common::DB; use crate::job_logger::CompactLogs; #[cfg(all(feature = "enterprise", feature = "parquet"))] -pub(crate) async fn s3_storage(_job_id: Uuid, _w_id: &String, _db: &sqlx::Pool, _logs: &String, _total_size: &Arc, _worker_name: &String) { +pub(crate) async fn s3_storage( + _job_id: Uuid, + _w_id: &String, + _db: &sqlx::Pool, + _logs: &String, + _total_size: &Arc, + _worker_name: &String, +) { tracing::info!("Logs length of {job_id} has exceeded a threshold. Implementation to store excess on s3 in not OSS"); } @@ -24,7 +31,9 @@ pub(crate) async fn default_disk_log_storage( tracing::info!("Logs length of {job_id} has exceeded a threshold. Implementation to store excess on disk in not OSS"); } - -pub(crate) fn process_streaming_log_lines(r: Result, io::Error>, _stderr: bool) -> Option> { +pub(crate) fn process_streaming_log_lines( + r: Result, io::Error>, + _stderr: bool, +) -> Option> { r.transpose() -} \ No newline at end of file +} diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index 4127af7f7b..3d1ba79def 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -749,8 +749,10 @@ mount {{ .stdout(Stdio::piped()) .stderr(Stdio::piped()); - #[cfg(windows)] - python_cmd.env("SystemRoot", SYSTEM_ROOT.as_str()); + #[cfg(windows)] { + python_cmd.env("SystemRoot", SYSTEM_ROOT.as_str()); + python_cmd.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); + } start_child_process(python_cmd, PYTHON_PATH.as_str()).await? }; @@ -1277,6 +1279,7 @@ async fn spawn_uv_install( .envs(envs) .envs(PROXY_ENVS.clone()) .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) .env( "TMP", std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), diff --git a/backend/windmill-worker/src/result_processor.rs b/backend/windmill-worker/src/result_processor.rs index ea8a883407..25bfade2e8 100644 --- a/backend/windmill-worker/src/result_processor.rs +++ b/backend/windmill-worker/src/result_processor.rs @@ -14,6 +14,7 @@ use windmill_common::{ add_time, error::{self, Error}, jobs::{JobKind, QueuedJob}, + utils::WarnAfterExt, worker::{to_raw_value, WORKER_GROUP}, DB, }; @@ -431,6 +432,7 @@ pub async fn process_completed_job( #[cfg(feature = "benchmark")] bench, ) + .warn_after_seconds(10) .await?; } } @@ -469,6 +471,7 @@ pub async fn process_completed_job( #[cfg(feature = "benchmark")] bench, ) + .warn_after_seconds(10) .await?; } } diff --git a/backend/windmill-worker/src/rust_executor.rs b/backend/windmill-worker/src/rust_executor.rs index 9391fd9a69..c6f258d6ac 100644 --- a/backend/windmill-worker/src/rust_executor.rs +++ b/backend/windmill-worker/src/rust_executor.rs @@ -209,6 +209,7 @@ pub async fn build_rust_crate( "TMP", std::env::var("TMP").unwrap_or_else(|_| "C:\\tmp".to_string()), ); + build_rust_cmd.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); } let build_rust_process = start_child_process(build_rust_cmd, CARGO_PATH.as_str()).await?; @@ -402,7 +403,10 @@ pub async fn handle_rust_job( .stderr(Stdio::piped()); #[cfg(windows)] - run_rust.env("SystemRoot", SYSTEM_ROOT.as_str()); + { + run_rust.env("SystemRoot", SYSTEM_ROOT.as_str()); + run_rust.env("USERPROFILE", crate::USERPROFILE_ENV.as_str()); + } start_child_process(run_rust, compiled_executable_name).await? }; diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 0d64b30c5a..47f2fb393f 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -9,6 +9,7 @@ use windmill_common::{ auth::{fetch_authed_from_permissioned_as, JWTAuthClaims, JobPerms, JWT_SECRET}, scripts::PREVIEW_IS_TAR_CODEBASE_HASH, + utils::WarnAfterExt, worker::{ get_memory, get_vcpus, get_windmill_memory_usage, get_worker_memory_usage, write_file, ROOT_CACHE_DIR, TMP_DIR, @@ -159,6 +160,7 @@ pub async fn create_token_for_owner_in_bg( &email, &job_id, ) + .warn_after_seconds(5) .await .expect("could not create job token"); *locked = token; @@ -366,6 +368,7 @@ lazy_static::lazy_static! { pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string()); pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new()); pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()); + pub static ref NODE_PATH: Option = std::env::var("NODE_PATH").ok(); pub static ref TZ_ENV: String = std::env::var("TZ").unwrap_or_else(|_| String::new()); @@ -419,12 +422,12 @@ lazy_static::lazy_static! { .and_then(|x| x.parse().ok()) .unwrap_or(false); - } #[cfg(windows)] lazy_static::lazy_static! { pub static ref SYSTEM_ROOT: String = std::env::var("SystemRoot").unwrap_or_else(|_| "C:\\Windows".to_string()); + pub static ref USERPROFILE_ENV: String = std::env::var("USERPROFILE").unwrap_or_else(|_| "/tmp".to_string()); } //only matter if CLOUD_HOSTED @@ -1823,7 +1826,9 @@ async fn handle_queued_job( if job.parent_job.is_none() && job.created_by.starts_with("email-") { let daily_count = sqlx::query!( "SELECT value FROM metrics WHERE id = 'email_trigger_usage' AND created_at > NOW() - INTERVAL '1 day' ORDER BY created_at DESC LIMIT 1" - ).fetch_optional(db).await?.map(|x| serde_json::from_value::(x.value).unwrap_or(1)); + ).fetch_optional(db) + .warn_after_seconds(5) + .await?.map(|x| serde_json::from_value::(x.value).unwrap_or(1)); if let Some(count) = daily_count { if count >= 100 { @@ -1836,6 +1841,7 @@ async fn handle_queued_job( serde_json::json!(count + 1) ) .execute(db) + .warn_after_seconds(5) .await?; } } else { @@ -1843,6 +1849,7 @@ async fn handle_queued_job( "INSERT INTO metrics (id, value) VALUES ('email_trigger_usage', to_jsonb(1))" ) .execute(db) + .warn_after_seconds(5) .await?; } } @@ -1855,6 +1862,7 @@ async fn handle_queued_job( .ok_or_else(|| Error::InternalErr(format!("expected parent job")))?, job.id, ) + .warn_after_seconds(5) .await?; Some(r) @@ -1867,6 +1875,7 @@ async fn handle_queued_job( &job.workspace_id ) .execute(db) + .warn_after_seconds(5) .await { tracing::error!("Could not update parent job started_at flow_status: {}", e); } @@ -1883,6 +1892,7 @@ async fn handle_queued_job( job.workspace_id ) .fetch_one(db) + .warn_after_seconds(5) .await .map(|record| (record.raw_code, record.raw_lock, record.raw_flow)) .unwrap_or_default(), @@ -1923,6 +1933,7 @@ async fn handle_queued_job( &job.parent_job.unwrap() ) .fetch_one(db) + .warn_after_seconds(5) .await .map_err(|e| { Error::InternalErr(format!( @@ -1957,6 +1968,7 @@ async fn handle_queued_job( &job.workspace_id, &cached_res_path, ) + .warn_after_seconds(5) .await; if let Some(cached_resource_value) = cached_resource_value_maybe { { @@ -1995,6 +2007,7 @@ async fn handle_queued_job( worker_dir, job_completed_tx.0.clone(), ) + .warn_after_seconds(10) .await?; Ok(true) } else { @@ -2285,9 +2298,11 @@ async fn handle_code_execution_job( .await? } JobKind::FlowScript => { - let (lockfile, content) = cache::flow::fetch_script(db, FlowNodeId( - job.script_hash.unwrap_or(ScriptHash(0)).0 - )).await?; + let (lockfile, content) = cache::flow::fetch_script( + db, + FlowNodeId(job.script_hash.unwrap_or(ScriptHash(0)).0), + ) + .await?; ContentReqLangEnvs { content, lockfile, diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index 33b17db790..050f0ebcf4 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -41,6 +41,7 @@ use windmill_common::jobs::{ script_hash_to_tag_and_limits, script_path_to_payload, BranchResults, JobPayload, QueuedJob, RawCode, ENTRYPOINT_OVERRIDE, }; +use windmill_common::utils::WarnAfterExt; use windmill_common::worker::to_raw_value; use windmill_common::{ error::{self, to_anyhow, Error}, @@ -1109,6 +1110,7 @@ pub async fn update_flow_status_after_job_completion_internal( worker_dir, job_completed_tx, ) + .warn_after_seconds(10) .await { Err(err) => { @@ -1510,7 +1512,7 @@ pub async fn handle_flow( let schedule_path = flow_job.schedule_path.as_ref().unwrap(); let schedule = - get_schedule_opt(&mut tx, &flow_job.workspace_id, schedule_path).await?; + get_schedule_opt(&mut tx, &flow_job.workspace_id, schedule_path).warn_after_seconds(5).await?; tx.commit().await?; @@ -1522,6 +1524,7 @@ pub async fn handle_flow( flow_job.script_path.as_ref().unwrap(), &flow_job.workspace_id, ) + .warn_after_seconds(5) .await { match err { @@ -1549,6 +1552,7 @@ pub async fn handle_flow( worker_dir, job_completed_tx, ) + .warn_after_seconds(10) .await?; Ok(()) } @@ -2133,6 +2137,7 @@ async fn push_next_flow_job( .bind(status.step) .bind(json!(status.retry.failed_jobs)) .execute(db) + .warn_after_seconds(2) .await .context("update flow retry")?; @@ -2558,6 +2563,7 @@ async fn push_next_flow_job( new_job_priority_override, job_perms.as_ref(), ) + .warn_after_seconds(2) .await?; tracing::debug!(id = %flow_job.id, root_id = %job_root, "pushed next flow job: {uuid}"); @@ -2770,7 +2776,7 @@ async fn push_next_flow_job( .execute(&mut *tx) .await?; - tx.commit().await?; + tx.commit().warn_after_seconds(3).await?; tracing::info!(id = %flow_job.id, root_id = %job_root, "all next flow jobs pushed: {uuids:?}"); if continue_on_same_worker { diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index 552e15dce5..0ea7456ec3 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -4,6 +4,7 @@ use std::path::{Component, Path, PathBuf}; use async_recursion::async_recursion; use serde_json::value::RawValue; use serde_json::{json, Value}; +use sha2::Digest; use sqlx::types::Json; use uuid::Uuid; use windmill_common::error::Error; @@ -606,9 +607,8 @@ pub async fn handle_flow_dependency_job( occupancy_metrics, ) .await?; - let new_flow_value = sqlx::types::Json( - serde_json::value::to_raw_value(&flow).map_err(to_anyhow)? - ); + let new_flow_value = + sqlx::types::Json(serde_json::value::to_raw_value(&flow).map_err(to_anyhow)?); // Re-check cancelation to ensure we don't accidentially override a flow. if sqlx::query_scalar!("SELECT canceled FROM queue WHERE id = $1", job.id) @@ -648,11 +648,20 @@ pub async fn handle_flow_dependency_job( // Compute a lite version of the flow value (`RawScript` => `FlowScript`). let mut value_lite = flow.clone(); - tx = reduce(tx, &mut value_lite.modules, &job_path, &job.workspace_id, flow.failure_module.as_ref(), flow.same_worker).await?; + tx = reduce( + tx, + &mut value_lite.modules, + &job_path, + &job.workspace_id, + flow.failure_module.as_ref(), + flow.same_worker, + ) + .await?; sqlx::query!( "INSERT INTO flow_version_lite (id, value) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET value = EXCLUDED.value", - version, sqlx::types::Json(to_raw_value(&value_lite)) as sqlx::types::Json>, + version, + sqlx::types::Json(to_raw_value(&value_lite)) as sqlx::types::Json>, ) .execute(db) .await?; @@ -829,8 +838,12 @@ async fn lock_modules<'c>( occupancy_metrics, )) .await?; - e.value = - FlowModuleValue::WhileloopFlow { modules: nmodules, modules_node, skip_failures }.into() + e.value = FlowModuleValue::WhileloopFlow { + modules: nmodules, + modules_node, + skip_failures, + } + .into() } FlowModuleValue::BranchOne { branches, default, default_node } => { let mut nbranches = vec![]; @@ -878,8 +891,12 @@ async fn lock_modules<'c>( occupancy_metrics, )) .await?; - e.value = FlowModuleValue::BranchOne { branches: nbranches, default: ndefault, default_node } - .into(); + e.value = FlowModuleValue::BranchOne { + branches: nbranches, + default: ndefault, + default_node, + } + .into(); } _ => (), }; @@ -1009,41 +1026,32 @@ async fn insert_flow_node<'c>( flow: Option<&Json>>, ) -> Result<(sqlx::Transaction<'c, sqlx::Postgres>, FlowNodeId)> { let hash = { - use std::hash::{DefaultHasher, Hasher, Hash}; - - let mut hasher = DefaultHasher::new(); - code.hash(&mut hasher); - lock.hash(&mut hasher); - flow.inspect(|flow| flow.get().hash(&mut hasher)); - hasher.finish() as i64 + let mut hasher = sha2::Sha256::new(); + hasher.update(path); + hasher.update(workspace_id); + hasher.update(code.unwrap_or(&Default::default())); + hasher.update(lock.unwrap_or(&Default::default())); + hasher.update(flow.unwrap_or(&Default::default()).get()); + format!("{:x}", hasher.finalize()) }; // Insert the flow node if it doesn't exist. let id = sqlx::query_scalar!( r#" - WITH existing AS ( - SELECT id FROM flow_node - WHERE hash = $1 AND path = $2 AND workspace_id = $3 - AND (code IS NOT DISTINCT FROM $4) - AND (lock IS NOT DISTINCT FROM $5) - AND (flow IS NOT DISTINCT FROM $6) - LIMIT 1 - ), - inserted AS ( - INSERT INTO flow_node (hash, path, workspace_id, code, lock, flow) - VALUES ($1, $2, $3, $4, $5, $6) - ON CONFLICT DO NOTHING - RETURNING id - ) - SELECT id FROM existing - UNION ALL - SELECT id FROM inserted + INSERT INTO flow_node (path, workspace_id, hash_v2, lock, code, flow) + VALUES ($1, $2, $3, $4, $5, $6) + ON CONFLICT (hash_v2) DO UPDATE SET path = EXCLUDED.path -- trivial update to return the id + RETURNING id "#, - hash, path, workspace_id, code, lock, flow as Option<&Json>> + path, + workspace_id, + hash, + lock, + code, + flow as Option<&Json>> ) .fetch_one(&mut *tx) - .await? - .ok_or(error::Error::InternalErr("Failed to cache".to_string()))?; + .await?; Ok((tx, FlowNodeId(id))) } @@ -1056,7 +1064,15 @@ async fn insert_flow_modules<'c>( modules: &mut Vec, modules_node: &mut Option, ) -> Result> { - tx = Box::pin(reduce(tx, modules, path, workspace_id, failure_module, same_worker)).await?; + tx = Box::pin(reduce( + tx, + modules, + path, + workspace_id, + failure_module, + same_worker, + )) + .await?; add_virtual_items_if_necessary(modules); if modules.is_empty() || crate::worker_flow::is_simple_modules(modules, failure_module) { return Ok(tx); @@ -1073,7 +1089,7 @@ async fn insert_flow_modules<'c>( failure_module: failure_module.cloned(), same_worker, ..Default::default() - }))) + }))), ) .await?; *modules_node = Some(id); @@ -1090,8 +1106,13 @@ async fn reduce<'c>( ) -> Result> { use FlowModuleValue::*; for module in &mut *modules { - let mut val = serde_json::from_str::(module.value.get()) - .map_err(|err| Error::InternalErr(format!("reduce: Failed to parse flow module value: {}", err)))?; + let mut val = + serde_json::from_str::(module.value.get()).map_err(|err| { + Error::InternalErr(format!( + "reduce: Failed to parse flow module value: {}", + err + )) + })?; match &mut val { RawScript { .. } => { // In order to avoid an unnecessary `.clone()` of `val`, take ownership of it's content @@ -1107,9 +1128,14 @@ async fn reduce<'c>( concurrency_time_window_s, is_trigger, .. - } = std::mem::replace(&mut val, Identity) else { unreachable!() }; + } = std::mem::replace(&mut val, Identity) + else { + unreachable!() + }; let id; - (tx, id) = insert_flow_node(tx, path, workspace_id, Some(&content), lock.as_ref(), None).await?; + (tx, id) = + insert_flow_node(tx, path, workspace_id, Some(&content), lock.as_ref(), None) + .await?; val = FlowScript { input_transforms, id, @@ -1120,32 +1146,56 @@ async fn reduce<'c>( concurrency_time_window_s, is_trigger, }; - }, + } ForloopFlow { modules, modules_node, .. } - | WhileloopFlow { modules, modules_node, .. } => { + | WhileloopFlow { modules, modules_node, .. } => { tx = insert_flow_modules( - tx, path, workspace_id, failure_module, same_worker, - modules, modules_node - ).await?; + tx, + path, + workspace_id, + failure_module, + same_worker, + modules, + modules_node, + ) + .await?; } BranchOne { branches, default, default_node, .. } => { for branch in branches.iter_mut() { tx = insert_flow_modules( - tx, path, workspace_id, failure_module, same_worker, - &mut branch.modules, &mut branch.modules_node - ).await?; + tx, + path, + workspace_id, + failure_module, + same_worker, + &mut branch.modules, + &mut branch.modules_node, + ) + .await?; } tx = insert_flow_modules( - tx, path, workspace_id, failure_module, same_worker, - default, default_node - ).await?; + tx, + path, + workspace_id, + failure_module, + same_worker, + default, + default_node, + ) + .await?; } BranchAll { branches, .. } => { for branch in branches.iter_mut() { tx = insert_flow_modules( - tx, path, workspace_id, failure_module, same_worker, - &mut branch.modules, &mut branch.modules_node - ).await?; + tx, + path, + workspace_id, + failure_module, + same_worker, + &mut branch.modules, + &mut branch.modules_node, + ) + .await?; } } _ => {} diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index aed23d3ca0..2848c702dc 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.434.1"; +export const VERSION = "v1.435.2"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index 3b1027fd14..1bad532ef9 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -60,7 +60,7 @@ export { // } // }); -export const VERSION = "1.434.1"; +export const VERSION = "1.435.2"; const command = new Command() .name("wmill") diff --git a/docker-compose.yml b/docker-compose.yml index d45e589808..13d1c3ac98 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -129,12 +129,6 @@ services: - PORT=8001 - DATABASE_URL=${DATABASE_URL} - MODE=indexer - - TANTIVY_MAX_INDEXED_JOB_LOG_SIZE__MB=1 # job logs bigger than this will be truncated before indexing - - TANTIVY_S3_BACKUP_PERIOD__S=3600 # how often to backup the index into object storage - - TANTIVY_INDEX_WRITER_MEMORY_BUDGET__MB=100 # higher budget for higher indexing throughput - - TANTIVY_REFRESH_INDEX_PERIOD__S=300 #how often to start indexing new jobs - - TANTIVY_DOC_COMMIT_MAX_BATCH_SIZE=100000 #how many documents to batch in one commit - - TANTIVY_SHOW_MEMORY_EVERY=10000 #log memory usage and progress every so many documents indexed depends_on: db: condition: service_healthy diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9bd2c2ee7d..300cb2649c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.434.1", + "version": "1.435.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.434.1", + "version": "1.435.2", "license": "AGPL-3.0", "dependencies": { "@anthropic-ai/sdk": "^0.32.1", @@ -87,10 +87,10 @@ "@hey-api/openapi-ts": "^0.43.0", "@playwright/test": "^1.34.3", "@rgossiaux/svelte-headlessui": "^2.0.0", - "@sveltejs/adapter-static": "^3.0.0", - "@sveltejs/kit": "^2.0.0", + "@sveltejs/adapter-static": "^3.0.6", + "@sveltejs/kit": "^2.9.0", "@sveltejs/package": "^2.2.2", - "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@sveltejs/vite-plugin-svelte": "^3.1.2", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.8", "@types/d3": "^7.4.0", @@ -130,7 +130,7 @@ "tailwindcss": "^3.4.1", "tslib": "^2.6.1", "typescript": "^5.1.3", - "vite": "^5", + "vite": "^5.4.11", "vite-plugin-circular-dependency": "^0.2.1", "vite-plugin-mkcert": "^1.17.5", "yootils": "^0.3.1" @@ -3584,9 +3584,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", @@ -3897,10 +3897,11 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.24", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", - "integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==", - "dev": true + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true, + "license": "MIT" }, "node_modules/@popperjs/core": { "version": "2.11.8", @@ -4214,32 +4215,33 @@ } }, "node_modules/@sveltejs/adapter-static": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.1.tgz", - "integrity": "sha512-6lMvf7xYEJ+oGeR5L8DFJJrowkefTK6ZgA4JiMqoClMkKq0s6yvsd3FZfCFvX1fQ0tpCD7fkuRVHsnUVgsHyNg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.6.tgz", + "integrity": "sha512-MGJcesnJWj7FxDcB/GbrdYD3q24Uk0PIL4QIX149ku+hlJuj//nxUbb0HxUTpjkecWfHjVveSUnUaQWnPRXlpg==", "dev": true, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "node_modules/@sveltejs/kit": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.0.tgz", - "integrity": "sha512-1uyXvzC2Lu1FZa30T4y5jUAC21R309ZMRG0TPt+PPPbNUoDpy8zSmSNVWYaBWxYDqLGQ5oPNWvjvvF2IjJ1jmA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.9.0.tgz", + "integrity": "sha512-W3E7ed3ChB6kPqRs2H7tcHp+Z7oiTFC6m+lLyAQQuyXeqw6LdNuuwEUla+5VM0OGgqQD+cYD6+7Xq80vVm17Vg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", - "devalue": "^4.3.2", - "esm-env": "^1.0.0", - "import-meta-resolve": "^4.0.0", + "devalue": "^5.1.0", + "esm-env": "^1.2.1", + "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", - "sirv": "^2.0.4", + "sirv": "^3.0.0", "tiny-glob": "^0.2.9" }, "bin": { @@ -4249,9 +4251,9 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3" + "vite": "^5.0.3 || ^6.0.0" } }, "node_modules/@sveltejs/package": { @@ -4277,17 +4279,17 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.2.tgz", - "integrity": "sha512-MpmF/cju2HqUls50WyTHQBZUV3ovV/Uk8k66AN2gwHogNAG8wnW8xtZDhzNBsFJJuvmq1qnzA5kE7YfMJNFv2Q==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", + "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", "dev": true, "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0", + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "svelte-hmr": "^0.15.3", + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", "vitefu": "^0.2.5" }, "engines": { @@ -4299,9 +4301,9 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz", - "integrity": "sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", + "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -6434,10 +6436,11 @@ } }, "node_modules/devalue": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.2.tgz", - "integrity": "sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==", - "dev": true + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", + "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", + "dev": true, + "license": "MIT" }, "node_modules/devlop": { "version": "1.1.0", @@ -6990,9 +6993,10 @@ } }, "node_modules/esm-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", - "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.1.tgz", + "integrity": "sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==", + "license": "MIT" }, "node_modules/esm-env-robust": { "version": "0.0.3", @@ -7932,10 +7936,11 @@ } }, "node_modules/import-meta-resolve": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", - "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", "dev": true, + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -8608,14 +8613,11 @@ } }, "node_modules/magic-string": { - "version": "0.30.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", - "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "version": "0.30.14", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.14.tgz", + "integrity": "sha512-5c99P1WKTed11ZC0HMJOj6CDIue6F8ySu+bJL+85q1zBEIY8IklrJ1eiKC2NDRh3Ct3FcvmJPyQHb9erXMTJNw==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/map-obj": { @@ -9781,6 +9783,7 @@ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -11960,17 +11963,18 @@ "dev": true }, "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", "dev": true, + "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/slash": { @@ -12642,9 +12646,9 @@ } }, "node_modules/svelte-hmr": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", - "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", "dev": true, "engines": { "node": "^12.20 || ^14.13.1 || >= 16" @@ -13064,6 +13068,7 @@ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -13395,15 +13400,14 @@ } }, "node_modules/vite": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", - "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.40", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" diff --git a/frontend/package.json b/frontend/package.json index 5b358f953e..3c4aa926ff 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.434.1", + "version": "1.435.2", "scripts": { "dev": "vite dev", "build": "vite build", @@ -21,10 +21,10 @@ "@hey-api/openapi-ts": "^0.43.0", "@playwright/test": "^1.34.3", "@rgossiaux/svelte-headlessui": "^2.0.0", - "@sveltejs/adapter-static": "^3.0.0", - "@sveltejs/kit": "^2.0.0", + "@sveltejs/adapter-static": "^3.0.6", + "@sveltejs/kit": "^2.9.0", "@sveltejs/package": "^2.2.2", - "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@sveltejs/vite-plugin-svelte": "^3.1.2", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/typography": "^0.5.8", "@types/d3": "^7.4.0", @@ -64,7 +64,7 @@ "tailwindcss": "^3.4.1", "tslib": "^2.6.1", "typescript": "^5.1.3", - "vite": "^5", + "vite": "^5.4.11", "vite-plugin-circular-dependency": "^0.2.1", "vite-plugin-mkcert": "^1.17.5", "yootils": "^0.3.1" diff --git a/frontend/src/app.html b/frontend/src/app.html index 7f93534ddb..718800105a 100644 --- a/frontend/src/app.html +++ b/frontend/src/app.html @@ -27,61 +27,63 @@
-
-
+
+
+
-
- - - - - + + + + + - - - - - -
- Loading... + /> + + + + + +
+ Loading... +
%sveltekit.body% diff --git a/frontend/src/lib/components/CronInput.svelte b/frontend/src/lib/components/CronInput.svelte index 3ce3caf385..039cc9409a 100644 --- a/frontend/src/lib/components/CronInput.svelte +++ b/frontend/src/lib/components/CronInput.svelte @@ -250,7 +250,7 @@ {#if !disabled} -
+
diff --git a/frontend/src/lib/components/Description.svelte b/frontend/src/lib/components/Description.svelte new file mode 100644 index 0000000000..59e233aaa8 --- /dev/null +++ b/frontend/src/lib/components/Description.svelte @@ -0,0 +1,10 @@ + + +
+ + {#if link} + Learn more + {/if} +
diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index 68a58a989b..ba23b76fdc 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -214,6 +214,7 @@ export let small = false export let scriptLang: Preview['language'] | 'bunnative' export let disabled: boolean = false + export let lineNumbersMinChars = 3 const rHash = randomHash() $: filePath = computePath(path) @@ -1131,6 +1132,7 @@ ...editorConfig(code, lang, automaticLayout, fixedOverflowWidgets), model, fontSize: !small ? 14 : 12, + lineNumbersMinChars, // overflowWidgetsDomNode: widgets, tabSize: lang == 'python' ? 4 : 2, folding diff --git a/frontend/src/lib/components/FlowGraphViewer.svelte b/frontend/src/lib/components/FlowGraphViewer.svelte index 326e1275b6..f69c1c9450 100644 --- a/frontend/src/lib/components/FlowGraphViewer.svelte +++ b/frontend/src/lib/components/FlowGraphViewer.svelte @@ -36,6 +36,8 @@ > Setting SMTP unlocks sending emails upon adding new users to the workspace or the instance or sending critical alerts. - Learn moreLearn more
{:else if category == "Indexer/Search"} diff --git a/frontend/src/lib/components/RunPageSchedules.svelte b/frontend/src/lib/components/RunPageSchedules.svelte index e7d5e68f79..f953d160a5 100644 --- a/frontend/src/lib/components/RunPageSchedules.svelte +++ b/frontend/src/lib/components/RunPageSchedules.svelte @@ -14,6 +14,7 @@ import Toggle from './Toggle.svelte' import { loadSchedules, saveSchedule } from './flows/scheduleUtils' import { type Writable, writable } from 'svelte/store' + import Description from '$lib/components/Description.svelte' export let schema: any export let isFlow: boolean @@ -48,14 +49,18 @@ } - { - updateSchedules(true) - }} - bind:this={scheduleEditor} -/> -
+ + Run scripts and flows automatically on a recurring basis using cron expressions. Each script or + flow can have multiple schedules, with one designated as primary. + + { + updateSchedules(true) + }} + bind:this={scheduleEditor} + /> + {#if $primarySchedule == undefined} {:else if $primarySchedule} @@ -163,7 +168,7 @@ }} variant="contained" color="dark" - size="md" + size="sm" startIcon={{ icon: Calendar }} > Set Primary Schedule diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte index 4728ae8294..3b9fddf6cf 100644 --- a/frontend/src/lib/components/ScriptBuilder.svelte +++ b/frontend/src/lib/components/ScriptBuilder.svelte @@ -648,7 +648,7 @@ Triggers Configure how this script will be triggered. diff --git a/frontend/src/lib/components/ScriptEditor.svelte b/frontend/src/lib/components/ScriptEditor.svelte index 3e1009d0d9..8adff7f21e 100644 --- a/frontend/src/lib/components/ScriptEditor.svelte +++ b/frontend/src/lib/components/ScriptEditor.svelte @@ -317,9 +317,10 @@ -
+
{#key lang} - {#if collapsable && collapsed} - - {/if} +
diff --git a/frontend/src/lib/components/SimpleEditor.svelte b/frontend/src/lib/components/SimpleEditor.svelte index 86ec070f86..18a42d8408 100644 --- a/frontend/src/lib/components/SimpleEditor.svelte +++ b/frontend/src/lib/components/SimpleEditor.svelte @@ -225,6 +225,8 @@ editor = meditor.create(divEl as HTMLDivElement, { ...editorConfig(code, lang, automaticLayout, fixedOverflowWidgets), model, + lineDecorationsWidth: 6, + lineNumbersMinChars: 2, // overflowWidgetsDomNode: widgets, fontSize: small ? 12 : 14 }) diff --git a/frontend/src/lib/components/TemplateEditor.svelte b/frontend/src/lib/components/TemplateEditor.svelte index 275bb06c80..0e999222f3 100644 --- a/frontend/src/lib/components/TemplateEditor.svelte +++ b/frontend/src/lib/components/TemplateEditor.svelte @@ -450,10 +450,11 @@ ...editorConfig(code, lang, automaticLayout, fixedOverflowWidgets), model, // overflowWidgetsDomNode: widgets, - lineNumbers: 'off', + // lineNumbers: 'on', + lineDecorationsWidth: 6, + lineNumbersMinChars: 2, fontSize, - suggestOnTriggerCharacters: true, - lineDecorationsWidth: 14 + suggestOnTriggerCharacters: true }) editor.onDidFocusEditorText(() => { diff --git a/frontend/src/lib/components/Toggle.svelte b/frontend/src/lib/components/Toggle.svelte index c50b29414a..068082d2f7 100644 --- a/frontend/src/lib/components/Toggle.svelte +++ b/frontend/src/lib/components/Toggle.svelte @@ -31,7 +31,7 @@ {#if eeOnly && disabled} - + EE only Enterprise Edition only feature diff --git a/frontend/src/lib/components/apps/editor/AppEditor.svelte b/frontend/src/lib/components/apps/editor/AppEditor.svelte index 66b2d3e009..f0380f09e2 100644 --- a/frontend/src/lib/components/apps/editor/AppEditor.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditor.svelte @@ -68,6 +68,7 @@ summary: string policy: any draft_only?: boolean + custom_path?: string } | undefined = undefined export let version: number | undefined = undefined diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index 82e47a0473..3c7783e3f4 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -11,12 +11,11 @@ import Toggle from '$lib/components/Toggle.svelte' import { AppService, DraftService, type Job, type Policy } from '$lib/gen' import { redo, undo } from '$lib/history' - import { enterpriseLicense, workspaceStore } from '$lib/stores' + import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' import { AlignHorizontalSpaceAround, BellOff, Bug, - Clipboard, DiffIcon, Expand, FileJson, @@ -39,7 +38,6 @@ import { classNames, cleanValueProperties, - copyToClipboard, truncateRev, orderedJsonStringify, type Value, @@ -90,6 +88,9 @@ import HideButton from './settingsPanel/HideButton.svelte' import DeployOverrideConfirmationModal from '$lib/components/common/confirmationModal/DeployOverrideConfirmationModal.svelte' import { computeS3FileInputPolicy, computeWorkspaceS3FileInputPolicy } from './appUtilsS3' + import { isCloudHosted } from '$lib/cloud' + import { base } from '$lib/base' + import ClipboardPanel from '$lib/components/details/ClipboardPanel.svelte' async function hash(message) { try { @@ -119,6 +120,7 @@ summary: string policy: any draft_only?: boolean + custom_path?: string } | undefined = undefined export let version: number | undefined = undefined @@ -479,14 +481,16 @@ summary: $summary, policy, path: npath, - deployment_message: deploymentMsg + deployment_message: deploymentMsg, + custom_path: $userStore?.is_admin || $userStore?.is_super_admin ? customPath : undefined } }) savedApp = { summary: $summary, value: structuredClone($app), path: npath, - policy + policy, + custom_path: customPath } const appHistory = await AppService.getAppHistoryByPath({ workspace: $workspaceStore!, @@ -885,6 +889,37 @@ let priorDarkMode = document.documentElement.classList.contains('dark') setTheme($app?.darkMode) + + let customPath = savedApp?.custom_path + let dirtyCustomPath = false + let customPathError = '' + $: fullCustomUrl = `${window.location.origin}${base}/a/${ + isCloudHosted() ? $workspaceStore + '/' : '' + }${customPath}` + async function appExists(customPath: string) { + return await AppService.customPathExists({ + workspace: $workspaceStore!, + customPath + }) + } + let validateTimeout: NodeJS.Timeout | undefined = undefined + async function validateCustomPath(customPath: string): Promise { + customPathError = '' + if (validateTimeout) { + clearTimeout(validateTimeout) + } + validateTimeout = setTimeout(async () => { + if (!/^[\w-]+(\/[\w-]+)*$/.test(customPath)) { + customPathError = 'Invalid path' + } else if (customPath !== savedApp?.custom_path && (await appExists(customPath))) { + customPathError = 'Path already taken' + } else { + customPathError = '' + } + validateTimeout = undefined + }, 500) + } + $: customPath !== undefined && validateCustomPath(customPath) @@ -1071,7 +1106,7 @@
- Public url: +
+
Public URL
+
{#if secretUrl} - {@const url = `${window.location.hostname}/public/${$workspaceStore}/${secretUrl}`} - {@const href = window.location.protocol + '//' + url} - { - e.preventDefault() - copyToClipboard(href) - }} - {href} - class="whitespace-nowrap text-ellipsis overflow-hidden mr-1 inline-flex gap-2" - > - {url} - - - - + {@const href = `${window.location.origin}${base}/public/${$workspaceStore}/${secretUrl}`} + {:else} {/if} -
Share this url directly or embed it using an iframe (if requiring login, top-level domain - of embedding app must be the same as the one of Windmill)
+
+ Share this url directly or embed it using an iframe (if requiring login, top-level domain + of embedding app must be the same as the one of Windmill) +
+ +
+ {#if !$enterpriseLicense} + + Custom path is an enterprise only feature. + +
+ {:else if !($userStore?.is_admin || $userStore?.is_super_admin)} + + Custom path can only be set by workspace admins + +
+ {/if} + { + customPath = detail ? '' : undefined + }} + checked={customPath !== undefined} + options={{ + right: 'Use a custom URL' + }} + disabled={!$enterpriseLicense || !($userStore?.is_admin || $userStore?.is_super_admin)} + /> + + {#if customPath !== undefined} +
+
Custom path
+
+ { + dirtyCustomPath = true + }} + /> +
+
Custom public URL
+
+ + +
{dirtyCustomPath ? customPathError : ''} +
+ {/if} +
You will still need to deploy the app to make visible the latest changes diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/EvalV2InputEditor.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/EvalV2InputEditor.svelte index 5296fbc21c..6072e2569b 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/EvalV2InputEditor.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/EvalV2InputEditor.svelte @@ -133,8 +133,9 @@
{/if} (fullscreen = true)}> {#if focus}
diff --git a/frontend/src/lib/components/common/alert/Notification.svelte b/frontend/src/lib/components/common/alert/Notification.svelte new file mode 100644 index 0000000000..4beca30071 --- /dev/null +++ b/frontend/src/lib/components/common/alert/Notification.svelte @@ -0,0 +1,16 @@ + + +{#if notificationCount > 0} +
+ {#if notificationLimit && notificationCount > notificationLimit} + {`${notificationLimit}+`} + {:else} + {notificationCount} + {/if} +
+{/if} diff --git a/frontend/src/lib/components/common/button/RefreshButton.svelte b/frontend/src/lib/components/common/button/RefreshButton.svelte new file mode 100644 index 0000000000..a44f0bb4b3 --- /dev/null +++ b/frontend/src/lib/components/common/button/RefreshButton.svelte @@ -0,0 +1,34 @@ + + + + + + {#if loading} + {#if buttonHover} + Stop Refreshing + {:else} + Refreshing... + {/if} + {:else} + Refresh + {/if} + + diff --git a/frontend/src/lib/components/common/layout/List.svelte b/frontend/src/lib/components/common/layout/List.svelte new file mode 100644 index 0000000000..19c5d062cd --- /dev/null +++ b/frontend/src/lib/components/common/layout/List.svelte @@ -0,0 +1,38 @@ + + +{#if horizontal} +
+ +
+{:else} +
+ +
+{/if} diff --git a/frontend/src/lib/components/common/layout/ListElement.svelte b/frontend/src/lib/components/common/layout/ListElement.svelte new file mode 100644 index 0000000000..891079ce42 --- /dev/null +++ b/frontend/src/lib/components/common/layout/ListElement.svelte @@ -0,0 +1,3 @@ +
+ +
diff --git a/frontend/src/lib/components/common/modal/Modal.svelte b/frontend/src/lib/components/common/modal/Modal.svelte index 300499d71a..806f7d33b0 100644 --- a/frontend/src/lib/components/common/modal/Modal.svelte +++ b/frontend/src/lib/components/common/modal/Modal.svelte @@ -70,10 +70,12 @@ >
-

- {title} -

-
+
+

{title}

+ +
+ +
diff --git a/frontend/src/lib/components/common/modal/Modal2.svelte b/frontend/src/lib/components/common/modal/Modal2.svelte new file mode 100644 index 0000000000..7d34172237 --- /dev/null +++ b/frontend/src/lib/components/common/modal/Modal2.svelte @@ -0,0 +1,110 @@ + + + + +{#if isOpen} + +
+
+
{ + close() + }} + > + +
+ +

{title}

+
+ +
+ +
+
+ + +
+ +
+
+
+
+
+
+
+ + + +
{}}> + +
+
+
+
+
+
+{/if} diff --git a/frontend/src/lib/components/common/popup/Popup.svelte b/frontend/src/lib/components/common/popup/Popup.svelte index 5f209b4e50..a559509d15 100644 --- a/frontend/src/lib/components/common/popup/Popup.svelte +++ b/frontend/src/lib/components/common/popup/Popup.svelte @@ -17,6 +17,7 @@ export let target: string | HTMLElement | undefined = undefined export let noTransition = false export let popupHover = false + export let preventPopupClosingOnClickInside = false @@ -28,6 +29,7 @@ +
{ popupHover = false }} + on:click={(e) => preventPopupClosingOnClickInside && e.stopPropagation()} > {#if !noTransition} import { copyToClipboard } from '$lib/utils' import { Clipboard } from 'lucide-svelte' + import { twMerge } from 'tailwind-merge' export let content: string export let title: string | undefined = undefined + export let size: 'sm' | 'md' = 'sm' {#if title !== undefined} @@ -19,6 +21,8 @@ copyToClipboard(content) }} > -
{content}
+
{content}
diff --git a/frontend/src/lib/components/details/EmailTriggerPanel.svelte b/frontend/src/lib/components/details/EmailTriggerPanel.svelte index fca824946f..0eb69d12bb 100644 --- a/frontend/src/lib/components/details/EmailTriggerPanel.svelte +++ b/frontend/src/lib/components/details/EmailTriggerPanel.svelte @@ -17,6 +17,7 @@ import Label from '$lib/components/Label.svelte' import TriggerTokens from '../triggers/TriggerTokens.svelte' let userSettings: UserSettings + import Description from '$lib/components/Description.svelte' export let token: string export let scopes: string[] = [] @@ -79,6 +80,11 @@ />
+ + Email triggers execute scripts and flows when emails are sent to specific addresses. Each + trigger has its own unique email address that can be used to invoke the script or flow. + + {#if loading} {:else} diff --git a/frontend/src/lib/components/flows/content/FlowInputs.svelte b/frontend/src/lib/components/flows/content/FlowInputs.svelte index fb27580ae5..65c8b137f6 100644 --- a/frontend/src/lib/components/flows/content/FlowInputs.svelte +++ b/frontend/src/lib/components/flows/content/FlowInputs.svelte @@ -121,7 +121,7 @@ By default, adding a trigger will set the schedule to 15 minutes. To see all ways to trigger a flow, check Triggering Flows. diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte index 97801d24ab..e5992f4ab5 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte @@ -303,6 +303,7 @@ {#if !noEditor} {#key flowModule.id} @@ -185,7 +185,8 @@ }} options={{ right: 'Cache the results for each possible inputs', - rightTooltip: 'When enabled, the flow will cache the results of the flow for each possible set of inputs.', + rightTooltip: + 'When enabled, the flow will cache the results of the flow for each possible set of inputs.', rightDocumentationLink: 'https://www.windmill.dev/docs/flows/cache#cache-flows' }} class="py-1" @@ -226,7 +227,8 @@ rightTooltip: 'If the inputs meet the predefined condition, the flow will not run.' + 'to decide if the flow should stop early.', - rightDocumentationLink: 'https://www.windmill.dev/docs/flows/early_stop#early-stop-for-flow' + rightDocumentationLink: + 'https://www.windmill.dev/docs/flows/early_stop#early-stop-for-flow' }} class="py-1" /> @@ -316,7 +318,8 @@ 'Steps will share a folder at `./shared` in which they can store heavier data and ' + 'pass them to the next step. Beware that the `./shared` folder is not ' + 'preserved across suspends and sleeps.', - rightDocumentationLink: 'https://www.windmill.dev/docs/core_concepts/persistent_storage/within_windmill#shared-directory' + rightDocumentationLink: + 'https://www.windmill.dev/docs/core_concepts/persistent_storage/within_windmill#shared-directory' }} class="py-1" /> @@ -339,7 +342,8 @@ right: 'Make runs invisible to others', rightTooltip: 'When this option is enabled, manual executions of this script are invisible to users other than the user running it, including the owner(s). This setting can be overridden when this script is run manually from the advanced menu.', - rightDocumentationLink: 'https://www.windmill.dev/docs/core_concepts/monitor_past_and_future_runs#invisible-runs' + rightDocumentationLink: + 'https://www.windmill.dev/docs/core_concepts/monitor_past_and_future_runs#invisible-runs' }} class="py-1" /> @@ -365,27 +369,29 @@ {#if customUi?.settingsTabs?.concurrency != false}
- { - if ($flowStore.value.concurrent_limit) { - $flowStore.value.concurrent_limit = undefined - } else { - $flowStore.value.concurrent_limit = 1 - } - }} - options={{ - right: 'Concurrency limits', - rightTooltip: 'Allowed concurrency within a given timeframe', - rightDocumentationLink: 'https://www.windmill.dev/docs/flows/concurrency_limit' - }} - class="py-1" - eeOnly={true} - /> +
+ { + if ($flowStore.value.concurrent_limit) { + $flowStore.value.concurrent_limit = undefined + } else { + $flowStore.value.concurrent_limit = 1 + } + }} + options={{ + right: 'Concurrency limits', + rightTooltip: 'Allowed concurrency within a given timeframe', + rightDocumentationLink: 'https://www.windmill.dev/docs/flows/concurrency_limit' + }} + class="py-1" + eeOnly={true} + /> +
{#if $flowStore.value.concurrent_limit}
@@ -483,28 +489,30 @@
- { - if ($flowStore.dedicated_worker) { - $flowStore.dedicated_worker = undefined - } else { - $flowStore.dedicated_worker = true - } - }} - options={{ - right: 'Flow is run on dedicated workers', - rightTooltip: - 'When enabled, the flow will be executed on a dedicated worker.', - rightDocumentationLink: 'https://www.windmill.dev/docs/core_concepts/jobs#high-priority-jobs' - }} - class="py-1" - eeOnly={true} - /> +
+ { + if ($flowStore.dedicated_worker) { + $flowStore.dedicated_worker = undefined + } else { + $flowStore.dedicated_worker = true + } + }} + options={{ + right: 'Flow is run on dedicated workers', + rightTooltip: 'When enabled, the flow will be executed on a dedicated worker.', + rightDocumentationLink: + 'https://www.windmill.dev/docs/core_concepts/jobs#high-priority-jobs' + }} + class="py-1" + eeOnly={true} + /> +
{#if $flowStore.dedicated_worker}
diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte index 8e53a1030c..9e89f92f73 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte @@ -330,6 +330,8 @@
('FlowCopilotContext') || {} @@ -62,7 +67,30 @@
{/if}
- +
+ {#if cache} + +
+ +
+ Cached +
+ {/if} + {#if earlyStop} + +
+ +
+ Early stop if condition met +
+ {/if} +
{#if inputJson && $flowPropPickerConfig && (Object.keys(inputJson).length > 0 || alwaysPluggable)}
diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index 2e46fb309c..48f7fc9430 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -57,6 +57,8 @@ export let newFlow: boolean = false export let insertable = false + export let earlyStop: boolean = false + export let cache: boolean = false export let scroll = false export let moving: string | undefined = undefined @@ -238,7 +240,9 @@ flowModuleStates, selectedId: $selectedId, path, - newFlow + newFlow, + cache, + earlyStop }, failureModule, preprocessorModule, diff --git a/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte index 30eb352800..bdfcdd30cb 100644 --- a/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte +++ b/frontend/src/lib/components/graph/renderers/nodes/InputNode.svelte @@ -18,6 +18,8 @@ index: number disableAi: boolean disableMoveIds: string[] + cache: boolean + earlyStop: boolean } const { selectedId } = getContext<{ @@ -83,5 +85,7 @@ inputJson={filteredInput} prefix="flow_input" alwaysPluggable + cache={data.cache} + earlyStop={data.earlyStop} /> diff --git a/frontend/src/lib/components/search/GlobalSearchModal.svelte b/frontend/src/lib/components/search/GlobalSearchModal.svelte index 599e8853cb..d76a25f96c 100644 --- a/frontend/src/lib/components/search/GlobalSearchModal.svelte +++ b/frontend/src/lib/components/search/GlobalSearchModal.svelte @@ -33,7 +33,7 @@ import ContentSearchInner from '../ContentSearchInner.svelte' import { goto } from '$app/navigation' import QuickMenuItem from '../search/QuickMenuItem.svelte' - import { enterpriseLicense, superadmin, workspaceStore } from '$lib/stores' + import { devopsRole, enterpriseLicense, workspaceStore } from '$lib/stores' import uFuzzy from '@leeoniya/ufuzzy' import BarsStaggered from '../icons/BarsStaggered.svelte' import { scroll_into_view_if_needed_polyfill } from '../multiselect/utils' @@ -244,7 +244,7 @@ queryParseErrors = searchResults.query_parse_errors indexMetadata = searchResults.index_metadata } catch (e) { - sendUserToast(e, true) + sendUserToast(e.body, true) } loadingCompletedRuns = false selectedItem = selectItem(0) @@ -610,7 +610,7 @@ /> {:else if tab === 'logs'}
- {#if !$superadmin} + {#if !$devopsRole} Service logs are only available to superadmins diff --git a/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte b/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte index d6d84c39bd..399e044ee5 100644 --- a/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte +++ b/frontend/src/lib/components/sidebar/CriticalAlertModal.svelte @@ -1,15 +1,28 @@ - + + + + + + + {#if $superadmin || $userStore?.is_admin} + + +
+ +
+
+ +
+ {#if $superadmin} + + {/if} +
+ +
+ +
+
+
+ {/if} + + {#if $superadmin} + + +
+ +
+
+ +
+ +
+
+ +
+
+
+ {:else} + + {/if} +
+
+ -
+ diff --git a/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte b/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte index c935f82944..6127475cfd 100644 --- a/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte +++ b/frontend/src/lib/components/sidebar/CriticalAlertModalInner.svelte @@ -1,14 +1,13 @@ -
-
-
- {#if !hasCriticalAlertChannels && $superadmin} -
- -

- No critical alert channels are set up. Go to the - Instance Settings - page to configure critical alert channels. -

-
- {/if} + + {#if !hasCriticalAlertChannels && $superadmin} +
+ + Go to the + Instance Settings + page to configure critical alert channels. +
+ {/if} -
-
- -
- - {#if $devopsRole} -
- -
- {/if} - - {#if $superadmin || $userStore?.is_admin} -
- {#if $superadmin} -
- -
- {/if} - -
+
+ +
+ + {#if $devopsRole} -
-
- {/if} + {/if} -
-
- -
- + +
-
+ + +
{`${totalNumberOfAlerts === 1000 ? '1000+' : totalNumberOfAlerts ?? '?'} items`} +
+ +
+
- -
- - - - - - - {#if $devopsRole} - - {/if} - - - - - {#each alerts as { id, alert_type, message, created_at, acknowledged, workspace_id }} - {#if !hideAcknowledged || !acknowledged} - - - - - - {#if $devopsRole} - - {/if} - - - {/if} - {/each} - -
TypeMessageCreated AtWorkspaceAcknowledge
- {#if alert_type === 'recovered_critical_error'} - - - - {:else} - - - - {/if} - {message}{formatDate(created_at)}{workspace_id ? workspace_id : 'global'} -
- {#if !acknowledged} - - {:else} - - {/if} -
-
-
-
- - Page {page} - -
- - {#if alerts.length === 0} -

No critical alerts available.

- {/if} -
+ + diff --git a/frontend/src/lib/components/sidebar/CriticalAlertTable.svelte b/frontend/src/lib/components/sidebar/CriticalAlertTable.svelte new file mode 100644 index 0000000000..82e9fd3e74 --- /dev/null +++ b/frontend/src/lib/components/sidebar/CriticalAlertTable.svelte @@ -0,0 +1,146 @@ + + +
+ + + +   + Message + Created At + {#if $devopsRole} + Context + {/if} + + + Acked + + + + + + + {#if alerts == undefined} + + {#each new Array(3) as _} + + {#each new Array(5) as _} + + + + {/each} + + {/each} + + {:else if alerts.length === 0} +
+

No critical alerts.

+
+ {:else} + + {#each alerts as { id, alert_type, message, created_at, acknowledged, workspace_id }} + {#if !hideAcknowledged || !acknowledged} + + +
+ {#if alert_type === 'recovered_critical_error'} + + + + {:else} + + + + {/if} +
+
+ + +
{message}
+
+ + {formatDate(created_at)} + {#if $devopsRole} + {workspace_id ? workspace_id : 'global'} + {/if} + +
+ {#if !acknowledged} + + {:else} + + {/if} +
+
+
+ {/if} + {/each} + + {/if} +
+
diff --git a/frontend/src/lib/components/sidebar/SideBarNotification.svelte b/frontend/src/lib/components/sidebar/SideBarNotification.svelte index d935769972..4e9ea4ba0f 100644 --- a/frontend/src/lib/components/sidebar/SideBarNotification.svelte +++ b/frontend/src/lib/components/sidebar/SideBarNotification.svelte @@ -1,14 +1,12 @@ {#if !small} -
- {notificationCount > 9 ? '9+' : notificationCount} -
+ {:else}
{/if} diff --git a/frontend/src/lib/components/sidebar/changelogs.ts b/frontend/src/lib/components/sidebar/changelogs.ts index 8de6be0fe4..478c2dc64b 100644 --- a/frontend/src/lib/components/sidebar/changelogs.ts +++ b/frontend/src/lib/components/sidebar/changelogs.ts @@ -5,6 +5,71 @@ export type Changelog = { } const changelogs: Changelog[] = [ + { + label: 'Full text search on jobs and logs', + href: 'https://www.windmill.dev/changelog/instant-full-text-search-on-jobs-and-logs', + date: '2024-12-05' + }, + { + label: 'Force dark/light theme in apps', + href: 'https://www.windmill.dev/changelog/force-dark-light-theme', + date: '2024-11-28' + }, + { + label: 'Kafka triggers', + href: 'https://www.windmill.dev/changelog/kafka-triggers', + date: '2024-11-18' + }, + { + label: 'Critical channels in UI', + href: 'https://www.windmill.dev/changelog/critical-channels-ui', + date: '2024-11-15' + }, + { + label: 'Support for Mistral and Anthropic AI models', + href: 'https://www.windmill.dev/changelog/mistral-anthropic-support', + date: '2024-11-14' + }, + { + label: 'Websocket triggers', + href: 'https://www.windmill.dev/changelog/websocket-triggers', + date: '2024-11-06' + }, + { + label: 'Autoscaling', + href: 'https://www.windmill.dev/changelog/autoscaling', + date: '2024-10-28' + }, + { + label: 'File download helper', + href: 'https://www.windmill.dev/changelog/file-download-helper', + date: '2024-10-12' + }, + { + label: 'Queue metric alerts', + href: 'https://www.windmill.dev/changelog/queue-metric-alerts', + date: '2024-10-10' + }, + { + label: 'Deno 2.0', + href: 'https://www.windmill.dev/changelog/deno-2.0', + date: '2024-10-10' + }, + { + label: 'Move components inside containers with ctrl+click', + href: 'https://www.windmill.dev/changelog/move-components-inside-containers-with-ctrl', + date: '2024-10-09' + }, + { + label: 'Support workers to run natively on Windows', + href: 'https://www.windmill.dev/changelog/workers-run-natively-windows', + date: '2024-10-03' + }, + { + label: 'Quick access menu for faster component insertion', + href: 'https://www.windmill.dev/changelog/flow-quick-access-menu', + date: '2024-10-03' + }, { label: 'Custom HTTP routes', href: 'https://www.windmill.dev/changelog/http-routing', @@ -22,7 +87,7 @@ const changelogs: Changelog[] = [ }, { label: 'Critical alert channels', - href: 'https://www.windmill.dev/changelog/critical-alert-channels', + href: 'https://www.windmill.dev/changelog/critical-alerts', date: '2024-09-01' }, { diff --git a/frontend/src/lib/components/splitPanes/SplitPanesOrColumnOnMobile.svelte b/frontend/src/lib/components/splitPanes/SplitPanesOrColumnOnMobile.svelte index a609b74637..39191e6ade 100644 --- a/frontend/src/lib/components/splitPanes/SplitPanesOrColumnOnMobile.svelte +++ b/frontend/src/lib/components/splitPanes/SplitPanesOrColumnOnMobile.svelte @@ -11,27 +11,27 @@ let clientWidth = window.innerWidth -
- {#if clientWidth >= 768} -