Compare commits
40 Commits
feat/php-v
...
glm/add-lo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
632c8868bc | ||
|
|
911df958e7 | ||
|
|
fb2bdc6a53 | ||
|
|
1503bf948e | ||
|
|
039b79dfe6 | ||
|
|
efb4a27d51 | ||
|
|
51957f7d92 | ||
|
|
533609989f | ||
|
|
88ad376791 | ||
|
|
f2f178eb31 | ||
|
|
c4be206c5a | ||
|
|
0e022b14fd | ||
|
|
b2c1e3de0a | ||
|
|
ff78f448be | ||
|
|
4e0b6db4ea | ||
|
|
041e1dcf82 | ||
|
|
49f943b51d | ||
|
|
75b191b3ad | ||
|
|
4e59a1a166 | ||
|
|
278c8fe416 | ||
|
|
446afb5b36 | ||
|
|
fd7f0d3da9 | ||
|
|
7de98c0df4 | ||
|
|
1bca2e931b | ||
|
|
0ab9a81e20 | ||
|
|
c0edbe4317 | ||
|
|
a8fa0cccef | ||
|
|
f2334e6564 | ||
|
|
f4489cbe64 | ||
|
|
2171cc8e0a | ||
|
|
1cfb40bdaa | ||
|
|
bee928276e | ||
|
|
391da1d5af | ||
|
|
9ca86f7a11 | ||
|
|
19129aa019 | ||
|
|
435de95e7d | ||
|
|
997dd6ac3a | ||
|
|
9a6ce44c84 | ||
|
|
66a8e844a6 | ||
|
|
e0857421aa |
10
.webmux.yaml
10
.webmux.yaml
@@ -44,7 +44,7 @@ profiles:
|
||||
- Pane 1: backend (cargo watch -x run)
|
||||
- Pane 2: frontend (npm run dev)
|
||||
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (backend) or \`tmux capture-pane -t .2 -p -S -50\` (frontend).
|
||||
When restarting backend or frontend, make sure to use ${BACKEND_PORT} and ${FRONTEND_PORT}.
|
||||
For this window specifically, backend is running on: ${BACKEND_PORT} and frontend is running on: ${FRONTEND_PORT}.
|
||||
To connect to the database, use this connection string: ${DATABASE_URL}
|
||||
Because we are running backend with cargo watch, to verify your changes, just check the logs in the backend pane. No need for cargo check.
|
||||
IMPORTANT: Read docs/autonomous-mode.md before starting any work.
|
||||
@@ -55,11 +55,11 @@ profiles:
|
||||
- id: backend
|
||||
kind: command
|
||||
split: right
|
||||
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/backend" && PORT=${BACKEND_PORT:-8000} cargo watch -x "run ${CARGO_FEATURES:+--features $CARGO_FEATURES}"
|
||||
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/backend" && cargo watch -x "run ${CARGO_FEATURES:+--features $CARGO_FEATURES}"
|
||||
- id: frontend
|
||||
kind: command
|
||||
split: bottom
|
||||
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0
|
||||
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && npm run dev -- --host 0.0.0.0
|
||||
|
||||
frontendOnly:
|
||||
runtime: host
|
||||
@@ -71,7 +71,7 @@ profiles:
|
||||
- Pane 0: this pane (claude agent)
|
||||
- Pane 1: frontend (npm run dev)
|
||||
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (frontend).
|
||||
When restarting frontend, make sure to use ${FRONTEND_PORT}.
|
||||
On this window specifically, frontend is running on: ${FRONTEND_PORT}.
|
||||
To connect to the database, use this connection string: ${DATABASE_URL}
|
||||
Because we are running frontend with npm run dev, to verify your changes, just check the logs in the frontend pane. No need for npm run build.
|
||||
IMPORTANT: Read docs/autonomous-mode.md before starting any work.
|
||||
@@ -82,7 +82,7 @@ profiles:
|
||||
- id: frontend
|
||||
kind: command
|
||||
split: right
|
||||
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0
|
||||
command: ROOT="$(git rev-parse --show-toplevel)"; cd "$ROOT/frontend" && npm run generate-backend-client && npm run dev -- --host 0.0.0.0
|
||||
|
||||
agentOnly:
|
||||
runtime: host
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
main_branch: main
|
||||
|
||||
merge_strategy: rebase
|
||||
# worktree_dir: .worktrees
|
||||
|
||||
worktree_naming: basename
|
||||
|
||||
worktree_prefix: ""
|
||||
|
||||
# Default: "wm-"
|
||||
window_prefix: "wm-"
|
||||
|
||||
auto_name:
|
||||
model: "gemini-2.5-flash-lite"
|
||||
system_prompt: |
|
||||
Generate a concise git branch name based on the task description.
|
||||
|
||||
Rules:
|
||||
- Use kebab-case (lowercase with hyphens)
|
||||
- Keep it short: 1-3 words, max 4 if necessary
|
||||
- Focus on the core task/feature, not implementation details
|
||||
- No prefixes like feat/, fix/, chore/
|
||||
|
||||
Examples of good branch names:
|
||||
- "Add dark mode toggle" → dark-mode
|
||||
- "Fix the search results not showing" → fix-search
|
||||
- "Refactor the authentication module" → auth-refactor
|
||||
- "Add CSV export to reports" → export-csv
|
||||
- "Shell completion is broken" → shell-completion
|
||||
|
||||
Output ONLY the branch name, nothing else.
|
||||
background: true
|
||||
|
||||
|
||||
# Commands to run in new worktree before tmux window opens.
|
||||
# These block window creation - use for short tasks only.
|
||||
# Use "<global>" to inherit from global config.
|
||||
# Set to empty list to disable: `post_create: []`
|
||||
# post_create:
|
||||
# - "<global>"
|
||||
# - mise use
|
||||
post_create:
|
||||
- ./scripts/worktree-env
|
||||
|
||||
pre_remove:
|
||||
- ./scripts/worktree-cleanup
|
||||
|
||||
panes:
|
||||
- command: >-
|
||||
claude --dangerously-skip-permissions --append-system-prompt
|
||||
"You are running inside a tmux session with other panes running services.\n
|
||||
Pane layout (current window):\n
|
||||
- Pane 0: this pane (claude agent)\n
|
||||
- Pane 1: backend (cargo watch -x run)\n
|
||||
- Pane 2: frontend (npm run dev)\n\n
|
||||
To check logs, use: \`tmux capture-pane -t .1 -p -S -50\` (backend) or \`tmux capture-pane -t .2 -p -S -50\` (frontend).\n
|
||||
When restarting backend or frontend, make sure to use the ports listed in .env.local.\n
|
||||
Because we are running backend with cargo watch, to verify your changes, just check the logs in the backend pane. No need for cargo check.\n\n
|
||||
IMPORTANT: Read docs/autonomous-mode.md before starting any work."
|
||||
focus: true
|
||||
- command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/backend" && PORT=${BACKEND_PORT:-8000} cargo watch -x "run ${CARGO_FEATURES:+--features $CARGO_FEATURES}"'
|
||||
split: horizontal
|
||||
- command: 'ROOT="$(git rev-parse --show-toplevel)"; [ -f "$ROOT/.env.local" ] && source "$ROOT/.env.local"; cd "$ROOT/frontend" && npm run generate-backend-client && REMOTE=${REMOTE:-http://localhost:${BACKEND_PORT:-8000}} npm run dev -- --port ${FRONTEND_PORT:-3000} --host 0.0.0.0'
|
||||
split: vertical
|
||||
|
||||
files:
|
||||
copy:
|
||||
- backend/.env
|
||||
- scripts/
|
||||
- wm-ts-nav/target/release/wm-ts-nav
|
||||
|
||||
sandbox:
|
||||
enabled: false
|
||||
toolchain: off
|
||||
57
CHANGELOG.md
57
CHANGELOG.md
@@ -1,5 +1,62 @@
|
||||
# Changelog
|
||||
|
||||
## [1.662.0](https://github.com/windmill-labs/windmill/compare/v1.661.0...v1.662.0) (2026-03-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* mcp oauth gateway ([#8443](https://github.com/windmill-labs/windmill/issues/8443)) ([51957f7](https://github.com/windmill-labs/windmill/commit/51957f7d921b624fc132ca9ea03cdd30a5810e51))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* replace email with permissioned_as for triggers/schedules ([#8439](https://github.com/windmill-labs/windmill/issues/8439)) ([efb4a27](https://github.com/windmill-labs/windmill/commit/efb4a27d5181bf9db3deb5e8100ec60adbe45e7f))
|
||||
* strip invalid enum values from MCP schemas ([#8462](https://github.com/windmill-labs/windmill/issues/8462)) ([88ad376](https://github.com/windmill-labs/windmill/commit/88ad3767916b86c4e0b272d040ee0b75a0580d76))
|
||||
|
||||
## [1.661.0](https://github.com/windmill-labs/windmill/compare/v1.660.1...v1.661.0) (2026-03-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add OTel metrics support ([#8442](https://github.com/windmill-labs/windmill/issues/8442)) ([7de98c0](https://github.com/windmill-labs/windmill/commit/7de98c0df464d8a7c9cf5d04228753294183f759))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix datatable setup on RDS ([#8450](https://github.com/windmill-labs/windmill/issues/8450)) ([446afb5](https://github.com/windmill-labs/windmill/commit/446afb5b36211e5cbe8a279ce68f2f790a5953b9))
|
||||
* full code apps deployable on merge UI and deploy UI ([#8451](https://github.com/windmill-labs/windmill/issues/8451)) ([0e022b1](https://github.com/windmill-labs/windmill/commit/0e022b14fd36e897106219010917bd7ceabf4078))
|
||||
* improve DND drag feedback in EditableSchemaForm ([#8449](https://github.com/windmill-labs/windmill/issues/8449)) ([fd7f0d3](https://github.com/windmill-labs/windmill/commit/fd7f0d3da9153d91c15df5847aaae51e67479cde))
|
||||
* prevent raw app iframe reload on userStore refresh ([#8455](https://github.com/windmill-labs/windmill/issues/8455)) ([4e59a1a](https://github.com/windmill-labs/windmill/commit/4e59a1a166847045897a6b576812bb53546e683b))
|
||||
* resolve blank inline script panel for components with underscores in ID ([#8457](https://github.com/windmill-labs/windmill/issues/8457)) ([b2c1e3d](https://github.com/windmill-labs/windmill/commit/b2c1e3de0a263f606127f0decedb11a2ce0b822b))
|
||||
|
||||
## [1.660.1](https://github.com/windmill-labs/windmill/compare/v1.660.0...v1.660.1) (2026-03-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* prevent S3 file browser crash when selecting storage ([#8444](https://github.com/windmill-labs/windmill/issues/8444)) ([a8fa0cc](https://github.com/windmill-labs/windmill/commit/a8fa0cccef870f841c68be77832d9be12109badb))
|
||||
* schema inference not updating on reset and language switch ([#8446](https://github.com/windmill-labs/windmill/issues/8446)) ([c0edbe4](https://github.com/windmill-labs/windmill/commit/c0edbe431773f878201e96a79ce291d4b37a10bb))
|
||||
|
||||
## [1.660.0](https://github.com/windmill-labs/windmill/compare/v1.659.1...v1.660.0) (2026-03-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **cli:** use local scripts when previewing flows ([#8365](https://github.com/windmill-labs/windmill/issues/8365)) ([435de95](https://github.com/windmill-labs/windmill/commit/435de95e7d5c9433dafac5369cfc533fd738fc22))
|
||||
* MCP server readiness for Anthropic connectors directory ([#8438](https://github.com/windmill-labs/windmill/issues/8438)) ([1cfb40b](https://github.com/windmill-labs/windmill/commit/1cfb40bdaa877f1616fc1c1cf5fb6b6aa1832b86))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* exclude wm_deployers group from CE group limit check ([#8429](https://github.com/windmill-labs/windmill/issues/8429)) ([9a6ce44](https://github.com/windmill-labs/windmill/commit/9a6ce44c8414810292ebc8a1ae64950ee2c76307))
|
||||
* prevent AI agent tool jobs from becoming zombies on cancellation ([#8437](https://github.com/windmill-labs/windmill/issues/8437)) ([f4489cb](https://github.com/windmill-labs/windmill/commit/f4489cbe645489a892994c70d17df2284b494568))
|
||||
* show cancelled WAC jobs as done in workflow timeline ([#8436](https://github.com/windmill-labs/windmill/issues/8436)) ([bee9282](https://github.com/windmill-labs/windmill/commit/bee928276e098ce7b17e20af74e34458e5c5353e))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* cache composer vendor dir to skip reinstall on repeated php executions ([#8330](https://github.com/windmill-labs/windmill/issues/8330)) ([66a8e84](https://github.com/windmill-labs/windmill/commit/66a8e844a64d91d57dcabb7ad31d9308dec99032))
|
||||
|
||||
## [1.659.1](https://github.com/windmill-labs/windmill/compare/v1.659.0...v1.659.1) (2026-03-18)
|
||||
|
||||
|
||||
|
||||
14
Caddyfile
14
Caddyfile
@@ -11,18 +11,8 @@
|
||||
{$BASE_URL} {
|
||||
bind {$ADDRESS}
|
||||
|
||||
# LSP - Language Server Protocol for code intelligence (windmill_extra:3001)
|
||||
reverse_proxy /ws/* http://windmill_extra:3001
|
||||
|
||||
# Multiplayer - Real-time collaboration, Enterprise Edition (windmill_extra:3002)
|
||||
# Uncomment and set ENABLE_MULTIPLAYER=true in docker-compose.yml
|
||||
# reverse_proxy /ws_mp/* http://windmill_extra:3002
|
||||
|
||||
# Debugger - Interactive debugging via DAP WebSocket (windmill_extra:3003)
|
||||
# Set ENABLE_DEBUGGER=true in docker-compose.yml to enable
|
||||
handle_path /ws_debug/* {
|
||||
reverse_proxy http://windmill_extra:3003
|
||||
}
|
||||
# Extra services: LSP, Multiplayer, Debugger (windmill_extra gateway)
|
||||
reverse_proxy /ws/* /ws_mp/* /ws_debug/* http://windmill_extra:3000
|
||||
|
||||
# Search indexer, Enterprise Edition (windmill_indexer:8002)
|
||||
# reverse_proxy /api/srch/* http://windmill_indexer:8002
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, email, server_id, error\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, permissioned_as, server_id, error\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -18,5 +18,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1d4bb4f53574ef95ef1016b760f849ec2372ac6a21bb2556d17a96dc72ea4980"
|
||||
"hash": "02748cae17e8966dbd57a33017ccb747c84fcc12fbfd93c6c749570b94d35696"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, email, mode\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::trigger_mode)\n ",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, permissioned_as, mode\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8::trigger_mode)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -28,5 +28,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7c1ae9cac13d1387cfa94149f039054dd8c30c16b4657e73cdb0d7c7f1cb3b6d"
|
||||
"hash": "02e04f9ebc0e14f98f290bf2dc3eb00bc613ba7d29f8dd5ff31a4acd0ef3adfd"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, email\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -18,5 +18,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1cad2ebfbdc46f9c0d93329897a71701f17a33b708b334d909563c9a0dcc9c23"
|
||||
"hash": "066c9690d1606bf889879b7e3c686529c37db0d5f18c83706bfbc63c8c3e4315"
|
||||
}
|
||||
35
backend/.sqlx/query-0f26c74f604e1c3c613de8ba654cac1a41b20b1d3ea0f1a1c4ea2fcbbd314d7e.json
generated
Normal file
35
backend/.sqlx/query-0f26c74f604e1c3c613de8ba654cac1a41b20b1d3ea0f1a1c4ea2fcbbd314d7e.json
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT email, permissioned_as, edited_by FROM schedule WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "edited_by",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "0f26c74f604e1c3c613de8ba654cac1a41b20b1d3ea0f1a1c4ea2fcbbd314d7e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, email, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14)\n ",
|
||||
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, permissioned_as, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -50,5 +50,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6afa076744233fc5e92188ff978990fa3a704afe3eec523f4e203f7f6e247261"
|
||||
"hash": "13d60d85694b5a5fcfc7687a07b78a54ff53245271466b1f3a9d9edf43cdaa1f"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE email_trigger\n SET\n script_path = $1,\n path = $2,\n is_flow = $3,\n local_part = $4,\n workspaced_local_part = $5,\n edited_by = $6,\n email = $7,\n edited_at = now(),\n error_handler_path = $8,\n error_handler_args = $9,\n retry = $10,\n mode = $11\n WHERE\n workspace_id = $12 AND path = $13\n ",
|
||||
"query": "\n UPDATE email_trigger\n SET\n script_path = $1,\n path = $2,\n is_flow = $3,\n local_part = $4,\n workspaced_local_part = $5,\n edited_by = $6,\n permissioned_as = $7,\n edited_at = now(),\n error_handler_path = $8,\n error_handler_args = $9,\n retry = $10,\n mode = $11\n WHERE\n workspace_id = $12 AND path = $13\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -33,5 +33,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "388ff2abd495cf71e87cf0c4ddc73b6c84867fb966df91b320c54acdd5e61315"
|
||||
"hash": "1b7803a2060a19cb6e71f1e97619891ea8449b4a9433908cf717738846f7eec5"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE sqs_trigger\n SET\n queue_url = $1,\n aws_resource_path = $2,\n message_attributes = $3,\n aws_auth_resource_type = $4,\n script_path = $5,\n path = $6,\n is_flow = $7,\n edited_by = $8,\n email = $9,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $12,\n error_handler_args = $13,\n retry = $14\n WHERE\n workspace_id = $10 AND path = $11\n ",
|
||||
"query": "\n UPDATE sqs_trigger\n SET\n queue_url = $1,\n aws_resource_path = $2,\n message_attributes = $3,\n aws_auth_resource_type = $4,\n script_path = $5,\n path = $6,\n is_flow = $7,\n edited_by = $8,\n permissioned_as = $9,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $12,\n error_handler_args = $13,\n retry = $14\n WHERE\n workspace_id = $10 AND path = $11\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -33,5 +33,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c723c3a5066a487b93e2642993f3bf624a1f50d06c7de75157420d97cf144763"
|
||||
"hash": "1d2514b3d75ffb6cc0eb09ceb8fde974a07881eb08164e885448d9a17c4ca6db"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO email_trigger (\n path, local_part, workspaced_local_part, script_path,\n is_flow, workspace_id, edited_by, email\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
|
||||
"query": "\n INSERT INTO email_trigger (\n path, local_part, workspaced_local_part, script_path,\n is_flow, workspace_id, edited_by, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -17,5 +17,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1074c6c98e6a0c83ac04172a39abea21c793f58947051d39931d4da0868a1d77"
|
||||
"hash": "1e28751bb98a1c477c0e582a2a39f81bf34e2d72f35ef1ea5d8c057ec9e694d8"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, email, auto_commit\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n ",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id, script_path,\n is_flow, workspace_id, edited_by, permissioned_as, auto_commit\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -19,5 +19,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "45fc21026fa76e5d69f00a68a7be81abb3ec627578f2d14f0ce33896dc6ab4cf"
|
||||
"hash": "1ef63255389bdc47d5392a84aad38adf1ccc3a4923f988d8abaafc9749307c0e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n mqtt_trigger\n SET\n mqtt_resource_path = $1,\n subscribe_topics = $2,\n client_version = $3,\n client_id = $4,\n v3_config = $5,\n v5_config = $6,\n is_flow = $7,\n edited_by = $8,\n email = $9,\n script_path = $10,\n path = $11,\n edited_at = now(),\n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND\n path = $13\n ",
|
||||
"query": "\n UPDATE\n mqtt_trigger\n SET\n mqtt_resource_path = $1,\n subscribe_topics = $2,\n client_version = $3,\n client_id = $4,\n v3_config = $5,\n v5_config = $6,\n is_flow = $7,\n edited_by = $8,\n permissioned_as = $9,\n script_path = $10,\n path = $11,\n edited_at = now(),\n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND\n path = $13\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -35,5 +35,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e486a64b76da5de97e404c81dd6e29d333ada2dcfbbddb028f37794b85778ca8"
|
||||
"hash": "1f693e2fba9885f7fc49bd2994240c4421fdae4aab496a3397c5c07d960582f2"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO postgres_trigger (\n workspace_id,\n path,\n postgres_resource_path,\n replication_slot_name,\n publication_name,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ",
|
||||
"query": "\n INSERT INTO postgres_trigger (\n workspace_id,\n path,\n postgres_resource_path,\n replication_slot_name,\n publication_name,\n script_path,\n is_flow,\n mode,\n edited_by,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -33,5 +33,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "fb942aa7894b4ae904f0233405f62f201e3f5deed593128017b886342ef6d210"
|
||||
"hash": "21d7ce033b5f67499f579aeae98806599401fcfa9765a58b8cd45a20b411d0ca"
|
||||
}
|
||||
34
backend/.sqlx/query-270cfaea4f888e73e21a957e0328ec1f990fce409160eb7b0e807bff56defff4.json
generated
Normal file
34
backend/.sqlx/query-270cfaea4f888e73e21a957e0328ec1f990fce409160eb7b0e807bff56defff4.json
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT created_by, permissioned_as, permissioned_as_email\n FROM v2_job\n WHERE workspace_id = 'test-workspace'\n AND trigger_kind = 'schedule'\n AND trigger = $1\n ORDER BY created_at DESC\n LIMIT 1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "permissioned_as_email",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "270cfaea4f888e73e21a957e0328ec1f990fce409160eb7b0e807bff56defff4"
|
||||
}
|
||||
16
backend/.sqlx/query-29935e89475f637d765c516f1aa2be2f0f31fb50d519b42a056d0d73417599a3.json
generated
Normal file
16
backend/.sqlx/query-29935e89475f637d765c516f1aa2be2f0f31fb50d519b42a056d0d73417599a3.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE v2_job_completed SET\n workflow_as_code_status = jsonb_set(\n jsonb_set(\n workflow_as_code_status,\n array[$1],\n COALESCE(workflow_as_code_status->$1, '{}'::jsonb)\n ),\n array[$1, 'duration_ms'],\n to_jsonb($2::bigint)\n )\n WHERE id = $3 AND workflow_as_code_status IS NOT NULL",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "29935e89475f637d765c516f1aa2be2f0f31fb50d519b42a056d0d73417599a3"
|
||||
}
|
||||
17
backend/.sqlx/query-3630aa84c1e84418e0f644a71849aea87a85c7199a5d3fa2a43f0e91b0a5be9d.json
generated
Normal file
17
backend/.sqlx/query-3630aa84c1e84418e0f644a71849aea87a85c7199a5d3fa2a43f0e91b0a5be9d.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO app_bundles (app_version_id, w_id, file_type, data)\n VALUES ($1, $2, $3, $4)",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Bytea"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3630aa84c1e84418e0f644a71849aea87a85c7199a5d3fa2a43f0e91b0a5be9d"
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT email, edited_by FROM websocket_trigger WHERE path = $1 AND workspace_id = $2",
|
||||
"query": "SELECT permissioned_as, edited_by FROM websocket_trigger WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "email",
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
@@ -25,5 +25,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "075d4749299af2cb81162bf396bec6aa89de43ec201c911196763e03e644ca7a"
|
||||
"hash": "39062cdb183b97906c25602000321a76d4e629ba027364190a2487cc9bd93235"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO email_trigger (\n workspace_id,\n path,\n script_path,\n is_flow,\n local_part,\n workspaced_local_part,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry,\n mode\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, now(), $9, $10, $11, $12\n )\n ",
|
||||
"query": "\n INSERT INTO email_trigger (\n workspace_id,\n path,\n script_path,\n is_flow,\n local_part,\n workspaced_local_part,\n edited_by,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry,\n mode\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, now(), $9, $10, $11, $12\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -32,5 +32,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "3d763dbb411e28ce026cc9ab525b20b409cfe17bf1cdef22aaffc719cf6c53e3"
|
||||
"hash": "3aa3d0362fa8ed97dd034454b05f14880c9bb43d0c3ce4ea9b4511aa2d092d0c"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO http_trigger (\n workspace_id,\n path,\n route_path,\n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path,\n summary,\n description,\n is_flow,\n mode,\n request_type,\n authentication_method,\n http_method,\n static_asset_config,\n edited_by,\n email,\n edited_at,\n is_static_website,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, now(), $20, $21, $22, $23\n )\n ",
|
||||
"query": "\n INSERT INTO http_trigger (\n workspace_id,\n path,\n route_path,\n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path,\n summary,\n description,\n is_flow,\n mode,\n request_type,\n authentication_method,\n http_method,\n static_asset_config,\n edited_by,\n permissioned_as,\n edited_at,\n is_static_website,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, now(), $20, $21, $22, $23\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -81,5 +81,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ee9ee0fbf5dd72d190e18c56622244b984fdb36fbf70197d9f8cb6306c9670db"
|
||||
"hash": "41ad9954bebe31b0545837147b1616da2e6743f3a45775cd556a7414bc8f9726"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE nats_trigger\n SET\n nats_resource_path = $1,\n subjects = $2,\n stream_name = $3,\n consumer_name = $4,\n use_jetstream = $5,\n script_path = $6,\n path = $7,\n is_flow = $8,\n edited_by = $9,\n email = $10,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $13,\n error_handler_args = $14,\n retry = $15\n WHERE\n workspace_id = $11 AND path = $12\n ",
|
||||
"query": "\n UPDATE nats_trigger\n SET\n nats_resource_path = $1,\n subjects = $2,\n stream_name = $3,\n consumer_name = $4,\n use_jetstream = $5,\n script_path = $6,\n path = $7,\n is_flow = $8,\n edited_by = $9,\n permissioned_as = $10,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $13,\n error_handler_args = $14,\n retry = $15\n WHERE\n workspace_id = $11 AND path = $12\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -24,5 +24,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9f41ea5cbe4cffa74e4a283fe8f023c813e349956487f7b6599da452c068e9b9"
|
||||
"hash": "4ba114f54ed88c27dd4aed05a273c91e98913397b310ff760c1dbb9077764e9b"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = COALESCE($24, email),\n edited_by = $25\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"query": "\n UPDATE schedule SET\n schedule = $1,\n timezone = $2,\n args = $3,\n on_failure = $4,\n on_failure_times = $5,\n on_failure_exact = $6,\n on_failure_extra_args = $7,\n on_recovery = $8,\n on_recovery_times = $9,\n on_recovery_extra_args = $10,\n on_success = $11,\n on_success_extra_args = $12,\n ws_error_handler_muted = $13,\n retry = $14,\n summary = $15,\n no_flow_overlap = $16,\n tag = $17,\n paused_until = $18,\n path = $19,\n workspace_id = $20,\n cron_version = COALESCE($21, cron_version),\n description = $22,\n dynamic_skip = $23,\n email = $24,\n edited_by = $25,\n permissioned_as = $26\n WHERE path = $19 AND workspace_id = $20\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -65,96 +65,101 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "on_failure",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "on_failure_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "on_failure_exact",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "on_failure_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 18,
|
||||
"name": "on_recovery",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 19,
|
||||
"name": "on_recovery_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 20,
|
||||
"name": "on_recovery_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 21,
|
||||
"name": "on_success",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 22,
|
||||
"name": "on_success_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 23,
|
||||
"name": "ws_error_handler_muted",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 24,
|
||||
"name": "retry",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 25,
|
||||
"name": "no_flow_overlap",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 26,
|
||||
"name": "summary",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 27,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 28,
|
||||
"name": "tag",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 29,
|
||||
"name": "paused_until",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 30,
|
||||
"name": "cron_version",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 31,
|
||||
"name": "dynamic_skip",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
@@ -185,6 +190,7 @@
|
||||
"Text",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
@@ -201,6 +207,7 @@
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -222,5 +229,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "987d79f7c6d7bc148cc8aab67e47161cfca045966e995e28c7a7ad090cffeda0"
|
||||
"hash": "54b4c762add9b1ebfdb2a6d5abd6d20e86dc0e6544f0bb22fa4ec68aa54a4dc8"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n http_trigger\n SET\n route_path = $1,\n route_path_key = $2,\n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n mode = $10,\n http_method = $11,\n static_asset_config = $12,\n edited_by = $13,\n email = $14,\n request_type = $15,\n authentication_method = $16,\n summary = $17,\n description = $18,\n edited_at = now(),\n is_static_website = $19,\n error_handler_path = $20,\n error_handler_args = $21,\n retry = $22\n WHERE\n workspace_id = $23 AND\n path = $24\n ",
|
||||
"query": "\n UPDATE\n http_trigger\n SET\n route_path = $1,\n route_path_key = $2,\n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n mode = $10,\n http_method = $11,\n static_asset_config = $12,\n edited_by = $13,\n permissioned_as = $14,\n request_type = $15,\n authentication_method = $16,\n summary = $17,\n description = $18,\n edited_at = now(),\n is_static_website = $19,\n error_handler_path = $20,\n error_handler_args = $21,\n retry = $22\n WHERE\n workspace_id = $23 AND\n path = $24\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -82,5 +82,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "2fd0d3224382b000028d98b0af4c431d3cadd54cca65d83c1ab7f2d2972e2282"
|
||||
"hash": "5efbf92ac7347e73769c66ffdc4037c7e56a5939d9ffcbee13e0264cbb2a6dfe"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n script_path,\n is_flow,\n mode as \"mode: _\",\n workspace_id,\n path,\n edited_by,\n email,\n delivery_config AS \"delivery_config: _\",\n retry as \"retry: _\",\n error_handler_path,\n error_handler_args as \"error_handler_args: _\"\n FROM\n gcp_trigger\n WHERE\n workspace_id = $1 AND\n path = $2 AND\n delivery_type = 'push'::DELIVERY_MODE\n ",
|
||||
"query": "\n SELECT\n gcp_resource_path,\n script_path,\n is_flow,\n mode as \"mode: _\",\n workspace_id,\n path,\n edited_by,\n permissioned_as,\n delivery_config AS \"delivery_config: _\",\n retry as \"retry: _\",\n error_handler_path,\n error_handler_args as \"error_handler_args: _\"\n FROM\n gcp_trigger\n WHERE\n workspace_id = $1 AND\n path = $2 AND\n delivery_type = 'push'::DELIVERY_MODE\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -51,7 +51,7 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "email",
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
@@ -96,5 +96,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1cf2eb1426e8be89c3649272103bcd029e99b029b7f1b71eda4411d1e24e790d"
|
||||
"hash": "6cfa6b5f16207863a03b77fffbb94ae91872a207ea2362e49e100ed1f0b35198"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, email\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -16,5 +16,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "57b7236cae0b6a1940f4c2d4b202692450ee231488d9a55ca59ff53a6f674626"
|
||||
"hash": "72bc0b4acb3fb155436df74b0bb11600df8e55d50fa48d21ce2c5ae82eaf9f1c"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n path,\n script_path,\n is_flow,\n route_path,\n authentication_resource_path,\n workspace_id,\n request_type AS \"request_type: _\",\n authentication_method AS \"authentication_method: _\",\n edited_by,\n email,\n static_asset_config AS \"static_asset_config: _\",\n wrap_body,\n raw_string,\n workspaced_route,\n is_static_website,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\",\n mode as \"mode: _\"\n FROM\n http_trigger\n WHERE\n http_method = $1 AND\n (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ",
|
||||
"query": "\n SELECT\n path,\n script_path,\n is_flow,\n route_path,\n authentication_resource_path,\n workspace_id,\n request_type AS \"request_type: _\",\n authentication_method AS \"authentication_method: _\",\n edited_by,\n permissioned_as,\n static_asset_config AS \"static_asset_config: _\",\n wrap_body,\n raw_string,\n workspaced_route,\n is_static_website,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\",\n mode as \"mode: _\"\n FROM\n http_trigger\n WHERE\n http_method = $1 AND\n (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -75,7 +75,7 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "email",
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
@@ -175,5 +175,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "3cd37daa80bc3697d331c19e01a49916fc03fdf9eceff73fa153020b4a48f4a2"
|
||||
"hash": "7402639802ba5f286db8436d21dcce24aa615fe1db7e8b4fc468963216aab69d"
|
||||
}
|
||||
18
backend/.sqlx/query-75b9a2153ba12ef443212d83743605e0f9d8c1f0fbe3271f0e65b1dc5cd47987.json
generated
Normal file
18
backend/.sqlx/query-75b9a2153ba12ef443212d83743605e0f9d8c1f0fbe3271f0e65b1dc5cd47987.json
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO nats_trigger (path, nats_resource_path, subjects, use_jetstream, script_path, is_flow, workspace_id, edited_by, permissioned_as) VALUES ($1, $2, $3, $4, $5, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Bool",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "75b9a2153ba12ef443212d83743605e0f9d8c1f0fbe3271f0e65b1dc5cd47987"
|
||||
}
|
||||
35
backend/.sqlx/query-80844305a8339ca453c7afe8ffaa0aee765b15e0d4cb917f79d82a2fec60dc1e.json
generated
Normal file
35
backend/.sqlx/query-80844305a8339ca453c7afe8ffaa0aee765b15e0d4cb917f79d82a2fec60dc1e.json
generated
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT app_version_id, file_type, data FROM app_bundles\n WHERE app_version_id = ANY($1) AND w_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "app_version_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "file_type",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "data",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8Array",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "80844305a8339ca453c7afe8ffaa0aee765b15e0d4cb917f79d82a2fec60dc1e"
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT email, edited_by FROM schedule WHERE path = $1 AND workspace_id = $2",
|
||||
"query": "SELECT permissioned_as, edited_by FROM schedule WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "email",
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
@@ -25,5 +25,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "17aafb72843659df9594d6d2466d2afaf26e666ffe52e0ea85792ea31b63410c"
|
||||
"hash": "893ff34f2b22cf89a24a0b613ed390077fe6c75f56a3419f530e542bab0fb1a4"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, email, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string, mode\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14, $15::trigger_mode)\n ",
|
||||
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, permissioned_as, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string, mode\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14, $15::trigger_mode)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -62,5 +62,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "57eca702e951f5303a74643c7ba64472e2c2a781fbb6366d998a0f1ca22fcdf2"
|
||||
"hash": "8d53b0f2df5fdb6c43b9f5e92c8a97669676249dbf512ff39db94ecacc51e04e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, edited_by, schedule, enabled,\n script_path, is_flow, email, timezone\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, edited_by, schedule, enabled,\n script_path, is_flow, email, timezone, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -13,10 +13,11 @@
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "75ce9753a8acc8eccb3f2e0baaa5a871f866a8d21961fac8a003bc40c406ee79"
|
||||
"hash": "906773b0de209734e9cf9c6421d4b3bf95ed2e00ac07942d8bba2b11b8462d3b"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n mode,\n filters,\n initial_messages,\n url_runnable_args,\n edited_by,\n can_return_message,\n can_return_error_result,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, now(), $14, $15, $16\n )\n ",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n mode,\n filters,\n initial_messages,\n url_runnable_args,\n edited_by,\n can_return_message,\n can_return_error_result,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, now(), $14, $15, $16\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -36,5 +36,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e99c958628d83e3fea054eaa182b1301e0b04c7eb9801d9de13b37bd908d902f"
|
||||
"hash": "942c0abb55c910862fd45d3fa56a4eb6729f1a658101bda2d0b0fca96b3cfee5"
|
||||
}
|
||||
22
backend/.sqlx/query-9517395ac7230ab7c40c03ddd2a95fd6118b329a4421c9e8022df90ff7e775c8.json
generated
Normal file
22
backend/.sqlx/query-9517395ac7230ab7c40c03ddd2a95fd6118b329a4421c9e8022df90ff7e775c8.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM script s WHERE s.workspace_id = $1 AND s.hash NOT IN (\n SELECT DISTINCT ON (path) hash FROM script\n WHERE workspace_id = $1 AND deleted = false AND draft_only IS NOT TRUE\n ORDER BY path, created_at DESC\n )",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "9517395ac7230ab7c40c03ddd2a95fd6118b329a4421c9e8022df90ff7e775c8"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO nats_trigger (\n path, nats_resource_path, subjects, script_path,\n is_flow, workspace_id, edited_by, email, use_jetstream\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"query": "\n INSERT INTO nats_trigger (\n path, nats_resource_path, subjects, script_path,\n is_flow, workspace_id, edited_by, permissioned_as, use_jetstream\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -18,5 +18,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "97c60a4193a75b611db41543e7fe6da81fc631cc6ac43576f8a18afedad4d4a4"
|
||||
"hash": "9883cff4f988767aeea7bce8b66672029b2a4c90e06c05d4356b3ec6bd8d2748"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n workspace_id,\n path,\n kafka_resource_path,\n group_id,\n topics,\n filters,\n auto_offset_reset,\n auto_commit,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, now(), $14, $15, $16\n )\n ",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n workspace_id,\n path,\n kafka_resource_path,\n group_id,\n topics,\n filters,\n auto_offset_reset,\n auto_commit,\n script_path,\n is_flow,\n mode,\n edited_by,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, now(), $14, $15, $16\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -36,5 +36,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5dd6315ec270c268e905262e4b0a920837354d91a0ae16b1236c1267da71765f"
|
||||
"hash": "a0a545fda5f3ebea0113d5daaf13358c964d9fb0f41bf2a1c834305b4d2398f2"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE kafka_trigger\n SET\n kafka_resource_path = $1,\n group_id = $2,\n topics = $3,\n filters = $4,\n auto_offset_reset = $5,\n auto_commit = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n edited_by = $10,\n email = $11,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND path = $13\n ",
|
||||
"query": "\n UPDATE kafka_trigger\n SET\n kafka_resource_path = $1,\n group_id = $2,\n topics = $3,\n filters = $4,\n auto_offset_reset = $5,\n auto_commit = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n edited_by = $10,\n permissioned_as = $11,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND path = $13\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -25,5 +25,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "072e5ab78f929c6b7264f98c1588cb24cc635836276ee6faa2438f494bfbce04"
|
||||
"hash": "a37cfc632dd37cf37c06743239b5ebc784e5da5ee25d47af187a75220d8fded7"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO gcp_trigger (\n gcp_resource_path,\n subscription_id,\n topic_id,\n delivery_type,\n delivery_config,\n workspace_id,\n path,\n script_path,\n is_flow,\n email,\n mode,\n edited_by,\n error_handler_path,\n error_handler_args,\n retry,\n auto_acknowledge_msg,\n ack_deadline\n )\n VALUES (\n $1,\n $2,\n $3,\n $4,\n $5,\n $6,\n $7,\n $8,\n $9,\n $10,\n $11,\n $12,\n $13,\n $14,\n $15,\n $16,\n $17\n )",
|
||||
"query": "\n INSERT INTO gcp_trigger (\n gcp_resource_path,\n subscription_id,\n topic_id,\n delivery_type,\n delivery_config,\n workspace_id,\n path,\n script_path,\n is_flow,\n permissioned_as,\n mode,\n edited_by,\n error_handler_path,\n error_handler_args,\n retry,\n auto_acknowledge_msg,\n ack_deadline\n )\n VALUES (\n $1,\n $2,\n $3,\n $4,\n $5,\n $6,\n $7,\n $8,\n $9,\n $10,\n $11,\n $12,\n $13,\n $14,\n $15,\n $16,\n $17\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -47,5 +47,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "aa59a96bf2d7edfa7c550e66c4d52ddc8e84eacc633e361e49a5219d0bec94b9"
|
||||
"hash": "a5b6ca174b7a3be1b6507ef40b9cc67d85089199c900afc6668f2336b803562e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO postgres_trigger (\n path, script_path, is_flow, workspace_id, edited_by, email,\n postgres_resource_path, replication_slot_name, publication_name\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"query": "\n INSERT INTO postgres_trigger (\n path, script_path, is_flow, workspace_id, edited_by, permissioned_as,\n postgres_resource_path, replication_slot_name, publication_name\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -18,5 +18,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0300afc35a880eef163dfdfd9d5299fac14562ee8595c792f3c30d042fa2d3eb"
|
||||
"hash": "a8245a3b29927c26894be884c38e4d848674d5318e20bb5fc6c4261da25744ca"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE email_trigger\n SET\n script_path = $1,\n path = $2,\n is_flow = $3,\n edited_by = $4,\n email = $5,\n edited_at = now(),\n error_handler_path = $6,\n error_handler_args = $7,\n retry = $8,\n mode = $9\n WHERE\n workspace_id = $10 AND path = $11\n ",
|
||||
"query": "\n UPDATE email_trigger\n SET\n script_path = $1,\n path = $2,\n is_flow = $3,\n edited_by = $4,\n permissioned_as = $5,\n edited_at = now(),\n error_handler_path = $6,\n error_handler_args = $7,\n retry = $8,\n mode = $9\n WHERE\n workspace_id = $10 AND path = $11\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -31,5 +31,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "b5cda8eb32384f315689001f45676d4bf44cb4397dd0a722e7c9d035b58a09c1"
|
||||
"hash": "aa7d3d159943250787a8ebc964cb46e5fbb157d1ec0684789a8d37eff1adf9a5"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE schedule SET\n enabled = $1,\n email = $2\n WHERE path = $3 AND workspace_id = $4\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"query": "\n UPDATE schedule SET\n enabled = $1,\n email = $2\n WHERE path = $3 AND workspace_id = $4\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -65,96 +65,101 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "on_failure",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "on_failure_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "on_failure_exact",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "on_failure_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 18,
|
||||
"name": "on_recovery",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 19,
|
||||
"name": "on_recovery_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 20,
|
||||
"name": "on_recovery_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 21,
|
||||
"name": "on_success",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 22,
|
||||
"name": "on_success_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 23,
|
||||
"name": "ws_error_handler_muted",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 24,
|
||||
"name": "retry",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 25,
|
||||
"name": "no_flow_overlap",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 26,
|
||||
"name": "summary",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 27,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 28,
|
||||
"name": "tag",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 29,
|
||||
"name": "paused_until",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 30,
|
||||
"name": "cron_version",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 31,
|
||||
"name": "dynamic_skip",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
@@ -180,6 +185,7 @@
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -201,5 +207,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "7927b80ce75d99b2a30f6b29196af000578a3c166509f032d14452cc637d884f"
|
||||
"hash": "ad96768ff61fab1cfb9421683bb13b64f5f157fafe114d50fb910dc36ebe0f91"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, email, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14)\n ",
|
||||
"query": "\n INSERT INTO http_trigger (\n path, route_path, route_path_key, script_path, is_flow,\n workspace_id, edited_by, permissioned_as, http_method,\n authentication_method, is_static_website, workspaced_route,\n wrap_body, raw_string\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::http_method,\n $10::authentication_method, $11, $12, $13, $14)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -50,5 +50,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "74c11f5a0315424574fe3e7429f967f7b94f9d9db7be628f9cb411d789085711"
|
||||
"hash": "ae8d7c07a4027bccf404951e75992bf3048c58bdf6fc67cb344eeb6cf72a9156"
|
||||
}
|
||||
22
backend/.sqlx/query-b418f7ae7c0dc6fbe55a9b479e7304475fadab8613b0f541fec33a819e15807b.json
generated
Normal file
22
backend/.sqlx/query-b418f7ae7c0dc6fbe55a9b479e7304475fadab8613b0f541fec33a819e15807b.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id FROM token WHERE token_hash = $1 AND workspace_id IS NOT NULL AND (expiration > NOW() OR expiration IS NULL)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "b418f7ae7c0dc6fbe55a9b479e7304475fadab8613b0f541fec33a819e15807b"
|
||||
}
|
||||
@@ -1,30 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT app_id, value, created_by, created_at, raw_app\n FROM app_version\n WHERE app_id = ANY(SELECT id FROM app WHERE workspace_id = $1)\n ORDER BY app_id, created_at",
|
||||
"query": "SELECT id, app_id, value, created_by, created_at, raw_app\n FROM app_version\n WHERE app_id = ANY(SELECT id FROM app WHERE workspace_id = $1)\n ORDER BY app_id, created_at",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "app_id",
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "app_id",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "value",
|
||||
"type_info": "Json"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 3,
|
||||
"name": "created_by",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 4,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 5,
|
||||
"name": "raw_app",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
@@ -39,8 +44,9 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "5d621d9d2bb37c3115e10a90452c42e563d1c7f2c4d27e9386fe9ed06fe3607a"
|
||||
"hash": "bb37254ed68d9644e75b4d177ed96b46ad0c8ef31c8f4f44a5a66817c5460733"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM group_ WHERE name != 'all' AND name != 'error_handler' AND name != 'slack'",
|
||||
"query": "SELECT COUNT(*) FROM group_ WHERE name != 'all' AND name != 'error_handler' AND name != 'slack' AND name != 'wm_deployers'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -16,5 +16,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "cec906e3bcfe5b477f23e8bb44500b97166c789ab1cf6487679d092eebf3a48b"
|
||||
"hash": "bc6ebef9d41aba232f115f95404922c6054df01fa7f38f5d15d6d4af6c726a3c"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO mqtt_trigger (\n mqtt_resource_path,\n subscribe_topics,\n client_version,\n client_id,\n v3_config,\n v5_config,\n workspace_id,\n path,\n script_path,\n is_flow,\n email,\n mode,\n edited_by,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16\n )",
|
||||
"query": "\n INSERT INTO mqtt_trigger (\n mqtt_resource_path,\n subscribe_topics,\n client_version,\n client_id,\n v3_config,\n v5_config,\n workspace_id,\n path,\n script_path,\n is_flow,\n permissioned_as,\n mode,\n edited_by,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -46,5 +46,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e25aa749cfc9f0bb1649d162e36f2c0ce2187e47d745aa5ba96f9453c722750c"
|
||||
"hash": "c301e668a5b88741e6c20700909be55518fabafcadaf6458fdcf1095643534c5"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n http_trigger\n SET\n wrap_body = $1,\n raw_string = $2,\n authentication_resource_path = $3,\n script_path = $4,\n path = $5,\n is_flow = $6,\n mode = $7,\n http_method = $8,\n static_asset_config = $9,\n edited_by = $10,\n email = $11,\n request_type = $12,\n authentication_method = $13,\n summary = $14,\n description = $15,\n edited_at = now(),\n is_static_website = $16,\n error_handler_path = $17,\n error_handler_args = $18,\n retry = $19\n WHERE\n workspace_id = $20 AND\n path = $21\n ",
|
||||
"query": "\n UPDATE\n http_trigger\n SET\n wrap_body = $1,\n raw_string = $2,\n authentication_resource_path = $3,\n script_path = $4,\n path = $5,\n is_flow = $6,\n mode = $7,\n http_method = $8,\n static_asset_config = $9,\n edited_by = $10,\n permissioned_as = $11,\n request_type = $12,\n authentication_method = $13,\n summary = $14,\n description = $15,\n edited_at = now(),\n is_static_website = $16,\n error_handler_path = $17,\n error_handler_args = $18,\n retry = $19\n WHERE\n workspace_id = $20 AND\n path = $21\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -79,5 +79,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "888f7e82c25b677172a276d042ae7066113f2a522068f04c8cee0895a49b787f"
|
||||
"hash": "c5b650748a4ac0d0a04e287d1b3c89a5cc2af0439fe3c3c5829fd0776ec0734e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n websocket_trigger\n SET\n url = $1,\n script_path = $2,\n path = $3,\n is_flow = $4,\n filters = $5,\n initial_messages = $6,\n url_runnable_args = $7,\n edited_by = $8,\n email = $9,\n can_return_message = $10,\n can_return_error_result = $11,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND path = $13\n ",
|
||||
"query": "\n UPDATE\n websocket_trigger\n SET\n url = $1,\n script_path = $2,\n path = $3,\n is_flow = $4,\n filters = $5,\n initial_messages = $6,\n url_runnable_args = $7,\n edited_by = $8,\n permissioned_as = $9,\n can_return_message = $10,\n can_return_error_result = $11,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16\n WHERE\n workspace_id = $12 AND path = $13\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -25,5 +25,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6c3c38ed5a0e6de0c97954fe4581daa788a7551b4a00dd33f78d36e246b98dd7"
|
||||
"hash": "c7aed7fe3b6774477d403bc3e7fcbce7cdbdd1feb553718cbde60bb8ccff4733"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id,\n script_path, is_flow, workspace_id, edited_by, email\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"query": "\n INSERT INTO kafka_trigger (\n path, kafka_resource_path, topics, group_id,\n script_path, is_flow, workspace_id, edited_by, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -18,5 +18,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8c3cc09c1bbb6209467c75723dd02e97dddb99789422012e85e86c3151a9f2e9"
|
||||
"hash": "ccd76be88fa9c11b3dc2e6d7711437ab3e02d8c3c10c53decc664533b8d04bc0"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n mode as \"mode: _\",\n edited_by, \n email, \n path, \n error_handler_path as \"error_handler_path: _\", \n error_handler_args as \"error_handler_args: _\", \n retry as \"retry: _\" \n FROM email_trigger \n WHERE local_part = $1 \n AND workspaced_local_part = FALSE\n AND (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ",
|
||||
"query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n mode as \"mode: _\",\n permissioned_as,\n path,\n error_handler_path as \"error_handler_path: _\",\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM email_trigger\n WHERE local_part = $1\n AND workspaced_local_part = FALSE\n AND (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -36,31 +36,26 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_by",
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "error_handler_path: _",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "error_handler_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "retry: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
@@ -77,11 +72,10 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "de656102e898ebc90e53af8d36c882cf47e798567bb733e8b528dcedc48ad4bf"
|
||||
"hash": "cd410aa458ad23d47945084a0b9614babda8e6f9fe1ce71224773b2d5f307161"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE postgres_trigger\n SET\n postgres_resource_path = $1,\n replication_slot_name = $2,\n publication_name = $3,\n script_path = $4,\n path = $5,\n is_flow = $6,\n edited_by = $7,\n email = $8,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $11,\n error_handler_args = $12,\n retry = $13\n WHERE\n workspace_id = $9 AND path = $10\n ",
|
||||
"query": "\n UPDATE postgres_trigger\n SET\n postgres_resource_path = $1,\n replication_slot_name = $2,\n publication_name = $3,\n script_path = $4,\n path = $5,\n is_flow = $6,\n edited_by = $7,\n permissioned_as = $8,\n edited_at = now(),\n server_id = NULL,\n error = NULL,\n error_handler_path = $11,\n error_handler_args = $12,\n retry = $13\n WHERE\n workspace_id = $9 AND path = $10\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -22,5 +22,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f4340e31adddc9a37bab64957b2b1b9b29f978c73f003dfbf81d5c487f666358"
|
||||
"hash": "d082ff0fcc2bb02257ab82376d87ff8535dfcb7c2b1143b7016fa17bc42a595d"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n gcp_trigger\n SET\n gcp_resource_path = $1,\n subscription_id = $2,\n topic_id = $3,\n delivery_type = $4,\n delivery_config = $5,\n is_flow = $6,\n edited_by = $7,\n email = $8,\n script_path = $9,\n path = $10,\n mode = $11,\n edited_at = now(),\n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16,\n auto_acknowledge_msg = $17,\n ack_deadline = $18\n WHERE\n workspace_id = $12 AND\n path = $13\n ",
|
||||
"query": "\n UPDATE\n gcp_trigger\n SET\n gcp_resource_path = $1,\n subscription_id = $2,\n topic_id = $3,\n delivery_type = $4,\n delivery_config = $5,\n is_flow = $6,\n edited_by = $7,\n permissioned_as = $8,\n script_path = $9,\n path = $10,\n mode = $11,\n edited_at = now(),\n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16,\n auto_acknowledge_msg = $17,\n ack_deadline = $18\n WHERE\n workspace_id = $12 AND\n path = $13\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -48,5 +48,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "8642e4fc3efb011a8b7f98d2080a165ec4046c3a7172b5635b818c0fa133411f"
|
||||
"hash": "d35300ab94202c181ee5d1e1c76ec9cf1044ff173375a82c529babe053acb24f"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO nats_trigger (\n workspace_id,\n path,\n nats_resource_path,\n subjects,\n stream_name,\n consumer_name,\n use_jetstream,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now(), $13, $14, $15\n )\n ",
|
||||
"query": "\n INSERT INTO nats_trigger (\n workspace_id,\n path,\n nats_resource_path,\n subjects,\n stream_name,\n consumer_name,\n use_jetstream,\n script_path,\n is_flow,\n mode,\n edited_by,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now(), $13, $14, $15\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -35,5 +35,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1bf8dc01326ebf6b8faa04e418b781e37bb9cedd1a89bf71a969b6db8cace48e"
|
||||
"hash": "d56a8a7291ce3141c06d79ef854ac2cf970c3e41a223e31d3991276742d2afe1"
|
||||
}
|
||||
22
backend/.sqlx/query-d592ba371d4ad6e7f1bcffc01749f4753b6fe4e42413cce3d55f92797c856f35.json
generated
Normal file
22
backend/.sqlx/query-d592ba371d4ad6e7f1bcffc01749f4753b6fe4e42413cce3d55f92797c856f35.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM app_version av\n JOIN app a ON a.id = av.app_id\n WHERE a.workspace_id = $1 AND av.id != a.versions[array_upper(a.versions, 1)]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "d592ba371d4ad6e7f1bcffc01749f4753b6fe4e42413cce3d55f92797c856f35"
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO app_version (app_id, value, created_by, created_at, raw_app)\n VALUES ($1, $2, $3, $4, $5)",
|
||||
"query": "INSERT INTO app_version (app_id, value, created_by, created_at, raw_app)\n VALUES ($1, $2, $3, $4, $5) RETURNING id",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
@@ -12,7 +18,9 @@
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "0924c79aca648e5ec3fcc5e91ca71d524fe9d4b46c2e8ed36ae99b5810a896ab"
|
||||
"hash": "da87e3c6678e4ada367dfe6bdaef2c99ea980fb89fdb1bd954c22ac3cf79624c"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n path, queue_url, aws_resource_path, script_path,\n is_flow, workspace_id, edited_by, email\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n path, queue_url, aws_resource_path, script_path,\n is_flow, workspace_id, edited_by, permissioned_as\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -17,5 +17,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c659cfe81441bb1b62a9f23f5d9b7f5b1f354b6bb0a78d007114fed084cfe292"
|
||||
"hash": "db9957ea9b5b326c27ff62f96a3981787a91ac587b1936c774de7a2a2094bf91"
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT email, edited_by FROM http_trigger WHERE path = $1 AND workspace_id = $2",
|
||||
"query": "SELECT permissioned_as, edited_by FROM http_trigger WHERE path = $1 AND workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "email",
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
@@ -25,5 +25,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "8311a553c44221751ffdbbe6a997d6feba8d43292daf6c5433b66bd8450e8854"
|
||||
"hash": "dbb16284b9dd98b9339816e43eebf0fef488102cd7b3fd38d8af3148545bf1a4"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, schedule, timezone, edited_by, script_path,\n is_flow, args, enabled, email,\n on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,\n on_recovery, on_recovery_times, on_recovery_extra_args,\n on_success, on_success_extra_args,\n ws_error_handler_muted, retry, summary, no_flow_overlap,\n tag, paused_until, cron_version, description, dynamic_skip\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n $7, $8, $9, $10,\n $11, $12, $13, $14,\n $15, $16, $17,\n $18, $19,\n $20, $21, $22, $23,\n $24, $25, $26, $27, $28\n )\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"query": "\n INSERT INTO schedule (\n workspace_id, path, schedule, timezone, edited_by, script_path,\n is_flow, args, enabled, email, permissioned_as,\n on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,\n on_recovery, on_recovery_times, on_recovery_extra_args,\n on_success, on_success_extra_args,\n ws_error_handler_muted, retry, summary, no_flow_overlap,\n tag, paused_until, cron_version, description, dynamic_skip\n ) VALUES (\n $1, $2, $3, $4, $5, $6,\n $7, $8, $9, $10, $11,\n $12, $13, $14, $15,\n $16, $17, $18,\n $19, $20,\n $21, $22, $23, $24,\n $25, $26, $27, $28, $29\n )\n RETURNING\n workspace_id,\n path,\n edited_by,\n edited_at,\n schedule,\n timezone,\n enabled,\n script_path,\n is_flow,\n args AS \"args: _\",\n extra_perms,\n email,\n permissioned_as,\n error,\n on_failure,\n on_failure_times,\n on_failure_exact,\n on_failure_extra_args AS \"on_failure_extra_args: _\",\n on_recovery,\n on_recovery_times,\n on_recovery_extra_args AS \"on_recovery_extra_args: _\",\n on_success,\n on_success_extra_args AS \"on_success_extra_args: _\",\n ws_error_handler_muted,\n retry,\n no_flow_overlap,\n summary,\n description,\n tag,\n paused_until,\n cron_version,\n dynamic_skip\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -65,96 +65,101 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "error",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "on_failure",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "on_failure_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "on_failure_exact",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "on_failure_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 18,
|
||||
"name": "on_recovery",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 19,
|
||||
"name": "on_recovery_times",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 20,
|
||||
"name": "on_recovery_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 21,
|
||||
"name": "on_success",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 22,
|
||||
"name": "on_success_extra_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 23,
|
||||
"name": "ws_error_handler_muted",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 24,
|
||||
"name": "retry",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 25,
|
||||
"name": "no_flow_overlap",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 26,
|
||||
"name": "summary",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 27,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 28,
|
||||
"name": "tag",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 29,
|
||||
"name": "paused_until",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 30,
|
||||
"name": "cron_version",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 31,
|
||||
"name": "dynamic_skip",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
@@ -172,6 +177,7 @@
|
||||
"Bool",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Jsonb",
|
||||
@@ -204,6 +210,7 @@
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -225,5 +232,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "23e4c6e3dc6a48f702c2b26a6b1f94668e086caaa0093a3b685f87483513b0d2"
|
||||
"hash": "dd20f94d560238096390371c98ded1f80825a11cd61c0bb431678ad9ab4a138e"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n workspace_id,\n path,\n queue_url,\n aws_resource_path,\n message_attributes,\n aws_auth_resource_type,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now(), $12, $13, $14\n )\n ",
|
||||
"query": "\n INSERT INTO sqs_trigger (\n workspace_id,\n path,\n queue_url,\n aws_resource_path,\n message_attributes,\n aws_auth_resource_type,\n script_path,\n is_flow,\n mode,\n edited_by,\n permissioned_as,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now(), $12, $13, $14\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -44,5 +44,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "cad914554762b6be72b289814f77072600f8f1bc1fa73ef6f63972775aedf044"
|
||||
"hash": "de4879b8e3002ca48005406c4e107df2663aca35b60e3c528f564da3114f0af1"
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "DELETE FROM v2_job_debounce_batch WHERE debounce_batch = (\n SELECT debounce_batch FROM v2_job_debounce_batch WHERE id = $1\n )",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e050734d7642b26f8859982c55ec2c8b1fc14a8de665b15a2f2dfd6e0b5b7fdf"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n mode as \"mode: _\",\n edited_by, \n email, \n path, \n error_handler_path as \"error_handler_path: _\", \n error_handler_args as \"error_handler_args: _\", \n retry as \"retry: _\" \n FROM email_trigger \n WHERE workspace_id = $1 \n AND local_part = $2 \n AND (workspaced_local_part = TRUE OR $3 IS TRUE)\n AND (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ",
|
||||
"query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n mode as \"mode: _\",\n permissioned_as,\n path,\n error_handler_path as \"error_handler_path: _\",\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM email_trigger\n WHERE workspace_id = $1\n AND local_part = $2\n AND (workspaced_local_part = TRUE OR $3 IS TRUE)\n AND (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -36,31 +36,26 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "edited_by",
|
||||
"name": "permissioned_as",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "path",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 6,
|
||||
"name": "error_handler_path: _",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 7,
|
||||
"name": "error_handler_args: _",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 8,
|
||||
"name": "retry: _",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
@@ -79,11 +74,10 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "3aad6340ea1f8dc8411742ef5de5a77d5de903845da9e686aef804bc09db1687"
|
||||
"hash": "e3f09fe777cbbc009ca1af31989b542ec2d585ef672f0e1496944f4dd0d082f4"
|
||||
}
|
||||
18
backend/.sqlx/query-e998bb8195b4fafe6de20fb0bf4315b3a10db9aca72447b8b8afd9474baa0614.json
generated
Normal file
18
backend/.sqlx/query-e998bb8195b4fafe6de20fb0bf4315b3a10db9aca72447b8b8afd9474baa0614.json
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO kafka_trigger (path, kafka_resource_path, group_id, topics, script_path, is_flow, workspace_id, edited_by, permissioned_as) VALUES ($1, $2, $3, $4, $5, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"VarcharArray",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e998bb8195b4fafe6de20fb0bf4315b3a10db9aca72447b8b8afd9474baa0614"
|
||||
}
|
||||
22
backend/.sqlx/query-ed202f69f5f0a8f21f2dbfcae98b9a94d039362537b050913ea2b7534f85a047.json
generated
Normal file
22
backend/.sqlx/query-ed202f69f5f0a8f21f2dbfcae98b9a94d039362537b050913ea2b7534f85a047.json
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COUNT(*) FROM flow_version fv\n JOIN flow f ON f.workspace_id = fv.workspace_id AND f.path = fv.path\n WHERE fv.workspace_id = $1 AND fv.id != f.versions[array_upper(f.versions, 1)]",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "count",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "ed202f69f5f0a8f21f2dbfcae98b9a94d039362537b050913ea2b7534f85a047"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, email, initial_messages\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
|
||||
"query": "\n INSERT INTO websocket_trigger (\n path, url, script_path, is_flow, workspace_id,\n edited_by, permissioned_as, initial_messages\n )\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8)\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
@@ -17,5 +17,5 @@
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e485c82978d10379c6d1b7cd850f3bf764e6c5bf775f3d710ba36a79f5e424eb"
|
||||
"hash": "f57a891d1b507ec6767099a388be7deb97a89168fd7f74040e73033075140655"
|
||||
}
|
||||
17
backend/.sqlx/query-f5f18e6c5b7376b2760915ee3c859fb2306f16c0c601668adaf30baf66f0fd0d.json
generated
Normal file
17
backend/.sqlx/query-f5f18e6c5b7376b2760915ee3c859fb2306f16c0c601668adaf30baf66f0fd0d.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO sqs_trigger (path, queue_url, aws_resource_path, script_path, is_flow, workspace_id, edited_by, permissioned_as) VALUES ($1, $2, $3, $4, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar",
|
||||
"Varchar"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f5f18e6c5b7376b2760915ee3c859fb2306f16c0c601668adaf30baf66f0fd0d"
|
||||
}
|
||||
23
backend/.sqlx/query-fc27f306a9399205eaa0946897d797e5b792674c353acb41bd4e7133e19e2a03.json
generated
Normal file
23
backend/.sqlx/query-fc27f306a9399205eaa0946897d797e5b792674c353acb41bd4e7133e19e2a03.json
generated
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT j.id FROM v2_job j\n JOIN v2_job_queue q ON q.id = j.id\n WHERE j.parent_job = $1 AND j.workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Uuid"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "fc27f306a9399205eaa0946897d797e5b792674c353acb41bd4e7133e19e2a03"
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
panes:
|
||||
# Pane 1: Install dependencies, then start dev server
|
||||
- command: cargo run
|
||||
|
||||
# Pane 2: AI agent
|
||||
- command: <agent>
|
||||
split: horizontal
|
||||
focus: true
|
||||
197
backend/Cargo.lock
generated
197
backend/Cargo.lock
generated
@@ -860,9 +860,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-rs"
|
||||
version = "1.16.1"
|
||||
version = "1.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf"
|
||||
checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc"
|
||||
dependencies = [
|
||||
"aws-lc-sys",
|
||||
"zeroize",
|
||||
@@ -870,9 +870,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "aws-lc-sys"
|
||||
version = "0.38.0"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e"
|
||||
checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cmake",
|
||||
@@ -1885,19 +1885,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f"
|
||||
checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"bytes",
|
||||
"cfg_aliases 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh-derive"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c"
|
||||
checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro-crate",
|
||||
@@ -7524,9 +7525,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
@@ -10641,9 +10642,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quick_cache"
|
||||
version = "0.6.20"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c901384fb8fb3d4510388129ce6e13ecd686eee610da778b6ea77219decd53"
|
||||
checksum = "5a70b1b8b47e31d0498ecbc3c5470bb931399a8bfed1fd79d1717a61ce7f96e3"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"equivalent",
|
||||
@@ -13843,9 +13844,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.44"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
|
||||
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
@@ -15741,7 +15742,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -15786,6 +15787,7 @@ dependencies = [
|
||||
"uuid",
|
||||
"windmill-api",
|
||||
"windmill-api-agent-workers",
|
||||
"windmill-api-auth",
|
||||
"windmill-api-client",
|
||||
"windmill-api-settings",
|
||||
"windmill-autoscaling",
|
||||
@@ -15799,6 +15801,14 @@ dependencies = [
|
||||
"windmill-queue",
|
||||
"windmill-runtime-nativets",
|
||||
"windmill-test-utils",
|
||||
"windmill-trigger",
|
||||
"windmill-trigger-gcp",
|
||||
"windmill-trigger-kafka",
|
||||
"windmill-trigger-mqtt",
|
||||
"windmill-trigger-nats",
|
||||
"windmill-trigger-postgres",
|
||||
"windmill-trigger-sqs",
|
||||
"windmill-trigger-websocket",
|
||||
"windmill-types",
|
||||
"windmill-worker",
|
||||
"windmill-worker-volumes",
|
||||
@@ -15808,7 +15818,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-alerting"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15821,7 +15831,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -15962,7 +15972,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-agent-workers"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15985,7 +15995,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-assets"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -15998,7 +16008,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-auth"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16024,7 +16034,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-client"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
@@ -16034,7 +16044,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-configs"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16051,7 +16061,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-debug"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"base64 0.22.1",
|
||||
@@ -16074,7 +16084,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-embeddings"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16097,7 +16107,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flow-conversations"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16113,7 +16123,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-flows"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16133,7 +16143,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-groups"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16153,7 +16163,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-inputs"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16167,7 +16177,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-integration-tests"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -16195,7 +16205,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-jobs"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16220,7 +16230,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-npm-proxy"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"flate2",
|
||||
@@ -16238,7 +16248,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-openapi"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16259,7 +16269,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-schedule"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16279,7 +16289,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-scripts"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16309,7 +16319,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-settings"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16336,7 +16346,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-sse"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"serde",
|
||||
@@ -16348,7 +16358,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-users"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum 0.7.9",
|
||||
@@ -16371,7 +16381,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workers"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16385,7 +16395,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-api-workspaces"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"axum 0.7.9",
|
||||
"chrono",
|
||||
@@ -16416,7 +16426,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-audit"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
@@ -16430,7 +16440,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-autoscaling"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -16449,7 +16459,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-common"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
@@ -16476,6 +16486,7 @@ dependencies = [
|
||||
"cron",
|
||||
"croner",
|
||||
"datafusion",
|
||||
"equivalent",
|
||||
"futures",
|
||||
"futures-core",
|
||||
"gethostname",
|
||||
@@ -16548,7 +16559,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-dep-map"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"itertools 0.14.0",
|
||||
@@ -16567,7 +16578,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-git-sync"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
@@ -16582,7 +16593,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-indexer"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"astral-tokio-tar",
|
||||
@@ -16606,7 +16617,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-jseval"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -16623,7 +16634,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-macros"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"lazy_static",
|
||||
@@ -16639,7 +16650,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-mcp"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16660,7 +16671,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-native-triggers"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -16691,7 +16702,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-oauth"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-oauth2",
|
||||
@@ -16715,7 +16726,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-object-store"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -16749,7 +16760,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-operator"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures",
|
||||
@@ -16767,7 +16778,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"convert_case 0.6.0",
|
||||
"serde",
|
||||
@@ -16776,7 +16787,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-bash"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16788,7 +16799,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-csharp"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16800,7 +16811,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-go"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gosyn",
|
||||
@@ -16812,7 +16823,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-graphql"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16824,7 +16835,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-java"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde_json",
|
||||
@@ -16836,7 +16847,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-nu"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nu-parser",
|
||||
@@ -16847,7 +16858,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-php"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16858,7 +16869,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.14.0",
|
||||
@@ -16870,7 +16881,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-asset"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -16881,7 +16892,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-py-imports"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -16905,7 +16916,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ruby"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16919,7 +16930,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-rust"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"convert_case 0.6.0",
|
||||
@@ -16936,7 +16947,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16950,7 +16961,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-sql-asset"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -16962,7 +16973,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"lazy_static",
|
||||
@@ -16980,7 +16991,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-ts-asset"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde-wasm-bindgen",
|
||||
@@ -16996,7 +17007,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-wac"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustpython-ast",
|
||||
@@ -17012,7 +17023,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-parser-yaml"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -17023,7 +17034,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-queue"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -17060,7 +17071,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-runtime-nativets"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"const_format",
|
||||
@@ -17098,7 +17109,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-sql-datatype-parser-wasm"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"wasm-bindgen",
|
||||
@@ -17109,7 +17120,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-store"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@@ -17138,7 +17149,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-test-utils"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum 0.7.9",
|
||||
@@ -17161,7 +17172,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17194,7 +17205,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-email"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17214,7 +17225,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-gcp"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17248,7 +17259,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-http"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17283,7 +17294,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-kafka"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17306,7 +17317,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-mqtt"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17330,7 +17341,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-nats"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-nats",
|
||||
@@ -17354,7 +17365,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-postgres"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17389,7 +17400,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-sqs"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17417,7 +17428,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-trigger-websocket"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -17440,7 +17451,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-types"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.9.4",
|
||||
@@ -17458,7 +17469,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-once-cell",
|
||||
@@ -17565,7 +17576,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windmill-worker-volumes"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
@@ -18454,18 +18465,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.42"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
|
||||
checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.42"
|
||||
version = "0.8.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
|
||||
checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "windmill"
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
@@ -82,7 +82,7 @@ members = [
|
||||
exclude = ["./windmill-duckdb-ffi-internal"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.659.1"
|
||||
version = "1.662.0"
|
||||
authors = ["Ruben Fiszel <ruben@windmill.dev>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -260,6 +260,15 @@ windmill-dep-map.workspace = true
|
||||
windmill-test-utils.workspace = true
|
||||
windmill-worker-volumes.workspace = true
|
||||
windmill-types.workspace = true
|
||||
windmill-trigger.workspace = true
|
||||
windmill-trigger-websocket.workspace = true
|
||||
windmill-trigger-postgres.workspace = true
|
||||
windmill-trigger-mqtt.workspace = true
|
||||
windmill-trigger-kafka.workspace = true
|
||||
windmill-trigger-nats.workspace = true
|
||||
windmill-trigger-sqs.workspace = true
|
||||
windmill-trigger-gcp.workspace = true
|
||||
windmill-api-auth.workspace = true
|
||||
axum.workspace = true
|
||||
serde.workspace = true
|
||||
windmill-api-client.workspace = true
|
||||
|
||||
@@ -1 +1 @@
|
||||
878cc2044717e0177228529a50433fe2768e70b5
|
||||
563877bf1c8b4184f638bab51be89b1c0aec6dad
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Revert: re-add email columns to trigger tables, drop permissioned_as
|
||||
|
||||
ALTER TABLE http_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE http_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE websocket_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE websocket_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE postgres_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE postgres_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE mqtt_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE mqtt_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE kafka_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE kafka_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE nats_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE nats_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE sqs_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE sqs_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE gcp_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE gcp_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE email_trigger ADD COLUMN email VARCHAR(255) NOT NULL DEFAULT '';
|
||||
ALTER TABLE email_trigger DROP COLUMN permissioned_as;
|
||||
|
||||
ALTER TABLE schedule DROP COLUMN permissioned_as;
|
||||
64
backend/migrations/20260318000000_add_permissioned_as.up.sql
Normal file
64
backend/migrations/20260318000000_add_permissioned_as.up.sql
Normal file
@@ -0,0 +1,64 @@
|
||||
-- Add permissioned_as column to all trigger tables and schedule
|
||||
-- permissioned_as stores 'u/{username}', 'g/{group}', or raw email
|
||||
-- We add nullable first, populate, then set NOT NULL to avoid a DEFAULT '' that could mask bugs.
|
||||
|
||||
-- Trigger tables: add permissioned_as, drop email
|
||||
ALTER TABLE http_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE http_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE http_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE http_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE websocket_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE websocket_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE websocket_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE websocket_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE postgres_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE postgres_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE postgres_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE postgres_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE mqtt_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE mqtt_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE mqtt_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE mqtt_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE kafka_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE kafka_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE kafka_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE kafka_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE nats_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE nats_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE nats_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE nats_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE sqs_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE sqs_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE sqs_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE sqs_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE gcp_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE gcp_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE gcp_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE gcp_trigger DROP COLUMN email;
|
||||
|
||||
ALTER TABLE email_trigger ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE email_trigger SET permissioned_as = CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END;
|
||||
ALTER TABLE email_trigger ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
ALTER TABLE email_trigger DROP COLUMN email;
|
||||
|
||||
-- Schedule table: add permissioned_as, keep email for backwards compat with old workers
|
||||
-- For superadmin-owned schedules, use the email to find the actual username (since edited_by
|
||||
-- may have been overwritten by a later edit). Otherwise use edited_by as the source.
|
||||
ALTER TABLE schedule ADD COLUMN permissioned_as VARCHAR(255);
|
||||
UPDATE schedule SET permissioned_as = CASE
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM password p WHERE p.email = schedule.email AND p.super_admin = true
|
||||
) THEN COALESCE(
|
||||
'u/' || (SELECT u.username FROM usr u WHERE u.email = schedule.email AND u.workspace_id = schedule.workspace_id LIMIT 1),
|
||||
schedule.email
|
||||
)
|
||||
ELSE CASE WHEN edited_by LIKE '%@%' THEN edited_by ELSE 'u/' || edited_by END
|
||||
END;
|
||||
ALTER TABLE schedule ALTER COLUMN permissioned_as SET NOT NULL;
|
||||
@@ -1035,7 +1035,10 @@ Windmill Community Edition {GIT_VERSION}
|
||||
}
|
||||
|
||||
if server_mode || worker_mode || indexer_mode || mcp_mode {
|
||||
let port_var = std::env::var("PORT").ok().and_then(|x| x.parse().ok());
|
||||
let port_var = std::env::var("PORT")
|
||||
.or_else(|_| std::env::var("BACKEND_PORT"))
|
||||
.ok()
|
||||
.and_then(|x| x.parse().ok());
|
||||
|
||||
let port = if server_mode || indexer_mode || mcp_mode {
|
||||
port_var.unwrap_or(DEFAULT_PORT as u16)
|
||||
@@ -1082,6 +1085,10 @@ Windmill Community Edition {GIT_VERSION}
|
||||
crate::monitor::monitor_pool(&db).await;
|
||||
}
|
||||
|
||||
if let Some(db) = conn.as_sql() {
|
||||
crate::monitor::monitor_pool_otel(&db).await;
|
||||
}
|
||||
|
||||
send_logs_to_object_store(&conn, &hostname, &mode);
|
||||
|
||||
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
|
||||
|
||||
@@ -39,6 +39,10 @@ use windmill_common::ee_oss::{jobs_waiting_alerts, worker_groups_alerts};
|
||||
|
||||
#[cfg(feature = "oauth2")]
|
||||
use windmill_common::global_settings::OAUTH_SETTING;
|
||||
use windmill_common::otel_oss::{
|
||||
otel_incr_zombie_delete_count, otel_incr_zombie_restart_count, otel_set_db_pool,
|
||||
otel_set_queue_count, otel_set_queue_running_count,
|
||||
};
|
||||
use windmill_common::{
|
||||
agent_workers::DECODED_AGENT_TOKEN,
|
||||
apps::APP_WORKSPACED_ROUTE,
|
||||
@@ -1960,28 +1964,39 @@ pub async fn monitor_pool(db: &DB) {
|
||||
if METRICS_ENABLED.load(Ordering::Relaxed) {
|
||||
let db = db.clone();
|
||||
tokio::spawn(async move {
|
||||
let active_pool_connections: prometheus::IntGauge = prometheus::register_int_gauge!(
|
||||
let active_gauge = prometheus::register_int_gauge!(
|
||||
"pool_connections_active",
|
||||
"Number of active postgresql connections in the pool"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let idle_pool_connections: prometheus::IntGauge = prometheus::register_int_gauge!(
|
||||
let idle_gauge = prometheus::register_int_gauge!(
|
||||
"pool_connections_idle",
|
||||
"Number of idle postgresql connections in the pool"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let max_pool_connections: prometheus::IntGauge = prometheus::register_int_gauge!(
|
||||
let max_gauge = prometheus::register_int_gauge!(
|
||||
"pool_connections_max",
|
||||
"Number of max postgresql connections in the pool"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
max_pool_connections.set(db.options().get_max_connections() as i64);
|
||||
max_gauge.set(db.options().get_max_connections() as i64);
|
||||
loop {
|
||||
active_pool_connections.set(db.size() as i64);
|
||||
idle_pool_connections.set(db.num_idle() as i64);
|
||||
active_gauge.set(db.size() as i64);
|
||||
idle_gauge.set(db.num_idle() as i64);
|
||||
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn monitor_pool_otel(db: &DB) {
|
||||
if OTEL_METRICS_ENABLED.load(Ordering::Relaxed) {
|
||||
let db = db.clone();
|
||||
tokio::spawn(async move {
|
||||
let max = db.options().get_max_connections() as i64;
|
||||
loop {
|
||||
otel_set_db_pool(db.size() as i64, db.num_idle() as i64, max);
|
||||
tokio::time::sleep(Duration::from_secs(30)).await;
|
||||
}
|
||||
});
|
||||
@@ -2320,7 +2335,7 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
|
||||
.map(|last_check| chrono::Utc::now() - last_check > chrono::Duration::seconds(25))
|
||||
.unwrap_or(true);
|
||||
|
||||
if metrics_enabled || save_metrics {
|
||||
if metrics_enabled || save_metrics || OTEL_METRICS_ENABLED.load(Ordering::Relaxed) {
|
||||
let queue_counts = windmill_common::queue::get_queue_counts(db).await;
|
||||
|
||||
#[cfg(feature = "prometheus")]
|
||||
@@ -2345,6 +2360,8 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
|
||||
tags_to_watch.push(tag.to_string());
|
||||
}
|
||||
|
||||
otel_set_queue_count(&tag, count as i64);
|
||||
|
||||
// save queue_count and delay metrics per tag
|
||||
if save_metrics {
|
||||
sqlx::query!(
|
||||
@@ -2378,28 +2395,45 @@ pub async fn expose_queue_metrics(db: &Pool<Postgres>) {
|
||||
*w = tags_to_watch;
|
||||
}
|
||||
|
||||
// Single DB query for running counts, shared by Prometheus and OTel
|
||||
let otel_running = OTEL_METRICS_ENABLED.load(Ordering::Relaxed);
|
||||
#[cfg(feature = "prometheus")]
|
||||
if metrics_enabled {
|
||||
// Handle queue running count metrics
|
||||
let need_running_counts = metrics_enabled || otel_running;
|
||||
#[cfg(not(feature = "prometheus"))]
|
||||
let need_running_counts = otel_running;
|
||||
|
||||
if need_running_counts {
|
||||
let queue_running_counts = windmill_common::queue::get_queue_running_counts(db).await;
|
||||
|
||||
for q in QUEUE_RUNNING_COUNT_TAGS.read().await.iter() {
|
||||
if queue_running_counts.get(q).is_none() {
|
||||
(*QUEUE_RUNNING_COUNT).with_label_values(&[q]).set(0);
|
||||
#[cfg(feature = "prometheus")]
|
||||
if metrics_enabled {
|
||||
for q in QUEUE_RUNNING_COUNT_TAGS.read().await.iter() {
|
||||
if queue_running_counts.get(q).is_none() {
|
||||
(*QUEUE_RUNNING_COUNT).with_label_values(&[q]).set(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut running_tags_to_watch = vec![];
|
||||
for q in queue_running_counts {
|
||||
let count = q.1;
|
||||
let tag = q.0;
|
||||
#[allow(unused_mut, unused_variables)]
|
||||
let mut running_tags_to_watch: Vec<String> = vec![];
|
||||
for (tag, count) in &queue_running_counts {
|
||||
#[cfg(feature = "prometheus")]
|
||||
if metrics_enabled {
|
||||
let metric = (*QUEUE_RUNNING_COUNT).with_label_values(&[tag]);
|
||||
metric.set(*count as i64);
|
||||
running_tags_to_watch.push(tag.to_string());
|
||||
}
|
||||
|
||||
let metric = (*QUEUE_RUNNING_COUNT).with_label_values(&[&tag]);
|
||||
metric.set(count as i64);
|
||||
running_tags_to_watch.push(tag.to_string());
|
||||
if otel_running {
|
||||
otel_set_queue_running_count(tag, *count as i64);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "prometheus")]
|
||||
if metrics_enabled {
|
||||
let mut w = QUEUE_RUNNING_COUNT_TAGS.write().await;
|
||||
*w = running_tags_to_watch;
|
||||
}
|
||||
let mut w = QUEUE_RUNNING_COUNT_TAGS.write().await;
|
||||
*w = running_tags_to_watch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2693,6 +2727,8 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, node_n
|
||||
QUEUE_ZOMBIE_RESTART_COUNT.inc_by(restarted.len() as _);
|
||||
}
|
||||
|
||||
otel_incr_zombie_restart_count(restarted.len() as u64);
|
||||
|
||||
let base_url = BASE_URL.read().await.clone();
|
||||
for r in restarted {
|
||||
let last_ping = if let Some(x) = r.ping {
|
||||
@@ -2861,6 +2897,8 @@ async fn handle_zombie_jobs(db: &Pool<Postgres>, base_internal_url: &str, node_n
|
||||
QUEUE_ZOMBIE_DELETE_COUNT.inc_by(timeouts.len() as _);
|
||||
}
|
||||
|
||||
otel_incr_zombie_delete_count(timeouts.len() as u64);
|
||||
|
||||
for (job_id, error_kind) in timeouts {
|
||||
// since the job is unrecoverable, the same worker queue should never be sent anything
|
||||
let (same_worker_tx_never_used, _same_worker_rx_never_used) =
|
||||
|
||||
8
backend/tests/fixtures/permissions_test.sql
vendored
8
backend/tests/fixtures/permissions_test.sql
vendored
@@ -204,14 +204,14 @@ VALUES ('test-workspace', 'u/bob/my_variable', 'bob_value', false,
|
||||
-- ============================================
|
||||
|
||||
-- Alice's personal schedule
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms)
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms, permissioned_as)
|
||||
VALUES ('test-workspace', 'u/alice/my_schedule', 'alice', NOW(), '0 * * * *', false,
|
||||
'u/alice/my_script', '{}', false, 'alice@windmill.dev', 'UTC', '{}');
|
||||
'u/alice/my_script', '{}', false, 'alice@windmill.dev', 'UTC', '{}', 'u/alice');
|
||||
|
||||
-- Bob's personal schedule
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms)
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms, permissioned_as)
|
||||
VALUES ('test-workspace', 'u/bob/my_schedule', 'bob', NOW(), '0 * * * *', false,
|
||||
'u/bob/my_script', '{}', false, 'bob@windmill.dev', 'UTC', '{}');
|
||||
'u/bob/my_script', '{}', false, 'bob@windmill.dev', 'UTC', '{}', 'u/bob');
|
||||
|
||||
-- ============================================
|
||||
-- APPS (with app_versions)
|
||||
|
||||
@@ -65,6 +65,11 @@ INSERT INTO usr_to_group(workspace_id, group_, usr) VALUES
|
||||
('test-workspace', 'wm_deployers', 'deployer-user')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Superadmin NOT in workspace (only in password table, not in usr)
|
||||
INSERT INTO password(email, password_hash, login_type, super_admin, verified, name, username)
|
||||
VALUES ('superadmin-external@windmill.dev', 'not-a-real-hash', 'password', true, true, 'External Superadmin', 'superadmin-external')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- Tokens for all users (token_hash = sha256 hex, token_prefix = first 10 chars)
|
||||
-- NOTE: plaintext `token` column is included for backward compat during transition.
|
||||
-- Remove it once the `token` column is dropped from the schema.
|
||||
@@ -80,6 +85,9 @@ ON CONFLICT DO NOTHING;
|
||||
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin)
|
||||
VALUES (encode(sha256('ORIGINAL_TOKEN'::bytea), 'hex'), 'ORIGINAL_T', 'ORIGINAL_TOKEN', 'original@windmill.dev', 'original token', false)
|
||||
ON CONFLICT DO NOTHING;
|
||||
INSERT INTO token(token_hash, token_prefix, token, email, label, super_admin)
|
||||
VALUES (encode(sha256('EXTERNAL_SUPERADMIN_TOKEN'::bytea), 'hex'), 'EXTERNAL_S', 'EXTERNAL_SUPERADMIN_TOKEN', 'superadmin-external@windmill.dev', 'external superadmin token', true)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_admin;
|
||||
GRANT ALL PRIVILEGES ON TABLE workspace_key TO windmill_user;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
385
backend/tests/trigger_listener_queries.rs
Normal file
385
backend/tests/trigger_listener_queries.rs
Normal file
@@ -0,0 +1,385 @@
|
||||
//! Tests that call trigger trait methods directly to verify
|
||||
//! all dynamic SQL correctly references the permissioned_as column.
|
||||
|
||||
use serde_json::json;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use windmill_common::DB;
|
||||
use windmill_trigger::handler::TriggerCrud;
|
||||
use windmill_trigger::listener::Listener;
|
||||
use windmill_trigger::types::TriggerMode;
|
||||
|
||||
/// Helper to insert a minimal trigger row.
|
||||
async fn insert_trigger(db: &DB, table: &str, path: &str, extra_cols: &str, extra_vals: &str) {
|
||||
let sql = format!(
|
||||
"INSERT INTO {} (path, script_path, is_flow, workspace_id, edited_by, permissioned_as{}) \
|
||||
VALUES ($1, 'f/test/handler', false, 'test-workspace', 'test-user', 'u/test-user'{})",
|
||||
table, extra_cols, extra_vals
|
||||
);
|
||||
sqlx::query(&sql).bind(path).execute(db).await.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "websocket")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_listener_query_websocket(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
insert_trigger(
|
||||
&db,
|
||||
"websocket_trigger",
|
||||
"f/test/listener_ws",
|
||||
", url",
|
||||
", 'wss://example.com'",
|
||||
)
|
||||
.await;
|
||||
let triggers = windmill_trigger_websocket::WebsocketTrigger
|
||||
.fetch_enabled_unlistened_triggers(&db)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.path == "f/test/listener_ws"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_listener_query_postgres(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
insert_trigger(
|
||||
&db,
|
||||
"postgres_trigger",
|
||||
"f/test/listener_pg",
|
||||
", postgres_resource_path, replication_slot_name, publication_name",
|
||||
", 'u/test/pg', 'slot', 'pub'",
|
||||
)
|
||||
.await;
|
||||
let triggers = windmill_trigger_postgres::PostgresTrigger
|
||||
.fetch_enabled_unlistened_triggers(&db)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.path == "f/test/listener_pg"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_listener_query_mqtt(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO mqtt_trigger (path, mqtt_resource_path, subscribe_topics, client_version, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, ARRAY[$3::jsonb], $4::mqtt_client_version, $5, false, 'test-workspace', 'test-user', 'u/test-user')"
|
||||
)
|
||||
.bind("f/test/listener_mqtt").bind("u/test/mqtt")
|
||||
.bind(json!({"topic": "t", "qos": "qos0"})).bind("v5").bind("f/test/handler")
|
||||
.execute(&db).await?;
|
||||
|
||||
let triggers = windmill_trigger_mqtt::MqttTrigger
|
||||
.fetch_enabled_unlistened_triggers(&db)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.path == "f/test/listener_mqtt"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_listener_query_kafka(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO kafka_trigger (path, kafka_resource_path, group_id, topics, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, $5, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"f/test/listener_kafka",
|
||||
"u/test/kafka",
|
||||
"grp",
|
||||
&["topic"] as &[&str],
|
||||
"f/test/handler"
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let triggers = windmill_trigger_kafka::KafkaTrigger
|
||||
.fetch_enabled_unlistened_triggers(&db)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.path == "f/test/listener_kafka"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_listener_query_nats(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO nats_trigger (path, nats_resource_path, subjects, use_jetstream, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, $5, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"f/test/listener_nats",
|
||||
"u/test/nats",
|
||||
&["subj"] as &[&str],
|
||||
false,
|
||||
"f/test/handler"
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let triggers = windmill_trigger_nats::NatsTrigger
|
||||
.fetch_enabled_unlistened_triggers(&db)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.path == "f/test/listener_nats"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_listener_query_sqs(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO sqs_trigger (path, queue_url, aws_resource_path, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"f/test/listener_sqs",
|
||||
"https://sqs.example.com/q",
|
||||
"u/test/aws",
|
||||
"f/test/handler"
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let triggers = windmill_trigger_sqs::SqsTrigger
|
||||
.fetch_enabled_unlistened_triggers(&db)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.path == "f/test/listener_sqs"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_listener_query_gcp(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO gcp_trigger (path, gcp_resource_path, topic_id, subscription_id, \
|
||||
delivery_type, subscription_mode, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, $5::delivery_mode, $6::gcp_subscription_mode, $7, false, 'test-workspace', 'test-user', 'u/test-user')"
|
||||
)
|
||||
.bind("f/test/listener_gcp").bind("u/test/gcp").bind("topic").bind("sub")
|
||||
.bind("pull").bind("existing").bind("f/test/handler")
|
||||
.execute(&db).await?;
|
||||
|
||||
let triggers = windmill_trigger_gcp::GcpTrigger
|
||||
.fetch_enabled_unlistened_triggers(&db)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.path == "f/test/listener_gcp"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Handler trait method tests (get_trigger_by_path, list_triggers, set_trigger_mode)
|
||||
// ============================================================================
|
||||
|
||||
fn make_authed() -> windmill_api_auth::ApiAuthed {
|
||||
windmill_api_auth::ApiAuthed {
|
||||
email: "test@windmill.dev".to_string(),
|
||||
username: "test-user".to_string(),
|
||||
is_admin: true,
|
||||
is_operator: false,
|
||||
groups: vec![],
|
||||
folders: vec![],
|
||||
scopes: None,
|
||||
username_override: None,
|
||||
token_prefix: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests get_trigger_by_path, list_triggers, set_trigger_mode for websocket (server_state=true).
|
||||
#[cfg(feature = "websocket")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_handler_queries_websocket(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
insert_trigger(
|
||||
&db,
|
||||
"websocket_trigger",
|
||||
"f/test/handler_ws",
|
||||
", url",
|
||||
", 'wss://example.com'",
|
||||
)
|
||||
.await;
|
||||
|
||||
let handler = windmill_trigger_websocket::WebsocketTrigger;
|
||||
let mut conn = db.acquire().await?;
|
||||
|
||||
let trigger = handler
|
||||
.get_trigger_by_path(&mut *conn, "test-workspace", "f/test/handler_ws")
|
||||
.await?;
|
||||
assert_eq!(trigger.base.permissioned_as, "u/test-user");
|
||||
|
||||
let triggers = handler
|
||||
.list_triggers(&mut *conn, "test-workspace", None)
|
||||
.await?;
|
||||
assert!(triggers.iter().any(|t| t.base.path == "f/test/handler_ws"));
|
||||
|
||||
let authed = make_authed();
|
||||
let updated = handler
|
||||
.set_trigger_mode(
|
||||
&authed,
|
||||
&mut *conn,
|
||||
"test-workspace",
|
||||
"f/test/handler_ws",
|
||||
&TriggerMode::Disabled,
|
||||
)
|
||||
.await?;
|
||||
assert!(updated);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests get_trigger_by_path, list_triggers, set_trigger_mode for mqtt (server_state=true).
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_handler_queries_mqtt(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO mqtt_trigger (path, mqtt_resource_path, subscribe_topics, client_version, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, ARRAY[$3::jsonb], $4::mqtt_client_version, $5, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
)
|
||||
.bind("f/test/handler_mqtt").bind("u/test/mqtt")
|
||||
.bind(json!({"topic": "t", "qos": "qos0"})).bind("v5").bind("f/test/handler")
|
||||
.execute(&db).await?;
|
||||
|
||||
let handler = windmill_trigger_mqtt::MqttTrigger;
|
||||
let mut conn = db.acquire().await?;
|
||||
|
||||
let trigger = handler
|
||||
.get_trigger_by_path(&mut *conn, "test-workspace", "f/test/handler_mqtt")
|
||||
.await?;
|
||||
assert_eq!(trigger.base.permissioned_as, "u/test-user");
|
||||
|
||||
let authed = make_authed();
|
||||
let updated = handler
|
||||
.set_trigger_mode(
|
||||
&authed,
|
||||
&mut *conn,
|
||||
"test-workspace",
|
||||
"f/test/handler_mqtt",
|
||||
&TriggerMode::Disabled,
|
||||
)
|
||||
.await?;
|
||||
assert!(updated);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests handler queries for kafka trigger (EE only — OSS stub returns ()).
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_handler_queries_kafka(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO kafka_trigger (path, kafka_resource_path, group_id, topics, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, $5, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"f/test/handler_kafka",
|
||||
"u/test/kafka",
|
||||
"grp",
|
||||
&["topic"] as &[&str],
|
||||
"f/test/handler"
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let handler = windmill_trigger_kafka::KafkaTrigger;
|
||||
let mut conn = db.acquire().await?;
|
||||
|
||||
let trigger = handler
|
||||
.get_trigger_by_path(&mut *conn, "test-workspace", "f/test/handler_kafka")
|
||||
.await?;
|
||||
assert_eq!(trigger.base.permissioned_as, "u/test-user");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests handler queries for postgres trigger.
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_handler_queries_postgres(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
insert_trigger(
|
||||
&db,
|
||||
"postgres_trigger",
|
||||
"f/test/handler_pg",
|
||||
", postgres_resource_path, replication_slot_name, publication_name",
|
||||
", 'u/test/pg', 'slot', 'pub'",
|
||||
)
|
||||
.await;
|
||||
|
||||
let handler = windmill_trigger_postgres::PostgresTrigger;
|
||||
let mut conn = db.acquire().await?;
|
||||
|
||||
let trigger = handler
|
||||
.get_trigger_by_path(&mut *conn, "test-workspace", "f/test/handler_pg")
|
||||
.await?;
|
||||
assert_eq!(trigger.base.permissioned_as, "u/test-user");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests handler queries for nats trigger (EE only).
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_handler_queries_nats(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO nats_trigger (path, nats_resource_path, subjects, use_jetstream, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, $5, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"f/test/handler_nats",
|
||||
"u/test/nats",
|
||||
&["subj"] as &[&str],
|
||||
false,
|
||||
"f/test/handler"
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let handler = windmill_trigger_nats::NatsTrigger;
|
||||
let mut conn = db.acquire().await?;
|
||||
|
||||
let trigger = handler
|
||||
.get_trigger_by_path(&mut *conn, "test-workspace", "f/test/handler_nats")
|
||||
.await?;
|
||||
assert_eq!(trigger.base.permissioned_as, "u/test-user");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests handler queries for sqs trigger (EE only).
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_handler_queries_sqs(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO sqs_trigger (path, queue_url, aws_resource_path, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
"f/test/handler_sqs",
|
||||
"https://sqs.example.com/q",
|
||||
"u/test/aws",
|
||||
"f/test/handler"
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
let handler = windmill_trigger_sqs::SqsTrigger;
|
||||
let mut conn = db.acquire().await?;
|
||||
|
||||
let trigger = handler
|
||||
.get_trigger_by_path(&mut *conn, "test-workspace", "f/test/handler_sqs")
|
||||
.await?;
|
||||
assert_eq!(trigger.base.permissioned_as, "u/test-user");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tests handler queries for gcp trigger (EE only).
|
||||
#[cfg(feature = "private")]
|
||||
#[sqlx::test(fixtures("preserve_on_behalf_of"))]
|
||||
async fn test_handler_queries_gcp(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
"INSERT INTO gcp_trigger (path, gcp_resource_path, topic_id, subscription_id, \
|
||||
delivery_type, subscription_mode, \
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as) \
|
||||
VALUES ($1, $2, $3, $4, $5::delivery_mode, $6::gcp_subscription_mode, $7, false, 'test-workspace', 'test-user', 'u/test-user')",
|
||||
)
|
||||
.bind("f/test/handler_gcp").bind("u/test/gcp").bind("topic").bind("sub")
|
||||
.bind("pull").bind("existing").bind("f/test/handler")
|
||||
.execute(&db).await?;
|
||||
|
||||
let handler = windmill_trigger_gcp::GcpTrigger;
|
||||
let mut conn = db.acquire().await?;
|
||||
|
||||
let trigger = handler
|
||||
.get_trigger_by_path(&mut *conn, "test-workspace", "f/test/handler_gcp")
|
||||
.await?;
|
||||
assert_eq!(trigger.base.permissioned_as, "u/test-user");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -571,16 +571,16 @@ async fn test_volume_sql_worker_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
std::fs::write(vol_dir.join("hello.txt"), b"hello from volume")?;
|
||||
|
||||
// 3. Push the job and run with SQL-connected worker
|
||||
let code = r#"// volume: test-vol /tmp/data
|
||||
let code = r#"// volume: test-vol data
|
||||
|
||||
import { readFileSync, writeFileSync, existsSync } from "fs";
|
||||
|
||||
export function main() {
|
||||
const content = readFileSync("/tmp/data/hello.txt", "utf-8");
|
||||
writeFileSync("/tmp/data/output.txt", "written by sql worker");
|
||||
const content = readFileSync("data/hello.txt", "utf-8");
|
||||
writeFileSync("data/output.txt", "written by sql worker");
|
||||
return {
|
||||
read_content: content,
|
||||
output_exists: existsSync("/tmp/data/output.txt"),
|
||||
output_exists: existsSync("data/output.txt"),
|
||||
};
|
||||
}"#;
|
||||
|
||||
|
||||
@@ -280,9 +280,7 @@ impl AuthCache {
|
||||
folders,
|
||||
scopes: None,
|
||||
username_override,
|
||||
token_prefix: Some(
|
||||
safe_token_prefix(token),
|
||||
),
|
||||
token_prefix: Some(safe_token_prefix(token)),
|
||||
})
|
||||
} else {
|
||||
let groups = vec![name.to_string()];
|
||||
@@ -297,16 +295,17 @@ impl AuthCache {
|
||||
.unwrap_or_default();
|
||||
Some(ApiAuthed {
|
||||
email: email,
|
||||
username: format!("group-{name}"),
|
||||
username: format!(
|
||||
"{}{name}",
|
||||
windmill_common::users::USERNAME_GROUP_PREFIX
|
||||
),
|
||||
is_admin: false,
|
||||
groups,
|
||||
is_operator: false,
|
||||
folders,
|
||||
scopes: None,
|
||||
username_override,
|
||||
token_prefix: Some(
|
||||
safe_token_prefix(token),
|
||||
),
|
||||
token_prefix: Some(safe_token_prefix(token)),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
@@ -369,9 +368,7 @@ impl AuthCache {
|
||||
folders,
|
||||
scopes,
|
||||
username_override,
|
||||
token_prefix: Some(
|
||||
safe_token_prefix(token),
|
||||
),
|
||||
token_prefix: Some(safe_token_prefix(token)),
|
||||
})
|
||||
}
|
||||
None if super_admin => Some(ApiAuthed {
|
||||
@@ -383,9 +380,7 @@ impl AuthCache {
|
||||
folders: vec![],
|
||||
scopes,
|
||||
username_override,
|
||||
token_prefix: Some(
|
||||
safe_token_prefix(token),
|
||||
),
|
||||
token_prefix: Some(safe_token_prefix(token)),
|
||||
}),
|
||||
None => None,
|
||||
}
|
||||
@@ -660,7 +655,12 @@ pub async fn resolve_opt_job_authed(
|
||||
.map(|x| x.0)
|
||||
.unwrap_or_default();
|
||||
let path_vec: Vec<&str> = original_uri.path().split("/").collect();
|
||||
let workspace_id = maybe_get_workspace_id_from_path(&path_vec);
|
||||
let workspace_id = maybe_get_workspace_id_from_path(&path_vec).or_else(|| {
|
||||
parts
|
||||
.extensions
|
||||
.get::<windmill_common::db::GatewayWorkspaceId>()
|
||||
.map(|g| g.0.clone())
|
||||
});
|
||||
|
||||
if let Some(mut opt_job_authed) =
|
||||
cache.get_opt_job_authed(workspace_id.clone(), &token).await
|
||||
|
||||
@@ -452,7 +452,7 @@ async fn create_flow(
|
||||
.await?;
|
||||
if nb_flows.unwrap_or(0) >= 1000 {
|
||||
return Err(Error::BadRequest(
|
||||
"You have reached the maximum number of flows (1000) on cloud. Contact support@windmill.dev to increase the limit"
|
||||
"You have reached the maximum number of flows (1000) on cloud. Check your usage in Workspace Settings > General > Cloud Quotas. Contact support@windmill.dev to increase the limit"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -215,12 +215,12 @@ pub async fn require_is_owner(
|
||||
}
|
||||
|
||||
async fn _check_nb_of_groups(db: &DB) -> Result<()> {
|
||||
let nb_groups = sqlx::query_scalar!("SELECT COUNT(*) FROM group_ WHERE name != 'all' AND name != 'error_handler' AND name != 'slack'",)
|
||||
let nb_groups = sqlx::query_scalar!("SELECT COUNT(*) FROM group_ WHERE name != 'all' AND name != 'error_handler' AND name != 'slack' AND name != 'wm_deployers'",)
|
||||
.fetch_one(db)
|
||||
.await?;
|
||||
if nb_groups.unwrap_or(0) >= 3 {
|
||||
return Err(Error::BadRequest(
|
||||
"You have reached the maximum number of groups (3 outside of native groups 'all', 'slack' and 'error_handler') without an enterprise license"
|
||||
"You have reached the maximum number of groups (3 outside of native groups 'all', 'slack', 'error_handler' and 'wm_deployers') without an enterprise license"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -104,6 +104,8 @@ pub struct Input {
|
||||
created_by: String,
|
||||
is_public: bool,
|
||||
success: bool,
|
||||
#[serde(skip_serializing_if = "std::ops::Not::not")]
|
||||
is_preview: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow)]
|
||||
@@ -113,6 +115,7 @@ pub struct CompletedJobMini {
|
||||
args: Option<sqlx::types::Json<Box<serde_json::value::RawValue>>>,
|
||||
created_by: String,
|
||||
success: bool,
|
||||
is_preview: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -153,9 +156,10 @@ async fn get_input_history(
|
||||
let inner_limit = 2 * (per_page + offset);
|
||||
|
||||
let sql = &format!(
|
||||
"SELECT id, completed_at, created_by, args, success FROM (\
|
||||
"SELECT id, completed_at, created_by, args, success, is_preview FROM (\
|
||||
SELECT id, v2_job_completed.completed_at, created_by, 'null'::jsonb as args, \
|
||||
status = 'success' as success \
|
||||
status = 'success' as success, \
|
||||
kind IN ('preview', 'flowpreview') as is_preview \
|
||||
FROM v2_job JOIN v2_job_completed USING (id) \
|
||||
WHERE v2_job.workspace_id = $3 AND {} = $1 AND kind = any($2) \
|
||||
{args_query} AND v2_job_completed.status != 'skipped' {include_non_root} \
|
||||
@@ -209,6 +213,7 @@ async fn get_input_history(
|
||||
created_by: row.created_by,
|
||||
is_public: true,
|
||||
success: row.success,
|
||||
is_preview: row.is_preview,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,6 +311,7 @@ async fn list_saved_inputs(
|
||||
created_at: row.created_at,
|
||||
is_public: row.is_public,
|
||||
success: true,
|
||||
is_preview: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -204,14 +204,14 @@ VALUES ('test-workspace', 'u/bob/my_variable', 'bob_value', false,
|
||||
-- ============================================
|
||||
|
||||
-- Alice's personal schedule
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms)
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms, permissioned_as)
|
||||
VALUES ('test-workspace', 'u/alice/my_schedule', 'alice', NOW(), '0 * * * *', false,
|
||||
'u/alice/my_script', '{}', false, 'alice@windmill.dev', 'UTC', '{}');
|
||||
'u/alice/my_script', '{}', false, 'alice@windmill.dev', 'UTC', '{}', 'u/alice');
|
||||
|
||||
-- Bob's personal schedule
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms)
|
||||
INSERT INTO schedule (workspace_id, path, edited_by, edited_at, schedule, enabled, script_path, args, is_flow, email, timezone, extra_perms, permissioned_as)
|
||||
VALUES ('test-workspace', 'u/bob/my_schedule', 'bob', NOW(), '0 * * * *', false,
|
||||
'u/bob/my_script', '{}', false, 'bob@windmill.dev', 'UTC', '{}');
|
||||
'u/bob/my_script', '{}', false, 'bob@windmill.dev', 'UTC', '{}', 'u/bob');
|
||||
|
||||
-- ============================================
|
||||
-- APPS (with app_versions)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#[cfg(feature = "run_inline")]
|
||||
use serde_json::json;
|
||||
#[cfg(feature = "run_inline")]
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
#[cfg(feature = "run_inline")]
|
||||
use windmill_test_utils::*;
|
||||
|
||||
#[cfg(feature = "run_inline")]
|
||||
@@ -20,18 +23,22 @@ async fn init_inline_utils(port: u16) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "run_inline")]
|
||||
fn run_inline_url(port: u16, endpoint: &str) -> String {
|
||||
format!("http://localhost:{port}/api/w/test-workspace/jobs/run_inline/{endpoint}")
|
||||
}
|
||||
|
||||
#[cfg(feature = "run_inline")]
|
||||
fn client() -> reqwest::Client {
|
||||
reqwest::Client::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "run_inline")]
|
||||
fn authed(builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
|
||||
builder.header("Authorization", "Bearer SECRET_TOKEN")
|
||||
}
|
||||
|
||||
#[cfg(feature = "run_inline")]
|
||||
fn new_script(
|
||||
path: &str,
|
||||
summary: &str,
|
||||
|
||||
@@ -158,7 +158,7 @@ async fn test_mqtt_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO mqtt_trigger (
|
||||
path, mqtt_resource_path, subscribe_topics, client_version,
|
||||
script_path, is_flow, workspace_id, edited_by, email
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, ARRAY[$3::jsonb], $4::mqtt_client_version, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -171,7 +171,7 @@ async fn test_mqtt_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -240,7 +240,7 @@ async fn test_websocket_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO websocket_trigger (
|
||||
path, url, script_path, is_flow, workspace_id,
|
||||
edited_by, email, initial_messages
|
||||
edited_by, permissioned_as, initial_messages
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
"#,
|
||||
@@ -250,7 +250,7 @@ async fn test_websocket_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
&[json!({"type": "RawMessage", "content": "hello from e2e test"})]
|
||||
as &[serde_json::Value],
|
||||
)
|
||||
@@ -338,7 +338,7 @@ async fn test_postgres_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO postgres_trigger (
|
||||
path, script_path, is_flow, workspace_id, edited_by, email,
|
||||
path, script_path, is_flow, workspace_id, edited_by, permissioned_as,
|
||||
postgres_resource_path, replication_slot_name, publication_name
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
@@ -349,7 +349,7 @@ async fn test_postgres_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.bind("u/test-user/pg_res")
|
||||
.bind(&slot_name)
|
||||
.bind(&pub_name)
|
||||
@@ -410,7 +410,7 @@ async fn test_kafka_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO kafka_trigger (
|
||||
path, kafka_resource_path, topics, group_id,
|
||||
script_path, is_flow, workspace_id, edited_by, email
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -422,7 +422,7 @@ async fn test_kafka_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -502,7 +502,7 @@ async fn test_nats_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO nats_trigger (
|
||||
path, nats_resource_path, subjects, script_path,
|
||||
is_flow, workspace_id, edited_by, email, use_jetstream
|
||||
is_flow, workspace_id, edited_by, permissioned_as, use_jetstream
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -513,7 +513,7 @@ async fn test_nats_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
false,
|
||||
)
|
||||
.execute(&db)
|
||||
@@ -580,7 +580,7 @@ async fn test_sqs_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO sqs_trigger (
|
||||
path, queue_url, aws_resource_path, script_path,
|
||||
is_flow, workspace_id, edited_by, email
|
||||
is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
"#,
|
||||
@@ -591,7 +591,7 @@ async fn test_sqs_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -675,7 +675,7 @@ async fn test_gcp_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
INSERT INTO gcp_trigger (
|
||||
path, gcp_resource_path, topic_id, subscription_id,
|
||||
delivery_type, subscription_mode, script_path, is_flow,
|
||||
workspace_id, edited_by, email
|
||||
workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5::delivery_mode, $6::gcp_subscription_mode, $7, $8, $9, $10, $11)
|
||||
"#,
|
||||
@@ -690,7 +690,7 @@ async fn test_gcp_e2e(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -557,7 +557,7 @@ async fn test_http_trigger_insert_and_query(db: Pool<Postgres>) -> anyhow::Resul
|
||||
r#"
|
||||
INSERT INTO http_trigger (
|
||||
path, route_path, route_path_key, script_path, is_flow,
|
||||
workspace_id, edited_by, email, http_method,
|
||||
workspace_id, edited_by, permissioned_as, http_method,
|
||||
authentication_method, is_static_website, workspaced_route,
|
||||
wrap_body, raw_string
|
||||
)
|
||||
@@ -571,7 +571,7 @@ async fn test_http_trigger_insert_and_query(db: Pool<Postgres>) -> anyhow::Resul
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
"post" as _,
|
||||
"none" as _,
|
||||
false,
|
||||
@@ -615,7 +615,7 @@ async fn test_http_trigger_multiple_methods(db: Pool<Postgres>) -> anyhow::Resul
|
||||
r#"
|
||||
INSERT INTO http_trigger (
|
||||
path, route_path, route_path_key, script_path, is_flow,
|
||||
workspace_id, edited_by, email, http_method,
|
||||
workspace_id, edited_by, permissioned_as, http_method,
|
||||
authentication_method, is_static_website, workspaced_route,
|
||||
wrap_body, raw_string
|
||||
)
|
||||
@@ -629,7 +629,7 @@ async fn test_http_trigger_multiple_methods(db: Pool<Postgres>) -> anyhow::Resul
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
*method as _,
|
||||
"none" as _,
|
||||
false,
|
||||
@@ -662,7 +662,7 @@ async fn test_http_trigger_authentication_methods(db: Pool<Postgres>) -> anyhow:
|
||||
r#"
|
||||
INSERT INTO http_trigger (
|
||||
path, route_path, route_path_key, script_path, is_flow,
|
||||
workspace_id, edited_by, email, http_method,
|
||||
workspace_id, edited_by, permissioned_as, http_method,
|
||||
authentication_method, is_static_website, workspaced_route,
|
||||
wrap_body, raw_string
|
||||
)
|
||||
@@ -676,7 +676,7 @@ async fn test_http_trigger_authentication_methods(db: Pool<Postgres>) -> anyhow:
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
"get" as _,
|
||||
*auth as _,
|
||||
false,
|
||||
@@ -710,7 +710,7 @@ async fn test_http_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO http_trigger (
|
||||
path, route_path, route_path_key, script_path, is_flow,
|
||||
workspace_id, edited_by, email, http_method,
|
||||
workspace_id, edited_by, permissioned_as, http_method,
|
||||
authentication_method, is_static_website, workspaced_route,
|
||||
wrap_body, raw_string
|
||||
)
|
||||
@@ -724,7 +724,7 @@ async fn test_http_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
"get" as _,
|
||||
"none" as _,
|
||||
false,
|
||||
@@ -763,7 +763,7 @@ async fn test_http_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO http_trigger (
|
||||
path, route_path, route_path_key, script_path, is_flow,
|
||||
workspace_id, edited_by, email, http_method,
|
||||
workspace_id, edited_by, permissioned_as, http_method,
|
||||
authentication_method, is_static_website, workspaced_route,
|
||||
wrap_body, raw_string
|
||||
)
|
||||
@@ -777,7 +777,7 @@ async fn test_http_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
"get" as _,
|
||||
"none" as _,
|
||||
false,
|
||||
@@ -818,7 +818,7 @@ async fn test_http_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<
|
||||
r#"
|
||||
INSERT INTO http_trigger (
|
||||
path, route_path, route_path_key, script_path, is_flow,
|
||||
workspace_id, edited_by, email, http_method,
|
||||
workspace_id, edited_by, permissioned_as, http_method,
|
||||
authentication_method, is_static_website, workspaced_route,
|
||||
wrap_body, raw_string, mode
|
||||
)
|
||||
@@ -832,7 +832,7 @@ async fn test_http_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
"get" as _,
|
||||
"none" as _,
|
||||
false,
|
||||
@@ -872,7 +872,7 @@ async fn test_websocket_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
r#"
|
||||
INSERT INTO websocket_trigger (
|
||||
path, url, script_path, is_flow, workspace_id,
|
||||
edited_by, email
|
||||
edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
"#,
|
||||
@@ -882,7 +882,7 @@ async fn test_websocket_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -911,7 +911,7 @@ async fn test_kafka_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO kafka_trigger (
|
||||
path, kafka_resource_path, topics, group_id, script_path,
|
||||
is_flow, workspace_id, edited_by, email
|
||||
is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -923,7 +923,7 @@ async fn test_kafka_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -958,7 +958,7 @@ async fn test_kafka_trigger_insert_auto_commit_disabled(db: Pool<Postgres>) -> a
|
||||
r#"
|
||||
INSERT INTO kafka_trigger (
|
||||
path, kafka_resource_path, topics, group_id, script_path,
|
||||
is_flow, workspace_id, edited_by, email, auto_commit
|
||||
is_flow, workspace_id, edited_by, permissioned_as, auto_commit
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
"#,
|
||||
@@ -970,7 +970,7 @@ async fn test_kafka_trigger_insert_auto_commit_disabled(db: Pool<Postgres>) -> a
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
false,
|
||||
)
|
||||
.execute(&db)
|
||||
@@ -998,7 +998,7 @@ async fn test_postgres_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO postgres_trigger (
|
||||
path, script_path, is_flow, workspace_id, edited_by, email,
|
||||
path, script_path, is_flow, workspace_id, edited_by, permissioned_as,
|
||||
postgres_resource_path, replication_slot_name, publication_name
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
@@ -1008,7 +1008,7 @@ async fn test_postgres_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
"u/admin/pg_resource",
|
||||
"test_slot",
|
||||
"test_publication",
|
||||
@@ -1042,7 +1042,7 @@ async fn test_nats_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO nats_trigger (
|
||||
path, nats_resource_path, subjects, script_path,
|
||||
is_flow, workspace_id, edited_by, email, use_jetstream
|
||||
is_flow, workspace_id, edited_by, permissioned_as, use_jetstream
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -1053,7 +1053,7 @@ async fn test_nats_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
false,
|
||||
)
|
||||
.execute(&db)
|
||||
@@ -1085,7 +1085,7 @@ async fn test_sqs_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO sqs_trigger (
|
||||
path, queue_url, aws_resource_path, script_path,
|
||||
is_flow, workspace_id, edited_by, email
|
||||
is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
"#,
|
||||
@@ -1096,7 +1096,7 @@ async fn test_sqs_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -1136,7 +1136,7 @@ async fn test_trigger_server_state_tracking(db: Pool<Postgres>) -> anyhow::Resul
|
||||
r#"
|
||||
INSERT INTO websocket_trigger (
|
||||
path, url, script_path, is_flow, workspace_id,
|
||||
edited_by, email, server_id, error
|
||||
edited_by, permissioned_as, server_id, error
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -1146,7 +1146,7 @@ async fn test_trigger_server_state_tracking(db: Pool<Postgres>) -> anyhow::Resul
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
"server-abc-123",
|
||||
"connection refused",
|
||||
)
|
||||
@@ -1194,7 +1194,7 @@ async fn test_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO websocket_trigger (
|
||||
path, url, script_path, is_flow, workspace_id,
|
||||
edited_by, email, mode
|
||||
edited_by, permissioned_as, mode
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8::trigger_mode)
|
||||
"#,
|
||||
@@ -1204,7 +1204,7 @@ async fn test_trigger_mode_filtering(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
*mode as _,
|
||||
)
|
||||
.execute(&db)
|
||||
@@ -1276,9 +1276,9 @@ async fn test_schedule_insert_and_query(db: Pool<Postgres>) -> anyhow::Result<()
|
||||
r#"
|
||||
INSERT INTO schedule (
|
||||
workspace_id, path, edited_by, schedule, enabled,
|
||||
script_path, is_flow, email, timezone
|
||||
script_path, is_flow, email, timezone, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
"#,
|
||||
"test-workspace",
|
||||
"f/test/my_schedule",
|
||||
@@ -1289,6 +1289,7 @@ async fn test_schedule_insert_and_query(db: Pool<Postgres>) -> anyhow::Result<()
|
||||
false,
|
||||
"test@windmill.dev",
|
||||
"UTC",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -1324,7 +1325,7 @@ async fn test_mqtt_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO mqtt_trigger (
|
||||
path, mqtt_resource_path, subscribe_topics, client_version,
|
||||
script_path, is_flow, workspace_id, edited_by, email
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, ARRAY[$3::jsonb], $4::mqtt_client_version, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -1337,7 +1338,7 @@ async fn test_mqtt_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -1368,7 +1369,7 @@ async fn test_mqtt_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO mqtt_trigger (
|
||||
path, mqtt_resource_path, subscribe_topics, client_version,
|
||||
script_path, is_flow, workspace_id, edited_by, email
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, ARRAY[$3::jsonb], $4::mqtt_client_version, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -1381,7 +1382,7 @@ async fn test_mqtt_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -1413,7 +1414,7 @@ async fn test_mqtt_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO mqtt_trigger (
|
||||
path, mqtt_resource_path, subscribe_topics, client_version,
|
||||
script_path, is_flow, workspace_id, edited_by, email
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, ARRAY[$3::jsonb], $4::mqtt_client_version, $5, $6, $7, $8, $9)
|
||||
"#,
|
||||
@@ -1426,7 +1427,7 @@ async fn test_mqtt_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -1462,7 +1463,7 @@ async fn test_gcp_trigger_insert_pull(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
INSERT INTO gcp_trigger (
|
||||
path, gcp_resource_path, topic_id, subscription_id,
|
||||
delivery_type, subscription_mode, script_path, is_flow,
|
||||
workspace_id, edited_by, email
|
||||
workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5::delivery_mode, $6::gcp_subscription_mode, $7, $8, $9, $10, $11)
|
||||
"#,
|
||||
@@ -1477,7 +1478,7 @@ async fn test_gcp_trigger_insert_pull(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -1513,7 +1514,7 @@ async fn test_gcp_trigger_insert_push(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
INSERT INTO gcp_trigger (
|
||||
path, gcp_resource_path, topic_id, subscription_id,
|
||||
delivery_type, delivery_config, subscription_mode,
|
||||
script_path, is_flow, workspace_id, edited_by, email
|
||||
script_path, is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5::delivery_mode, $6::jsonb, $7::gcp_subscription_mode, $8, $9, $10, $11, $12)
|
||||
"#,
|
||||
@@ -1529,7 +1530,7 @@ async fn test_gcp_trigger_insert_push(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -1562,7 +1563,7 @@ async fn test_gcp_trigger_unique_constraint(db: Pool<Postgres>) -> anyhow::Resul
|
||||
INSERT INTO gcp_trigger (
|
||||
path, gcp_resource_path, topic_id, subscription_id,
|
||||
delivery_type, subscription_mode, script_path, is_flow,
|
||||
workspace_id, edited_by, email
|
||||
workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5::delivery_mode, $6::gcp_subscription_mode, $7, $8, $9, $10, $11)
|
||||
"#;
|
||||
@@ -1578,7 +1579,7 @@ async fn test_gcp_trigger_unique_constraint(db: Pool<Postgres>) -> anyhow::Resul
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -1594,7 +1595,7 @@ async fn test_gcp_trigger_unique_constraint(db: Pool<Postgres>) -> anyhow::Resul
|
||||
.bind(false)
|
||||
.bind("test-workspace")
|
||||
.bind("test-user")
|
||||
.bind("test@windmill.dev")
|
||||
.bind("u/test-user")
|
||||
.execute(&db)
|
||||
.await;
|
||||
|
||||
@@ -1616,7 +1617,7 @@ async fn test_email_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO email_trigger (
|
||||
path, local_part, workspaced_local_part, script_path,
|
||||
is_flow, workspace_id, edited_by, email
|
||||
is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
"#,
|
||||
@@ -1627,7 +1628,7 @@ async fn test_email_trigger_insert(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -1659,7 +1660,7 @@ async fn test_email_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO email_trigger (
|
||||
path, local_part, workspaced_local_part, script_path,
|
||||
is_flow, workspace_id, edited_by, email
|
||||
is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
"#,
|
||||
@@ -1670,7 +1671,7 @@ async fn test_email_trigger_update(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
@@ -1705,7 +1706,7 @@ async fn test_email_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
r#"
|
||||
INSERT INTO email_trigger (
|
||||
path, local_part, workspaced_local_part, script_path,
|
||||
is_flow, workspace_id, edited_by, email
|
||||
is_flow, workspace_id, edited_by, permissioned_as
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
"#,
|
||||
@@ -1716,7 +1717,7 @@ async fn test_email_trigger_delete(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
false,
|
||||
"test-workspace",
|
||||
"test-user",
|
||||
"test@windmill.dev",
|
||||
"u/test-user",
|
||||
)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
@@ -33,42 +33,22 @@ use windmill_common::{
|
||||
use windmill_git_sync::{handle_deployment_metadata, DeployedObject};
|
||||
use windmill_queue::schedule::push_scheduled_job;
|
||||
|
||||
/// Resolves the email to use for a schedule based on preservation settings.
|
||||
/// When preserving, looks up the email from the provided username.
|
||||
async fn resolve_email(
|
||||
username: Option<&String>,
|
||||
preserve_email: Option<bool>,
|
||||
authed: &ApiAuthed,
|
||||
db: &DB,
|
||||
w_id: &str,
|
||||
) -> Result<String> {
|
||||
if let Some(username) = username {
|
||||
if preserve_email.unwrap_or(false) && can_preserve_on_behalf_of(authed) {
|
||||
let email = sqlx::query_scalar!(
|
||||
"SELECT email FROM usr WHERE username = $1 AND workspace_id = $2",
|
||||
username,
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(db)
|
||||
.await?;
|
||||
if let Some(email) = email {
|
||||
return Ok(email);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(authed.email.clone())
|
||||
}
|
||||
|
||||
fn resolve_edited_by(
|
||||
username: Option<&String>,
|
||||
preserve_edited_by: Option<bool>,
|
||||
/// Resolves the permissioned_as value for a schedule.
|
||||
/// When preserving, uses the provided permissioned_as value directly.
|
||||
fn resolve_permissioned_as(
|
||||
permissioned_as: Option<&String>,
|
||||
preserve_permissioned_as: Option<bool>,
|
||||
authed: &ApiAuthed,
|
||||
) -> String {
|
||||
if let Some(username) = username {
|
||||
if preserve_edited_by.unwrap_or(false) && can_preserve_on_behalf_of(authed) {
|
||||
return username.clone();
|
||||
if let Some(permissioned_as) = permissioned_as {
|
||||
if preserve_permissioned_as.unwrap_or(false) && can_preserve_on_behalf_of(authed) {
|
||||
return permissioned_as.clone();
|
||||
}
|
||||
}
|
||||
windmill_common::users::username_to_permissioned_as(&authed.username)
|
||||
}
|
||||
|
||||
fn resolve_edited_by(authed: &ApiAuthed) -> String {
|
||||
authed.username.clone()
|
||||
}
|
||||
|
||||
@@ -117,8 +97,8 @@ pub struct NewSchedule {
|
||||
pub paused_until: Option<DateTime<Utc>>,
|
||||
pub cron_version: Option<String>,
|
||||
pub dynamic_skip: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub preserve_email: Option<bool>,
|
||||
pub permissioned_as: Option<String>,
|
||||
pub preserve_permissioned_as: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -244,14 +224,26 @@ async fn create_schedule(
|
||||
validate_dynamic_skip(&mut tx, &w_id, handler_path).await?;
|
||||
}
|
||||
|
||||
let resolved_edited_by = resolve_edited_by(ns.email.as_ref(), ns.preserve_email, &authed);
|
||||
let resolved_edited_by = resolve_edited_by(&authed);
|
||||
let resolved_permissioned_as = resolve_permissioned_as(
|
||||
ns.permissioned_as.as_ref(),
|
||||
ns.preserve_permissioned_as,
|
||||
&authed,
|
||||
);
|
||||
// email is still written for backwards compat with old workers that don't know about permissioned_as
|
||||
let resolved_email = windmill_common::users::get_email_from_permissioned_as(
|
||||
&resolved_permissioned_as,
|
||||
&w_id,
|
||||
&db,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let schedule = sqlx::query_as!(
|
||||
Schedule,
|
||||
r#"
|
||||
INSERT INTO schedule (
|
||||
workspace_id, path, schedule, timezone, edited_by, script_path,
|
||||
is_flow, args, enabled, email,
|
||||
is_flow, args, enabled, email, permissioned_as,
|
||||
on_failure, on_failure_times, on_failure_exact, on_failure_extra_args,
|
||||
on_recovery, on_recovery_times, on_recovery_extra_args,
|
||||
on_success, on_success_extra_args,
|
||||
@@ -259,12 +251,12 @@ async fn create_schedule(
|
||||
tag, paused_until, cron_version, description, dynamic_skip
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6,
|
||||
$7, $8, $9, $10,
|
||||
$11, $12, $13, $14,
|
||||
$15, $16, $17,
|
||||
$18, $19,
|
||||
$20, $21, $22, $23,
|
||||
$24, $25, $26, $27, $28
|
||||
$7, $8, $9, $10, $11,
|
||||
$12, $13, $14, $15,
|
||||
$16, $17, $18,
|
||||
$19, $20,
|
||||
$21, $22, $23, $24,
|
||||
$25, $26, $27, $28, $29
|
||||
)
|
||||
RETURNING
|
||||
workspace_id,
|
||||
@@ -279,6 +271,7 @@ async fn create_schedule(
|
||||
args AS "args: _",
|
||||
extra_perms,
|
||||
email,
|
||||
permissioned_as,
|
||||
error,
|
||||
on_failure,
|
||||
on_failure_times,
|
||||
@@ -309,7 +302,8 @@ async fn create_schedule(
|
||||
to_json_raw_opt(ns.args.as_ref())
|
||||
as Option<sqlx::types::Json<Box<serde_json::value::RawValue>>>,
|
||||
ns.enabled.unwrap_or(false),
|
||||
resolve_email(ns.email.as_ref(), ns.preserve_email, &authed, &db, &w_id).await?,
|
||||
resolved_email,
|
||||
resolved_permissioned_as,
|
||||
ns.on_failure,
|
||||
ns.on_failure_times,
|
||||
ns.on_failure_exact,
|
||||
@@ -355,8 +349,8 @@ async fn create_schedule(
|
||||
)
|
||||
.await?;
|
||||
if let Some(on_behalf_of) = windmill_common::check_on_behalf_of_preservation(
|
||||
ns.email.as_deref(),
|
||||
ns.preserve_email.unwrap_or(false),
|
||||
ns.permissioned_as.as_deref(),
|
||||
ns.preserve_permissioned_as.unwrap_or(false),
|
||||
&authed,
|
||||
&authed.username,
|
||||
) {
|
||||
@@ -421,7 +415,28 @@ async fn edit_schedule(
|
||||
|
||||
clear_schedule(&mut tx, path, &w_id).await?;
|
||||
|
||||
let resolved_edited_by = resolve_edited_by(es.email.as_ref(), es.preserve_email, &authed);
|
||||
let resolved_edited_by = resolve_edited_by(&authed);
|
||||
|
||||
let resolved_permissioned_as = resolve_permissioned_as(
|
||||
es.permissioned_as.as_ref(),
|
||||
es.preserve_permissioned_as,
|
||||
&authed,
|
||||
);
|
||||
|
||||
// email is still written for backwards compat with old workers that don't know about permissioned_as.
|
||||
// When permissioned_as is preserved to a different user, derive email from it.
|
||||
let resolved_email = if resolved_permissioned_as
|
||||
!= windmill_common::users::username_to_permissioned_as(&authed.username)
|
||||
{
|
||||
windmill_common::users::get_email_from_permissioned_as(
|
||||
&resolved_permissioned_as,
|
||||
&w_id,
|
||||
&db,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
authed.email.clone()
|
||||
};
|
||||
|
||||
let schedule = sqlx::query_as!(
|
||||
Schedule,
|
||||
@@ -450,8 +465,9 @@ async fn edit_schedule(
|
||||
cron_version = COALESCE($21, cron_version),
|
||||
description = $22,
|
||||
dynamic_skip = $23,
|
||||
email = COALESCE($24, email),
|
||||
edited_by = $25
|
||||
email = $24,
|
||||
edited_by = $25,
|
||||
permissioned_as = $26
|
||||
WHERE path = $19 AND workspace_id = $20
|
||||
RETURNING
|
||||
workspace_id,
|
||||
@@ -466,6 +482,7 @@ async fn edit_schedule(
|
||||
args AS "args: _",
|
||||
extra_perms,
|
||||
email,
|
||||
permissioned_as,
|
||||
error,
|
||||
on_failure,
|
||||
on_failure_times,
|
||||
@@ -513,8 +530,9 @@ async fn edit_schedule(
|
||||
es.cron_version,
|
||||
es.description,
|
||||
es.dynamic_skip,
|
||||
Some(resolve_email(es.email.as_ref(), es.preserve_email, &authed, &db, &w_id).await?),
|
||||
resolved_edited_by
|
||||
resolved_email,
|
||||
resolved_edited_by,
|
||||
resolved_permissioned_as
|
||||
)
|
||||
.fetch_one(&mut *tx)
|
||||
.await
|
||||
@@ -535,9 +553,10 @@ async fn edit_schedule(
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(on_behalf_of) = windmill_common::check_on_behalf_of_preservation(
|
||||
es.email.as_deref(),
|
||||
es.preserve_email.unwrap_or(false),
|
||||
es.permissioned_as.as_deref(),
|
||||
es.preserve_permissioned_as.unwrap_or(false),
|
||||
&authed,
|
||||
&authed.username,
|
||||
) {
|
||||
@@ -547,14 +566,8 @@ async fn edit_schedule(
|
||||
"schedule.on_behalf_of",
|
||||
ActionKind::Update,
|
||||
&w_id,
|
||||
Some(path),
|
||||
Some(
|
||||
[
|
||||
("on_behalf_of", on_behalf_of.as_str()),
|
||||
("action", "update"),
|
||||
]
|
||||
.into(),
|
||||
),
|
||||
Some(&path.to_string()),
|
||||
Some([("on_behalf_of", on_behalf_of.as_str()), ("action", "edit")].into()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
@@ -788,6 +801,7 @@ pub async fn set_enabled(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let path = path.to_path();
|
||||
check_scopes(&authed, || format!("schedules:write:{}", path))?;
|
||||
// email is still written for backwards compat with old workers that don't know about permissioned_as
|
||||
let schedule_o = sqlx::query_as!(
|
||||
Schedule,
|
||||
r#"
|
||||
@@ -808,6 +822,7 @@ pub async fn set_enabled(
|
||||
args AS "args: _",
|
||||
extra_perms,
|
||||
email,
|
||||
permissioned_as,
|
||||
error,
|
||||
on_failure,
|
||||
on_failure_times,
|
||||
@@ -1179,8 +1194,8 @@ pub struct EditSchedule {
|
||||
pub paused_until: Option<DateTime<Utc>>,
|
||||
pub cron_version: Option<String>,
|
||||
pub dynamic_skip: Option<String>,
|
||||
pub email: Option<String>,
|
||||
pub preserve_email: Option<bool>,
|
||||
pub permissioned_as: Option<String>,
|
||||
pub preserve_permissioned_as: Option<bool>,
|
||||
}
|
||||
|
||||
pub use windmill_queue::schedule::clear_schedule;
|
||||
|
||||
@@ -636,7 +636,7 @@ async fn create_script_internal<'c>(
|
||||
.await?;
|
||||
if nb_scripts.unwrap_or(0) >= 5000 {
|
||||
return Err(Error::BadRequest(
|
||||
"You have reached the maximum number of scripts (5000) on cloud. Contact support@windmill.dev to increase the limit"
|
||||
"You have reached the maximum number of scripts (5000) on cloud. Check your usage in Workspace Settings > General > Cloud Quotas. Contact support@windmill.dev to increase the limit"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -972,8 +972,7 @@ async fn setup_custom_instance_pg_database_inner(
|
||||
GRANT CREATE ON DATABASE \"{dbname}\" TO custom_instance_user;
|
||||
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO custom_instance_user;
|
||||
ALTER ROLE custom_instance_user CREATEROLE;
|
||||
ALTER ROLE custom_instance_user REPLICATION;"
|
||||
ALTER ROLE custom_instance_user CREATEROLE;"
|
||||
))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@@ -982,6 +981,14 @@ async fn setup_custom_instance_pg_database_inner(
|
||||
e.to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
if let Err(e) = client
|
||||
.batch_execute(&format!("ALTER ROLE custom_instance_user REPLICATION;"))
|
||||
.await
|
||||
{
|
||||
tracing::error!("Failed to grant replication permission to custom_instance_user: {e:#}");
|
||||
}
|
||||
|
||||
logs.grant_permissions = "OK".to_string();
|
||||
|
||||
drop(client); // /!\ Drop before joining to avoid deadlock
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
pub mod users;
|
||||
pub mod users_oss;
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use windmill_api_auth::ApiAuthed;
|
||||
|
||||
use crate::users::{EditPassword, NewUser};
|
||||
|
||||
use windmill_common::webhook::WebhookShared;
|
||||
use windmill_common::DB;
|
||||
|
||||
use argon2::Argon2;
|
||||
|
||||
use axum::{extract::Extension, Json};
|
||||
|
||||
use http::StatusCode;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use windmill_common::error::{Error, Result};
|
||||
|
||||
pub async fn create_user(
|
||||
_authed: ApiAuthed,
|
||||
_db: DB,
|
||||
_webhook: WebhookShared,
|
||||
_argon2: Arc<Argon2<'_>>,
|
||||
mut _nu: NewUser,
|
||||
) -> Result<(StatusCode, String)> {
|
||||
Err(Error::internal_err(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn set_password(
|
||||
_db: DB,
|
||||
_argon2: Arc<Argon2<'_>>,
|
||||
_authed: ApiAuthed,
|
||||
_user_email: &str,
|
||||
_ep: EditPassword,
|
||||
) -> Result<String> {
|
||||
Err(Error::internal_err(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn hash_password(_argon2: Arc<Argon2<'_>>, _password: String) -> Result<String> {
|
||||
Err(Error::internal_err(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct OnboardingData {
|
||||
pub touch_point: String,
|
||||
pub use_case: String,
|
||||
}
|
||||
|
||||
pub async fn submit_onboarding_data(
|
||||
_authed: ApiAuthed,
|
||||
Extension(_db): Extension<DB>,
|
||||
Json(_data): Json<OnboardingData>,
|
||||
) -> Result<String> {
|
||||
Err(Error::internal_err(
|
||||
"Not implemented in Windmill's Open Source repository".to_string(),
|
||||
))
|
||||
}
|
||||
@@ -158,6 +158,8 @@ pub fn workspaced_service() -> Router {
|
||||
post(update_protection_rule).delete(delete_protection_rule),
|
||||
)
|
||||
.route("/log_chat", post(log_ai_chat))
|
||||
.route("/cloud_quotas", get(get_cloud_quotas))
|
||||
.route("/prune_versions", post(prune_versions))
|
||||
}
|
||||
pub fn global_service() -> Router {
|
||||
Router::new()
|
||||
@@ -3249,10 +3251,12 @@ async fn clone_apps(
|
||||
app_id_mapping.insert(app.id, new_app_id);
|
||||
}
|
||||
|
||||
let mut version_id_mapping: HashMap<i64, i64> = HashMap::new();
|
||||
|
||||
{
|
||||
// Clone app versions
|
||||
let app_versions = sqlx::query!(
|
||||
"SELECT app_id, value, created_by, created_at, raw_app
|
||||
"SELECT id, app_id, value, created_by, created_at, raw_app
|
||||
FROM app_version
|
||||
WHERE app_id = ANY(SELECT id FROM app WHERE workspace_id = $1)
|
||||
ORDER BY app_id, created_at",
|
||||
@@ -3263,15 +3267,45 @@ async fn clone_apps(
|
||||
|
||||
for version in app_versions {
|
||||
if let Some(&new_app_id) = app_id_mapping.get(&version.app_id) {
|
||||
sqlx::query!(
|
||||
let new_version_id = sqlx::query_scalar!(
|
||||
"INSERT INTO app_version (app_id, value, created_by, created_at, raw_app)
|
||||
VALUES ($1, $2, $3, $4, $5)",
|
||||
VALUES ($1, $2, $3, $4, $5) RETURNING id",
|
||||
new_app_id,
|
||||
version.value,
|
||||
version.created_by,
|
||||
version.created_at,
|
||||
version.raw_app,
|
||||
)
|
||||
.fetch_one(&mut **tx)
|
||||
.await?;
|
||||
|
||||
version_id_mapping.insert(version.id, new_version_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone app bundles for raw apps
|
||||
if !version_id_mapping.is_empty() {
|
||||
let old_ids: Vec<i64> = version_id_mapping.keys().copied().collect();
|
||||
let bundles = sqlx::query!(
|
||||
"SELECT app_version_id, file_type, data FROM app_bundles
|
||||
WHERE app_version_id = ANY($1) AND w_id = $2",
|
||||
&old_ids,
|
||||
source_workspace_id
|
||||
)
|
||||
.fetch_all(&mut **tx)
|
||||
.await?;
|
||||
|
||||
for bundle in bundles {
|
||||
if let Some(&new_version_id) = version_id_mapping.get(&bundle.app_version_id) {
|
||||
sqlx::query!(
|
||||
"INSERT INTO app_bundles (app_version_id, w_id, file_type, data)
|
||||
VALUES ($1, $2, $3, $4)",
|
||||
new_version_id,
|
||||
target_workspace_id,
|
||||
bundle.file_type,
|
||||
bundle.data,
|
||||
)
|
||||
.execute(&mut **tx)
|
||||
.await?;
|
||||
}
|
||||
@@ -4739,7 +4773,7 @@ async fn compare_workspaces(
|
||||
compare_two_flows(&db, &source_workspace_id, &fork_workspace_id, &item.path)
|
||||
.await?,
|
||||
),
|
||||
"app" => Some(
|
||||
"app" | "raw_app" => Some(
|
||||
compare_two_apps(&db, &source_workspace_id, &fork_workspace_id, &item.path).await?,
|
||||
),
|
||||
"resource" => Some(
|
||||
@@ -4834,7 +4868,10 @@ async fn compare_workspaces(
|
||||
.fold(0, |acc, s| acc + s.try_into().unwrap_or(0)),
|
||||
scripts_changed: visible_diffs.iter().filter(|s| s.kind == "script").count(),
|
||||
flows_changed: visible_diffs.iter().filter(|s| s.kind == "flow").count(),
|
||||
apps_changed: visible_diffs.iter().filter(|s| s.kind == "app").count(),
|
||||
apps_changed: visible_diffs
|
||||
.iter()
|
||||
.filter(|s| s.kind == "app" || s.kind == "raw_app")
|
||||
.count(),
|
||||
resources_changed: visible_diffs
|
||||
.iter()
|
||||
.filter(|s| s.kind == "resource")
|
||||
@@ -4943,7 +4980,7 @@ async fn query_visible_items<'c>(
|
||||
.fetch_all(&mut **tx)
|
||||
.await?
|
||||
}
|
||||
"app" => {
|
||||
"app" | "raw_app" => {
|
||||
sqlx::query_scalar!(
|
||||
"SELECT path FROM app
|
||||
WHERE workspace_id = $1 AND path = ANY($2)",
|
||||
@@ -5398,3 +5435,199 @@ async fn log_ai_chat(
|
||||
.await?;
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct QuotaInfo {
|
||||
used: i64,
|
||||
limit: i64,
|
||||
prunable: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct CloudQuotas {
|
||||
scripts: QuotaInfo,
|
||||
flows: QuotaInfo,
|
||||
apps: QuotaInfo,
|
||||
variables: QuotaInfo,
|
||||
resources: QuotaInfo,
|
||||
}
|
||||
|
||||
async fn get_cloud_quotas(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
) -> JsonResult<CloudQuotas> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
if !*CLOUD_HOSTED {
|
||||
return Err(Error::BadRequest(
|
||||
"Cloud quotas are only available on cloud-hosted instances".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let scripts_used =
|
||||
sqlx::query_scalar!("SELECT COUNT(*) FROM script WHERE workspace_id = $1", &w_id)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let scripts_prunable = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM script s WHERE s.workspace_id = $1 AND s.hash NOT IN (
|
||||
SELECT DISTINCT ON (path) hash FROM script
|
||||
WHERE workspace_id = $1 AND deleted = false AND draft_only IS NOT TRUE
|
||||
ORDER BY path, created_at DESC
|
||||
)",
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let flows_used =
|
||||
sqlx::query_scalar!("SELECT COUNT(*) FROM flow WHERE workspace_id = $1", &w_id)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let flows_prunable = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM flow_version fv
|
||||
JOIN flow f ON f.workspace_id = fv.workspace_id AND f.path = fv.path
|
||||
WHERE fv.workspace_id = $1 AND fv.id != f.versions[array_upper(f.versions, 1)]",
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let apps_used = sqlx::query_scalar!("SELECT COUNT(*) FROM app WHERE workspace_id = $1", &w_id)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let apps_prunable = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM app_version av
|
||||
JOIN app a ON a.id = av.app_id
|
||||
WHERE a.workspace_id = $1 AND av.id != a.versions[array_upper(a.versions, 1)]",
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let variables_used = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM variable WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
let resources_used = sqlx::query_scalar!(
|
||||
"SELECT COUNT(*) FROM resource WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(Json(CloudQuotas {
|
||||
scripts: QuotaInfo { used: scripts_used, limit: 5000, prunable: scripts_prunable },
|
||||
flows: QuotaInfo { used: flows_used, limit: 1000, prunable: flows_prunable },
|
||||
apps: QuotaInfo { used: apps_used, limit: 1000, prunable: apps_prunable },
|
||||
variables: QuotaInfo { used: variables_used, limit: 10000, prunable: 0 },
|
||||
resources: QuotaInfo { used: resources_used, limit: 10000, prunable: 0 },
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PruneVersionsRequest {
|
||||
resource_type: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct PruneVersionsResponse {
|
||||
pruned: u64,
|
||||
}
|
||||
|
||||
async fn prune_versions(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Path(w_id): Path<String>,
|
||||
Json(req): Json<PruneVersionsRequest>,
|
||||
) -> JsonResult<PruneVersionsResponse> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
if !*CLOUD_HOSTED {
|
||||
return Err(Error::BadRequest(
|
||||
"Version pruning is only available on cloud-hosted instances".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let pruned = match req.resource_type.as_str() {
|
||||
"scripts" => {
|
||||
let result = sqlx::query(
|
||||
"DELETE FROM script
|
||||
WHERE workspace_id = $1 AND hash NOT IN (
|
||||
SELECT DISTINCT ON (path) hash FROM script
|
||||
WHERE workspace_id = $1 AND deleted = false AND draft_only IS NOT TRUE
|
||||
ORDER BY path, created_at DESC
|
||||
)",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
result.rows_affected()
|
||||
}
|
||||
"flows" => {
|
||||
let deleted = sqlx::query(
|
||||
"DELETE FROM flow_version fv
|
||||
USING flow f
|
||||
WHERE fv.workspace_id = f.workspace_id AND fv.path = f.path
|
||||
AND fv.workspace_id = $1
|
||||
AND fv.id != f.versions[array_upper(f.versions, 1)]",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
sqlx::query(
|
||||
"UPDATE flow SET versions = ARRAY[versions[array_upper(versions, 1)]]
|
||||
WHERE workspace_id = $1 AND array_length(versions, 1) > 1",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
deleted.rows_affected()
|
||||
}
|
||||
"apps" => {
|
||||
let deleted = sqlx::query(
|
||||
"DELETE FROM app_version av
|
||||
USING app a
|
||||
WHERE av.app_id = a.id AND a.workspace_id = $1
|
||||
AND av.id != a.versions[array_upper(a.versions, 1)]",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
sqlx::query(
|
||||
"UPDATE app SET versions = ARRAY[versions[array_upper(versions, 1)]]
|
||||
WHERE workspace_id = $1 AND array_length(versions, 1) > 1",
|
||||
)
|
||||
.bind(&w_id)
|
||||
.execute(&db)
|
||||
.await?;
|
||||
|
||||
deleted.rows_affected()
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"Invalid resource type '{}'. Must be 'scripts', 'flows', or 'apps'",
|
||||
req.resource_type
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PruneVersionsResponse { pruned }))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: "3.0.3"
|
||||
|
||||
info:
|
||||
version: 1.659.1
|
||||
version: 1.662.0
|
||||
title: Windmill API
|
||||
|
||||
contact:
|
||||
@@ -4491,6 +4491,76 @@ paths:
|
||||
responses:
|
||||
"204":
|
||||
description: logged
|
||||
|
||||
/w/{workspace}/workspaces/cloud_quotas:
|
||||
get:
|
||||
summary: get cloud quota usage and limits for workspace
|
||||
operationId: getCloudQuotas
|
||||
tags:
|
||||
- workspace
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
responses:
|
||||
"200":
|
||||
description: cloud quota usage and limits
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
scripts:
|
||||
$ref: "#/components/schemas/QuotaInfo"
|
||||
flows:
|
||||
$ref: "#/components/schemas/QuotaInfo"
|
||||
apps:
|
||||
$ref: "#/components/schemas/QuotaInfo"
|
||||
variables:
|
||||
$ref: "#/components/schemas/QuotaInfo"
|
||||
resources:
|
||||
$ref: "#/components/schemas/QuotaInfo"
|
||||
required:
|
||||
- scripts
|
||||
- flows
|
||||
- apps
|
||||
- variables
|
||||
- resources
|
||||
|
||||
/w/{workspace}/workspaces/prune_versions:
|
||||
post:
|
||||
summary: prune old versions of scripts, flows, or apps
|
||||
operationId: pruneVersions
|
||||
tags:
|
||||
- workspace
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
resource_type:
|
||||
type: string
|
||||
enum:
|
||||
- scripts
|
||||
- flows
|
||||
- apps
|
||||
required:
|
||||
- resource_type
|
||||
responses:
|
||||
"200":
|
||||
description: number of pruned versions
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
pruned:
|
||||
type: integer
|
||||
required:
|
||||
- pruned
|
||||
|
||||
/w/{workspace}/workspaces/public_app_rate_limit:
|
||||
post:
|
||||
summary: Set public app rate limit for this workspace
|
||||
@@ -20413,6 +20483,9 @@ components:
|
||||
email:
|
||||
type: string
|
||||
description: Email of the user who owns this schedule, used for permissioned_as
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: The user or group this schedule runs as (e.g., 'u/admin' or 'g/mygroup')
|
||||
error:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -20495,6 +20568,7 @@ components:
|
||||
- is_flow
|
||||
- enabled
|
||||
- email
|
||||
- permissioned_as
|
||||
|
||||
ScheduleWJobs:
|
||||
allOf:
|
||||
@@ -20616,12 +20690,12 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Path to a script that validates scheduled datetimes. Receives scheduled_for datetime and returns boolean to skip (true) or run (false)
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who the scheduled jobs run as. Used during deployment to preserve the original schedule owner.
|
||||
preserve_email:
|
||||
description: The user or group this schedule runs as. Used during deployment to preserve the original schedule owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- schedule
|
||||
@@ -20709,12 +20783,14 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
description: Path to a script that validates scheduled datetimes. Receives scheduled_for datetime and returns boolean to skip (true) or run (false)
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who the scheduled jobs run as. Used during deployment to preserve the original schedule owner.
|
||||
preserve_email:
|
||||
nullable: true
|
||||
description: "The user or group this schedule runs as (e.g., 'u/admin' or 'g/mygroup'). Only admins and wm_deployers can set this via preserve_permissioned_as."
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
nullable: true
|
||||
description: "If true and user is admin/wm_deployers, preserve the provided permissioned_as instead of using the deploying user's identity"
|
||||
required:
|
||||
- schedule
|
||||
- timezone
|
||||
@@ -20755,9 +20831,9 @@ components:
|
||||
script_path:
|
||||
type: string
|
||||
description: Path to the script or flow to execute when triggered
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who owns this trigger, used for permissioned_as
|
||||
description: The user or group this trigger runs as (permissioned_as)
|
||||
extra_perms:
|
||||
type: object
|
||||
description: Additional permissions for this trigger
|
||||
@@ -20782,7 +20858,7 @@ components:
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
- email
|
||||
- permissioned_as
|
||||
- extra_perms
|
||||
- workspace_id
|
||||
- edited_by
|
||||
@@ -21071,12 +21147,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
|
||||
required:
|
||||
- path
|
||||
@@ -21163,12 +21239,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -21331,12 +21407,12 @@ components:
|
||||
retry:
|
||||
description: Retry configuration for failed executions
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
|
||||
required:
|
||||
- path
|
||||
@@ -21399,12 +21475,12 @@ components:
|
||||
retry:
|
||||
description: Retry configuration for failed executions
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
|
||||
required:
|
||||
- path
|
||||
@@ -21574,12 +21650,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -21634,12 +21710,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -21776,12 +21852,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: "Retry configuration for failed executions."
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -21944,12 +22020,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- queue_url
|
||||
- aws_resource_path
|
||||
@@ -21996,12 +22072,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- queue_url
|
||||
- aws_resource_path
|
||||
@@ -22160,12 +22236,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -22208,12 +22284,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -22343,12 +22419,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
|
||||
required:
|
||||
- path
|
||||
@@ -22413,12 +22489,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
|
||||
required:
|
||||
- path
|
||||
@@ -22520,12 +22596,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
|
||||
required:
|
||||
- path
|
||||
@@ -22575,12 +22651,12 @@ components:
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
description: Retry configuration for failed executions
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -22629,12 +22705,12 @@ components:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
mode:
|
||||
$ref: "#/components/schemas/TriggerMode"
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
|
||||
required:
|
||||
- path
|
||||
@@ -22661,12 +22737,12 @@ components:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
retry:
|
||||
$ref: "../../openflow.openapi.yaml#/components/schemas/Retry"
|
||||
email:
|
||||
permissioned_as:
|
||||
type: string
|
||||
description: Email of the user who triggered jobs run as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_email:
|
||||
description: The user or group this trigger runs as. Used during deployment to preserve the original trigger owner.
|
||||
preserve_permissioned_as:
|
||||
type: boolean
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original email value instead of overwriting it."
|
||||
description: "When true and the caller is a member of the 'wm_deployers' group, preserves the original permissioned_as value instead of overwriting it."
|
||||
required:
|
||||
- path
|
||||
- script_path
|
||||
@@ -24092,7 +24168,7 @@ components:
|
||||
kind:
|
||||
type: string
|
||||
enum:
|
||||
["script", "flow", "app", "resource", "variable", "resource_type"]
|
||||
["script", "flow", "app", "raw_app", "resource", "variable", "resource_type"]
|
||||
description: Type of the item
|
||||
path:
|
||||
type: string
|
||||
@@ -24394,6 +24470,19 @@ components:
|
||||
description: Users that can bypass this ruleset
|
||||
items:
|
||||
type: string
|
||||
QuotaInfo:
|
||||
type: object
|
||||
properties:
|
||||
used:
|
||||
type: integer
|
||||
limit:
|
||||
type: integer
|
||||
prunable:
|
||||
type: integer
|
||||
required:
|
||||
- used
|
||||
- limit
|
||||
- prunable
|
||||
NativeServiceName:
|
||||
type: string
|
||||
enum:
|
||||
|
||||
@@ -1179,7 +1179,7 @@ async fn create_app_internal<'a>(
|
||||
.await?;
|
||||
if nb_apps.unwrap_or(0) >= 1000 {
|
||||
return Err(Error::BadRequest(
|
||||
"You have reached the maximum number of apps (1000) on cloud. Contact support@windmill.dev to increase the limit"
|
||||
"You have reached the maximum number of apps (1000) on cloud. Check your usage in Workspace Settings > General > Cloud Quotas. Contact support@windmill.dev to increase the limit"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ use tokio::sync::RwLock;
|
||||
|
||||
use crate::db::{ApiAuthed, DB};
|
||||
use windmill_common::min_version::MIN_KEEP_ALIVE_VERSION;
|
||||
use windmill_common::otel_oss::{
|
||||
otel_set_health_db_latency, otel_set_health_db_unresponsive, otel_set_health_status_phase,
|
||||
};
|
||||
use windmill_common::utils::GIT_VERSION;
|
||||
use windmill_common::IS_READY;
|
||||
|
||||
@@ -448,6 +451,9 @@ async fn perform_health_check(db: &DB) -> HealthCheckResult {
|
||||
let checked_at = Utc::now();
|
||||
let db_check = check_database_with_latency(db).await;
|
||||
|
||||
otel_set_health_db_latency(db_check.latency_ms as f64);
|
||||
otel_set_health_db_unresponsive(!db_check.healthy);
|
||||
|
||||
let workers_alive = if db_check.healthy {
|
||||
check_worker_count(db).await
|
||||
} else {
|
||||
@@ -462,6 +468,12 @@ async fn perform_health_check(db: &DB) -> HealthCheckResult {
|
||||
HealthStatus::Healthy
|
||||
};
|
||||
|
||||
otel_set_health_status_phase(match status {
|
||||
HealthStatus::Healthy => "healthy",
|
||||
HealthStatus::Degraded => "degraded",
|
||||
HealthStatus::Unhealthy => "unhealthy",
|
||||
});
|
||||
|
||||
let response = HealthStatusResponse {
|
||||
status,
|
||||
checked_at,
|
||||
|
||||
@@ -64,7 +64,6 @@ use crate::scim_oss::has_scim_token;
|
||||
use windmill_common::error::AppError;
|
||||
|
||||
mod ai;
|
||||
mod google;
|
||||
mod apps;
|
||||
pub mod args;
|
||||
mod audit;
|
||||
@@ -77,6 +76,7 @@ mod bedrock;
|
||||
mod capture;
|
||||
mod concurrency_groups;
|
||||
mod db;
|
||||
mod google;
|
||||
|
||||
mod drafts;
|
||||
#[cfg(feature = "private")]
|
||||
@@ -376,7 +376,7 @@ pub async fn run_server(
|
||||
));
|
||||
|
||||
let cors = CorsLayer::new()
|
||||
.allow_methods([http::Method::GET, http::Method::POST])
|
||||
.allow_methods([http::Method::GET, http::Method::POST, http::Method::DELETE])
|
||||
.allow_headers([http::header::CONTENT_TYPE, http::header::AUTHORIZATION])
|
||||
.allow_origin(Any);
|
||||
|
||||
@@ -461,24 +461,39 @@ pub async fn run_server(
|
||||
|
||||
// Setup MCP server
|
||||
#[allow(unused_variables)]
|
||||
let (mcp_router, mcp_cancellation_token) = {
|
||||
let (mcp_router, gateway_mcp_router, mcp_cancellation_token) = {
|
||||
#[cfg(feature = "mcp")]
|
||||
if server_mode || mcp_mode {
|
||||
use mcp::add_www_authenticate_header;
|
||||
use mcp::{
|
||||
add_www_authenticate_header, add_www_authenticate_header_gateway,
|
||||
extract_workspace_from_token,
|
||||
};
|
||||
let (mcp_router, mcp_cancellation_token) =
|
||||
setup_mcp_server(db.clone(), user_db, _base_internal_url.clone()).await?;
|
||||
// Apply middleware: auth check inside WWW-Authenticate wrapper so 401s get the header
|
||||
let mcp_router = mcp_router
|
||||
// Workspace-scoped MCP router
|
||||
let workspaced_mcp_router = mcp_router
|
||||
.clone()
|
||||
.route_layer(from_extractor::<ApiAuthed>())
|
||||
.layer(axum::middleware::from_fn(add_www_authenticate_header))
|
||||
.layer(axum::middleware::from_fn(extract_and_store_workspace_id));
|
||||
(mcp_router, Some(mcp_cancellation_token))
|
||||
// Gateway MCP router — resolves workspace from token
|
||||
let gateway_mcp_router = mcp_router
|
||||
.route_layer(from_extractor::<ApiAuthed>())
|
||||
.layer(axum::middleware::from_fn(
|
||||
add_www_authenticate_header_gateway,
|
||||
))
|
||||
.layer(axum::middleware::from_fn(extract_workspace_from_token));
|
||||
(
|
||||
workspaced_mcp_router,
|
||||
gateway_mcp_router,
|
||||
Some(mcp_cancellation_token),
|
||||
)
|
||||
} else {
|
||||
(Router::new(), None)
|
||||
(Router::new(), Router::new(), None)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "mcp"))]
|
||||
(Router::new(), Option::<()>::None)
|
||||
(Router::new(), Router::new(), Option::<()>::None)
|
||||
};
|
||||
|
||||
let mcp_list_tools_service = {
|
||||
@@ -623,6 +638,19 @@ pub async fn run_server(
|
||||
.nest("/indexer", indexer_oss::management_service())
|
||||
.nest("/mcp/w/:workspace_id/list_tools", mcp_list_tools_service)
|
||||
.nest("/health/detailed", health::detailed_service())
|
||||
.nest(
|
||||
"/saml",
|
||||
saml_oss::authed_service().layer(Extension(Arc::clone(&sp_extension))),
|
||||
)
|
||||
.nest("/mcp/gateway/oauth/server", {
|
||||
#[cfg(feature = "mcp")]
|
||||
{
|
||||
mcp::oauth_server::gateway_authed_service()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "mcp"))]
|
||||
Router::new()
|
||||
})
|
||||
.route_layer(from_extractor::<ApiAuthed>())
|
||||
.route_layer(from_extractor::<users::Tokened>())
|
||||
// Workspace-scoped OAuth endpoints that don't require authentication
|
||||
@@ -636,6 +664,16 @@ pub async fn run_server(
|
||||
#[cfg(not(feature = "mcp"))]
|
||||
Router::new()
|
||||
})
|
||||
// Gateway OAuth endpoints (authorize + token) — no auth required
|
||||
.nest("/mcp/gateway/oauth/server", {
|
||||
#[cfg(feature = "mcp")]
|
||||
{
|
||||
mcp::oauth_server::gateway_unauthed_service().layer(cors.clone())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "mcp"))]
|
||||
Router::new()
|
||||
})
|
||||
.nest("/jobs", jobs::global_root_service())
|
||||
.nest(
|
||||
"/srch/w/:workspace_id/index",
|
||||
@@ -675,8 +713,15 @@ pub async fn run_server(
|
||||
)
|
||||
.layer(from_extractor::<OptAuthed>())
|
||||
// Deprecated, here for backwards compatibility: user should use /mcp/w/:workspace_id/mcp instead
|
||||
.nest("/mcp/w/:workspace_id/sse", mcp_router.clone())
|
||||
.nest("/mcp/w/:workspace_id/mcp", mcp_router)
|
||||
.nest(
|
||||
"/mcp/w/:workspace_id/sse",
|
||||
mcp_router.clone().layer(cors.clone()),
|
||||
)
|
||||
.nest(
|
||||
"/mcp/w/:workspace_id/mcp",
|
||||
mcp_router.clone().layer(cors.clone()),
|
||||
)
|
||||
.nest("/mcp/gateway", gateway_mcp_router.layer(cors.clone()))
|
||||
.nest("/agent_workers", {
|
||||
#[cfg(feature = "agent_worker_server")]
|
||||
{
|
||||
@@ -863,6 +908,30 @@ pub async fn run_server(
|
||||
}
|
||||
},
|
||||
)
|
||||
// Gateway OAuth well-known endpoints
|
||||
.route(
|
||||
"/.well-known/oauth-authorization-server/api/mcp/gateway/oauth/server",
|
||||
{
|
||||
#[cfg(feature = "mcp")]
|
||||
{
|
||||
get(mcp::oauth_server::gateway_oauth_metadata)
|
||||
}
|
||||
#[cfg(not(feature = "mcp"))]
|
||||
{
|
||||
get(|| async { axum::http::StatusCode::NOT_FOUND })
|
||||
}
|
||||
},
|
||||
)
|
||||
.route("/.well-known/oauth-protected-resource/api/mcp/gateway", {
|
||||
#[cfg(feature = "mcp")]
|
||||
{
|
||||
get(mcp::oauth_server::gateway_protected_resource_metadata)
|
||||
}
|
||||
#[cfg(not(feature = "mcp"))]
|
||||
{
|
||||
get(|| async { axum::http::StatusCode::NOT_FOUND })
|
||||
}
|
||||
})
|
||||
// JWKS endpoint for HashiCorp Vault JWT authentication (must be outside /api prefix)
|
||||
.route(
|
||||
"/.well-known/jwks.json",
|
||||
|
||||
@@ -35,9 +35,14 @@ use windmill_mcp::server::{
|
||||
use windmill_mcp::WorkspaceId;
|
||||
|
||||
use axum::{
|
||||
extract::Path, http::Request, middleware::Next, response::Response, routing::get, Json, Router,
|
||||
extract::{Extension, Path},
|
||||
http::Request,
|
||||
middleware::Next,
|
||||
response::Response,
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use windmill_common::error::JsonResult;
|
||||
use windmill_common::{auth::hash_token, db::GatewayWorkspaceId, error::JsonResult};
|
||||
|
||||
// McpAuth impl for ApiAuthed is in windmill-api-auth (same crate as the type)
|
||||
|
||||
@@ -67,9 +72,16 @@ impl McpBackend for WindmillBackend {
|
||||
path_prefix: Option<&str>,
|
||||
) -> BackendResult<Vec<ScriptInfo>> {
|
||||
let scope_type = if favorites_only { "favorites" } else { "all" };
|
||||
get_items::<ScriptInfo>(&self.user_db, auth, workspace_id, scope_type, "script", path_prefix)
|
||||
.await
|
||||
.map_err(|e| ErrorData::internal_error(e.message, None))
|
||||
get_items::<ScriptInfo>(
|
||||
&self.user_db,
|
||||
auth,
|
||||
workspace_id,
|
||||
scope_type,
|
||||
"script",
|
||||
path_prefix,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ErrorData::internal_error(e.message, None))
|
||||
}
|
||||
|
||||
async fn list_flows(
|
||||
@@ -80,9 +92,16 @@ impl McpBackend for WindmillBackend {
|
||||
path_prefix: Option<&str>,
|
||||
) -> BackendResult<Vec<FlowInfo>> {
|
||||
let scope_type = if favorites_only { "favorites" } else { "all" };
|
||||
get_items::<FlowInfo>(&self.user_db, auth, workspace_id, scope_type, "flow", path_prefix)
|
||||
.await
|
||||
.map_err(|e| ErrorData::internal_error(e.message, None))
|
||||
get_items::<FlowInfo>(
|
||||
&self.user_db,
|
||||
auth,
|
||||
workspace_id,
|
||||
scope_type,
|
||||
"flow",
|
||||
path_prefix,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ErrorData::internal_error(e.message, None))
|
||||
}
|
||||
|
||||
async fn list_resource_types(
|
||||
@@ -439,6 +458,72 @@ pub async fn add_www_authenticate_header(
|
||||
}
|
||||
}
|
||||
|
||||
/// Middleware for gateway: extract workspace_id from the Bearer token in the DB
|
||||
/// and inject it as WorkspaceId extension so the MCP runner can use it.
|
||||
pub async fn extract_workspace_from_token(
|
||||
Extension(db): Extension<DB>,
|
||||
mut request: Request<axum::body::Body>,
|
||||
next: Next,
|
||||
) -> Response {
|
||||
if let Some(auth_header) = request
|
||||
.headers()
|
||||
.get(axum::http::header::AUTHORIZATION)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
{
|
||||
if let Some(token) = auth_header.strip_prefix("Bearer ") {
|
||||
let t_hash = hash_token(token);
|
||||
match sqlx::query_scalar!(
|
||||
"SELECT workspace_id FROM token WHERE token_hash = $1 AND workspace_id IS NOT NULL AND (expiration > NOW() OR expiration IS NULL)",
|
||||
t_hash
|
||||
)
|
||||
.fetch_optional(&db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(Some(workspace_id))) => {
|
||||
request
|
||||
.extensions_mut()
|
||||
.insert(GatewayWorkspaceId(workspace_id.clone()));
|
||||
request.extensions_mut().insert(WorkspaceId(workspace_id));
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
tracing::error!("Gateway token workspace lookup failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next.run(request).await
|
||||
}
|
||||
|
||||
/// Middleware that adds WWW-Authenticate header for gateway 401 responses
|
||||
pub async fn add_www_authenticate_header_gateway(
|
||||
request: Request<axum::body::Body>,
|
||||
next: Next,
|
||||
) -> Response {
|
||||
use axum::http::StatusCode;
|
||||
use windmill_common::BASE_URL;
|
||||
|
||||
let response = next.run(request).await;
|
||||
|
||||
if response.status() == StatusCode::UNAUTHORIZED {
|
||||
let base_url = BASE_URL.read().await;
|
||||
let resource_url = format!("{}/api/mcp/gateway", base_url);
|
||||
let www_authenticate = format!("Bearer resource=\"{}\"", resource_url);
|
||||
|
||||
let (mut parts, body) = response.into_parts();
|
||||
parts.headers.insert(
|
||||
axum::http::header::WWW_AUTHENTICATE,
|
||||
www_authenticate
|
||||
.parse()
|
||||
.unwrap_or_else(|_| "Bearer".parse().unwrap()),
|
||||
);
|
||||
Response::from_parts(parts, body)
|
||||
} else {
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
/// Setup the MCP server with HTTP transport
|
||||
pub async fn setup_mcp_server(
|
||||
db: DB,
|
||||
|
||||
@@ -10,6 +10,7 @@ mod utils;
|
||||
// Re-export only what's needed externally
|
||||
pub mod oauth_server;
|
||||
pub use core::{
|
||||
add_www_authenticate_header, extract_and_store_workspace_id, list_tools_service,
|
||||
add_www_authenticate_header, add_www_authenticate_header_gateway,
|
||||
extract_and_store_workspace_id, extract_workspace_from_token, list_tools_service,
|
||||
setup_mcp_server,
|
||||
};
|
||||
|
||||
@@ -234,19 +234,11 @@ fn supported_scopes() -> Vec<String> {
|
||||
}
|
||||
|
||||
/// GET /.well-known/oauth-authorization-server/api/w/:workspace_id/mcp/oauth/server
|
||||
pub async fn workspaced_oauth_metadata(
|
||||
Path(workspace_id): Path<String>,
|
||||
) -> Json<AuthorizationMetadata> {
|
||||
let base_url = BASE_URL.read().await;
|
||||
let issuer = format!("{}/api/w/{}/mcp/oauth/server", base_url, workspace_id);
|
||||
|
||||
Json(AuthorizationMetadata {
|
||||
issuer,
|
||||
authorization_endpoint: format!(
|
||||
"{}/api/w/{}/mcp/oauth/server/authorize",
|
||||
base_url, workspace_id
|
||||
),
|
||||
token_endpoint: format!("{}/api/w/{}/mcp/oauth/server/token", base_url, workspace_id),
|
||||
fn build_oauth_metadata(oauth_prefix: &str, base_url: &str) -> AuthorizationMetadata {
|
||||
AuthorizationMetadata {
|
||||
issuer: format!("{}{}", base_url, oauth_prefix),
|
||||
authorization_endpoint: format!("{}{}/authorize", base_url, oauth_prefix),
|
||||
token_endpoint: format!("{}{}/token", base_url, oauth_prefix),
|
||||
registration_endpoint: Some(format!("{}/api/mcp/oauth/server/register", base_url)),
|
||||
scopes_supported: Some(supported_scopes()),
|
||||
response_types_supported: Some(vec!["code".to_string()]),
|
||||
@@ -255,7 +247,28 @@ pub async fn workspaced_oauth_metadata(
|
||||
"refresh_token".to_string(),
|
||||
]),
|
||||
code_challenge_methods_supported: Some(vec!["S256".to_string()]),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn build_protected_resource_metadata(
|
||||
resource_path: &str,
|
||||
oauth_prefix: &str,
|
||||
base_url: &str,
|
||||
) -> ProtectedResourceMetadata {
|
||||
ProtectedResourceMetadata {
|
||||
resource: format!("{}{}", base_url, resource_path),
|
||||
authorization_servers: vec![format!("{}{}", base_url, oauth_prefix)],
|
||||
scopes_supported: Some(supported_scopes()),
|
||||
bearer_methods_supported: Some(vec!["header".to_string()]),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn workspaced_oauth_metadata(
|
||||
Path(workspace_id): Path<String>,
|
||||
) -> Json<AuthorizationMetadata> {
|
||||
let base_url = BASE_URL.read().await;
|
||||
let oauth_prefix = format!("/api/w/{}/mcp/oauth/server", workspace_id);
|
||||
Json(build_oauth_metadata(&oauth_prefix, &base_url))
|
||||
}
|
||||
|
||||
/// GET /.well-known/oauth-protected-resource/api/mcp/w/:workspace_id/mcp
|
||||
@@ -263,14 +276,13 @@ pub async fn protected_resource_metadata_by_path(
|
||||
Path(workspace_id): Path<String>,
|
||||
) -> Json<ProtectedResourceMetadata> {
|
||||
let base_url = BASE_URL.read().await;
|
||||
let resource_url = format!("{}/api/mcp/w/{}/mcp", base_url, workspace_id);
|
||||
let auth_server_url = format!("{}/api/w/{}/mcp/oauth/server", base_url, workspace_id);
|
||||
Json(ProtectedResourceMetadata {
|
||||
resource: resource_url,
|
||||
authorization_servers: vec![auth_server_url],
|
||||
scopes_supported: Some(supported_scopes()),
|
||||
bearer_methods_supported: Some(vec!["header".to_string()]),
|
||||
})
|
||||
let resource_path = format!("/api/mcp/w/{}/mcp", workspace_id);
|
||||
let oauth_prefix = format!("/api/w/{}/mcp/oauth/server", workspace_id);
|
||||
Json(build_protected_resource_metadata(
|
||||
&resource_path,
|
||||
&oauth_prefix,
|
||||
&base_url,
|
||||
))
|
||||
}
|
||||
|
||||
/// POST /api/mcp/oauth/server/register - dynamic client registration
|
||||
@@ -386,7 +398,9 @@ async fn handle_authorization_code_grant(
|
||||
|
||||
let access_token = rd_string(32);
|
||||
let access_token_hash = hash_token(&access_token);
|
||||
let access_token_prefix = access_token.get(..TOKEN_PREFIX_LEN).unwrap_or(&access_token);
|
||||
let access_token_prefix = access_token
|
||||
.get(..TOKEN_PREFIX_LEN)
|
||||
.unwrap_or(&access_token);
|
||||
let plaintext: Option<&str> = if MIN_VERSION_SUPPORTS_TOKEN_HASH.met().await {
|
||||
None
|
||||
} else {
|
||||
@@ -530,7 +544,9 @@ async fn handle_refresh_token_grant(
|
||||
// Generate new tokens
|
||||
let new_access_token = rd_string(32);
|
||||
let new_access_token_hash = hash_token(&new_access_token);
|
||||
let new_access_token_prefix = new_access_token.get(..TOKEN_PREFIX_LEN).unwrap_or(&new_access_token);
|
||||
let new_access_token_prefix = new_access_token
|
||||
.get(..TOKEN_PREFIX_LEN)
|
||||
.unwrap_or(&new_access_token);
|
||||
let new_plaintext: Option<&str> = if MIN_VERSION_SUPPORTS_TOKEN_HASH.met().await {
|
||||
None
|
||||
} else {
|
||||
@@ -595,18 +611,19 @@ async fn handle_refresh_token_grant(
|
||||
}))
|
||||
}
|
||||
|
||||
/// GET /api/w/:workspace_id/mcp/oauth/server/authorize - redirects to consent page
|
||||
pub async fn workspaced_oauth_authorize(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(workspace_id): Path<String>,
|
||||
Query(params): Query<AuthorizeQuery>,
|
||||
) -> impl IntoResponse {
|
||||
/// Shared authorize logic — validates params, looks up client, redirects to consent page.
|
||||
/// When `workspace_id` is Some, passes it to frontend; when None, passes `gateway=true`.
|
||||
async fn oauth_authorize_inner(
|
||||
db: &DB,
|
||||
params: AuthorizeQuery,
|
||||
workspace_id: Option<String>,
|
||||
) -> axum::response::Response {
|
||||
let client = match sqlx::query_as!(
|
||||
OAuthClient,
|
||||
"SELECT client_id, client_name, redirect_uris FROM mcp_oauth_server_client WHERE client_id = $1",
|
||||
params.client_id
|
||||
)
|
||||
.fetch_optional(&db)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
{
|
||||
Ok(Some(client)) => client,
|
||||
@@ -677,12 +694,18 @@ pub async fn workspaced_oauth_authorize(
|
||||
}
|
||||
};
|
||||
|
||||
// Build frontend redirect params — gateway mode vs workspace mode
|
||||
let (mode_key, mode_value) = match &workspace_id {
|
||||
Some(w_id) => ("workspace_id", w_id.as_str()),
|
||||
None => ("gateway", "true"),
|
||||
};
|
||||
|
||||
let base_url = BASE_URL.read().await;
|
||||
let frontend_url = format!(
|
||||
"{}/oauth/mcp_authorize?{}",
|
||||
base_url,
|
||||
serde_urlencoded::to_string(&[
|
||||
("workspace_id", workspace_id.as_str()),
|
||||
(mode_key, mode_value),
|
||||
("client_id", params.client_id.as_str()),
|
||||
("client_name", client.client_name.as_str()),
|
||||
("redirect_uri", params.redirect_uri.as_str()),
|
||||
@@ -698,12 +721,12 @@ pub async fn workspaced_oauth_authorize(
|
||||
Redirect::temporary(&frontend_url).into_response()
|
||||
}
|
||||
|
||||
/// POST /api/w/:workspace_id/mcp/oauth/server/approve - user approval (frontend)
|
||||
pub async fn workspaced_oauth_approve(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(workspace_id): Path<String>,
|
||||
authed: ApiAuthed,
|
||||
Json(form): Json<ApprovalForm>,
|
||||
/// Shared approve logic — validates form, checks membership, stores auth code.
|
||||
async fn oauth_approve_inner(
|
||||
db: &DB,
|
||||
authed: &ApiAuthed,
|
||||
workspace_id: &str,
|
||||
form: ApprovalForm,
|
||||
) -> Result<Json<ApprovalResponse>> {
|
||||
// Verify user is a member of the workspace
|
||||
let is_member = sqlx::query_scalar!(
|
||||
@@ -711,7 +734,7 @@ pub async fn workspaced_oauth_approve(
|
||||
workspace_id,
|
||||
authed.email
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Database error: {}", e)))?
|
||||
.unwrap_or(false);
|
||||
@@ -728,7 +751,7 @@ pub async fn workspaced_oauth_approve(
|
||||
"SELECT client_id, client_name, redirect_uris FROM mcp_oauth_server_client WHERE client_id = $1",
|
||||
form.client_id
|
||||
)
|
||||
.fetch_optional(&db)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Database error: {}", e)))?
|
||||
.ok_or_else(|| Error::BadRequest("Unknown client_id".to_string()))?;
|
||||
@@ -778,7 +801,7 @@ pub async fn workspaced_oauth_approve(
|
||||
&form.code_challenge,
|
||||
&form.code_challenge_method,
|
||||
)
|
||||
.execute(&db)
|
||||
.execute(db)
|
||||
.await
|
||||
.map_err(|e| Error::InternalErr(format!("Failed to store authorization code: {}", e)))?;
|
||||
|
||||
@@ -792,6 +815,25 @@ pub async fn workspaced_oauth_approve(
|
||||
}))
|
||||
}
|
||||
|
||||
/// GET /api/w/:workspace_id/mcp/oauth/server/authorize
|
||||
pub async fn workspaced_oauth_authorize(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(workspace_id): Path<String>,
|
||||
Query(params): Query<AuthorizeQuery>,
|
||||
) -> impl IntoResponse {
|
||||
oauth_authorize_inner(&db, params, Some(workspace_id)).await
|
||||
}
|
||||
|
||||
/// POST /api/w/:workspace_id/mcp/oauth/server/approve
|
||||
pub async fn workspaced_oauth_approve(
|
||||
Extension(db): Extension<DB>,
|
||||
Path(workspace_id): Path<String>,
|
||||
authed: ApiAuthed,
|
||||
Json(form): Json<ApprovalForm>,
|
||||
) -> Result<Json<ApprovalResponse>> {
|
||||
oauth_approve_inner(&db, &authed, &workspace_id, form).await
|
||||
}
|
||||
|
||||
/// PKCE validation (S256 only)
|
||||
fn validate_pkce_s256(verifier: &str, challenge: &str) -> bool {
|
||||
let mut hasher = Sha256::new();
|
||||
@@ -851,6 +893,73 @@ impl IntoResponse for OAuthErrorRedirect {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Gateway (workspace-agnostic) OAuth handlers ──
|
||||
// Thin wrappers that delegate to the shared inner functions above.
|
||||
|
||||
pub async fn gateway_oauth_metadata() -> Json<AuthorizationMetadata> {
|
||||
let base_url = BASE_URL.read().await;
|
||||
Json(build_oauth_metadata(
|
||||
"/api/mcp/gateway/oauth/server",
|
||||
&base_url,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn gateway_protected_resource_metadata() -> Json<ProtectedResourceMetadata> {
|
||||
let base_url = BASE_URL.read().await;
|
||||
Json(build_protected_resource_metadata(
|
||||
"/api/mcp/gateway",
|
||||
"/api/mcp/gateway/oauth/server",
|
||||
&base_url,
|
||||
))
|
||||
}
|
||||
|
||||
/// GET /api/mcp/gateway/oauth/server/authorize — gateway mode (no workspace in path)
|
||||
pub async fn gateway_oauth_authorize(
|
||||
Extension(db): Extension<DB>,
|
||||
Query(params): Query<AuthorizeQuery>,
|
||||
) -> impl IntoResponse {
|
||||
oauth_authorize_inner(&db, params, None).await
|
||||
}
|
||||
|
||||
/// POST /api/mcp/gateway/oauth/server/approve — workspace_id comes from JSON body
|
||||
pub async fn gateway_oauth_approve(
|
||||
Extension(db): Extension<DB>,
|
||||
authed: ApiAuthed,
|
||||
Json(form): Json<GatewayApprovalForm>,
|
||||
) -> Result<Json<ApprovalResponse>> {
|
||||
if form.workspace_id.is_empty() {
|
||||
return Err(Error::BadRequest("workspace_id is required".to_string()));
|
||||
}
|
||||
oauth_approve_inner(
|
||||
&db,
|
||||
&authed,
|
||||
&form.workspace_id,
|
||||
ApprovalForm {
|
||||
client_id: form.client_id,
|
||||
redirect_uri: form.redirect_uri,
|
||||
scope: form.scope,
|
||||
state: form.state,
|
||||
code_challenge: form.code_challenge,
|
||||
code_challenge_method: form.code_challenge_method,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gateway approval form — same as ApprovalForm but includes workspace_id
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct GatewayApprovalForm {
|
||||
pub client_id: String,
|
||||
pub redirect_uri: String,
|
||||
pub scope: String,
|
||||
pub state: String,
|
||||
pub code_challenge: String,
|
||||
pub code_challenge_method: String,
|
||||
pub workspace_id: String,
|
||||
}
|
||||
|
||||
// ── Router constructors ──
|
||||
|
||||
/// Mounted at /api/mcp/oauth/server
|
||||
pub fn global_service() -> Router {
|
||||
Router::new().route("/register", post(oauth_register))
|
||||
@@ -869,3 +978,17 @@ pub fn workspaced_unauthed_service() -> Router {
|
||||
pub fn workspaced_authed_service() -> Router {
|
||||
Router::new().route("/approve", post(workspaced_oauth_approve))
|
||||
}
|
||||
|
||||
/// Gateway OAuth endpoints that don't require authentication
|
||||
/// Mounted at /api/mcp/gateway/oauth/server (outside authenticated section)
|
||||
pub fn gateway_unauthed_service() -> Router {
|
||||
Router::new()
|
||||
.route("/authorize", get(gateway_oauth_authorize))
|
||||
.route("/token", post(oauth_token))
|
||||
}
|
||||
|
||||
/// Gateway OAuth endpoints that require authentication
|
||||
/// Mounted at /api/mcp/gateway/oauth/server (inside authenticated section)
|
||||
pub fn gateway_authed_service() -> Router {
|
||||
Router::new().route("/approve", post(gateway_oauth_approve))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user